11use anyhow:: { Context , Result } ;
22use async_trait:: async_trait;
3- use reqwest:: Client ;
3+ use reqwest:: { Client , StatusCode } ;
44use serde:: { Deserialize , Serialize } ;
5+ use std:: time:: Duration ;
6+ use tokio:: time:: sleep;
57use crate :: adapters:: llm:: { LLMAdapter , LLMRequest , LLMResponse , ModelConfig , Usage } ;
68
79pub struct AnthropicAdapter {
@@ -66,6 +68,42 @@ impl AnthropicAdapter {
6668 base_url,
6769 } )
6870 }
71+
72+ async fn send_with_retry < F > ( & self , mut make_request : F ) -> Result < reqwest:: Response >
73+ where
74+ F : FnMut ( ) -> reqwest:: RequestBuilder ,
75+ {
76+ const MAX_RETRIES : usize = 2 ;
77+ const BASE_DELAY_MS : u64 = 250 ;
78+
79+ for attempt in 0 ..=MAX_RETRIES {
80+ match make_request ( ) . send ( ) . await {
81+ Ok ( response) => {
82+ if response. status ( ) . is_success ( ) {
83+ return Ok ( response) ;
84+ }
85+
86+ let status = response. status ( ) ;
87+ let body = response. text ( ) . await . unwrap_or_default ( ) ;
88+ if is_retryable_status ( status) && attempt < MAX_RETRIES {
89+ sleep ( Duration :: from_millis ( BASE_DELAY_MS * ( attempt as u64 + 1 ) ) ) . await ;
90+ continue ;
91+ }
92+
93+ anyhow:: bail!( "Anthropic API error ({}): {}" , status, body) ;
94+ }
95+ Err ( err) => {
96+ if attempt < MAX_RETRIES {
97+ sleep ( Duration :: from_millis ( BASE_DELAY_MS * ( attempt as u64 + 1 ) ) ) . await ;
98+ continue ;
99+ }
100+ return Err ( err. into ( ) ) ;
101+ }
102+ }
103+ }
104+
105+ anyhow:: bail!( "Anthropic request failed after retries" ) ;
106+ }
69107}
70108
71109#[ async_trait]
@@ -86,21 +124,18 @@ impl LLMAdapter for AnthropicAdapter {
86124 system : request. system_prompt ,
87125 } ;
88126
89- let response = self . client
90- . post ( format ! ( "{}/messages" , self . base_url) )
91- . header ( "x-api-key" , & self . api_key )
92- . header ( "anthropic-version" , "2023-06-01" )
93- . header ( "anthropic-beta" , "messages-2023-12-15" )
94- . header ( "Content-Type" , "application/json" )
95- . json ( & anthropic_request)
96- . send ( )
97- . await
98- . context ( "Failed to send request to Anthropic" ) ?;
99-
100- if !response. status ( ) . is_success ( ) {
101- let error_text = response. text ( ) . await ?;
102- anyhow:: bail!( "Anthropic API error: {}" , error_text) ;
103- }
127+ let url = format ! ( "{}/messages" , self . base_url) ;
128+ let response = self . send_with_retry ( || {
129+ self . client
130+ . post ( & url)
131+ . header ( "x-api-key" , & self . api_key )
132+ . header ( "anthropic-version" , "2023-06-01" )
133+ . header ( "anthropic-beta" , "messages-2023-12-15" )
134+ . header ( "Content-Type" , "application/json" )
135+ . json ( & anthropic_request)
136+ } )
137+ . await
138+ . context ( "Failed to send request to Anthropic" ) ?;
104139
105140 let anthropic_response: AnthropicResponse = response. json ( ) . await
106141 . context ( "Failed to parse Anthropic response" ) ?;
@@ -131,4 +166,8 @@ impl LLMAdapter for AnthropicAdapter {
131166 fn _model_name ( & self ) -> & str {
132167 & self . config . model_name
133168 }
134- }
169+ }
170+
171+ fn is_retryable_status ( status : StatusCode ) -> bool {
172+ status == StatusCode :: TOO_MANY_REQUESTS || status. is_server_error ( )
173+ }
0 commit comments