a2aproject / a2aproject/a2a-python

[Feat]: Add RetryTransport for automatic retry with exponential backoff

Aperta
#871 6 commenti 0 reazioni 1 assegnatario Assegnata a @cchinchilla-dev Vedi su GitHub
component: client
Lingua principale
Python
Stelle
2.1k
Fork
496
Merge medio
4g 17h
PR unite (30g)
12

Descrizione

### Is your feature request related to a problem? Please describe.

The SDK's transports (`JsonRpcTransport`, `RestTransport`, `GrpcTransport`) raise immediately on transient failures (network errors, timeouts, rate limits, 5xx responses, gRPC `UNAVAILABLE`/`RESOURCE_EXHAUSTED`) with no built-in retry mechanism. Every caller has to reimplement the same retry/backoff loop, and to do it correctly they have to inspect `__cause__` chains because `A2AClientError` doesn't expose HTTP status codes or gRPC codes directly.

### Describe the solution you'd like

A `RetryTransport` decorator wrapping any `ClientTransport`, mirroring the existing `TenantTransportDecorator` pattern:

```python
inner = JsonRpcTransport(httpx_client=client, agent_card=card)
transport = RetryTransport(base=inner, max_retries=3)
```

The default predicate retries on:
- `A2AClientTimeoutError` (always).
- `A2AClientError` chained from `httpx.RequestError` or `httpx.HTTPStatusError(408/429/502/503/504)`.
- `A2AClientError` chained from `grpc.aio.AioRpcError(UNAVAILABLE/RESOURCE_EXHAUSTED)`.
- Domain errors (`TaskNotFoundError`, etc.) are never retried.

`retry_predicate` is configurable for custom logic, and `on_retry` is exposed as a hook for logging/metrics. Streaming methods (`send_message_streaming`, `subscribe`) only retry before the first event is yielded.

### Describe alternatives you've considered

- Retry via `ClientCallInterceptor` — not feasible; `after()` only fires on successful results, so interceptors never see exceptions.
- Transport-specific retry (httpx custom transport, gRPC `retryPolicy` / `UnaryUnaryClientInterceptor`) — operates below the SDK's exception layer, requires two separate configurations, and doesn't cover all three transports uniformly.
- Retry inside each transport implementation — triples the logic across `JsonRpcTransport`, `RestTransport`, `GrpcTransport` and forces every future transport to reimplement it.

### Additional context

Purely additive: new `RetryTransport` class, tests, and one export change. No new dependencies — the `grpc` import is conditional, matching the SDK's existing pattern. `ClientFactory` integration is left as a follow-up.

### Code of Conduct

- [x] I agree to follow this project's Code of Conduct

Guida per i contributori

Apri la guida per i contributori

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.