@@ -58,6 +58,7 @@ def network_retry_decorator(retry_config, logger, strategy_name, risk_category_n
5858 def decorator (func ):
5959 @retry (** retry_config ["network_retry" ])
6060 async def wrapper (* args , ** kwargs ):
61+ prompt_detail = f" for prompt { prompt_idx } " if prompt_idx is not None else ""
6162 try :
6263 return await func (* args , ** kwargs )
6364 except (
@@ -72,12 +73,59 @@ async def wrapper(*args, **kwargs):
7273 httpcore .ReadTimeout ,
7374 httpx .HTTPStatusError ,
7475 ) as e :
75- prompt_detail = f" for prompt { prompt_idx } " if prompt_idx is not None else ""
7676 logger .warning (
77- f"Network error{ prompt_detail } for { strategy_name } /{ risk_category_name } : { type (e ).__name__ } : { str (e )} "
77+ f"Retrying network error{ prompt_detail } for { strategy_name } /{ risk_category_name } : { type (e ).__name__ } : { str (e )} "
7878 )
7979 await asyncio .sleep (2 )
8080 raise
81+ except ValueError as e :
82+ # Treat missing converted prompt text as a transient transport issue so tenacity will retry.
83+ if "Converted prompt text is None" in str (e ):
84+ logger .warning (
85+ f"Endpoint produced empty converted prompt{ prompt_detail } for { strategy_name } /{ risk_category_name } ; retrying."
86+ )
87+ await asyncio .sleep (2 )
88+ raise httpx .HTTPError (
89+ "Converted prompt text is None; treating as transient endpoint failure"
90+ ) from e
91+ raise
92+ except Exception as e :
93+ message = str (e )
94+ cause = e .__cause__
95+
96+ def _is_network_cause (exc : BaseException ) -> bool :
97+ return isinstance (
98+ exc ,
99+ (
100+ httpx .HTTPError ,
101+ httpx .ConnectTimeout ,
102+ httpx .ReadTimeout ,
103+ httpcore .ReadTimeout ,
104+ asyncio .TimeoutError ,
105+ ConnectionError ,
106+ TimeoutError ,
107+ OSError ,
108+ ),
109+ )
110+
111+ def _is_converted_prompt_error (exc : BaseException ) -> bool :
112+ return isinstance (exc , ValueError ) and "Converted prompt text is None" in str (exc )
113+
114+ if (
115+ "Error sending prompt with conversation ID" in message
116+ and cause
117+ and (_is_network_cause (cause ) or _is_converted_prompt_error (cause ))
118+ ):
119+ logger .warning (
120+ f"Wrapped network error{ prompt_detail } for { strategy_name } /{ risk_category_name } : { message } . Retrying."
121+ )
122+ await asyncio .sleep (2 )
123+ if _is_converted_prompt_error (cause ):
124+ raise httpx .HTTPError (
125+ "Converted prompt text is None; treating as transient endpoint failure"
126+ ) from cause
127+ raise httpx .HTTPError (message ) from cause
128+ raise
81129
82130 return wrapper
83131
0 commit comments