Skip to content

Commit f363f9a

Browse files
authored
Handle wrapped prompt send timeouts and empty responses in red team retry logic (#43977)
* Handle wrapped prompt send timeouts and empty responses in red team retry logic * code review comments
1 parent 395044e commit f363f9a

File tree

1 file changed

+50
-2
lines changed

1 file changed

+50
-2
lines changed

sdk/evaluation/azure-ai-evaluation/azure/ai/evaluation/red_team/_orchestrator_manager.py

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)