From 56881ba32645edfc279df55b2fb28cd02ab14f99 Mon Sep 17 00:00:00 2001 From: Vitalii Kyktov Date: Fri, 12 Sep 2025 12:12:14 +0200 Subject: [PATCH 1/3] [Platform][OpenAI] Add AuthenticationException for better error handling When OpenAI API returns 401 (authentication error), users now get a clear AuthenticationException with the actual API error message instead of the confusing "Response does not contain choices" error. Changes: - Add AuthenticationException class for API authentication failures - Update OpenAI ResultConverter to check HTTP 401 status before processing - Add test coverage for authentication error scenarios Co-authored-by: Dmytro Liashko Fixes symfony/ai#528 --- .../src/Bridge/OpenAi/Gpt/ResultConverter.php | 8 ++++++++ .../src/Exception/AuthenticationException.php | 20 +++++++++++++++++++ .../Bridge/OpenAi/Gpt/ResultConverterTest.php | 18 +++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 src/platform/src/Exception/AuthenticationException.php diff --git a/src/platform/src/Bridge/OpenAi/Gpt/ResultConverter.php b/src/platform/src/Bridge/OpenAi/Gpt/ResultConverter.php index c973b1c0e..8f9acd63a 100644 --- a/src/platform/src/Bridge/OpenAi/Gpt/ResultConverter.php +++ b/src/platform/src/Bridge/OpenAi/Gpt/ResultConverter.php @@ -12,6 +12,7 @@ namespace Symfony\AI\Platform\Bridge\OpenAi\Gpt; use Symfony\AI\Platform\Bridge\OpenAi\Gpt; +use Symfony\AI\Platform\Exception\AuthenticationException; use Symfony\AI\Platform\Exception\ContentFilterException; use Symfony\AI\Platform\Exception\RuntimeException; use Symfony\AI\Platform\Model; @@ -42,6 +43,13 @@ public function supports(Model $model): bool public function convert(RawResultInterface|RawHttpResult $result, array $options = []): ResultInterface { + $response = $result->getObject(); + + if (401 === $response->getStatusCode()) { + $errorMessage = json_decode($response->getContent(false), true)['error']['message']; + throw new AuthenticationException($errorMessage); + } + if ($options['stream'] ?? false) { return new StreamResult($this->convertStream($result->getObject())); } diff --git a/src/platform/src/Exception/AuthenticationException.php b/src/platform/src/Exception/AuthenticationException.php new file mode 100644 index 000000000..f75b55667 --- /dev/null +++ b/src/platform/src/Exception/AuthenticationException.php @@ -0,0 +1,20 @@ + + * @author Dmytro Liashko + */ +class AuthenticationException extends RuntimeException +{ +} diff --git a/src/platform/tests/Bridge/OpenAi/Gpt/ResultConverterTest.php b/src/platform/tests/Bridge/OpenAi/Gpt/ResultConverterTest.php index 032769f4f..be1694ae0 100644 --- a/src/platform/tests/Bridge/OpenAi/Gpt/ResultConverterTest.php +++ b/src/platform/tests/Bridge/OpenAi/Gpt/ResultConverterTest.php @@ -16,6 +16,7 @@ use PHPUnit\Framework\Attributes\UsesClass; use PHPUnit\Framework\TestCase; use Symfony\AI\Platform\Bridge\OpenAi\Gpt\ResultConverter; +use Symfony\AI\Platform\Exception\AuthenticationException; use Symfony\AI\Platform\Exception\ContentFilterException; use Symfony\AI\Platform\Exception\RuntimeException; use Symfony\AI\Platform\Result\ChoiceResult; @@ -155,6 +156,23 @@ public function getResponse(): ResponseInterface $converter->convert(new RawHttpResult($httpResponse)); } + public function testThrowsAuthenticationExceptionOnInvalidApiKey() + { + $converter = new ResultConverter(); + $httpResponse = self::createMock(ResponseInterface::class); + $httpResponse->method('getStatusCode')->willReturn(401); + $httpResponse->method('getContent')->willReturn(json_encode([ + 'error' => [ + 'message' => 'Invalid API key provided: sk-invalid' + ], + ])); + + $this->expectException(AuthenticationException::class); + $this->expectExceptionMessage('Invalid API key provided: sk-invalid'); + + $converter->convert(new RawHttpResult($httpResponse)); + } + public function testThrowsExceptionWhenNoChoices() { $converter = new ResultConverter(); From 7da46cb3bb7561ceee58a3ef9e895a897005e55f Mon Sep 17 00:00:00 2001 From: vitality Date: Fri, 12 Sep 2025 12:56:15 +0200 Subject: [PATCH 2/3] [Platform][OpenAI] simplify authentication error test structure Remove unnecessary JSON fields from mock response to focus on the essential error message extraction logic. --- src/platform/tests/Bridge/OpenAi/Gpt/ResultConverterTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/tests/Bridge/OpenAi/Gpt/ResultConverterTest.php b/src/platform/tests/Bridge/OpenAi/Gpt/ResultConverterTest.php index be1694ae0..447397b10 100644 --- a/src/platform/tests/Bridge/OpenAi/Gpt/ResultConverterTest.php +++ b/src/platform/tests/Bridge/OpenAi/Gpt/ResultConverterTest.php @@ -163,7 +163,7 @@ public function testThrowsAuthenticationExceptionOnInvalidApiKey() $httpResponse->method('getStatusCode')->willReturn(401); $httpResponse->method('getContent')->willReturn(json_encode([ 'error' => [ - 'message' => 'Invalid API key provided: sk-invalid' + 'message' => 'Invalid API key provided: sk-invalid', ], ])); From 193b56aeb88bf329df4f7d7bac548ec3ef0a89fd Mon Sep 17 00:00:00 2001 From: vitality Date: Fri, 12 Sep 2025 13:09:02 +0200 Subject: [PATCH 3/3] [Platform][OpenAI] remove incomplete @author tags from AuthenticationException Keep the class clean without partial author information until the full implementation is complete. --- src/platform/src/Exception/AuthenticationException.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/platform/src/Exception/AuthenticationException.php b/src/platform/src/Exception/AuthenticationException.php index f75b55667..4fe78b41f 100644 --- a/src/platform/src/Exception/AuthenticationException.php +++ b/src/platform/src/Exception/AuthenticationException.php @@ -3,6 +3,8 @@ /* * This file is part of the Symfony package. * + * (c) Fabien Potencier + * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */