diff --git a/examples/bootstrap.php b/examples/bootstrap.php index bf3892d9..0ede75c1 100644 --- a/examples/bootstrap.php +++ b/examples/bootstrap.php @@ -11,6 +11,7 @@ use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerInterface; +use Symfony\AI\Platform\Exception\ModelException; use Symfony\Component\Console\Logger\ConsoleLogger; use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\Dotenv\Dotenv; @@ -52,3 +53,12 @@ function logger(): LoggerInterface return new ConsoleLogger(new ConsoleOutput($verbosity)); } + +set_exception_handler(function ($exception) { + if ($exception instanceof ModelException) { + echo $exception->getMessage().\PHP_EOL; + exit(1); + } + + throw $exception; +}); diff --git a/src/platform/src/Bridge/Anthropic/ResultConverter.php b/src/platform/src/Bridge/Anthropic/ResultConverter.php index 0ced3f0e..e7cbcefd 100644 --- a/src/platform/src/Bridge/Anthropic/ResultConverter.php +++ b/src/platform/src/Bridge/Anthropic/ResultConverter.php @@ -11,6 +11,7 @@ namespace Symfony\AI\Platform\Bridge\Anthropic; +use Symfony\AI\Platform\Exception\ModelException; use Symfony\AI\Platform\Exception\RuntimeException; use Symfony\AI\Platform\Model; use Symfony\AI\Platform\Result\RawHttpResult; @@ -44,6 +45,10 @@ public function convert(RawHttpResult|RawResultInterface $result, array $options $data = $result->getData(); + if (isset($data['error'])) { + throw new ModelException($data['error']['message'], $data['error']); + } + if (!isset($data['content']) || [] === $data['content']) { throw new RuntimeException('Response does not contain any content'); } diff --git a/src/platform/src/Bridge/OpenAI/GPT/ResultConverter.php b/src/platform/src/Bridge/OpenAI/GPT/ResultConverter.php index ce8bf4a0..5f2328e3 100644 --- a/src/platform/src/Bridge/OpenAI/GPT/ResultConverter.php +++ b/src/platform/src/Bridge/OpenAI/GPT/ResultConverter.php @@ -12,7 +12,7 @@ namespace Symfony\AI\Platform\Bridge\OpenAI\GPT; use Symfony\AI\Platform\Bridge\OpenAI\GPT; -use Symfony\AI\Platform\Exception\ContentFilterException; +use Symfony\AI\Platform\Exception\ModelException; use Symfony\AI\Platform\Exception\RuntimeException; use Symfony\AI\Platform\Model; use Symfony\AI\Platform\Result\Choice; @@ -49,8 +49,8 @@ public function convert(RawResultInterface|RawHttpResult $result, array $options $data = $result->getData(); - if (isset($data['error']['code']) && 'content_filter' === $data['error']['code']) { - throw new ContentFilterException($data['error']['message']); + if (isset($data['error'])) { + throw new ModelException($data['error']['message'], $data['error']); } if (!isset($data['choices'])) { diff --git a/src/platform/src/Bridge/Replicate/Client.php b/src/platform/src/Bridge/Replicate/Client.php index f3483352..f63a7723 100644 --- a/src/platform/src/Bridge/Replicate/Client.php +++ b/src/platform/src/Bridge/Replicate/Client.php @@ -11,6 +11,7 @@ namespace Symfony\AI\Platform\Bridge\Replicate; +use Symfony\AI\Platform\Exception\ModelException; use Symfony\Component\Clock\ClockInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\ResponseInterface; @@ -40,7 +41,11 @@ public function request(string $model, string $endpoint, array $body): ResponseI 'auth_bearer' => $this->apiKey, 'json' => ['input' => $body], ]); - $data = $response->toArray(); + $data = $response->toArray(false); + + if (isset($data['detail'])) { + throw new ModelException($data['detail'], $data); + } while (!\in_array($data['status'], ['succeeded', 'failed', 'canceled'], true)) { $this->clock->sleep(1); // we need to wait until the prediction is ready diff --git a/src/platform/src/Bridge/Voyage/ResultConverter.php b/src/platform/src/Bridge/Voyage/ResultConverter.php index 60a26d17..38a36a0f 100644 --- a/src/platform/src/Bridge/Voyage/ResultConverter.php +++ b/src/platform/src/Bridge/Voyage/ResultConverter.php @@ -11,6 +11,7 @@ namespace Symfony\AI\Platform\Bridge\Voyage; +use Symfony\AI\Platform\Exception\ModelException; use Symfony\AI\Platform\Exception\RuntimeException; use Symfony\AI\Platform\Model; use Symfony\AI\Platform\Result\RawResultInterface; @@ -33,6 +34,10 @@ public function convert(RawResultInterface $result, array $options = []): Result { $result = $result->getData(); + if (isset($result['detail'])) { + throw new ModelException($result['detail']); + } + if (!isset($result['data'])) { throw new RuntimeException('Response does not contain embedding data'); } diff --git a/src/platform/src/Exception/ModelException.php b/src/platform/src/Exception/ModelException.php new file mode 100644 index 00000000..da5e1374 --- /dev/null +++ b/src/platform/src/Exception/ModelException.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\AI\Platform\Exception; + +final class ModelException extends \Exception implements ExceptionInterface +{ + public function __construct( + string $message, + public array $errorDetails = [], + ) { + parent::__construct($message); + } +}