| 2163 | @pytest.mark.respx(base_url=base_url) |
| 2164 | @pytest.mark.parametrize("failure_mode", ["status", "exception"]) |
| 2165 | async def test_retries_taken( |
| 2166 | self, |
| 2167 | async_client: AsyncAnthropic, |
| 2168 | failures_before_success: int, |
| 2169 | failure_mode: Literal["status", "exception"], |
| 2170 | respx_mock: MockRouter, |
| 2171 | ) -> None: |
| 2172 | client = async_client.with_options(max_retries=4) |
| 2173 | |
| 2174 | nb_retries = 0 |
| 2175 | |
| 2176 | def retry_handler(_request: httpx.Request) -> httpx.Response: |
| 2177 | nonlocal nb_retries |
| 2178 | if nb_retries < failures_before_success: |
| 2179 | nb_retries += 1 |
| 2180 | if failure_mode == "exception": |
| 2181 | raise RuntimeError("oops") |
| 2182 | return httpx.Response(500) |
| 2183 | return httpx.Response(200) |
| 2184 | |
| 2185 | respx_mock.post("/v1/messages").mock(side_effect=retry_handler) |
| 2186 | |
| 2187 | response = await client.messages.with_raw_response.create( |
| 2188 | max_tokens=1024, |
| 2189 | messages=[ |
| 2190 | { |
| 2191 | "content": "Hello, world", |
| 2192 | "role": "user", |
| 2193 | } |
| 2194 | ], |
| 2195 | model="claude-opus-4-6", |
| 2196 | ) |
| 2197 | |
| 2198 | assert response.retries_taken == failures_before_success |
| 2199 | assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success |
| 2200 | |
| 2201 | @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) |
| 2202 | @mock.patch("anthropic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) |