Files
2026-07-13 12:39:17 +08:00

115 lines
3.7 KiB
Python

import asyncio
import inspect
from agents import (
Agent,
ModelRetrySettings,
ModelSettings,
RetryDecision,
RunConfig,
Runner,
retry_policies,
)
def format_error(error: object) -> str:
if not isinstance(error, BaseException):
return "Unknown error"
return str(error) or error.__class__.__name__
async def main() -> None:
apply_policies = retry_policies.any(
# On OpenAI-backed models, provider_suggested() follows provider retry advice,
# including fallback retryable statuses when x-should-retry is absent
# (for example 408/409/429/5xx).
retry_policies.provider_suggested(),
retry_policies.retry_after(),
retry_policies.network_error(),
retry_policies.http_status([408, 409, 429, 500, 502, 503, 504]),
)
async def policy(context) -> bool | RetryDecision:
raw_decision = apply_policies(context)
decision: bool | RetryDecision
if inspect.isawaitable(raw_decision):
decision = await raw_decision
else:
decision = raw_decision
if isinstance(decision, RetryDecision):
if not decision.retry:
print(
f"[retry] stop after attempt {context.attempt}/{context.max_retries + 1}: "
f"{format_error(context.error)}"
)
return False
print(
" | ".join(
part
for part in [
f"[retry] retry attempt {context.attempt}/{context.max_retries + 1}",
(
f"waiting {decision.delay:.2f}s"
if decision.delay is not None
else "using default backoff"
),
f"reason: {decision.reason}" if decision.reason else None,
f"error: {format_error(context.error)}",
]
if part is not None
)
)
return decision
if not decision:
print(
f"[retry] stop after attempt {context.attempt}/{context.max_retries + 1}: "
f"{format_error(context.error)}"
)
return decision
retry = ModelRetrySettings(
max_retries=4,
backoff={
"initial_delay": 0.5,
"max_delay": 5.0,
"multiplier": 2.0,
"jitter": True,
},
policy=policy,
)
# RunConfig-level model_settings are shared defaults for the run.
# If an Agent also defines model_settings, the Agent wins for overlapping
# keys, while nested objects like retry/backoff are merged.
run_config = RunConfig(model_settings=ModelSettings(retry=retry))
agent = Agent(
name="Assistant",
instructions="You are a concise assistant. Answer in 3 short bullet points at most.",
# Prefix with litellm/ to route this request through the LiteLLM adapter.
model="litellm/openai/gpt-4o-mini",
# This Agent repeats the same retry config for clarity. In real code you
# can keep shared defaults in RunConfig and only put per-agent overrides
# here when you need different retry behavior.
model_settings=ModelSettings(retry=retry),
)
print(
"Retry support is configured. You will only see [retry] logs if a transient failure happens."
)
result = await Runner.run(
agent,
"Explain exponential backoff for API retries in plain English.",
run_config=run_config,
)
print("\nFinal output:\n")
print(result.final_output)
if __name__ == "__main__":
asyncio.run(main())