diff --git a/CHANGELOG.md b/CHANGELOG.md index 52a1a81..4fd2eec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.ht ## [Unreleased] +### Fixed + +- `CalleCalls.wait_for_result` keeps polling when GET returns `call_not_ready` + instead of treating that documented "not terminal yet" code as a hard failure. + ## [0.7.1] - 2026-09-04 ### Added diff --git a/src/calle/calls.py b/src/calle/calls.py index a3cb6ee..0101de4 100644 --- a/src/calle/calls.py +++ b/src/calle/calls.py @@ -4,7 +4,7 @@ import httpx -from calle.errors import CalleConnectionError, CalleTimeoutError, api_error_from_response +from calle.errors import CalleAPIError, CalleConnectionError, CalleTimeoutError, api_error_from_response JsonObject = dict[str, Any] @@ -56,9 +56,14 @@ def wait_for_result( ) -> JsonObject: deadline = time.monotonic() + timeout_seconds while time.monotonic() <= deadline: - call = self.get(call_id) - if call.get("status") in {"completed", "failed", "canceled"}: - return call + try: + call = self.get(call_id) + except CalleAPIError as exc: + if exc.code != "call_not_ready": + raise + else: + if call.get("status") in {"completed", "failed", "canceled"}: + return call time.sleep(interval_seconds) raise CalleTimeoutError(f"Timed out waiting for CALL-E call {call_id}.") diff --git a/tests/test_calls.py b/tests/test_calls.py index b0b9796..b1df214 100644 --- a/tests/test_calls.py +++ b/tests/test_calls.py @@ -153,6 +153,30 @@ def test_wait_for_result_returns_failed_call() -> None: assert call["failure_code"] == "no_answer" +@respx.mock +def test_wait_for_result_retries_call_not_ready() -> None: + route = respx.get("https://api.heycall-e.com/v1/calls/call_123").mock( + side_effect=[ + httpx.Response( + 409, + json={ + "error": { + "code": "call_not_ready", + "message": "The call task has not reached a terminal state.", + } + }, + ), + httpx.Response(200, json=COMPLETED_CALL), + ] + ) + client = CalleClient(api_key="key_test", base_url="https://api.heycall-e.com") + + call = client.calls.wait_for_result("call_123", interval_seconds=0.001, timeout_seconds=0.5) + + assert call["status"] == "completed" + assert route.call_count == 2 + + @respx.mock def test_wait_for_result_raises_timeout() -> None: queued = {**COMPLETED_CALL, "status": "queued", "structured_result": None, "completed_at": None}