@@ -46,7 +46,23 @@ pub enum RecvError {
4646 Client ( #[ from] fig_api_client:: Error ) ,
4747 #[ error( "{0}" ) ]
4848 Json ( #[ from] serde_json:: Error ) ,
49+ /// An error was encountered while waiting for the next event in the stream after a noticeably
50+ /// long wait time.
51+ ///
52+ /// *Context*: the client can throw an error after ~100s of waiting with no response, likely due
53+ /// to an exceptionally complex tool use taking too long to generate.
54+ #[ error( "The stream ended after {}s: {source}" , . duration. as_secs( ) ) ]
55+ StreamTimeout {
56+ source : fig_api_client:: Error ,
57+ duration : std:: time:: Duration ,
58+ } ,
4959 /// Unexpected end of stream while receiving a tool use.
60+ ///
61+ /// *Context*: the stream can unexpectedly end with `Ok(None)` while waiting for an
62+ /// exceptionally complex tool use. This is due to some proxy server dropping idle
63+ /// connections after some timeout is reached.
64+ ///
65+ /// TODO: should this be removed?
5066 #[ error( "Unexpected end of stream for tool: {} with id: {}" , . name, . tool_use_id) ]
5167 UnexpectedToolUseEos {
5268 tool_use_id : String ,
@@ -154,7 +170,7 @@ impl ResponseParser {
154170 } ;
155171 return Ok ( ResponseEvent :: EndStream { message } ) ;
156172 } ,
157- Err ( err) => return Err ( err. into ( ) ) ,
173+ Err ( err) => return Err ( err) ,
158174 }
159175 }
160176 }
@@ -221,7 +237,7 @@ impl ResponseParser {
221237 }
222238
223239 /// Returns the next event in the [SendMessageOutput] without consuming it.
224- async fn peek ( & mut self ) -> Result < Option < & ChatResponseStream > , fig_api_client :: Error > {
240+ async fn peek ( & mut self ) -> Result < Option < & ChatResponseStream > , RecvError > {
225241 if self . peek . is_some ( ) {
226242 return Ok ( self . peek . as_ref ( ) ) ;
227243 }
@@ -235,14 +251,27 @@ impl ResponseParser {
235251 }
236252
237253 /// Consumes the next [SendMessageOutput] event.
238- async fn next ( & mut self ) -> Result < Option < ChatResponseStream > , fig_api_client :: Error > {
254+ async fn next ( & mut self ) -> Result < Option < ChatResponseStream > , RecvError > {
239255 if let Some ( ev) = self . peek . take ( ) {
240256 return Ok ( Some ( ev) ) ;
241257 }
242258 trace ! ( "Attempting to recv next event" ) ;
243- let r = self . response . recv ( ) . await ?;
244- trace ! ( ?r, "Received new event" ) ;
245- Ok ( r)
259+ let start = std:: time:: Instant :: now ( ) ;
260+ let result = self . response . recv ( ) . await ;
261+ let duration = std:: time:: Instant :: now ( ) . duration_since ( start) ;
262+ match result {
263+ Ok ( r) => {
264+ trace ! ( ?r, "Received new event" ) ;
265+ Ok ( r)
266+ } ,
267+ Err ( err) => {
268+ if duration. as_secs ( ) >= 59 {
269+ Err ( RecvError :: StreamTimeout { source : err, duration } )
270+ } else {
271+ Err ( err. into ( ) )
272+ }
273+ } ,
274+ }
246275 }
247276}
248277
0 commit comments