Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/Exceptions/RateLimitException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace OpenAI\Exceptions;

use Exception;
use Psr\Http\Message\ResponseInterface;

final class RateLimitException extends Exception
{
public function __construct(public ResponseInterface $response)
{
parent::__construct('Request rate limit has been exceeded.');
}
}
14 changes: 14 additions & 0 deletions src/Transporters/HttpTransporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use OpenAI\Contracts\TransporterContract;
use OpenAI\Enums\Transporter\ContentType;
use OpenAI\Exceptions\ErrorException;
use OpenAI\Exceptions\RateLimitException;
use OpenAI\Exceptions\TransporterException;
use OpenAI\Exceptions\UnserializableResponse;
use OpenAI\ValueObjects\Transporter\AdaptableResponse;
Expand Down Expand Up @@ -51,6 +52,7 @@ public function requestObject(Payload $payload): Response

$contents = (string) $response->getBody();

$this->throwIfRateLimit($response);
$this->throwIfJsonError($response, $contents);

try {
Expand Down Expand Up @@ -78,6 +80,7 @@ public function requestStringOrObject(Payload $payload): AdaptableResponse
return AdaptableResponse::from($contents, $response->getHeaders());
}

$this->throwIfRateLimit($response);
$this->throwIfJsonError($response, $contents);

try {
Expand All @@ -101,6 +104,7 @@ public function requestContent(Payload $payload): string

$contents = (string) $response->getBody();

$this->throwIfRateLimit($response);
$this->throwIfJsonError($response, $contents);

return $contents;
Expand All @@ -115,6 +119,7 @@ public function requestStream(Payload $payload): ResponseInterface

$response = $this->sendRequest(fn () => ($this->streamHandler)($request));

$this->throwIfRateLimit($response);
$this->throwIfJsonError($response, $response);

return $response;
Expand All @@ -133,6 +138,15 @@ private function sendRequest(Closure $callable): ResponseInterface
}
}

private function throwIfRateLimit(ResponseInterface $response): void
{
if ($response->getStatusCode() !== 429) {
return;
}

throw new RateLimitException($response);
}

private function throwIfJsonError(ResponseInterface $response, string|ResponseInterface $contents): void
{
if ($response->getStatusCode() < 400) {
Expand Down
26 changes: 19 additions & 7 deletions tests/Transporters/HttpTransporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use GuzzleHttp\Psr7\Response;
use OpenAI\Enums\Transporter\ContentType;
use OpenAI\Exceptions\ErrorException;
use OpenAI\Exceptions\RateLimitException;
use OpenAI\Exceptions\TransporterException;
use OpenAI\Exceptions\UnserializableResponse;
use OpenAI\Responses\Models\ListResponse;
Expand Down Expand Up @@ -189,7 +190,7 @@
});
})->with('request methods');

test('error type may be null', function (string $requestMethod) {
test('error type may be null on 429', function (string $requestMethod) {
$payload = Payload::list('models');

$response = new Response(429, ['Content-Type' => 'application/json; charset=utf-8'], json_encode([
Expand All @@ -207,12 +208,23 @@
->andReturn($response);

expect(fn () => $this->http->$requestMethod($payload))
->toThrow(function (ErrorException $e) {
expect($e->getMessage())->toBe('You exceeded your current quota, please check')
->and($e->getErrorMessage())->toBe('You exceeded your current quota, please check')
->and($e->getErrorCode())->toBe('quota_exceeded')
->and($e->getErrorType())->toBeNull();
});
->toThrow(RateLimitException::class);
})->with('request methods');

test('429 may not follow OpenAI structure', function (string $requestMethod) {
$payload = Payload::list('models');

$response = new Response(429, ['Content-Type' => 'application/json; charset=utf-8'], json_encode([
'message' => 'Requests rate limit exceeded',
]));

$this->client
->shouldReceive('sendRequest')
->once()
->andReturn($response);

expect(fn () => $this->http->$requestMethod($payload))
->toThrow(RateLimitException::class);
})->with('request methods');

test('error message may be an array', function (string $requestMethod) {
Expand Down