From 314e89c8c3c1cb8ad9d132ccea435b2fefe7fe77 Mon Sep 17 00:00:00 2001 From: Marcello Duarte Date: Fri, 31 Jul 2026 15:45:18 +0100 Subject: [PATCH 1/2] Refresh the default model, and declare tool-selection capability Requires papi-core ^0.15. The default model deepseek-chat was discontinued on 24 July 2026, along with deepseek-reasoner, so both now hard-fail. Moves to deepseek-v4-flash, which is the like-for-like replacement. --- composer.json | 2 +- src/DeepSeekProvider.php | 12 ++++++++++-- tests/Unit/DeepSeekProviderTest.php | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 50415b2..77dd12f 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ ], "require": { "php": "^8.2", - "papi-ai/papi-core": "^0.14", + "papi-ai/papi-core": "^0.15", "ext-curl": "*" }, "require-dev": { diff --git a/src/DeepSeekProvider.php b/src/DeepSeekProvider.php index 518de6a..9d557d8 100644 --- a/src/DeepSeekProvider.php +++ b/src/DeepSeekProvider.php @@ -43,13 +43,21 @@ * * @see https://api-docs.deepseek.com/ * - * @psalm-import-type ChatOptions from ProviderInterface + * @psalm-import-type ChatOptions from ProviderInterface * + * The neutral `effort` option is accepted and ignored here. DeepSeek does expose reasoning control, as a nested thinking object rather than a flat level, but papi does not map it yet, so the option is accepted and ignored for now. Ignoring it + * degrades nothing the caller was promised, which is why it is silent where an unhonourable + * `toolChoice` throws. */ class DeepSeekProvider implements ProviderInterface { private const API_URL = 'https://api.deepseek.com/chat/completions'; + public const MODEL_DEEPSEEK_V4_FLASH = 'deepseek-v4-flash'; + public const MODEL_DEEPSEEK_V4_PRO = 'deepseek-v4-pro'; + + /** @deprecated Discontinued 24 July 2026; requests fail. Use MODEL_DEEPSEEK_V4_FLASH. */ public const MODEL_DEEPSEEK_CHAT = 'deepseek-chat'; + /** @deprecated Discontinued 24 July 2026; requests fail. Use MODEL_DEEPSEEK_V4_FLASH. */ public const MODEL_DEEPSEEK_REASONER = 'deepseek-reasoner'; /** @@ -61,7 +69,7 @@ class DeepSeekProvider implements ProviderInterface */ public function __construct( private readonly string $apiKey, - private readonly string $defaultModel = self::MODEL_DEEPSEEK_CHAT, + private readonly string $defaultModel = self::MODEL_DEEPSEEK_V4_FLASH, private readonly int $defaultMaxTokens = 4096, ) { } diff --git a/tests/Unit/DeepSeekProviderTest.php b/tests/Unit/DeepSeekProviderTest.php index de97e3a..1069b35 100644 --- a/tests/Unit/DeepSeekProviderTest.php +++ b/tests/Unit/DeepSeekProviderTest.php @@ -138,7 +138,7 @@ public function callThrowForStatusCode(int $httpCode, ?array $data): never $this->provider->chat([Message::user('Hello')]); - expect($this->provider->lastPayload['model'])->toBe('deepseek-chat'); + expect($this->provider->lastPayload['model'])->toBe('deepseek-v4-flash'); }); it('overrides model and options from parameters', function () { From 847a99b8489725ff420bd93b5027b653e36e9db2 Mon Sep 17 00:00:00 2001 From: Marcello Duarte Date: Fri, 31 Jul 2026 14:17:15 +0100 Subject: [PATCH 2/2] Declare tool-selection capability as interfaces, and forward toolChoice from Agent Parked mid-review: complete and green locally, awaiting the v0.16 release decision. --- src/DeepSeekProvider.php | 3 ++- tests/Unit/DeepSeekToolChoiceTest.php | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/DeepSeekProvider.php b/src/DeepSeekProvider.php index 9d557d8..f00c3f0 100644 --- a/src/DeepSeekProvider.php +++ b/src/DeepSeekProvider.php @@ -15,6 +15,7 @@ namespace PapiAI\DeepSeek; use Generator; +use PapiAI\Core\Contracts\NamedToolSelectableInterface; use PapiAI\Core\Contracts\ProviderInterface; use PapiAI\Core\Exception\AuthenticationException; use PapiAI\Core\Exception\ProviderException; @@ -48,7 +49,7 @@ * degrades nothing the caller was promised, which is why it is silent where an unhonourable * `toolChoice` throws. */ -class DeepSeekProvider implements ProviderInterface +class DeepSeekProvider implements ProviderInterface, NamedToolSelectableInterface { private const API_URL = 'https://api.deepseek.com/chat/completions'; diff --git a/tests/Unit/DeepSeekToolChoiceTest.php b/tests/Unit/DeepSeekToolChoiceTest.php index 713f173..bf15154 100644 --- a/tests/Unit/DeepSeekToolChoiceTest.php +++ b/tests/Unit/DeepSeekToolChoiceTest.php @@ -12,6 +12,8 @@ declare(strict_types=1); +use PapiAI\Core\Contracts\NamedToolSelectableInterface; +use PapiAI\Core\Contracts\ToolSelectableInterface; use PapiAI\Core\Message; use PapiAI\DeepSeek\DeepSeekProvider; @@ -67,3 +69,10 @@ protected function request(array $payload): array expect($this->provider->lastPayload)->toBe([]); }); }); + +describe('DeepSeekProvider tool-selection capability', function () { + it('declares what it can force, so callers can ask instead of catching', function () { + expect(is_subclass_of(DeepSeekProvider::class, NamedToolSelectableInterface::class))->toBeTrue(); + expect(is_subclass_of(DeepSeekProvider::class, ToolSelectableInterface::class))->toBeTrue(); + }); +});