From b3bd6ddda1836b898743b20d43618274e318f845 Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Sat, 1 Feb 2025 00:13:50 +0300 Subject: [PATCH 01/47] Remove unnecessary using --- src/Contracts/DeepseekClientContract.php | 2 -- src/DeepseekClient.php | 1 - 2 files changed, 3 deletions(-) diff --git a/src/Contracts/DeepseekClientContract.php b/src/Contracts/DeepseekClientContract.php index 6df9032..1590e45 100644 --- a/src/Contracts/DeepseekClientContract.php +++ b/src/Contracts/DeepseekClientContract.php @@ -2,8 +2,6 @@ namespace DeepSeekPhp\Contracts; -use DeepSeekPhp\DeepseekClient; - interface DeepseekClientContract { public static function build(string $apiKey): self; diff --git a/src/DeepseekClient.php b/src/DeepseekClient.php index e7e66b3..a85d408 100644 --- a/src/DeepseekClient.php +++ b/src/DeepseekClient.php @@ -9,7 +9,6 @@ use DeepSeekPhp\Factories\ApiFactory; use DeepSeekPhp\Enums\Queries\QueryRoles; use DeepSeekPhp\Enums\Requests\QueryFlags; -use DeepSeekPhp\Enums\Requests\HeaderFlags; use DeepSeekPhp\Enums\Configs\TemperatureValues; use DeepSeekPhp\Traits\Resources\{HasChat, HasCoder}; From 21ab962c30eb0b56ddaf65e79c04d5b8af30fbfb Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Sat, 1 Feb 2025 01:33:22 +0300 Subject: [PATCH 02/47] clean up --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a8311b9..a1bdab9 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ $response = DeepSeekClient::build('your-api-key') ->query('Explain quantum computing in simple terms') ->run(); -echo $response; // "Quantum computing uses qubits to..." +echo $response; ``` 📌 Defaults used: From e4fbe56bf96dfbe7d69ffd3b34f0a13d9f6c01f4 Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Sat, 1 Feb 2025 03:18:46 +0300 Subject: [PATCH 03/47] feat / test cases --- composer.json | 3 ++- tests/DeepseekTest.php | 9 --------- tests/Helpers/TestCase.php | 23 +++++++++++++++++++++++ tests/Integration/ApiIntegrationTest.php | 21 +++++++++++++++++++++ tests/Pest.php | 5 +++++ tests/Unit/DeepSeekClientTest.php | 24 ++++++++++++++++++++++++ 6 files changed, 75 insertions(+), 10 deletions(-) delete mode 100644 tests/DeepseekTest.php create mode 100644 tests/Helpers/TestCase.php create mode 100644 tests/Integration/ApiIntegrationTest.php create mode 100644 tests/Pest.php create mode 100644 tests/Unit/DeepSeekClientTest.php diff --git a/composer.json b/composer.json index 7e39749..864db28 100644 --- a/composer.json +++ b/composer.json @@ -61,7 +61,7 @@ "laravel/pint": "^1.18.1", "mockery/mockery": "^1.6.12", "nunomaduro/collision": "^7.11.0|^8.5.0", - "pestphp/pest": "^2.36.0|^3.5.0", + "pestphp/pest": "^3.7", "pestphp/pest-plugin-arch": "^2.7|^3.0", "pestphp/pest-plugin-type-coverage": "^2.8.7|^3.1.0", "phpstan/phpstan": "^1.12.7", @@ -96,6 +96,7 @@ "config": { "sort-packages": true, "preferred-install": "dist", + "optimize-autoloader": true, "allow-plugins": { "pestphp/pest-plugin": true, "php-http/discovery": true diff --git a/tests/DeepseekTest.php b/tests/DeepseekTest.php deleted file mode 100644 index a8a7141..0000000 --- a/tests/DeepseekTest.php +++ /dev/null @@ -1,9 +0,0 @@ - $handlerStack]); + } + + protected function createDeepSeekClient($apiKey, $httpClient = null) + { + return new DeepSeekClient($apiKey, $httpClient); + } +} diff --git a/tests/Integration/ApiIntegrationTest.php b/tests/Integration/ApiIntegrationTest.php new file mode 100644 index 0000000..02f980f --- /dev/null +++ b/tests/Integration/ApiIntegrationTest.php @@ -0,0 +1,21 @@ +client = new DeepSeekClient(getenv('DEEPSEEK_API_KEY')); +}); + +it('fetches data from the API', function () { + $response = $this->client->get('/some-endpoint'); + expect($response)->toBeArray(); + expect($response)->toHaveKey('data'); +}); diff --git a/tests/Pest.php b/tests/Pest.php new file mode 100644 index 0000000..a04204d --- /dev/null +++ b/tests/Pest.php @@ -0,0 +1,5 @@ +in('Unit', 'Integration'); diff --git a/tests/Unit/DeepSeekClientTest.php b/tests/Unit/DeepSeekClientTest.php new file mode 100644 index 0000000..8f2476c --- /dev/null +++ b/tests/Unit/DeepSeekClientTest.php @@ -0,0 +1,24 @@ +mockHandler = new MockHandler(); + $this->httpClient = new HttpClient(['handler' => HandlerStack::create($this->mockHandler)]); + $this->client = new DeepSeekClient('test-api-key', $this->httpClient); +}); + +it('can set API key', function () { + expect($this->client->getApiKey())->toBe('test-api-key'); +}); + +it('makes a GET request successfully', function () { + $this->mockHandler->append(new \GuzzleHttp\Psr7\Response(200, [], '{"data": "success"}')); + + $response = $this->client->get('/endpoint'); + expect($response)->toBeArray(); + expect($response['data'])->toBe('success'); +}); From a83d60607e2057d37cde01d6c2e458950dec57ea Mon Sep 17 00:00:00 2001 From: moassaad Date: Sat, 1 Feb 2025 20:11:52 +0200 Subject: [PATCH 04/47] update: add set result function. --- src/DeepseekClient.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/DeepseekClient.php b/src/DeepseekClient.php index bd5e1f5..cb31090 100644 --- a/src/DeepseekClient.php +++ b/src/DeepseekClient.php @@ -76,7 +76,7 @@ public function run(): string ]; // Clear queries after sending $this->queries = []; - $this->result = (new Resource($this->httpClient))->sendRequest($requestData); + $this->setResult((new Resource($this->httpClient))->sendRequest($requestData)); return $this->getResult()->getContent(); } @@ -150,6 +150,17 @@ protected function buildQuery(string $content, ?string $role = null): array ]; } + /** + * set result model + * @param \DeepseekPhp\Contracts\Models\ResultContract $result + * @return self The current instance for method chaining. + */ + public function setResult(ResultContract $result) + { + $this->result = $result; + return $this; + } + /** * response result model * @return \DeepseekPhp\Contracts\Models\ResultContract From 914a0e093e135ad793d82a661c57adc9a3a8ad24 Mon Sep 17 00:00:00 2001 From: moassaad Date: Sat, 1 Feb 2025 20:12:40 +0200 Subject: [PATCH 05/47] update: fix handel response result. --- src/Traits/Resources/HasChat.php | 3 ++- src/Traits/Resources/HasCoder.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Traits/Resources/HasChat.php b/src/Traits/Resources/HasChat.php index 29ae330..1ba8dd7 100644 --- a/src/Traits/Resources/HasChat.php +++ b/src/Traits/Resources/HasChat.php @@ -19,6 +19,7 @@ public function chat(): string 'stream' => $this->stream, ]; $this->queries = []; - return (new Chat($this->httpClient))->sendRequest($requestData); + $this->setResult((new Chat($this->httpClient))->sendRequest($requestData)); + return $this->getResult()->getContent(); } } diff --git a/src/Traits/Resources/HasCoder.php b/src/Traits/Resources/HasCoder.php index 600b92b..041677e 100644 --- a/src/Traits/Resources/HasCoder.php +++ b/src/Traits/Resources/HasCoder.php @@ -19,6 +19,7 @@ public function code(): string 'stream' => $this->stream, ]; $this->queries = []; - return (new Coder($this->httpClient))->sendRequest($requestData); + $this->setResult((new Coder($this->httpClient))->sendRequest($requestData)); + return $this->getResult()->getContent(); } } From 00745a8d7c69d5018cd4c92e986060dccc77a6f2 Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Sun, 2 Feb 2025 00:53:15 +0300 Subject: [PATCH 06/47] fix xml file --- README.md | 3 +++ composer.json | 4 ++-- tests/Feature/ExampleTest.php | 5 +++++ tests/TestCase.php | 10 ++++++++++ tests/Unit/DeepSeekClientTest.php | 23 +++-------------------- tests/Unit/ExampleTest.php | 5 +++++ 6 files changed, 28 insertions(+), 22 deletions(-) create mode 100644 tests/Feature/ExampleTest.php create mode 100644 tests/TestCase.php create mode 100644 tests/Unit/ExampleTest.php diff --git a/README.md b/README.md index a1bdab9..b2201ec 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,9 @@ Detailed release notes available in [CHANGELOG.md](CHANGELOG.md) ```bash composer test + +./vendor/bin/pest + ``` Test coverage coming in v2.1. diff --git a/composer.json b/composer.json index 864db28..fd9edea 100644 --- a/composer.json +++ b/composer.json @@ -61,7 +61,7 @@ "laravel/pint": "^1.18.1", "mockery/mockery": "^1.6.12", "nunomaduro/collision": "^7.11.0|^8.5.0", - "pestphp/pest": "^3.7", + "pestphp/pest": "^2.36", "pestphp/pest-plugin-arch": "^2.7|^3.0", "pestphp/pest-plugin-type-coverage": "^2.8.7|^3.1.0", "phpstan/phpstan": "^1.12.7", @@ -75,7 +75,7 @@ }, "autoload-dev": { "psr-4": { - "DeepSeek\\Tests\\": "tests/" + "Tests\\": "tests/" } }, "minimum-stability": "dev", diff --git a/tests/Feature/ExampleTest.php b/tests/Feature/ExampleTest.php new file mode 100644 index 0000000..61cd84c --- /dev/null +++ b/tests/Feature/ExampleTest.php @@ -0,0 +1,5 @@ +toBeTrue(); +}); diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..cfb05b6 --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,10 @@ +mockHandler = new MockHandler(); - $this->httpClient = new HttpClient(['handler' => HandlerStack::create($this->mockHandler)]); - $this->client = new DeepSeekClient('test-api-key', $this->httpClient); -}); - -it('can set API key', function () { - expect($this->client->getApiKey())->toBe('test-api-key'); -}); - -it('makes a GET request successfully', function () { - $this->mockHandler->append(new \GuzzleHttp\Psr7\Response(200, [], '{"data": "success"}')); - - $response = $this->client->get('/endpoint'); - expect($response)->toBeArray(); - expect($response['data'])->toBe('success'); + expect($result)->toBe(3); }); diff --git a/tests/Unit/ExampleTest.php b/tests/Unit/ExampleTest.php new file mode 100644 index 0000000..61cd84c --- /dev/null +++ b/tests/Unit/ExampleTest.php @@ -0,0 +1,5 @@ +toBeTrue(); +}); From 879ef902a31e21a5830c0481ce579f4eeceae812 Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Sun, 2 Feb 2025 00:56:16 +0300 Subject: [PATCH 07/47] fix xml file --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index b2201ec..b63d2e0 100644 --- a/README.md +++ b/README.md @@ -117,9 +117,8 @@ Detailed release notes available in [CHANGELOG.md](CHANGELOG.md) ```bash composer test - +OR ./vendor/bin/pest - ``` Test coverage coming in v2.1. From 1358ad607257276549ad64e6eb17d78e0ef55015 Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Sun, 2 Feb 2025 01:03:26 +0300 Subject: [PATCH 08/47] update --- tests/Helpers/TestCase.php | 23 ----------------------- tests/Integration/ApiIntegrationTest.php | 21 --------------------- tests/Unit/DeepSeekClientTest.php | 7 ------- tests/Unit/ExampleTest.php | 5 ----- 4 files changed, 56 deletions(-) delete mode 100644 tests/Helpers/TestCase.php delete mode 100644 tests/Integration/ApiIntegrationTest.php delete mode 100644 tests/Unit/DeepSeekClientTest.php delete mode 100644 tests/Unit/ExampleTest.php diff --git a/tests/Helpers/TestCase.php b/tests/Helpers/TestCase.php deleted file mode 100644 index 785915f..0000000 --- a/tests/Helpers/TestCase.php +++ /dev/null @@ -1,23 +0,0 @@ - $handlerStack]); - } - - protected function createDeepSeekClient($apiKey, $httpClient = null) - { - return new DeepSeekClient($apiKey, $httpClient); - } -} diff --git a/tests/Integration/ApiIntegrationTest.php b/tests/Integration/ApiIntegrationTest.php deleted file mode 100644 index 02f980f..0000000 --- a/tests/Integration/ApiIntegrationTest.php +++ /dev/null @@ -1,21 +0,0 @@ -client = new DeepSeekClient(getenv('DEEPSEEK_API_KEY')); -}); - -it('fetches data from the API', function () { - $response = $this->client->get('/some-endpoint'); - expect($response)->toBeArray(); - expect($response)->toHaveKey('data'); -}); diff --git a/tests/Unit/DeepSeekClientTest.php b/tests/Unit/DeepSeekClientTest.php deleted file mode 100644 index 11bae6e..0000000 --- a/tests/Unit/DeepSeekClientTest.php +++ /dev/null @@ -1,7 +0,0 @@ -toBe(3); -}); diff --git a/tests/Unit/ExampleTest.php b/tests/Unit/ExampleTest.php deleted file mode 100644 index 61cd84c..0000000 --- a/tests/Unit/ExampleTest.php +++ /dev/null @@ -1,5 +0,0 @@ -toBeTrue(); -}); From 6e9e5f98f3196bab6626845a139990dfa223533e Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Sun, 2 Feb 2025 01:34:47 +0300 Subject: [PATCH 09/47] clean up --- tests/{ => Feature}/HandelResultDeepseekTest.php | 6 +++--- tests/Pest.php | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) rename tests/{ => Feature}/HandelResultDeepseekTest.php (97%) diff --git a/tests/HandelResultDeepseekTest.php b/tests/Feature/HandelResultDeepseekTest.php similarity index 97% rename from tests/HandelResultDeepseekTest.php rename to tests/Feature/HandelResultDeepseekTest.php index 18a9007..9b2ab44 100644 --- a/tests/HandelResultDeepseekTest.php +++ b/tests/Feature/HandelResultDeepseekTest.php @@ -1,10 +1,10 @@ setTemperature(1.5); $response = $deepseek->run(); $result = $deepseek->getResult(); - + $this->assertNotEmpty($response); $this->assertEquals(HTTPState::UNAUTHORIZED->value, $result->getStatusCode()); } diff --git a/tests/Pest.php b/tests/Pest.php index a04204d..7ea77fb 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -1,5 +1,3 @@ in('Unit', 'Integration'); + // From b748ed4fa376e2852ce66c8b8e87d751f99c24fe Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Fri, 7 Feb 2025 02:53:25 +0300 Subject: [PATCH 10/47] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b63d2e0..05f3298 100644 --- a/README.md +++ b/README.md @@ -175,9 +175,9 @@ A huge thank you to these amazing people who have contributed to this project! - Hisham Abdullah + Hisham Bin Ateya
- Hisham Abdullah + Hisham Bin Ateya

⭐ Contributor From 1029a471de74a9422aae8b62138e48bafe6b3ebd Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Sat, 8 Feb 2025 04:35:04 +0300 Subject: [PATCH 11/47] feat / list available models --- README.md | 13 +++++++++++++ src/DeepSeekClient.php | 21 ++++++++++++++++++++- src/Enums/Requests/EndpointSuffixes.php | 1 + src/Resources/Resource.php | 17 ++++++++++------- 4 files changed, 44 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 05f3298..3cf2f27 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ - [🚀 Quick Start](#-quick-start) - [Basic Usage](#basic-usage) - [Advanced Configuration](#advanced-configuration) + - [Get Models List](#get-models-list) - [Framework Integration](#-framework-integration) - [🆕 Migration Guide](#-migration-guide) - [📝 Changelog](#-changelog) @@ -93,6 +94,18 @@ $response = DeepSeekClient::build('your-api-key') echo 'API Response:'.$response; ``` +### Get Models List + +```php +use DeepSeek\DeepSeekClient; + +$response = DeepSeekClient::build('your-api-key') + ->getModelsList() + ->run(); + +echo $response; // {"object":"list","data":[{"id":"deepseek-chat","object":"model","owned_by":"deepseek"},{"id":"deepseek-reasoner","object":"model","owned_by":"deepseek"}]} +``` + ### 🛠 Framework Integration ### [Laravel Deepseek Package](https://github.com/deepseek-php/deepseek-laravel) diff --git a/src/DeepSeekClient.php b/src/DeepSeekClient.php index 882a8ec..3b3dad2 100644 --- a/src/DeepSeekClient.php +++ b/src/DeepSeekClient.php @@ -4,6 +4,7 @@ use DeepSeek\Contracts\DeepseekClientContract; use DeepSeek\Contracts\Models\ResultContract; +use DeepSeek\Enums\Requests\EndpointSuffixes; use DeepSeek\Resources\Resource; use Psr\Http\Client\ClientInterface; use DeepSeek\Factories\ApiFactory; @@ -53,6 +54,10 @@ class DeepSeekClient implements DeepseekClientContract */ protected ResultContract $result; + protected string $requestMethod; + + protected ?string $endpointSuffixes; + /** * Initialize the DeepSeekClient with a PSR-compliant HTTP client. * @@ -63,6 +68,8 @@ public function __construct(ClientInterface $httpClient) $this->httpClient = $httpClient; $this->model = null; $this->stream = false; + $this->requestMethod = 'POST'; + $this->endpointSuffixes = EndpointSuffixes::CHAT->value; $this->temperature = (float) TemperatureValues::GENERAL_CONVERSATION->value; } @@ -76,7 +83,7 @@ public function run(): string ]; // Clear queries after sending $this->queries = []; - $this->setResult((new Resource($this->httpClient))->sendRequest($requestData)); + $this->setResult((new Resource($this->httpClient, $this->endpointSuffixes))->sendRequest($requestData, $this->requestMethod)); return $this->getResult()->getContent(); } @@ -112,6 +119,18 @@ public function query(string $content, ?string $role = null): self return $this; } + /** + * get list of available models .. + * + * @return self The current instance for method chaining. + */ + public function getModelsList(): self + { + $this->endpointSuffixes = EndpointSuffixes::MODELS_LIST->value; + $this->requestMethod = 'GET'; + return $this; + } + /** * Set the model to be used for API requests. * diff --git a/src/Enums/Requests/EndpointSuffixes.php b/src/Enums/Requests/EndpointSuffixes.php index 43dc3dd..f903d43 100644 --- a/src/Enums/Requests/EndpointSuffixes.php +++ b/src/Enums/Requests/EndpointSuffixes.php @@ -5,4 +5,5 @@ enum EndpointSuffixes: string { case CHAT = '/chat/completions'; + case MODELS_LIST = '/models'; } diff --git a/src/Resources/Resource.php b/src/Resources/Resource.php index a3075cf..ea2740a 100644 --- a/src/Resources/Resource.php +++ b/src/Resources/Resource.php @@ -30,14 +30,17 @@ class Resource implements ResourceContract */ protected ClientInterface $client; + protected ?string $endpointSuffixes; + /** * Initialize the Resource with a Guzzle HTTP client. * * @param ClientInterface $client The HTTP client instance for making requests. */ - public function __construct(ClientInterface $client) + public function __construct(ClientInterface $client, ?string $endpointSuffixes = null) { $this->client = $client; + $this->endpointSuffixes = $endpointSuffixes ?: EndpointSuffixes::CHAT->value; } /** @@ -47,13 +50,13 @@ public function __construct(ClientInterface $client) * and custom headers, and returns the response as a result contract. * * @param array $requestData The data to send in the request. + * @param string|null $requestMethod method of request Get or POST. * @return ResultContract The response result. - * */ - public function sendRequest(array $requestData): ResultContract + public function sendRequest(array $requestData, ?string $requestMethod = 'POST'): ResultContract { try { - $response = $this->client->post($this->getEndpointSuffix(), [ + $response = $this->client->{$requestMethod}($this->getEndpointSuffix(), [ 'json' => $this->resolveHeaders($requestData), ]); @@ -62,9 +65,9 @@ public function sendRequest(array $requestData): ResultContract $response = $badResponse->getResponse(); return (new BadResult())->setResponse($response); - + } catch (GuzzleException $error) { - + return new FailureResult($error->getCode(), $error->getMessage()); } catch (\Exception $error) { @@ -119,7 +122,7 @@ public function prepareCustomHeaderParams(array $query): array */ public function getEndpointSuffix(): string { - return EndpointSuffixes::CHAT->value; + return $this->endpointSuffixes; } /** From 3c145cdcc684681ac189c5c741e7a3182d84978b Mon Sep 17 00:00:00 2001 From: Omar Alalwi Date: Sat, 8 Feb 2025 08:06:53 +0300 Subject: [PATCH 12/47] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3cf2f27..c5823ba 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ use DeepSeek\Enums\Models; $response = DeepSeekClient::build('your-api-key') ->withBaseUrl('https://api.deepseek.com/v2') - ->withModel(Models::CODER) + ->withModel(Models::CODER->value) ->withTemperature(1.2) ->run(); From 321cfefe2978cc96439f3656deb2cabe02b62467 Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Sat, 8 Feb 2025 09:14:37 +0300 Subject: [PATCH 13/47] Replace PHP unit test with PEST test --- tests/Feature/DeepSeekClient.php | 52 +++++++++++++++++++++ tests/Feature/HandelResultDeepseekTest.php | 54 ---------------------- 2 files changed, 52 insertions(+), 54 deletions(-) create mode 100644 tests/Feature/DeepSeekClient.php delete mode 100644 tests/Feature/HandelResultDeepseekTest.php diff --git a/tests/Feature/DeepSeekClient.php b/tests/Feature/DeepSeekClient.php new file mode 100644 index 0000000..a0866e0 --- /dev/null +++ b/tests/Feature/DeepSeekClient.php @@ -0,0 +1,52 @@ +query('Hello DeepSeek, how are you today?') + ->setTemperature(1.5); + + // Act + $response = $client->run(); + $result = $client->getResult(); + + // Assert + expect($response)->not->toBeEmpty($response) + ->and($result->getStatusCode())->toEqual(HTTPState::OK->value); +}); + +test('Run query with valid API Key & insufficient balance should return 402', function () { + // Arrange + $apiKey = "insufficient-balance-api-key"; + $client = DeepSeekClient::build($apiKey) + ->query('Hello DeepSeek, how are you today?') + ->setTemperature(1.5); + + // Act + $response = $client->run(); + $result = $client->getResult(); + + // Assert + expect($response)->not->toBeEmpty($response) + ->and($result->getStatusCode())->toEqual(HTTPState::PAYMENT_REQUIRED->value); +}); + +test('Run query with invalid API key should return 401', function () { + // Arrange + $apiKey = "insufficient-balance-api-key"; + $client = DeepSeekClient::build($apiKey) + ->query('Hello DeepSeek, how are you today?') + ->setTemperature(1.5); + + // Act + $response = $client->run(); + $result = $client->getResult(); + + // Assert + expect($response)->not->toBeEmpty($response) + ->and($result->getStatusCode())->toEqual(HTTPState::UNAUTHORIZED->value); +}); diff --git a/tests/Feature/HandelResultDeepseekTest.php b/tests/Feature/HandelResultDeepseekTest.php deleted file mode 100644 index 9b2ab44..0000000 --- a/tests/Feature/HandelResultDeepseekTest.php +++ /dev/null @@ -1,54 +0,0 @@ -apiKey = "valid-api-key"; - $this->expiredApiKey = "expired-api-key"; - } - public function test_ok_response() - { - $deepseek = DeepSeekClient::build($this->apiKey) - ->query('Hello Deepseek, how are you today?') - ->setTemperature(1.5); - $response = $deepseek->run(); - $result = $deepseek->getResult(); - - $this->assertNotEmpty($response); - $this->assertEquals(HTTPState::OK->value, $result->getStatusCode()); - } - public function test_can_not_access_with_api_expired_payment() - { - $deepseek = DeepSeekClient::build($this->expiredApiKey) - ->query('Hello Deepseek, how are you today?') - ->setTemperature(1.5); - $response = $deepseek->run(); - $result = $deepseek->getResult(); - - $this->assertNotEmpty($response); - if(!$result->isSuccess()) - { - $this->assertEquals(HTTPState::PAYMENT_REQUIRED->value, $result->getStatusCode()); - } - } - public function test_access_with_wrong_api_key() - { - $deepseek = DeepSeekClient::build($this->apiKey."wrong-api-key") - ->query('Hello Deepseek, how are you today?') - ->setTemperature(1.5); - $response = $deepseek->run(); - $result = $deepseek->getResult(); - - $this->assertNotEmpty($response); - $this->assertEquals(HTTPState::UNAUTHORIZED->value, $result->getStatusCode()); - } -} From 10eb95e791804215702b641d6aa13ca4060b49ed Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Sun, 9 Feb 2025 03:28:00 +0300 Subject: [PATCH 14/47] enhance client contract --- ...{DeepseekClientContract.php => ClientContract.php} | 6 ++++-- src/DeepSeekClient.php | 11 +++++------ 2 files changed, 9 insertions(+), 8 deletions(-) rename src/Contracts/{DeepseekClientContract.php => ClientContract.php} (52%) diff --git a/src/Contracts/DeepseekClientContract.php b/src/Contracts/ClientContract.php similarity index 52% rename from src/Contracts/DeepseekClientContract.php rename to src/Contracts/ClientContract.php index 7ea06b9..12172e3 100644 --- a/src/Contracts/DeepseekClientContract.php +++ b/src/Contracts/ClientContract.php @@ -2,11 +2,13 @@ namespace DeepSeek\Contracts; -interface DeepseekClientContract +interface ClientContract { - public static function build(string $apiKey): self; public function run(): string; + public static function build(string $apiKey, ?string $baseUrl = null, ?int $timeout = null): self; public function query(string $content, ?string $role = "user"): self; + public function getModelsList(): self; public function withModel(?string $model = null): self; public function withStream(bool $stream = true): self; + public function buildQuery(string $content, ?string $role = null): array; } diff --git a/src/DeepSeekClient.php b/src/DeepSeekClient.php index 3b3dad2..f78d8e5 100644 --- a/src/DeepSeekClient.php +++ b/src/DeepSeekClient.php @@ -2,7 +2,7 @@ namespace DeepSeek; -use DeepSeek\Contracts\DeepseekClientContract; +use DeepSeek\Contracts\ClientContract; use DeepSeek\Contracts\Models\ResultContract; use DeepSeek\Enums\Requests\EndpointSuffixes; use DeepSeek\Resources\Resource; @@ -10,11 +10,10 @@ use DeepSeek\Factories\ApiFactory; use DeepSeek\Enums\Queries\QueryRoles; use DeepSeek\Enums\Requests\QueryFlags; -use DeepSeek\Enums\Requests\HeaderFlags; use DeepSeek\Enums\Configs\TemperatureValues; use DeepSeek\Traits\Resources\{HasChat, HasCoder}; -class DeepSeekClient implements DeepseekClientContract +class DeepSeekClient implements ClientContract { use HasChat, HasCoder; @@ -113,14 +112,14 @@ public static function build(string $apiKey, ?string $baseUrl = null, ?int $time * @param string|null $role * @return self The current instance for method chaining. */ - public function query(string $content, ?string $role = null): self + public function query(string $content, ?string $role = "user"): self { $this->queries[] = $this->buildQuery($content, $role); return $this; } /** - * get list of available models .. + * get list of available models . * * @return self The current instance for method chaining. */ @@ -161,7 +160,7 @@ public function setTemperature(float $temperature): self return $this; } - protected function buildQuery(string $content, ?string $role = null): array + public function buildQuery(string $content, ?string $role = null): array { return [ 'role' => $role ?: QueryRoles::USER->value, From c3651b906a36b0dab26e644a4ab69a2eb4347623 Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Sun, 9 Feb 2025 03:34:11 +0300 Subject: [PATCH 15/47] update version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index fd9edea..69b83d7 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,7 @@ "role": "creator" } ], - "version": "2.0.0", + "version": "2.0.1", "require": { "php": "^8.1.0", "php-http/discovery": "^1.20.0", From 70d3bce2a9f9947a96a5f4a74cbd79c73968eb9a Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Sun, 9 Feb 2025 03:50:33 +0300 Subject: [PATCH 16/47] initilize tests --- ...pSeekClient.php => DeepSeekClientTest.php} | 0 tests/Pest.php | 44 ++++++++++++++++++- tests/Unit/ExampleTest.php | 5 +++ 3 files changed, 48 insertions(+), 1 deletion(-) rename tests/Feature/{DeepSeekClient.php => DeepSeekClientTest.php} (100%) create mode 100644 tests/Unit/ExampleTest.php diff --git a/tests/Feature/DeepSeekClient.php b/tests/Feature/DeepSeekClientTest.php similarity index 100% rename from tests/Feature/DeepSeekClient.php rename to tests/Feature/DeepSeekClientTest.php diff --git a/tests/Pest.php b/tests/Pest.php index 7ea77fb..5949c61 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -1,3 +1,45 @@ in('Feature'); + +/* +|-------------------------------------------------------------------------- +| Expectations +|-------------------------------------------------------------------------- +| +| When you're writing tests, you often need to check that values meet certain conditions. The +| "expect()" function gives you access to a set of "expectations" methods that you can use +| to assert different things. Of course, you may extend the Expectation API at any time. +| +*/ + +expect()->extend('toBeOne', function () { + return $this->toBe(1); +}); + +/* +|-------------------------------------------------------------------------- +| Functions +|-------------------------------------------------------------------------- +| +| While Pest is very powerful out-of-the-box, you may have some testing code specific to your +| project that you don't want to repeat in every file. Here you can also expose helpers as +| global functions to help you to reduce the number of lines of code in your test files. +| +*/ + +function something() +{ + // .. +} diff --git a/tests/Unit/ExampleTest.php b/tests/Unit/ExampleTest.php new file mode 100644 index 0000000..61cd84c --- /dev/null +++ b/tests/Unit/ExampleTest.php @@ -0,0 +1,5 @@ +toBeTrue(); +}); From 37057a82314e23a9d5891aca5359df35f5ba6ace Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Sun, 9 Feb 2025 03:52:03 +0300 Subject: [PATCH 17/47] update doc --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index c5823ba..55603d8 100644 --- a/README.md +++ b/README.md @@ -129,8 +129,6 @@ Detailed release notes available in [CHANGELOG.md](CHANGELOG.md) ## 🧪 Testing ```bash -composer test -OR ./vendor/bin/pest ``` From 6913fbea50f55059563abc08ead2b4a938d38cfb Mon Sep 17 00:00:00 2001 From: Vinchan Date: Tue, 18 Feb 2025 11:20:09 +0800 Subject: [PATCH 18/47] feat(*):Add Chinese README --- README-CN.md | 205 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 README-CN.md diff --git a/README-CN.md b/README-CN.md new file mode 100644 index 0000000..26dd190 --- /dev/null +++ b/README-CN.md @@ -0,0 +1,205 @@ +

+

DeepSeek PHP Client

+

🚀 社区驱动的 PHP SDK,用于 DeepSeek AI 接口集成

+ +

+ + Latest Version + + + PHP Version + + + License + + + Tests Status + +

+

+ +## 目录 +- [✨ 特性](#-特性) +- [📦 安装](#-安装) +- [🚀 快速入门](#-快速入门) + - [基本用法](#基本用法) + - [高级配置](#高级配置) + - [获取模型列表](#获取模型列表) + - [框架集成](#-框架集成) +- [🆕 迁移指南](#-迁移指南) +- [📝 更新日志](#-更新日志) +- [🧪 测试](#-测试) +- [🔒 安全](#-安全) +- [🤝 贡献者](#-贡献者) +- [📄 许可](#-许可) + +--- + +## ✨ 特性 + +- **无缝 API 集成**: DeepSeek AI 功能的 PHP 优先接口 +- **构建器模式**: 直观的链接请求构建方法 +- **企业级别**: 符合 PSR-18 规范 +- **模型灵活性**: 支持多种 DeepSeek 模型(Coder、Chat 等) +- **流式传输**: 内置对实时响应处理的支持 +- **框架友好**: 提供 Laravel 和 Symfony 包 + +--- + +## 📦 安装 + +通过 Composer 安装: + +```bash +composer require deepseek-php/deepseek-php-client +``` + +**要求**: +- PHP 8.1+ + +--- + +## 🚀 快速入门 + +### 基本用法 + +只需两行代码即可开始: + +```php +use DeepSeek\DeepSeekClient; + +$response = DeepSeekClient::build('your-api-key') + ->query('Explain quantum computing in simple terms') + ->run(); + +echo $response; +``` + +📌 默认配置: +- Model: `deepseek-chat` +- Temperature: 0.8 + +### 高级配置 + +```php +use DeepSeek\DeepSeekClient; +use DeepSeek\Enums\Models; + +$response = DeepSeekClient::build('your-api-key') + ->withBaseUrl('https://api.deepseek.com/v2') + ->withModel(Models::CODER->value) + ->withTemperature(1.2) + ->run(); + +echo 'API Response:'.$response; +``` + +### 获取模型列表 + +```php +use DeepSeek\DeepSeekClient; + +$response = DeepSeekClient::build('your-api-key') + ->getModelsList() + ->run(); + +echo $response; // {"object":"list","data":[{"id":"deepseek-chat","object":"model","owned_by":"deepseek"},{"id":"deepseek-reasoner","object":"model","owned_by":"deepseek"}]} +``` + +### 🛠 框架集成 + +### [Laravel Deepseek Package](https://github.com/deepseek-php/deepseek-laravel) + +### [Symfony Deepseek Package](https://github.com/deepseek-php/deepseek-symfony) + +--- + +## 🚧 迁移指南 + +从 v1.x 升级?请查看我们全面的 [迁移指南](MIGRATION.md) 了解重大变更和升级说明。 + +--- + +## 📝 更新日志 + +详细的发布说明可在 [CHANGELOG.md](CHANGELOG.md) 查看。 + +--- + +## 🧪 测试 + +```bash +./vendor/bin/pest +``` + +测试覆盖范围涵盖 v2.1。 + +--- + +## 🔒 安全 + +**报告漏洞**: [omaralwi2010@gmail.com](mailto:omaralwi2010@gmail.com) + +--- + +## 🤝 贡献者 + +非常感谢为这个项目做出贡献的人! 🎉💖 + + + + + + + + + +
+ + Omar AlAlwi +
+ Omar AlAlwi +
+
+ 🏆 Creator +
+ + Ayman Alhattami +
+ Ayman Alhattami +
+
+ ⭐ Contributor +
+ + Mohammad Asaad +
+ Mohammad Asaad +
+
+ ⭐ Contributor +
+ + Opada Alzaiede +
+ Opada Alzaiede +
+
+ ⭐ Contributor +
+ + Hisham Bin Ateya +
+ Hisham Bin Ateya +
+
+ ⭐ Contributor +
+ +**想要贡献?** 查看 [contributing guidelines](./CONTRIBUTING.md) 并提交拉取请求! 🚀 + +--- + +## 📄 许可 + +基于 [MIT License](LICENSE.md) 开源协议。 From 52a652b78090d43879b73eb4df28d512a67683c4 Mon Sep 17 00:00:00 2001 From: Omar Alalwi Date: Wed, 19 Feb 2025 19:46:38 +0300 Subject: [PATCH 19/47] Update README.md --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index 55603d8..d45142e 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,25 @@ Detailed release notes available in [CHANGELOG.md](CHANGELOG.md) Test coverage coming in v2.1. +--- +
+ +# 🐘✨ **DeepSeek PHP Community** ✨🐘 + +Click the button bellow or [join here](https://t.me/deepseek_php_community) to be part of our growing community! + +[![Join Telegram](https://img.shields.io/badge/Join-Telegram-blue?style=for-the-badge&logo=telegram)](https://t.me/deepseek_php_community) + +
+ +### **Channel Structure** 🏗️ +- 🗨️ **General** - Daily chatter +- 💡 **Ideas & Suggestions** - Shape the community's future +- 📢 **Announcements & News** - Official updates & news +- 🚀 **Releases & Updates** - Version tracking & migration support +- 🐞 **Issues & Bug Reports** - Collective problem-solving +- 🤝 **Pull Requests** - Code collaboration & reviews + --- ## 🔒 Security From dea86c50b3c4bc805588e39499e1014b73cee2b3 Mon Sep 17 00:00:00 2001 From: Omar Alalwi Date: Wed, 19 Feb 2025 19:47:36 +0300 Subject: [PATCH 20/47] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d45142e..c340dc0 100644 --- a/README.md +++ b/README.md @@ -135,7 +135,7 @@ Detailed release notes available in [CHANGELOG.md](CHANGELOG.md) Test coverage coming in v2.1. --- -
+
# 🐘✨ **DeepSeek PHP Community** ✨🐘 @@ -143,7 +143,6 @@ Click the button bellow or [join here](https://t.me/deepseek_php_community) to b [![Join Telegram](https://img.shields.io/badge/Join-Telegram-blue?style=for-the-badge&logo=telegram)](https://t.me/deepseek_php_community) -
### **Channel Structure** 🏗️ - 🗨️ **General** - Daily chatter @@ -153,6 +152,8 @@ Click the button bellow or [join here](https://t.me/deepseek_php_community) to b - 🐞 **Issues & Bug Reports** - Collective problem-solving - 🤝 **Pull Requests** - Code collaboration & reviews +
+ --- ## 🔒 Security From 1599c4d840975a7666ff03419ec7fe34c3e85eac Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Sat, 22 Feb 2025 05:53:07 +0300 Subject: [PATCH 21/47] support many http clients --- README.md | 27 +++++--- composer.json | 10 ++- src/DeepSeekClient.php | 7 +- src/Enums/Requests/ClientTypes.php | 10 +++ src/Factories/ApiFactory.php | 104 ++++++++++++----------------- src/Resources/Resource.php | 65 +++++++++--------- 6 files changed, 116 insertions(+), 107 deletions(-) create mode 100644 src/Enums/Requests/ClientTypes.php diff --git a/README.md b/README.md index c340dc0..79775c3 100644 --- a/README.md +++ b/README.md @@ -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) @@ -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. --- @@ -94,6 +96,17 @@ $response = DeepSeekClient::build('your-api-key') 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 +$response = DeepSeekClient::build('your-api-key', clientType:'symfony') + ->query('Explain quantum computing in simple terms') + ->run(); +``` + ### Get Models List ```php @@ -110,8 +123,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 diff --git a/composer.json b/composer.json index 69b83d7..83f33d9 100644 --- a/composer.json +++ b/composer.json @@ -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", @@ -48,12 +52,14 @@ "version": "2.0.1", "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", diff --git a/src/DeepSeekClient.php b/src/DeepSeekClient.php index f78d8e5..a8d7f01 100644 --- a/src/DeepSeekClient.php +++ b/src/DeepSeekClient.php @@ -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; @@ -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); } diff --git a/src/Enums/Requests/ClientTypes.php b/src/Enums/Requests/ClientTypes.php new file mode 100644 index 0000000..fbc01d9 --- /dev/null +++ b/src/Enums/Requests/ClientTypes.php @@ -0,0 +1,10 @@ +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(); + } + + switch (strtolower($clientType)) { + case ClientTypes::GUZZLE->value: + return new Client($this->clientConfig); + case ClientTypes::SYMFONY->value: + $symfonyClient = HttpClient::create($this->clientConfig); + return new Psr18Client($symfonyClient); + default: + throw new InvalidArgumentException("Unsupported client type: {$clientType}"); + } } } diff --git a/src/Resources/Resource.php b/src/Resources/Resource.php index ea2740a..65ed2fe 100644 --- a/src/Resources/Resource.php +++ b/src/Resources/Resource.php @@ -18,62 +18,57 @@ use GuzzleHttp\Exception\BadResponseException; use GuzzleHttp\Exception\GuzzleException; use Psr\Http\Client\ClientInterface; +use Psr\Http\Message\RequestFactoryInterface; +use Psr\Http\Message\StreamFactoryInterface; +use Nyholm\Psr7\Factory\Psr17Factory; class Resource implements ResourceContract { use HasQueryParams; - /** - * HTTP client for making requests. - * - * @var ClientInterface - */ protected ClientInterface $client; - protected ?string $endpointSuffixes; - - /** - * Initialize the Resource with a Guzzle HTTP client. - * - * @param ClientInterface $client The HTTP client instance for making requests. - */ - public function __construct(ClientInterface $client, ?string $endpointSuffixes = null) - { + protected RequestFactoryInterface $requestFactory; + protected StreamFactoryInterface $streamFactory; + + public function __construct( + ClientInterface $client, + ?string $endpointSuffixes = null, + ?RequestFactoryInterface $requestFactory = null, + ?StreamFactoryInterface $streamFactory = null + ) { $this->client = $client; $this->endpointSuffixes = $endpointSuffixes ?: EndpointSuffixes::CHAT->value; + $this->requestFactory = $requestFactory ?: new Psr17Factory(); + $this->streamFactory = $streamFactory ?: new Psr17Factory(); } - /** - * Send a request to the API endpoint. - * - * This method sends a POST request to the API endpoint, including the query data - * and custom headers, and returns the response as a result contract. - * - * @param array $requestData The data to send in the request. - * @param string|null $requestMethod method of request Get or POST. - * @return ResultContract The response result. - */ public function sendRequest(array $requestData, ?string $requestMethod = 'POST'): ResultContract { try { - $response = $this->client->{$requestMethod}($this->getEndpointSuffix(), [ - 'json' => $this->resolveHeaders($requestData), - ]); + $request = $this->requestFactory->createRequest( + $requestMethod, + $this->getEndpointSuffix() + ); + + if ($requestMethod === 'POST') { + $request = $request + ->withHeader('Content-Type', 'application/json') + ->withBody( + $this->streamFactory->createStream( + json_encode($this->resolveHeaders($requestData)) + )); + } + + $response = $this->client->sendRequest($request); return (new SuccessResult())->setResponse($response); } catch (BadResponseException $badResponse) { - - $response = $badResponse->getResponse(); - return (new BadResult())->setResponse($response); - + return (new BadResult())->setResponse($badResponse->getResponse()); } catch (GuzzleException $error) { - return new FailureResult($error->getCode(), $error->getMessage()); - } catch (\Exception $error) { - return new FailureResult($error->getCode(), '{"error":"'.$error->getMessage().'"}'); - } } From cd1c6c04cf4174167e71d168d3d3733989a13210 Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Sat, 22 Feb 2025 05:57:02 +0300 Subject: [PATCH 22/47] clean up --- src/Contracts/Factories/ApiFactoryContract.php | 3 ++- src/Factories/ApiFactory.php | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Contracts/Factories/ApiFactoryContract.php b/src/Contracts/Factories/ApiFactoryContract.php index 80c9c53..9732d59 100644 --- a/src/Contracts/Factories/ApiFactoryContract.php +++ b/src/Contracts/Factories/ApiFactoryContract.php @@ -4,6 +4,7 @@ use DeepSeek\Factories\ApiFactory; use GuzzleHttp\Client; +use Psr\Http\Client\ClientInterface; interface ApiFactoryContract { @@ -43,5 +44,5 @@ public function setTimeout(?int $timeout = null): ApiFactory; * * @return Client */ - public function run(): Client; + public function run(?string $clientType = null): ClientInterface; } diff --git a/src/Factories/ApiFactory.php b/src/Factories/ApiFactory.php index 3330682..60980ba 100644 --- a/src/Factories/ApiFactory.php +++ b/src/Factories/ApiFactory.php @@ -13,7 +13,7 @@ use RuntimeException; use InvalidArgumentException; -final class ApiFactory +final class ApiFactory implements ApiFactoryContract { protected string $apiKey; protected string $baseUrl; From 7df84a2583dd3965ce68403c2f4596d861f7ab87 Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Sat, 22 Feb 2025 05:57:27 +0300 Subject: [PATCH 23/47] clean up --- src/Contracts/Factories/ApiFactoryContract.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Contracts/Factories/ApiFactoryContract.php b/src/Contracts/Factories/ApiFactoryContract.php index 9732d59..7799e7e 100644 --- a/src/Contracts/Factories/ApiFactoryContract.php +++ b/src/Contracts/Factories/ApiFactoryContract.php @@ -40,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(?string $clientType = null): ClientInterface; } From 67a9e559895e8b678ba94b2df89acb94494f9c10 Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Sat, 22 Feb 2025 06:00:59 +0300 Subject: [PATCH 24/47] clean up --- src/Factories/ApiFactory.php | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/Factories/ApiFactory.php b/src/Factories/ApiFactory.php index 60980ba..0a38466 100644 --- a/src/Factories/ApiFactory.php +++ b/src/Factories/ApiFactory.php @@ -77,14 +77,10 @@ public function run(?string $clientType = null): ClientInterface $this->initialize(); } - switch (strtolower($clientType)) { - case ClientTypes::GUZZLE->value: - return new Client($this->clientConfig); - case ClientTypes::SYMFONY->value: - $symfonyClient = HttpClient::create($this->clientConfig); - return new Psr18Client($symfonyClient); - default: - throw new InvalidArgumentException("Unsupported client type: {$clientType}"); - } + 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}") + }; } } From 930665ec819def24ef442a3ae0f083fa2f5f16c8 Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Sat, 22 Feb 2025 06:17:49 +0300 Subject: [PATCH 25/47] update doc --- README.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 79775c3..a2ddb9e 100644 --- a/README.md +++ b/README.md @@ -87,9 +87,11 @@ 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(); @@ -102,9 +104,13 @@ the package already built with `symfony Http client`, if you need to use packag ex with symfony: ```php -$response = DeepSeekClient::build('your-api-key', clientType:'symfony') - ->query('Explain quantum computing in simple terms') - ->run(); +// 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 From 5e7a48231a1567941b23efbeb9da9691803cfe93 Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Sat, 22 Feb 2025 06:18:55 +0300 Subject: [PATCH 26/47] update version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 83f33d9..4b7b927 100644 --- a/composer.json +++ b/composer.json @@ -49,7 +49,7 @@ "role": "creator" } ], - "version": "2.0.1", + "version": "2.0.2", "require": { "php": "^8.1.0", "nyholm/psr7": "^1.8", From 26653268b462d827705aa9c71cd31d1e7b6b2278 Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Sat, 22 Feb 2025 06:32:09 +0300 Subject: [PATCH 27/47] Add Arabic documentation --- README-AR.md | 252 +++++++++++++++++++++++++++++++++++++++++++++++++++ README-CN.md | 36 +++++++- README.md | 9 ++ 3 files changed, 292 insertions(+), 5 deletions(-) create mode 100644 README-AR.md diff --git a/README-AR.md b/README-AR.md new file mode 100644 index 0000000..c8918c7 --- /dev/null +++ b/README-AR.md @@ -0,0 +1,252 @@ + +

+

عميل DeepSeek PHP

+

🚀 حزمة SDK لـ PHP مدعومة من المجتمع لتكامل واجهة برمجة التطبيقات الذكية DeepSeek

+ +

+ + أحدث إصدار + + + نسخة PHP + + + الرخصة + + + حالة الاختبارات + +

+

+ +## فهرس المحتويات +- [✨ المميزات](#-المميزات) +- [📦 التثبيت](#-التثبيت) +- [🚀 البداية السريعة](#-البداية-السريعة) + - [الاستخدام الأساسي](#الاستخدام-الأساسي) + - [التكوين المتقدم](#التكوين-المتقدم) + - [الاستخدام مع عميل HTTP من Symfony](#الاستخدام-مع-عميل-http-من-symfony) + - [الحصول على قائمة النماذج](#الحصول-على-قائمة-النماذج) + - [تكامل مع الأطر](#-تكامل-مع-الأطر) +- [🆕 دليل الترحيل](#-دليل-الترحيل) +- [📝 سجل التغييرات](#-سجل-التغييرات) +- [🧪 الاختبارات](#-الاختبارات) +- [🔒 الأمان](#-الأمان) +- [🤝 المساهمين](#-المساهمين) +- [📄 الرخصة](#-الرخصة) + +--- + +## ✨ المميزات + +- **تكامل API سلس**: واجهة تعتمد على PHP لميزات الذكاء الاصطناعي في DeepSeek. +- **نمط الباني السلس**: أساليب قابلة للسلسلة لبناء الطلبات بطريقة بديهية. +- **جاهز للمؤسسات**: تكامل مع عميل HTTP متوافق مع PSR-18. +- **مرونة النماذج**: دعم لعدة نماذج من DeepSeek (Coder, Chat, وغيرها). +- **جاهز للبث**: دعم مدمج للتعامل مع الردود في الوقت الفعلي. +- **العديد من عملاء HTTP**: يمكنك استخدام عميل `Guzzle http client` (افتراضي) أو `symfony http client` بسهولة. +- **متوافق مع الأطر**: حزم Laravel و Symfony متاحة. + +--- + +## 📦 التثبيت + +قم بتثبيت الحزمة عبر Composer: + +```bash +composer require deepseek-php/deepseek-php-client +``` + +**المتطلبات**: +- PHP 8.1+ + +--- + +## 🚀 البداية السريعة + +### الاستخدام الأساسي + +ابدأ مع سطرين من الكود فقط: + +```php +use DeepSeek\DeepSeekClient; + +$response = DeepSeekClient::build('your-api-key') + ->query('Explain quantum computing in simple terms') + ->run(); + +echo $response; +``` + +📌 الإعدادات الافتراضية المستخدمة: +- النموذج: `deepseek-chat` +- الحرارة: 0.8 + +### التكوين المتقدم + +```php +use DeepSeek\DeepSeekClient; +use DeepSeek\Enums\Models; + +$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; +``` + +### الاستخدام مع عميل HTTP من Symfony +الحزمة مبنية مسبقاً مع `symfony Http client`، فإذا كنت بحاجة إلى استخدامها مع عميل HTTP الخاص بـ Symfony، فيمكن تحقيق ذلك بسهولة عن طريق تمرير `clientType:'symfony'` إلى دالة `build`. + +مثال باستخدام Symfony: + +```php +// مع القيم الافتراضية للـ baseUrl و timeout +$client = DeepSeekClient::build('your-api-key', clientType:'symfony') +// مع التخصيص +$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(); +``` + +### الحصول على قائمة النماذج + +```php +use DeepSeek\DeepSeekClient; + +$response = DeepSeekClient::build('your-api-key') + ->getModelsList() + ->run(); + +echo $response; // {"object":"list","data":[{"id":"deepseek-chat","object":"model","owned_by":"deepseek"},{"id":"deepseek-reasoner","object":"model","owned_by":"deepseek"}]} +``` + +### 🛠 تكامل مع الأطر + +### [حزمة Deepseek لـ Laravel](https://github.com/deepseek-php/deepseek-laravel) + +--- + +## 🚧 دليل الترحيل + +هل تقوم بالترقية من الإصدار v1.x؟ اطلع على دليل الترحيل الشامل الخاص بنا للتغييرات الجذرية وتعليمات الترقية. + +--- + +## 📝 سجل التغييرات + +ملاحظات الإصدار التفصيلية متوفرة في [CHANGELOG.md](CHANGELOG.md) + +--- + +## 🧪 الاختبارات + +```bash +./vendor/bin/pest +``` + +تغطية الاختبارات ستتوفر في الإصدار v2.1. + +--- +
+ +# 🐘✨ **مجتمع DeepSeek PHP** ✨🐘 + +انقر على الزر أدناه أو [انضم هنا](https://t.me/deepseek_php_community) لتكون جزءًا من مجتمعنا المتنامي! + +[![Join Telegram](https://img.shields.io/badge/Join-Telegram-blue?style=for-the-badge&logo=telegram)](https://t.me/deepseek_php_community) + + +### **هيكل القناة** 🏗️ +- 🗨️ **عام** - دردشة يومية +- 💡 **الأفكار والاقتراحات** - تشكيل مستقبل المجتمع +- 📢 **الإعلانات والأخبار** - التحديثات والأخبار الرسمية +- 🚀 **الإصدارات والتحديثات** - تتبع الإصدارات ودعم الترحيل +- 🐞 **المشاكل وتقارير الأخطاء** - حل مشكلات جماعي +- 🤝 **طلبات السحب** - التعاون والمراجعة البرمجية + +
+ +--- + +## 🔒 الأمان + +**الإبلاغ عن الثغرات**: إلى [omaralwi2010@gmail.com](mailto:omaralwi2010@gmail.com) + +--- + +## 🤝 المساهمين + +شكراً جزيلاً لهؤلاء الأشخاص المذهلين الذين ساهموا في هذا المشروع! 🎉💖 + + + + + + + + + + +
+ + Omar AlAlwi +
+ Omar AlAlwi +
+
+ 🏆 المُنشئ +
+ + Ayman Alhattami +
+ Ayman Alhattami +
+
+ ⭐ مساهم +
+ + Mohammad Asaad +
+ Mohammad Asaad +
+
+ ⭐ مساهم +
+ + Opada Alzaiede +
+ Opada Alzaiede +
+
+ ⭐ مساهم +
+ + Hisham Bin Ateya +
+ Hisham Bin Ateya +
+
+ ⭐ مساهم +
+ + Vinchan +
+ Vinchan +
+
+ ⭐ مساهم +
+ +**هل ترغب في المساهمة؟** اطلع على [إرشادات المساهمة](./CONTRIBUTING.md) وقدم طلب سحب! 🚀 + +--- + +## 📄 الرخصة + +هذه الحزمة هي برنامج مفتوح المصدر مرخص بموجب [رخصة MIT](LICENSE.md). diff --git a/README-CN.md b/README-CN.md index 26dd190..530833e 100644 --- a/README-CN.md +++ b/README-CN.md @@ -23,7 +23,8 @@ - [📦 安装](#-安装) - [🚀 快速入门](#-快速入门) - [基本用法](#基本用法) - - [高级配置](#高级配置) + - [高级配置](#advanced-configuration) + - [Use with Symfony HttpClient](#use-with-symfony-httpclient) - [获取模型列表](#获取模型列表) - [框架集成](#-框架集成) - [🆕 迁移指南](#-迁移指南) @@ -79,21 +80,38 @@ echo $response; - Model: `deepseek-chat` - Temperature: 0.8 -### 高级配置 +### Advanced Configuration ```php 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(); +``` + ### 获取模型列表 ```php @@ -110,7 +128,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) --- @@ -193,6 +210,15 @@ echo $response; // {"object":"list","data":[{"id":"deepseek-chat","object":"mode
⭐ Contributor + + + 陈文锋 +
+ 陈文锋 +
+
+ ⭐ Contributor + diff --git a/README.md b/README.md index a2ddb9e..9be3a07 100644 --- a/README.md +++ b/README.md @@ -230,6 +230,15 @@ A huge thank you to these amazing people who have contributed to this project!
⭐ Contributor + + + Vinchan +
+ Vinchan +
+
+ ⭐ Contributor + From 05e4331afcd4a2c35c84a01db6a36870979772e6 Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Sat, 22 Feb 2025 06:55:46 +0300 Subject: [PATCH 28/47] update doc --- README-AR.md | 3 +++ README-CN.md | 3 +++ README.md | 3 +++ 3 files changed, 9 insertions(+) diff --git a/README-AR.md b/README-AR.md index c8918c7..5cf564e 100644 --- a/README-AR.md +++ b/README-AR.md @@ -17,6 +17,9 @@ حالة الاختبارات

+ +[الإنجليزية](README.md) | [الصينية](README-CN.md) +

## فهرس المحتويات diff --git a/README-CN.md b/README-CN.md index 530833e..8ef7f67 100644 --- a/README-CN.md +++ b/README-CN.md @@ -16,6 +16,9 @@ Tests Status

+ +[EN](README.md) | [AR](README-AR.md) +

## 目录 diff --git a/README.md b/README.md index 9be3a07..2edb83d 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,9 @@ Tests Status

+ +[AR](README-AR.md) | [CN](README-CN.md) +

## Table of Contents From d645985c4da57bd17d0f8a8a9386313a8a8815bf Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Sat, 22 Feb 2025 07:00:28 +0300 Subject: [PATCH 29/47] update doc --- README-AR.md | 2 +- README-CN.md | 2 +- README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README-AR.md b/README-AR.md index 5cf564e..8b6ce37 100644 --- a/README-AR.md +++ b/README-AR.md @@ -111,7 +111,7 @@ echo 'API Response:'.$response; // مع القيم الافتراضية للـ baseUrl و timeout $client = DeepSeekClient::build('your-api-key', clientType:'symfony') // مع التخصيص -$client = DeepSeekClient::build(apiKey:'your-api-key', baseUrl:'https://api.deepseek.com/v3', timeout:30, 'symfony'); +$client = DeepSeekClient::build(apiKey:'your-api-key', baseUrl:'https://api.deepseek.com/v3', timeout:30, clientType:'symfony'); $client->query('Explain quantum computing in simple terms') ->run(); diff --git a/README-CN.md b/README-CN.md index 8ef7f67..8977cf9 100644 --- a/README-CN.md +++ b/README-CN.md @@ -109,7 +109,7 @@ ex with symfony: // 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 = DeepSeekClient::build(apiKey:'your-api-key', baseUrl:'https://api.deepseek.com/v3', timeout:30, clientType:'symfony'); $client->query('Explain quantum computing in simple terms') ->run(); diff --git a/README.md b/README.md index 2edb83d..1ec6776 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ ex with symfony: // 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 = DeepSeekClient::build(apiKey:'your-api-key', baseUrl:'https://api.deepseek.com/v3', timeout:30, clientType:'symfony'); $client->query('Explain quantum computing in simple terms') ->run(); From 52117612cbfcd1de9ea67d599c555282925dd835 Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Mon, 24 Feb 2025 02:49:45 +0300 Subject: [PATCH 30/47] fix symfony client Compatibility --- composer.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index 4b7b927..b3d0459 100644 --- a/composer.json +++ b/composer.json @@ -49,17 +49,17 @@ "role": "creator" } ], - "version": "2.0.2", + "version": "2.0.3", "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-client-implementation": "1.0", "psr/http-factory-implementation": "*", "psr/http-message": "^1.1.0|^2.0.0", - "symfony/http-client": "^7.2" + "symfony/http-client": "^6.4" }, "require-dev": { "guzzlehttp/guzzle": "^7.9.2", @@ -68,8 +68,8 @@ "mockery/mockery": "^1.6.12", "nunomaduro/collision": "^7.11.0|^8.5.0", "pestphp/pest": "^2.36", - "pestphp/pest-plugin-arch": "^2.7|^3.0", - "pestphp/pest-plugin-type-coverage": "^2.8.7|^3.1.0", + "pestphp/pest-plugin-arch": "^2.7", + "pestphp/pest-plugin-type-coverage": "^2.8.7", "phpstan/phpstan": "^1.12.7", "roave/security-advisories": "dev-latest", "symfony/var-dumper": "^6.4.11|^7.1.5" @@ -108,4 +108,4 @@ "php-http/discovery": true } } -} +} \ No newline at end of file From 6d7dbbac71e6aae08f575e304b52fded877ba620 Mon Sep 17 00:00:00 2001 From: moassaad Date: Tue, 4 Mar 2025 23:03:17 +0200 Subject: [PATCH 31/47] update: add case (assistant, tool) role. --- src/Enums/Queries/QueryRoles.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Enums/Queries/QueryRoles.php b/src/Enums/Queries/QueryRoles.php index 4659084..a23925c 100644 --- a/src/Enums/Queries/QueryRoles.php +++ b/src/Enums/Queries/QueryRoles.php @@ -6,4 +6,6 @@ enum QueryRoles: string { case USER = 'user'; case SYSTEM = 'system'; + case ASSISTANT = 'assistant'; + case TOOL = 'tool'; } From ef5514f56adc842cf62728dc2d496ddf784a7947 Mon Sep 17 00:00:00 2001 From: moassaad Date: Tue, 4 Mar 2025 23:03:37 +0200 Subject: [PATCH 32/47] update: add case tools flag. --- src/Enums/Requests/QueryFlags.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Enums/Requests/QueryFlags.php b/src/Enums/Requests/QueryFlags.php index 19c1cdd..0e2b49a 100644 --- a/src/Enums/Requests/QueryFlags.php +++ b/src/Enums/Requests/QueryFlags.php @@ -8,4 +8,5 @@ enum QueryFlags: string case MODEL = 'model'; case STREAM = 'stream'; case TEMPERATURE = 'temperature'; + case TOOLS = 'tools'; } From 21b5dbc4f185912beae1933cca819fe7bf31fc4b Mon Sep 17 00:00:00 2001 From: moassaad Date: Tue, 4 Mar 2025 23:03:56 +0200 Subject: [PATCH 33/47] create: trait tools function calling for using in deepseek client. --- src/Traits/Client/HasToolsFunctionCalling.php | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 src/Traits/Client/HasToolsFunctionCalling.php diff --git a/src/Traits/Client/HasToolsFunctionCalling.php b/src/Traits/Client/HasToolsFunctionCalling.php new file mode 100644 index 0000000..97d53fc --- /dev/null +++ b/src/Traits/Client/HasToolsFunctionCalling.php @@ -0,0 +1,66 @@ +tools = $tools; + return $this; + } + + /** + * Add a query tool calls to the accumulated queries list. + * + * @param array $toolCalls The tool calls generated by the model, such as function calls. + * @param string $content + * @param string|null $role + * @return self The current instance for method chaining. + */ + public function queryToolCall(array $toolCalls, string $content, ?string $role = null): self + { + $this->queries[] = $this->buildToolCallQuery($toolCalls, $content, $role); + return $this; + } + + public function buildToolCallQuery(array $toolCalls, string $content, ?string $role = null): array + { + $query = [ + 'role' => $role ?: QueryRoles::ASSISTANT->value, + 'tool_calls' => $toolCalls, + 'content' => $content, + ]; + return $query; + } + + /** + * Add a query tool to the accumulated queries list. + * + * @param string $toolCallId + * @param string $content + * @param string|null $role + * @return self The current instance for method chaining. + */ + public function queryTool(string $toolCallId, string $content , ?string $role = null): self + { + $this->queries[] = $this->buildToolQuery($toolCallId, $content, $role); + return $this; + } + + public function buildToolQuery(string $toolCallId, string $content, ?string $role): array + { + $query = [ + 'role' => $role ?: QueryRoles::TOOL->value, + 'tool_call_id' => $toolCallId, + 'content' => $content, + ]; + return $query; + } +} From ade67b33f3de29b751cedcd7a885d38bb55591d3 Mon Sep 17 00:00:00 2001 From: moassaad Date: Tue, 4 Mar 2025 23:04:08 +0200 Subject: [PATCH 34/47] update: add tool property and reset query function. --- src/DeepSeekClient.php | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/DeepSeekClient.php b/src/DeepSeekClient.php index a8d7f01..1c0cb58 100644 --- a/src/DeepSeekClient.php +++ b/src/DeepSeekClient.php @@ -13,10 +13,12 @@ use DeepSeek\Enums\Requests\QueryFlags; use DeepSeek\Enums\Configs\TemperatureValues; use DeepSeek\Traits\Resources\{HasChat, HasCoder}; +use DeepSeek\Traits\Client\HasToolsFunctionCalling; class DeepSeekClient implements ClientContract { use HasChat, HasCoder; + use HasToolsFunctionCalling; /** * PSR-18 HTTP client for making requests. @@ -58,6 +60,12 @@ class DeepSeekClient implements ClientContract protected ?string $endpointSuffixes; + /** + * Array of tools for using function calling. + * @var array|null $tools + */ + protected ?array $tools; + /** * Initialize the DeepSeekClient with a PSR-compliant HTTP client. * @@ -71,6 +79,7 @@ public function __construct(ClientInterface $httpClient) $this->requestMethod = 'POST'; $this->endpointSuffixes = EndpointSuffixes::CHAT->value; $this->temperature = (float) TemperatureValues::GENERAL_CONVERSATION->value; + $this->tools = null; } public function run(): string @@ -80,9 +89,9 @@ public function run(): string QueryFlags::MODEL->value => $this->model, QueryFlags::STREAM->value => $this->stream, QueryFlags::TEMPERATURE->value => $this->temperature, + QueryFlags::TOOLS->value => $this->tools, ]; - // Clear queries after sending - $this->queries = []; + $this->setResult((new Resource($this->httpClient, $this->endpointSuffixes))->sendRequest($requestData, $this->requestMethod)); return $this->getResult()->getContent(); } @@ -120,6 +129,17 @@ public function query(string $content, ?string $role = "user"): self $this->queries[] = $this->buildQuery($content, $role); return $this; } + + /** + * Reset a queries list to empty. + * + * @return self The current instance for method chaining. + */ + public function resetQuery() + { + $this->query = []; + return $this; + } /** * get list of available models . @@ -173,7 +193,7 @@ public function buildQuery(string $content, ?string $role = null): array /** * set result model - * @param \DeepseekPhp\Contracts\Models\ResultContract $result + * @param \DeepSeek\Contracts\Models\ResultContract $result * @return self The current instance for method chaining. */ public function setResult(ResultContract $result) From 8b41e4421907119267896713c3e3c5a94ee64f35 Mon Sep 17 00:00:00 2001 From: moassaad Date: Tue, 4 Mar 2025 23:04:18 +0200 Subject: [PATCH 35/47] create: fake response class to using in test mock. --- .../Feature/ClientDependency/FakeResponse.php | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 tests/Feature/ClientDependency/FakeResponse.php diff --git a/tests/Feature/ClientDependency/FakeResponse.php b/tests/Feature/ClientDependency/FakeResponse.php new file mode 100644 index 0000000..3b1b725 --- /dev/null +++ b/tests/Feature/ClientDependency/FakeResponse.php @@ -0,0 +1,69 @@ + Date: Tue, 4 Mar 2025 23:04:29 +0200 Subject: [PATCH 36/47] create: test function calling. --- tests/Feature/FunctionCallingTest.php | 155 ++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 tests/Feature/FunctionCallingTest.php diff --git a/tests/Feature/FunctionCallingTest.php b/tests/Feature/FunctionCallingTest.php new file mode 100644 index 0000000..6efae8f --- /dev/null +++ b/tests/Feature/FunctionCallingTest.php @@ -0,0 +1,155 @@ + ["temperature"=> 22, "condition" => "Sunny"], + "gharbia" => ["temperature"=> 23, "condition" => "Sunny"], + "sharkia" => ["temperature"=> 24, "condition" => "Sunny"], + "beheira" => ["temperature"=> 21, "condition" => "Sunny"], + default => "not found city name." + }; + return json_encode($city); +} + +test('Test function calling with fake responses.', function () { + // Arrange + $fake = new FakeResponse(); + + /** @var DeepSeekClient&LegacyMockInterface&MockInterface */ + $mockClient = Mockery::mock(DeepSeekClient::class); + + $mockClient->shouldReceive('build')->andReturn($mockClient); + $mockClient->shouldReceive('setTools')->andReturn($mockClient); + $mockClient->shouldReceive('query')->andReturn($mockClient); + $mockClient->shouldReceive('run')->once()->andReturn($fake->toolFunctionCalling()); + + // Act + $response = $mockClient::build('your-api-key') + ->query('What is the weather like in Cairo?') + ->setTools([ + [ + "type" => "function", + "function" => [ + "name" => "get_weather", + "description" => "Get the current weather in a given city", + "parameters" => [ + "type" => "object", + "properties" => [ + "city" => [ + "type" => "string", + "description" => "The city name", + ], + ], + "required" => ["city"], + ], + ], + ], + ] + )->run(); + + // Assert + expect($fake->toolFunctionCalling())->toEqual($response); + + //------------------------------------------ + + // Arrange + $response = json_decode($response, true); + $message = $response['choices'][0]['message']; + + $firstFunction = $message['tool_calls'][0]; + if ($firstFunction['function']['name'] == "get_weather") + { + $weather_data = get_weather($firstFunction['function']['arguments']['city']); + } + + $mockClient->shouldReceive('queryCallTool')->andReturn($mockClient); + $mockClient->shouldReceive('queryTool')->andReturn($mockClient); + $mockClient->shouldReceive('run')->andReturn($fake->resultToolFunctionCalling()); + + // Act + $response2 = $mockClient->queryCallTool( + $message['tool_calls'], + $message['content'], + $message['role'] + )->queryTool( + $firstFunction['id'], + $weather_data, + 'tool' + )->run(); + + // Assert + expect($fake->resultToolFunctionCalling())->toEqual($response2); +}); + +test('Test function calling use base data with real responses.', function () { + // Arrange + $client = DeepSeekClient::build('your-api-key') + ->query('What is the weather like in Cairo?') + ->setTools([ + [ + "type" => "function", + "function" => [ + "name" => "get_weather", + "description" => "Get the current weather in a given city", + "parameters" => [ + "type" => "object", + "properties" => [ + "city" => [ + "type" => "string", + "description" => "The city name", + ], + ], + "required" => ["city"], + ], + ], + ], + ] + ); + + // Act + $response = $client->run(); + $result = $client->getResult(); + + // Assert + expect($response)->not()->toBeEmpty($response) + ->and($result->getStatusCode())->toEqual(HTTPState::OK->value); + + //----------------------------------------------------------------- + + // Arrange + $response = json_decode($response, true); + $message = $response['choices'][0]['message']; + $firstFunction = $message['tool_calls'][0]; + if ($firstFunction['function']['name'] == "get_weather") + { + $weather_data = get_weather($firstFunction['function']['arguments']['city']); + } + + $response2 = $client->queryToolCall( + $message['tool_calls'], + $message['content'], + $message['role'] + )->queryTool( + $firstFunction['id'], + $weather_data, + 'tool' + ); + + // Act + $response2 = $response2->run(); + $result2 = $client->getResult(); + + // Assert + expect($response2)->not()->toBeEmpty($response2) + ->and($result2->getStatusCode())->toEqual(HTTPState::OK->value); +}); From 2a0040f39938a7c778c53a6e4158d5c59e1cf102 Mon Sep 17 00:00:00 2001 From: moassaad Date: Mon, 10 Mar 2025 22:41:15 +0200 Subject: [PATCH 37/47] fix: reset queries function. --- src/DeepSeekClient.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DeepSeekClient.php b/src/DeepSeekClient.php index 1c0cb58..93c989d 100644 --- a/src/DeepSeekClient.php +++ b/src/DeepSeekClient.php @@ -135,9 +135,9 @@ public function query(string $content, ?string $role = "user"): self * * @return self The current instance for method chaining. */ - public function resetQuery() + public function resetQueries() { - $this->query = []; + $this->queries = []; return $this; } From 218ea3f0ef05025d586cd980c7fdbe119c981ab5 Mon Sep 17 00:00:00 2001 From: moassaad Date: Sat, 22 Mar 2025 00:46:19 +0200 Subject: [PATCH 38/47] update: edit test real responses. --- tests/Feature/FunctionCallingTest.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/Feature/FunctionCallingTest.php b/tests/Feature/FunctionCallingTest.php index 6efae8f..041e153 100644 --- a/tests/Feature/FunctionCallingTest.php +++ b/tests/Feature/FunctionCallingTest.php @@ -128,14 +128,16 @@ function get_weather($city) // Arrange $response = json_decode($response, true); + $message = $response['choices'][0]['message']; $firstFunction = $message['tool_calls'][0]; if ($firstFunction['function']['name'] == "get_weather") { - $weather_data = get_weather($firstFunction['function']['arguments']['city']); + $args = json_decode($firstFunction['function']['arguments'], true); + $weather_data = get_weather($args['city']); } - $response2 = $client->queryToolCall( + $client2 = $client->queryToolCall( $message['tool_calls'], $message['content'], $message['role'] @@ -146,8 +148,8 @@ function get_weather($city) ); // Act - $response2 = $response2->run(); - $result2 = $client->getResult(); + $response2 = $client2->run(); + $result2 = $client2->getResult(); // Assert expect($response2)->not()->toBeEmpty($response2) From f5642d65ad12b267fd7ddeaeeab723de09e7a443 Mon Sep 17 00:00:00 2001 From: Kenneth Gerald Hamilton <29099829+kghamilton89@users.noreply.github.com> Date: Sat, 5 Apr 2025 15:00:07 +0200 Subject: [PATCH 39/47] update docs to correct `setTemperature` parameter --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1ec6776..a8c4db2 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ $client = DeepSeekClient::build(apiKey:'your-api-key', baseUrl:'https://api.deep $response = $client ->withModel(Models::CODER->value) ->withStream() - ->withTemperature(1.2) + ->setTemperature(1.2) ->run(); echo 'API Response:'.$response; From 2091d51a06ab805e53b482ae4de02c8dcad08232 Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Thu, 12 Jun 2025 01:50:33 +0300 Subject: [PATCH 40/47] Docs / add function calilng docs --- README-AR.md | 14 ++- README-CN.md | 33 ++++++- README.md | 10 ++ docs/FUNCTION-CALLING.md | 192 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 243 insertions(+), 6 deletions(-) create mode 100644 docs/FUNCTION-CALLING.md diff --git a/README-AR.md b/README-AR.md index 8b6ce37..83163f9 100644 --- a/README-AR.md +++ b/README-AR.md @@ -1,4 +1,3 @@ -

عميل DeepSeek PHP

🚀 حزمة SDK لـ PHP مدعومة من المجتمع لتكامل واجهة برمجة التطبيقات الذكية DeepSeek

@@ -30,6 +29,7 @@ - [التكوين المتقدم](#التكوين-المتقدم) - [الاستخدام مع عميل HTTP من Symfony](#الاستخدام-مع-عميل-http-من-symfony) - [الحصول على قائمة النماذج](#الحصول-على-قائمة-النماذج) + - [استدعاء الدوال](#استدعاء-الدوال) - [تكامل مع الأطر](#-تكامل-مع-الأطر) - [🆕 دليل الترحيل](#-دليل-الترحيل) - [📝 سجل التغييرات](#-سجل-التغييرات) @@ -97,6 +97,7 @@ $response = $client ->withModel(Models::CODER->value) ->withStream() ->withTemperature(1.2) + ->query('Explain quantum computing in simple terms') ->run(); echo 'API Response:'.$response; @@ -129,6 +130,17 @@ $response = DeepSeekClient::build('your-api-key') echo $response; // {"object":"list","data":[{"id":"deepseek-chat","object":"model","owned_by":"deepseek"},{"id":"deepseek-reasoner","object":"model","owned_by":"deepseek"}]} ``` +### استدعاء الدوال + +يتيح **استدعاء الدوال** للنموذج استدعاء أدوات خارجية لتعزيز قدراته. +يمكنك الرجوع إلى الوثائق الخاصة باستدعاء الدوال في الملف: +[FUNCTION-CALLING.md](docs/FUNCTION-CALLING.md) + +--- + +هل ترغب في أن أضع النسخ الثلاث (الإنجليزية + العربية + الصينية) ضمن ملف Markdown موحد؟ + + ### 🛠 تكامل مع الأطر ### [حزمة Deepseek لـ Laravel](https://github.com/deepseek-php/deepseek-laravel) diff --git a/README-CN.md b/README-CN.md index 8977cf9..df859b4 100644 --- a/README-CN.md +++ b/README-CN.md @@ -25,11 +25,12 @@ - [✨ 特性](#-特性) - [📦 安装](#-安装) - [🚀 快速入门](#-快速入门) - - [基本用法](#基本用法) - - [高级配置](#advanced-configuration) - - [Use with Symfony HttpClient](#use-with-symfony-httpclient) - - [获取模型列表](#获取模型列表) - - [框架集成](#-框架集成) + - [基本用法](#基本用法) + - [高级配置](#高级配置) + - [使用 Symfony HttpClient](#使用-symfony-httpclient) + - [获取模型列表](#获取模型列表) + - [函数调用](#函数调用) + - [框架集成](#-框架集成) - [🆕 迁移指南](#-迁移指南) - [📝 更新日志](#-更新日志) - [🧪 测试](#-测试) @@ -95,6 +96,7 @@ $response = $client ->withModel(Models::CODER->value) ->withStream() ->withTemperature(1.2) + ->query('Explain quantum computing in simple terms') ->run(); echo 'API Response:'.$response; @@ -127,11 +129,32 @@ $response = DeepSeekClient::build('your-api-key') echo $response; // {"object":"list","data":[{"id":"deepseek-chat","object":"model","owned_by":"deepseek"},{"id":"deepseek-reasoner","object":"model","owned_by":"deepseek"}]} ``` +### 函数调用 + +**函数调用**允许模型调用外部工具以增强其功能。 +你可以在文档中查看有关函数调用的详细信息: +[FUNCTION-CALLING.md](docs/FUNCTION-CALLING.md) + + ### 🛠 框架集成 ### [Laravel Deepseek Package](https://github.com/deepseek-php/deepseek-laravel) +# 🐘✨ **DeepSeek PHP Community** ✨🐘 + +Click the button bellow or [join here](https://t.me/deepseek_php_community) to be part of our growing community! + +[![Join Telegram](https://img.shields.io/badge/Join-Telegram-blue?style=for-the-badge&logo=telegram)](https://t.me/deepseek_php_community) + +### **Channel Structure** 🏗️ +- 🗨️ **General** - Daily chatter +- 💡 **Ideas & Suggestions** - Shape the community's future +- 📢 **Announcements & News** - Official updates & news +- 🚀 **Releases & Updates** - Version tracking & migration support +- 🐞 **Issues & Bug Reports** - Collective problem-solving +- 🤝 **Pull Requests** - Code collaboration & reviews + --- ## 🚧 迁移指南 diff --git a/README.md b/README.md index a8c4db2..7ed8c3e 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ - [Advanced Configuration](#advanced-configuration) - [Use with Symfony HttpClient](#use-with-symfony-httpclient) - [Get Models List](#get-models-list) + - [Function Calling](#function-calling) - [Framework Integration](#-framework-integration) - [🆕 Migration Guide](#-migration-guide) - [📝 Changelog](#-changelog) @@ -96,6 +97,7 @@ $response = $client ->withModel(Models::CODER->value) ->withStream() ->setTemperature(1.2) + ->query('Explain quantum computing in simple terms') ->run(); echo 'API Response:'.$response; @@ -128,6 +130,14 @@ $response = DeepSeekClient::build('your-api-key') echo $response; // {"object":"list","data":[{"id":"deepseek-chat","object":"model","owned_by":"deepseek"},{"id":"deepseek-reasoner","object":"model","owned_by":"deepseek"}]} ``` + +### Function Calling + +Function Calling allows the model to call external tools to enhance its capabilities.[[1]](https://api-docs.deepseek.com/guides/function_calling) + +You Can check the documentation for function calling in [FUNCTION-CALLING.md](docs/FUNCTION-CALLING.md) + + ### 🛠 Framework Integration ### [Laravel Deepseek Package](https://github.com/deepseek-php/deepseek-laravel) diff --git a/docs/FUNCTION-CALLING.md b/docs/FUNCTION-CALLING.md new file mode 100644 index 0000000..4d115cf --- /dev/null +++ b/docs/FUNCTION-CALLING.md @@ -0,0 +1,192 @@ +## Function Calling + +Function Calling allows the model to call external tools to enhance its capabilities.[[1]](https://api-docs.deepseek.com/guides/function_calling) + +#### 1. Define the tools used by the model and pass them with each message passed to the model, Receive query messages from the end user and pass them to the model with the defined tools. +- example function `get_weather($city)`. +```php +function get_weather($city) +{ + $city = strtolower($city); + $city = match($city){ + "cairo" => ["temperature"=> 22, "condition" => "Sunny"], + "gharbia" => ["temperature"=> 23, "condition" => "Sunny"], + "sharkia" => ["temperature"=> 24, "condition" => "Sunny"], + "beheira" => ["temperature"=> 21, "condition" => "Sunny"], + default => "not found city name." + }; + return json_encode($city); +} +``` +The user requests the weather in Cairo. +```php +$client = DeepSeekClient::build('your-api-key') + ->query('What is the weather like in Cairo?') + ->setTools([ + [ + "type" => "function", + "function" => [ + "name" => "get_weather", + "description" => "Get the current weather in a given city", + "parameters" => [ + "type" => "object", + "properties" => [ + "city" => [ + "type" => "string", + "description" => "The city name", + ], + ], + "required" => ["city"], + ], + ], + ], + ] +); + +$response = $client->run(); + +``` + +Output response like. +```json +{ + "id": "chat_12345", + "object": "chat.completion", + "created": 1677654321, + "model": "deepseek-chat", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": null, + "tool_calls": [ + { + "id": "call_12345", + "type": "function", + "function": { + "name": "get_weather", + "arguments": "{\"city\": \"Cairo\"}" + } + } + ] + }, + "finish_reason": "tool_calls" + } + ] +} +``` + +#### 2. Receive the response and check if it has called one or more tools to execute it in the system ,And execute the tool called by the model. +The deepseek api responds to the system and requests the execution of the tool responsible for fetching the weather status. +```php + +$response = $client->run(); + +$response = json_decode($response, true); +$message = $response['choices'][0]['message']; +$firstFunction = $message['tool_calls'][0]; +if ($firstFunction['function']['name'] == "get_weather") +{ + $weather_data = get_weather($firstFunction['function']['arguments']['city']); +} + +``` + +#### 3. Coordinate the results and send the previous response with the results of the executed tools. +Formats the response, and sends it back to the form. +```php +$response2 = $client->queryToolCall( + $message['tool_calls'], + $message['content'], + $message['role'] + )->queryTool( + $firstFunction['id'], + $weather_data +); +``` + +Request like +```json +{ + "messages": [ + { + "role": "user", + "content": "What is the weather like in Cairo?" + }, + { + "content": "What is the weather like in Cairo?", + "tool_calls": [ + { + "id": "930c60df-3ec75f81e00e", + "type": "function", + "function": { + "name": "get_weather", + "arguments": { + "city": "Cairo" + } + } + } + ], + "role": "assistant" + }, + { + "role": "tool", + "tool_call_id": "930c60df-3ec75f81e00e", + "content": "{\"temperature\":22,\"condition\":\"Sunny\"}" + } + ], + "model": "deepseek-chat", + "stream": false, + "temperature": 1.3, + "tools": [ + { + "type": "function", + "function": { + "name": "get_weather", + "description": "Get the current weather in a given city", + "parameters": { + "type": "object", + "properties": { + "city": { + "type": "string", + "description": "The city name" + } + }, + "required": [ + "city" + ] + } + } + } + ] +} +``` + +#### 4. Receive the final response from the model and pass it to the end user. +The deepseek api responds with the final response, which is the weather status according to the data passed to it in the example. +```php + +$response2 = $response2->run(); +echo $response2; +``` +Output response like :- +```json +{ + "id": "chat_67890", + "object": "chat.completion", + "created": 1677654322, + "model": "deepseek-chat", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "The weather in Cairo is 22℃." + }, + "finish_reason": "stop" + } + ] +} +``` + From 844e4f718a220dcee2d46e332e9a9a4f0c420d33 Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Thu, 12 Jun 2025 01:56:30 +0300 Subject: [PATCH 41/47] update version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index b3d0459..5f03fd6 100644 --- a/composer.json +++ b/composer.json @@ -49,7 +49,7 @@ "role": "creator" } ], - "version": "2.0.3", + "version": "2.0.4", "require": { "php": "^8.1.0", "nyholm/psr7": "^1.8", From fa02d9a5b22d55ddbf1093f8c260936e7e181fee Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Wed, 16 Jul 2025 22:51:04 +0300 Subject: [PATCH 42/47] update bardes --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7ed8c3e..0a59cec 100644 --- a/README.md +++ b/README.md @@ -6,17 +6,19 @@ Latest Version + + Total Downloads + PHP Version License - - Tests Status + + GitHub Stars -

- + [AR](README-AR.md) | [CN](README-CN.md)

From 1f078c7b507b7f587caf82476af65b087194fe02 Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Wed, 16 Jul 2025 23:02:11 +0300 Subject: [PATCH 43/47] update badges --- README-AR.md | 15 +++++++++------ README-CN.md | 19 +++++++++++-------- README.md | 2 +- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/README-AR.md b/README-AR.md index 83163f9..10905a9 100644 --- a/README-AR.md +++ b/README-AR.md @@ -1,19 +1,22 @@

-

عميل DeepSeek PHP

-

🚀 حزمة SDK لـ PHP مدعومة من المجتمع لتكامل واجهة برمجة التطبيقات الذكية DeepSeek

+

عميل DeepSeek بلغة PHP

+

🚀 حزمة PHP مفتوحة المصدر ومدعومة من المجتمع للتكامل مع واجهة DeepSeek API

أحدث إصدار + + إجمالي التحميلات + - نسخة PHP + إصدار PHP - الرخصة + الترخيص - - حالة الاختبارات + + النجوم على GitHub

diff --git a/README-CN.md b/README-CN.md index df859b4..ebd7e19 100644 --- a/README-CN.md +++ b/README-CN.md @@ -1,19 +1,22 @@

-

DeepSeek PHP Client

-

🚀 社区驱动的 PHP SDK,用于 DeepSeek AI 接口集成

- +

DeepSeek PHP 客户端

+

🚀 由社区驱动的 PHP SDK,用于集成 DeepSeek AI API

+

- Latest Version + 最新版本 + + + 总下载次数 - PHP Version + PHP 版本 - License + 许可证 - - Tests Status + + GitHub 收藏数

diff --git a/README.md b/README.md index 0a59cec..c70c40f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

DeepSeek PHP Client

-

🚀 Community-Driven PHP SDK for DeepSeek AI API Integration

+

🚀 Community-Driven PHP Client for DeepSeek AI API Integration

From c0d27f605cf0f747cdced9cb6806f2227994d726 Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Wed, 16 Jul 2025 23:56:11 +0300 Subject: [PATCH 44/47] feat / support pass max tokens option --- README-AR.md | 1 + README-CN.md | 1 + README.md | 1 + src/DeepSeekClient.php | 11 +++++++++++ src/Enums/Configs/TemperatureValues.php | 1 + src/Enums/Requests/QueryFlags.php | 1 + 6 files changed, 16 insertions(+) diff --git a/README-AR.md b/README-AR.md index 10905a9..8e9ad5a 100644 --- a/README-AR.md +++ b/README-AR.md @@ -100,6 +100,7 @@ $response = $client ->withModel(Models::CODER->value) ->withStream() ->withTemperature(1.2) + ->setMaxTokens(8192) ->query('Explain quantum computing in simple terms') ->run(); diff --git a/README-CN.md b/README-CN.md index ebd7e19..b93bb99 100644 --- a/README-CN.md +++ b/README-CN.md @@ -99,6 +99,7 @@ $response = $client ->withModel(Models::CODER->value) ->withStream() ->withTemperature(1.2) + ->setMaxTokens(8192) ->query('Explain quantum computing in simple terms') ->run(); diff --git a/README.md b/README.md index c70c40f..6ec9b7e 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,7 @@ $response = $client ->withModel(Models::CODER->value) ->withStream() ->setTemperature(1.2) + ->setMaxTokens(8192) ->query('Explain quantum computing in simple terms') ->run(); diff --git a/src/DeepSeekClient.php b/src/DeepSeekClient.php index 93c989d..63592a1 100644 --- a/src/DeepSeekClient.php +++ b/src/DeepSeekClient.php @@ -49,6 +49,7 @@ class DeepSeekClient implements ClientContract protected bool $stream; protected float $temperature; + protected int $maxTokens; /** * response result contract @@ -79,6 +80,7 @@ public function __construct(ClientInterface $httpClient) $this->requestMethod = 'POST'; $this->endpointSuffixes = EndpointSuffixes::CHAT->value; $this->temperature = (float) TemperatureValues::GENERAL_CONVERSATION->value; + $this->maxTokens = (int) TemperatureValues::MAX_TOKENS->value; $this->tools = null; } @@ -89,9 +91,12 @@ public function run(): string QueryFlags::MODEL->value => $this->model, QueryFlags::STREAM->value => $this->stream, QueryFlags::TEMPERATURE->value => $this->temperature, + QueryFlags::MAX_TOKENS->value => $this->maxTokens, QueryFlags::TOOLS->value => $this->tools, ]; + \Log::info(['debug info: this is $requestData :' => $requestData ]); + $this->setResult((new Resource($this->httpClient, $this->endpointSuffixes))->sendRequest($requestData, $this->requestMethod)); return $this->getResult()->getContent(); } @@ -183,6 +188,12 @@ public function setTemperature(float $temperature): self return $this; } + public function setMaxTokens(int $maxTokens): self + { + $this->maxTokens = $maxTokens; + return $this; + } + public function buildQuery(string $content, ?string $role = null): array { return [ diff --git a/src/Enums/Configs/TemperatureValues.php b/src/Enums/Configs/TemperatureValues.php index 12c21b1..b9b9a93 100644 --- a/src/Enums/Configs/TemperatureValues.php +++ b/src/Enums/Configs/TemperatureValues.php @@ -12,4 +12,5 @@ enum TemperatureValues: string case TRANSLATION = "1.4"; case CREATIVE_WRITING = "1.5"; case POETRY = "1.6"; + case MAX_TOKENS = "4096"; } diff --git a/src/Enums/Requests/QueryFlags.php b/src/Enums/Requests/QueryFlags.php index 0e2b49a..2b4b557 100644 --- a/src/Enums/Requests/QueryFlags.php +++ b/src/Enums/Requests/QueryFlags.php @@ -8,5 +8,6 @@ enum QueryFlags: string case MODEL = 'model'; case STREAM = 'stream'; case TEMPERATURE = 'temperature'; + case MAX_TOKENS = 'max_tokens'; case TOOLS = 'tools'; } From ab01fa544cbbd0225fa46c5500b4dcc4125679b8 Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Thu, 17 Jul 2025 00:03:22 +0300 Subject: [PATCH 45/47] clean u p --- composer.json | 4 ++-- src/DeepSeekClient.php | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 5f03fd6..20f2766 100644 --- a/composer.json +++ b/composer.json @@ -47,9 +47,9 @@ "name": "Omar Alalwi", "email": "omaralwi2010@gmail.com", "role": "creator" - } + }g ], - "version": "2.0.4", + "version": "2.0.5", "require": { "php": "^8.1.0", "nyholm/psr7": "^1.8", diff --git a/src/DeepSeekClient.php b/src/DeepSeekClient.php index 63592a1..c44d0cd 100644 --- a/src/DeepSeekClient.php +++ b/src/DeepSeekClient.php @@ -95,8 +95,6 @@ public function run(): string QueryFlags::TOOLS->value => $this->tools, ]; - \Log::info(['debug info: this is $requestData :' => $requestData ]); - $this->setResult((new Resource($this->httpClient, $this->endpointSuffixes))->sendRequest($requestData, $this->requestMethod)); return $this->getResult()->getContent(); } From dde2965ef938b365e6b24e41c29d88e29b29c521 Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Thu, 17 Jul 2025 00:03:37 +0300 Subject: [PATCH 46/47] clean up --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 20f2766..e4a48f2 100644 --- a/composer.json +++ b/composer.json @@ -47,7 +47,7 @@ "name": "Omar Alalwi", "email": "omaralwi2010@gmail.com", "role": "creator" - }g + } ], "version": "2.0.5", "require": { From 0dc68a2ce8ee8997ee76b15eba3d8ec8501300c4 Mon Sep 17 00:00:00 2001 From: omaralalwi Date: Thu, 17 Jul 2025 00:44:05 +0300 Subject: [PATCH 47/47] feat / support response format option --- README-AR.md | 39 +++++++++++++++++++++-- README-CN.md | 38 +++++++++++++++++++++-- README.md | 41 +++++++++++++++++++++++-- composer.json | 2 +- src/DeepSeekClient.php | 11 +++++++ src/Enums/Configs/TemperatureValues.php | 1 + src/Enums/Requests/QueryFlags.php | 1 + 7 files changed, 125 insertions(+), 8 deletions(-) diff --git a/README-AR.md b/README-AR.md index 8e9ad5a..cccbfc0 100644 --- a/README-AR.md +++ b/README-AR.md @@ -22,14 +22,13 @@ [الإنجليزية](README.md) | [الصينية](README-CN.md) -

- ## فهرس المحتويات - [✨ المميزات](#-المميزات) - [📦 التثبيت](#-التثبيت) - [🚀 البداية السريعة](#-البداية-السريعة) - [الاستخدام الأساسي](#الاستخدام-الأساسي) - [التكوين المتقدم](#التكوين-المتقدم) + - [تحذير هام عند استخدام وضع JSON](#-متطلب-وضع-json-في-deepseek) - [الاستخدام مع عميل HTTP من Symfony](#الاستخدام-مع-عميل-http-من-symfony) - [الحصول على قائمة النماذج](#الحصول-على-قائمة-النماذج) - [استدعاء الدوال](#استدعاء-الدوال) @@ -101,12 +100,48 @@ $response = $client ->withStream() ->withTemperature(1.2) ->setMaxTokens(8192) + ->setResponseFormat('text') ->query('Explain quantum computing in simple terms') ->run(); echo 'API Response:'.$response; ``` + +## ⚠️ متطلب وضع JSON في DeepSeek + +عند استخدام: + +```php +->setResponseFormat('json_object') +``` + +يجب أن يحتوي الـ برومبت على **كلمة "json"** بشكل واضح. + +وإلا سيتم رفض الطلب من قبل وترجع رسالة الخطأ التالية: + +> `"Prompt must contain the word 'json' in some form to use 'response_format' of type 'json_object'"` + +--- + +### 🚫 استخدام غير صحيح + +```php +->setResponseFormat('json_object') +->query('اشرح الحوسبة الكمومية بطريقة مبسطة') +``` + +### ✅ استخدام صحيح + +```php +->setResponseFormat('json_object') +->query('أجب بصيغة JSON صحيحة. اشرح الحوسبة الكمومية بطريقة مبسطة.') +``` + +> ✅ **نصيحة**: للحصول على أفضل النتائج، قم أيضًا بإعطاء مثال على صيغة JSON في الرسالة. + +--- + ### الاستخدام مع عميل HTTP من Symfony الحزمة مبنية مسبقاً مع `symfony Http client`، فإذا كنت بحاجة إلى استخدامها مع عميل HTTP الخاص بـ Symfony، فيمكن تحقيق ذلك بسهولة عن طريق تمرير `clientType:'symfony'` إلى دالة `build`. diff --git a/README-CN.md b/README-CN.md index b93bb99..0458b5c 100644 --- a/README-CN.md +++ b/README-CN.md @@ -22,14 +22,13 @@ [EN](README.md) | [AR](README-AR.md) -

- ## 目录 - [✨ 特性](#-特性) - [📦 安装](#-安装) - [🚀 快速入门](#-快速入门) - [基本用法](#基本用法) - [高级配置](#高级配置) + - [使用 JSON 模式的重要警告](#-deepseek-json-模式使用要求) - [使用 Symfony HttpClient](#使用-symfony-httpclient) - [获取模型列表](#获取模型列表) - [函数调用](#函数调用) @@ -100,12 +99,47 @@ $response = $client ->withStream() ->withTemperature(1.2) ->setMaxTokens(8192) + ->setResponseFormat('text') ->query('Explain quantum computing in simple terms') ->run(); echo 'API Response:'.$response; ``` + +## ⚠️ DeepSeek JSON 模式使用要求 + +当使用: + +```php +->setResponseFormat('json_object') +``` + +你的提示语(prompt)**必须包含 "json" 这个词**,否则 API 会返回以下错误: + +> `"Prompt must contain the word 'json' in some form to use 'response_format' of type 'json_object'"` + +--- + +### 🚫 错误示例 + +```php +->setResponseFormat('json_object') +->query('用简单的语言解释量子计算') +``` + +### ✅ 正确示例 + +```php +->setResponseFormat('json_object') +->query('请以有效的 JSON 格式回答,并用简单语言解释量子计算。') +``` + +> ✅ **建议**:为了获得更好的结果,最好也在提示中提供一个 JSON 示例,并强调 “只返回 JSON”。 + + +--- + ### 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. diff --git a/README.md b/README.md index 6ec9b7e..262f8ca 100644 --- a/README.md +++ b/README.md @@ -18,10 +18,9 @@
GitHub Stars - -[AR](README-AR.md) | [CN](README-CN.md) +

-

+[AR](README-AR.md) | [CN](README-CN.md) ## Table of Contents - [✨ Features](#-features) @@ -29,6 +28,7 @@ - [🚀 Quick Start](#-quick-start) - [Basic Usage](#basic-usage) - [Advanced Configuration](#advanced-configuration) + - [important warning with json mode](#-deepseek-json-mode-requirement) - [Use with Symfony HttpClient](#use-with-symfony-httpclient) - [Get Models List](#get-models-list) - [Function Calling](#function-calling) @@ -100,12 +100,47 @@ $response = $client ->withStream() ->setTemperature(1.2) ->setMaxTokens(8192) + ->setResponseFormat('text') // or "json_object" with careful . ->query('Explain quantum computing in simple terms') ->run(); echo 'API Response:'.$response; ``` +## ⚠️ DeepSeek JSON Mode Requirement + +When using: + +```php +->setResponseFormat('json_object') +``` + +Your prompt **must contain the word `"json"`** in some form. Otherwise, the API will reject the request with the following error: + +> `"Prompt must contain the word 'json' in some form to use 'response_format' of type 'json_object'"` + +--- + +### 🚫 Incorrect Usage + +```php +->setResponseFormat('json_object') +->query('Explain quantum computing in simple terms') +``` + +### ✅ Correct Usage + +```php +->setResponseFormat('json_object') +->query('Respond in valid JSON format. Explain quantum computing in simple terms.') +``` + +> ✅ **Tip**: For best results, also provide a JSON example or explicitly say: +> *"Respond only in valid JSON."* + + +--- + ### 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. diff --git a/composer.json b/composer.json index e4a48f2..e9e0a67 100644 --- a/composer.json +++ b/composer.json @@ -49,7 +49,7 @@ "role": "creator" } ], - "version": "2.0.5", + "version": "2.0.6", "require": { "php": "^8.1.0", "nyholm/psr7": "^1.8", diff --git a/src/DeepSeekClient.php b/src/DeepSeekClient.php index c44d0cd..62e39f5 100644 --- a/src/DeepSeekClient.php +++ b/src/DeepSeekClient.php @@ -50,6 +50,7 @@ class DeepSeekClient implements ClientContract protected float $temperature; protected int $maxTokens; + protected string $responseFormatType; /** * response result contract @@ -81,6 +82,7 @@ public function __construct(ClientInterface $httpClient) $this->endpointSuffixes = EndpointSuffixes::CHAT->value; $this->temperature = (float) TemperatureValues::GENERAL_CONVERSATION->value; $this->maxTokens = (int) TemperatureValues::MAX_TOKENS->value; + $this->responseFormatType = TemperatureValues::RESPONSE_FORMAT_TYPE->value; $this->tools = null; } @@ -93,6 +95,9 @@ public function run(): string QueryFlags::TEMPERATURE->value => $this->temperature, QueryFlags::MAX_TOKENS->value => $this->maxTokens, QueryFlags::TOOLS->value => $this->tools, + QueryFlags::RESPONSE_FORMAT->value => [ + 'type' => $this->responseFormatType + ], ]; $this->setResult((new Resource($this->httpClient, $this->endpointSuffixes))->sendRequest($requestData, $this->requestMethod)); @@ -192,6 +197,12 @@ public function setMaxTokens(int $maxTokens): self return $this; } + public function setResponseFormat(string $type): self + { + $this->responseFormatType = $type; + return $this; + } + public function buildQuery(string $content, ?string $role = null): array { return [ diff --git a/src/Enums/Configs/TemperatureValues.php b/src/Enums/Configs/TemperatureValues.php index b9b9a93..c09d315 100644 --- a/src/Enums/Configs/TemperatureValues.php +++ b/src/Enums/Configs/TemperatureValues.php @@ -13,4 +13,5 @@ enum TemperatureValues: string case CREATIVE_WRITING = "1.5"; case POETRY = "1.6"; case MAX_TOKENS = "4096"; + case RESPONSE_FORMAT_TYPE = "text"; } diff --git a/src/Enums/Requests/QueryFlags.php b/src/Enums/Requests/QueryFlags.php index 2b4b557..29e2586 100644 --- a/src/Enums/Requests/QueryFlags.php +++ b/src/Enums/Requests/QueryFlags.php @@ -10,4 +10,5 @@ enum QueryFlags: string case TEMPERATURE = 'temperature'; case MAX_TOKENS = 'max_tokens'; case TOOLS = 'tools'; + case RESPONSE_FORMAT = 'response_format'; }