77from __future__ import annotations
88import logging
99import requests
10+ import time
1011from datetime import datetime
1112from typing import Any , Dict , List , Optional , Protocol
1213
@@ -280,8 +281,9 @@ def get_evaluation_rows(
280281 from_timestamp : Optional [datetime ] = None ,
281282 to_timestamp : Optional [datetime ] = None ,
282283 include_tool_calls : bool = True ,
283- sleep_between_gets : float = 2.5 ,
284- max_retries : int = 3 ,
284+ backend_sleep_between_gets : float = 0.1 ,
285+ backend_max_retries : int = 3 ,
286+ proxy_max_retries : int = 3 ,
285287 span_name : Optional [str ] = None ,
286288 converter : Optional [TraceDictConverter ] = None ,
287289 ) -> List [EvaluationRow ]:
@@ -303,8 +305,9 @@ def get_evaluation_rows(
303305 from_timestamp: Explicit start time (ISO format)
304306 to_timestamp: Explicit end time (ISO format)
305307 include_tool_calls: Whether to include tool calling traces
306- sleep_between_gets: Sleep time between trace.get() calls (handled by proxy)
307- max_retries: Maximum retries for rate limit errors (handled by proxy)
308+ backend_sleep_between_gets: Sleep time between backend trace fetches (passed to proxy)
309+ backend_max_retries: Maximum retries for backend operations (passed to proxy)
310+ proxy_max_retries: Maximum retries when proxy returns 404 (client-side retries with exponential backoff)
308311 span_name: If provided, extract messages from generations within this named span
309312 converter: Optional custom converter implementing TraceDictConverter protocol.
310313 If provided, this will be used instead of the default conversion logic.
@@ -336,25 +339,60 @@ def get_evaluation_rows(
336339 "hours_back" : hours_back ,
337340 "from_timestamp" : from_timestamp .isoformat () if from_timestamp else None ,
338341 "to_timestamp" : to_timestamp .isoformat () if to_timestamp else None ,
339- "sleep_between_gets" : sleep_between_gets ,
340- "max_retries" : max_retries ,
342+ "sleep_between_gets" : backend_sleep_between_gets ,
343+ "max_retries" : backend_max_retries ,
341344 }
342345
343346 # Remove None values
344347 params = {k : v for k , v in params .items () if v is not None }
345348
346- # Make request to proxy
349+ # Make request to proxy with retry logic
347350 if self .project_id :
348351 url = f"{ self .base_url } /v1/project_id/{ self .project_id } /traces"
349352 else :
350353 url = f"{ self .base_url } /v1/traces"
351354
352- try :
353- response = requests .get (url , params = params , timeout = self .timeout )
354- response .raise_for_status ()
355- result = response .json ()
356- except requests .exceptions .RequestException as e :
357- logger .error ("Failed to fetch traces from proxy: %s" , e )
355+ # Retry loop for handling backend indexing delays (proxy returns 404)
356+ result = None
357+ for attempt in range (proxy_max_retries ):
358+ try :
359+ response = requests .get (url , params = params , timeout = self .timeout )
360+ response .raise_for_status ()
361+ result = response .json ()
362+ break # Success, exit retry loop
363+ except requests .exceptions .HTTPError as e :
364+ error_msg = str (e )
365+ should_retry = False
366+
367+ # Try to extract detail message from response
368+ if e .response is not None :
369+ try :
370+ error_detail = e .response .json ().get ("detail" , "" )
371+ error_msg = error_detail or e .response .text
372+
373+ # Retry on 404 if it's due to incomplete/missing traces (backend still indexing)
374+ if e .response .status_code == 404 and (
375+ "Incomplete traces" in error_detail or "No traces found" in error_detail
376+ ):
377+ should_retry = True
378+ except Exception :
379+ error_msg = e .response .text
380+
381+ if should_retry and attempt < proxy_max_retries - 1 :
382+ sleep_time = 2 ** (attempt + 1 )
383+ logger .warning (error_msg )
384+ time .sleep (sleep_time )
385+ else :
386+ # Final retry or non-retryable error
387+ logger .error ("Failed to fetch traces from proxy: %s" , error_msg )
388+ return eval_rows
389+ except requests .exceptions .RequestException as e :
390+ # Non-HTTP errors (network issues, timeouts, etc.)
391+ logger .error ("Failed to fetch traces from proxy: %s" , str (e ))
392+ return eval_rows
393+
394+ if result is None :
395+ logger .error ("Failed to fetch traces after %d retries" , proxy_max_retries )
358396 return eval_rows
359397
360398 # Extract traces from response
0 commit comments