66
77use EchoLabs \Prism \Contracts \PrismRequest ;
88use EchoLabs \Prism \Exceptions \PrismException ;
9+ use EchoLabs \Prism \Exceptions \PrismRateLimitedException ;
10+ use EchoLabs \Prism \ValueObjects \ProviderRateLimit ;
911use EchoLabs \Prism \ValueObjects \ProviderResponse ;
1012use Illuminate \Http \Client \PendingRequest ;
1113use Illuminate \Http \Client \Response ;
14+ use Illuminate \Support \Arr ;
15+ use Illuminate \Support \Carbon ;
16+ use Illuminate \Support \Str ;
1217use Throwable ;
1318
1419abstract class AnthropicHandlerAbstract
@@ -35,17 +40,7 @@ public function handle(PrismRequest $request): ProviderResponse
3540 throw PrismException::providerRequestError ($ this ->request ->model , $ e ); // @phpstan-ignore property.notFound
3641 }
3742
38- $ data = $ this ->httpResponse ->json ();
39-
40- if (data_get ($ data , 'type ' ) === 'error ' ) {
41- throw PrismException::providerResponseError (vsprintf (
42- 'Anthropic Error: [%s] %s ' ,
43- [
44- data_get ($ data , 'error.type ' , 'unknown ' ),
45- data_get ($ data , 'error.message ' ),
46- ]
47- ));
48- }
43+ $ this ->handleResponseErrors ();
4944
5045 return $ this ->buildProviderResponse ();
5146 }
@@ -75,4 +70,62 @@ protected function extractText(array $data): string
7570 return $ text ;
7671 }, '' );
7772 }
73+
74+ protected function handleResponseErrors (): void
75+ {
76+ if ($ this ->httpResponse ->getStatusCode () === 429 ) {
77+ $ this ->rateLimitException ();
78+ }
79+
80+ $ data = $ this ->httpResponse ->json ();
81+
82+ if (data_get ($ data , 'type ' ) === 'error ' ) {
83+ throw PrismException::providerResponseError (vsprintf (
84+ 'Anthropic Error: [%s] %s ' ,
85+ [
86+ data_get ($ data , 'error.type ' , 'unknown ' ),
87+ data_get ($ data , 'error.message ' ),
88+ ]
89+ ));
90+ }
91+ }
92+
93+ protected function rateLimitException (): void
94+ {
95+ $ rate_limits = [];
96+
97+ foreach ($ this ->httpResponse ->getHeaders () as $ headerName => $ headerValues ) {
98+ if (Str::startsWith ($ headerName , 'anthropic-ratelimit- ' ) === false ) {
99+ continue ;
100+ }
101+
102+ $ limit_name = Str::of ($ headerName )->after ('anthropic-ratelimit- ' )->beforeLast ('- ' )->toString ();
103+
104+ $ field_name = Str::of ($ headerName )->afterLast ('- ' )->toString ();
105+
106+ $ rate_limits [$ limit_name ][$ field_name ] = $ headerValues [0 ];
107+ }
108+
109+ $ rate_limits = Arr::map ($ rate_limits , function ($ fields , $ limit_name ): ProviderRateLimit {
110+ $ resets_at = data_get ($ fields , 'reset ' );
111+
112+ return new ProviderRateLimit (
113+ name: $ limit_name ,
114+ limit: data_get ($ fields , 'limit ' )
115+ ? (int ) data_get ($ fields , 'limit ' )
116+ : null ,
117+ remaining: data_get ($ fields , 'remaining ' )
118+ ? (int ) data_get ($ fields , 'remaining ' )
119+ : null ,
120+ resetsAt: data_get ($ fields , 'reset ' ) ? new Carbon ($ resets_at ) : null
121+ );
122+ });
123+
124+ throw PrismRateLimitedException::make (
125+ rateLimits: array_values ($ rate_limits ),
126+ retryAfter: $ this ->httpResponse ->hasHeader ('retry-after ' )
127+ ? (int ) $ this ->httpResponse ->getHeader ('retry-after ' )[0 ]
128+ : null
129+ );
130+ }
78131}
0 commit comments