From e77a4f5fb0b2b9f5cd5b2c152c1862aec872889e Mon Sep 17 00:00:00 2001 From: AKSHAT ANAND Date: Sat, 11 Oct 2025 16:06:50 +0000 Subject: [PATCH 1/7] feat: Add wait_until_ready() method for agent deployment polling Resolves #40 - Add wait_until_ready() method to AgentsResource and AsyncAgentsResource - Add AgentDeploymentError and AgentDeploymentTimeoutError exceptions - Add comprehensive test coverage for sync and async variants - Add example usage documentation Users can now call client.agents.wait_until_ready(agent_id) instead of writing custom polling loops to wait for agent deployment completion. The method polls the agent status every 5 seconds (configurable) until: - SUCCESS: Agent reaches STATUS_RUNNING (returns AgentRetrieveResponse) - FAILURE: Agent reaches STATUS_FAILED/STATUS_UNDEPLOYMENT_FAILED/STATUS_DELETED (raises AgentDeploymentError) - TIMEOUT: Agent doesn't reach STATUS_RUNNING within timeout period (raises AgentDeploymentTimeoutError) Default timeout is 300 seconds (5 minutes) and poll_interval is 5 seconds. Both sync and async implementations are provided. --- examples/agent_wait_until_ready.py | 100 +++++++++++++ src/gradient/_exceptions.py | 18 +++ src/gradient/resources/agents/agents.py | 188 +++++++++++++++++++++++ tests/api_resources/test_agents.py | 191 ++++++++++++++++++++++++ 4 files changed, 497 insertions(+) create mode 100644 examples/agent_wait_until_ready.py diff --git a/examples/agent_wait_until_ready.py b/examples/agent_wait_until_ready.py new file mode 100644 index 00000000..66d6dc75 --- /dev/null +++ b/examples/agent_wait_until_ready.py @@ -0,0 +1,100 @@ +""" +Example: Wait for Agent Deployment to Complete + +This example demonstrates how to use the wait_until_ready() method to wait for +an agent to finish deploying before using it. +""" + +from gradient import Gradient +from gradient._exceptions import AgentDeploymentError, AgentDeploymentTimeoutError + +# Initialize the Gradient client +client = Gradient() + +# Create a new agent +agent_response = client.agents.create( + name="My Agent", + instruction="You are a helpful assistant", + model_uuid="", + region="nyc1", +) + +agent_id = agent_response.agent.uuid if agent_response.agent else None + +if agent_id: + print(f"Agent created with ID: {agent_id}") + print("Waiting for agent to be ready...") + + try: + # Wait for the agent to be deployed and ready + # This will poll the agent status every 5 seconds (default) + # and wait up to 5 minutes (default timeout=300 seconds) + ready_agent = client.agents.wait_until_ready( + agent_id, + poll_interval=5.0, # Check every 5 seconds + timeout=300.0, # Wait up to 5 minutes + ) + + if ready_agent.agent and ready_agent.agent.deployment: + print(f"Agent is ready! Status: {ready_agent.agent.deployment.status}") + print(f"Agent URL: {ready_agent.agent.url}") + + # Now you can use the agent + # ... + + except AgentDeploymentError as e: + print(f"Agent deployment failed: {e}") + print(f"Failed status: {e.status}") + + except AgentDeploymentTimeoutError as e: + print(f"Agent deployment timed out: {e}") + print(f"Agent ID: {e.agent_id}") + + except Exception as e: + print(f"Unexpected error: {e}") + + +# Async example +from gradient import AsyncGradient + + +async def main(): + async_client = AsyncGradient() + + # Create a new agent + agent_response = await async_client.agents.create( + name="My Async Agent", + instruction="You are a helpful assistant", + model_uuid="", + region="nyc1", + ) + + agent_id = agent_response.agent.uuid if agent_response.agent else None + + if agent_id: + print(f"Agent created with ID: {agent_id}") + print("Waiting for agent to be ready...") + + try: + # Wait for the agent to be deployed and ready (async) + ready_agent = await async_client.agents.wait_until_ready( + agent_id, + poll_interval=5.0, + timeout=300.0, + ) + + if ready_agent.agent and ready_agent.agent.deployment: + print(f"Agent is ready! Status: {ready_agent.agent.deployment.status}") + print(f"Agent URL: {ready_agent.agent.url}") + + except AgentDeploymentError as e: + print(f"Agent deployment failed: {e}") + print(f"Failed status: {e.status}") + + except AgentDeploymentTimeoutError as e: + print(f"Agent deployment timed out: {e}") + print(f"Agent ID: {e.agent_id}") + + +# Uncomment to run async example +# asyncio.run(main()) diff --git a/src/gradient/_exceptions.py b/src/gradient/_exceptions.py index 5db08573..0ced4aba 100644 --- a/src/gradient/_exceptions.py +++ b/src/gradient/_exceptions.py @@ -15,6 +15,8 @@ "UnprocessableEntityError", "RateLimitError", "InternalServerError", + "AgentDeploymentError", + "AgentDeploymentTimeoutError", ] @@ -106,3 +108,19 @@ class RateLimitError(APIStatusError): class InternalServerError(APIStatusError): pass + + +class AgentDeploymentError(GradientError): + """Raised when an agent deployment fails.""" + + def __init__(self, message: str, status: str) -> None: + super().__init__(message) + self.status = status + + +class AgentDeploymentTimeoutError(GradientError): + """Raised when waiting for an agent deployment times out.""" + + def __init__(self, message: str, agent_id: str) -> None: + super().__init__(message) + self.agent_id = agent_id diff --git a/src/gradient/resources/agents/agents.py b/src/gradient/resources/agents/agents.py index a4d32fca..40405fae 100644 --- a/src/gradient/resources/agents/agents.py +++ b/src/gradient/resources/agents/agents.py @@ -2,6 +2,7 @@ from __future__ import annotations +import time import httpx from .routes import ( @@ -612,6 +613,93 @@ def update_status( cast_to=AgentUpdateStatusResponse, ) + def wait_until_ready( + self, + uuid: str, + *, + timeout: float = 300.0, + poll_interval: float = 5.0, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + ) -> AgentRetrieveResponse: + """Wait for an agent to be ready (deployment status is STATUS_RUNNING). + + This method polls the agent status until it reaches STATUS_RUNNING or a terminal + error state. It handles timeout and deployment failures automatically. + + Args: + uuid: The unique identifier of the agent to wait for + + timeout: Maximum time to wait in seconds (default: 300 seconds / 5 minutes) + + poll_interval: Time to wait between status checks in seconds (default: 5 seconds) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + Returns: + AgentRetrieveResponse: The agent response when it reaches STATUS_RUNNING + + Raises: + AgentDeploymentError: If the agent deployment fails (STATUS_FAILED, + STATUS_UNDEPLOYMENT_FAILED, or STATUS_DELETED) + AgentDeploymentTimeoutError: If the agent doesn't reach STATUS_RUNNING + within the timeout period + ValueError: If uuid is empty + """ + from ..._exceptions import AgentDeploymentError, AgentDeploymentTimeoutError + + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + + start_time = time.time() + + while True: + agent_response = self.retrieve( + uuid, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + ) + + # Check if agent and deployment exist + if agent_response.agent and agent_response.agent.deployment: + status = agent_response.agent.deployment.status + + # Success case + if status == "STATUS_RUNNING": + return agent_response + + # Failure cases + if status in ("STATUS_FAILED", "STATUS_UNDEPLOYMENT_FAILED", "STATUS_DELETED"): + raise AgentDeploymentError( + f"Agent deployment failed with status: {status}", + status=status or "UNKNOWN", + ) + + # Check timeout + elapsed_time = time.time() - start_time + if elapsed_time >= timeout: + current_status = ( + agent_response.agent.deployment.status + if agent_response.agent and agent_response.agent.deployment + else "UNKNOWN" + ) + raise AgentDeploymentTimeoutError( + f"Agent did not reach STATUS_RUNNING within {timeout} seconds. " + f"Current status: {current_status}", + agent_id=uuid, + ) + + # Wait before polling again + time.sleep(poll_interval) + class AsyncAgentsResource(AsyncAPIResource): @cached_property @@ -1108,6 +1196,94 @@ async def update_status( cast_to=AgentUpdateStatusResponse, ) + async def wait_until_ready( + self, + uuid: str, + *, + timeout: float = 300.0, + poll_interval: float = 5.0, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + ) -> AgentRetrieveResponse: + """Wait for an agent to be ready (deployment status is STATUS_RUNNING). + + This method polls the agent status until it reaches STATUS_RUNNING or a terminal + error state. It handles timeout and deployment failures automatically. + + Args: + uuid: The unique identifier of the agent to wait for + + timeout: Maximum time to wait in seconds (default: 300 seconds / 5 minutes) + + poll_interval: Time to wait between status checks in seconds (default: 5 seconds) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + Returns: + AgentRetrieveResponse: The agent response when it reaches STATUS_RUNNING + + Raises: + AgentDeploymentError: If the agent deployment fails (STATUS_FAILED, + STATUS_UNDEPLOYMENT_FAILED, or STATUS_DELETED) + AgentDeploymentTimeoutError: If the agent doesn't reach STATUS_RUNNING + within the timeout period + ValueError: If uuid is empty + """ + import asyncio + from ..._exceptions import AgentDeploymentError, AgentDeploymentTimeoutError + + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + + start_time = time.time() + + while True: + agent_response = await self.retrieve( + uuid, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + ) + + # Check if agent and deployment exist + if agent_response.agent and agent_response.agent.deployment: + status = agent_response.agent.deployment.status + + # Success case + if status == "STATUS_RUNNING": + return agent_response + + # Failure cases + if status in ("STATUS_FAILED", "STATUS_UNDEPLOYMENT_FAILED", "STATUS_DELETED"): + raise AgentDeploymentError( + f"Agent deployment failed with status: {status}", + status=status or "UNKNOWN", + ) + + # Check timeout + elapsed_time = time.time() - start_time + if elapsed_time >= timeout: + current_status = ( + agent_response.agent.deployment.status + if agent_response.agent and agent_response.agent.deployment + else "UNKNOWN" + ) + raise AgentDeploymentTimeoutError( + f"Agent did not reach STATUS_RUNNING within {timeout} seconds. " + f"Current status: {current_status}", + agent_id=uuid, + ) + + # Wait before polling again + await asyncio.sleep(poll_interval) + class AgentsResourceWithRawResponse: def __init__(self, agents: AgentsResource) -> None: @@ -1134,6 +1310,9 @@ def __init__(self, agents: AgentsResource) -> None: self.update_status = to_raw_response_wrapper( agents.update_status, ) + self.wait_until_ready = to_raw_response_wrapper( + agents.wait_until_ready, + ) @cached_property def api_keys(self) -> APIKeysResourceWithRawResponse: @@ -1201,6 +1380,9 @@ def __init__(self, agents: AsyncAgentsResource) -> None: self.update_status = async_to_raw_response_wrapper( agents.update_status, ) + self.wait_until_ready = async_to_raw_response_wrapper( + agents.wait_until_ready, + ) @cached_property def api_keys(self) -> AsyncAPIKeysResourceWithRawResponse: @@ -1268,6 +1450,9 @@ def __init__(self, agents: AgentsResource) -> None: self.update_status = to_streamed_response_wrapper( agents.update_status, ) + self.wait_until_ready = to_streamed_response_wrapper( + agents.wait_until_ready, + ) @cached_property def api_keys(self) -> APIKeysResourceWithStreamingResponse: @@ -1335,6 +1520,9 @@ def __init__(self, agents: AsyncAgentsResource) -> None: self.update_status = async_to_streamed_response_wrapper( agents.update_status, ) + self.wait_until_ready = async_to_streamed_response_wrapper( + agents.wait_until_ready, + ) @cached_property def api_keys(self) -> AsyncAPIKeysResourceWithStreamingResponse: diff --git a/tests/api_resources/test_agents.py b/tests/api_resources/test_agents.py index 2069d2d8..6b6bd229 100644 --- a/tests/api_resources/test_agents.py +++ b/tests/api_resources/test_agents.py @@ -5,6 +5,7 @@ import os from typing import Any, cast +import httpx import pytest from gradient import Gradient, AsyncGradient @@ -363,6 +364,104 @@ def test_path_params_update_status(self, client: Gradient) -> None: path_uuid="", ) + @parametrize + def test_method_wait_until_ready(self, client: Gradient, respx_mock: Any) -> None: + """Test successful wait_until_ready when agent becomes ready.""" + agent_uuid = "test-agent-id" + + # Create side effect that returns different responses + call_count = [0] + def get_response(request: httpx.Request) -> httpx.Response: + call_count[0] += 1 + if call_count[0] == 1: + # First call: deploying + return httpx.Response( + 200, + json={ + "agent": { + "uuid": agent_uuid, + "deployment": {"status": "STATUS_DEPLOYING"}, + } + }, + ) + else: + # Subsequent calls: running + return httpx.Response( + 200, + json={ + "agent": { + "uuid": agent_uuid, + "deployment": {"status": "STATUS_RUNNING"}, + } + }, + ) + + respx_mock.get(f"/v2/gen-ai/agents/{agent_uuid}").mock(side_effect=get_response) + + agent = client.agents.wait_until_ready(agent_uuid, poll_interval=0.1, timeout=10.0) + assert_matches_type(AgentRetrieveResponse, agent, path=["response"]) + assert agent.agent is not None + assert agent.agent.deployment is not None + assert agent.agent.deployment.status == "STATUS_RUNNING" + + @parametrize + def test_wait_until_ready_timeout(self, client: Gradient, respx_mock: Any) -> None: + """Test that wait_until_ready raises timeout error.""" + from gradient._exceptions import AgentDeploymentTimeoutError + + agent_uuid = "test-agent-id" + + # Mock always returns deploying + respx_mock.get(f"/v2/gen-ai/agents/{agent_uuid}").mock( + return_value=httpx.Response( + 200, + json={ + "agent": { + "uuid": agent_uuid, + "deployment": {"status": "STATUS_DEPLOYING"}, + } + }, + ) + ) + + with pytest.raises(AgentDeploymentTimeoutError) as exc_info: + client.agents.wait_until_ready(agent_uuid, poll_interval=0.1, timeout=0.5) + + assert "did not reach STATUS_RUNNING within" in str(exc_info.value) + assert exc_info.value.agent_id == agent_uuid + + @parametrize + def test_wait_until_ready_deployment_failed(self, client: Gradient, respx_mock: Any) -> None: + """Test that wait_until_ready raises error on deployment failure.""" + from gradient._exceptions import AgentDeploymentError + + agent_uuid = "test-agent-id" + + # Mock returns failed status + respx_mock.get(f"/v2/gen-ai/agents/{agent_uuid}").mock( + return_value=httpx.Response( + 200, + json={ + "agent": { + "uuid": agent_uuid, + "deployment": {"status": "STATUS_FAILED"}, + } + }, + ) + ) + + with pytest.raises(AgentDeploymentError) as exc_info: + client.agents.wait_until_ready(agent_uuid, poll_interval=0.1, timeout=10.0) + + assert "deployment failed with status: STATUS_FAILED" in str(exc_info.value) + assert exc_info.value.status == "STATUS_FAILED" + + @parametrize + def test_wait_until_ready_empty_uuid(self, client: Gradient) -> None: + """Test that wait_until_ready validates empty uuid.""" + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid`"): + client.agents.wait_until_ready("") + class TestAsyncAgents: parametrize = pytest.mark.parametrize( @@ -706,3 +805,95 @@ async def test_path_params_update_status(self, async_client: AsyncGradient) -> N await async_client.agents.with_raw_response.update_status( path_uuid="", ) + + @parametrize + async def test_method_wait_until_ready(self, async_client: AsyncGradient, respx_mock: Any) -> None: + """Test successful async wait_until_ready when agent becomes ready.""" + agent_uuid = "test-agent-id" + + # Create side effect that returns different responses + call_count = [0] + def get_response(request: httpx.Request) -> httpx.Response: + call_count[0] += 1 + if call_count[0] == 1: + # First call: deploying + return httpx.Response( + 200, + json={ + "agent": { + "uuid": agent_uuid, + "deployment": {"status": "STATUS_DEPLOYING"}, + } + }, + ) + else: + # Subsequent calls: running + return httpx.Response( + 200, + json={ + "agent": { + "uuid": agent_uuid, + "deployment": {"status": "STATUS_RUNNING"}, + } + }, + ) + + respx_mock.get(f"/v2/gen-ai/agents/{agent_uuid}").mock(side_effect=get_response) + + agent = await async_client.agents.wait_until_ready(agent_uuid, poll_interval=0.1, timeout=10.0) + assert_matches_type(AgentRetrieveResponse, agent, path=["response"]) + assert agent.agent is not None + assert agent.agent.deployment is not None + assert agent.agent.deployment.status == "STATUS_RUNNING" + + @parametrize + async def test_wait_until_ready_timeout(self, async_client: AsyncGradient, respx_mock: Any) -> None: + """Test that async wait_until_ready raises timeout error.""" + from gradient._exceptions import AgentDeploymentTimeoutError + + agent_uuid = "test-agent-id" + + # Mock always returns deploying + respx_mock.get(f"/v2/gen-ai/agents/{agent_uuid}").mock( + return_value=httpx.Response( + 200, + json={ + "agent": { + "uuid": agent_uuid, + "deployment": {"status": "STATUS_DEPLOYING"}, + } + }, + ) + ) + + with pytest.raises(AgentDeploymentTimeoutError) as exc_info: + await async_client.agents.wait_until_ready(agent_uuid, poll_interval=0.1, timeout=0.5) + + assert "did not reach STATUS_RUNNING within" in str(exc_info.value) + assert exc_info.value.agent_id == agent_uuid + + @parametrize + async def test_wait_until_ready_deployment_failed(self, async_client: AsyncGradient, respx_mock: Any) -> None: + """Test that async wait_until_ready raises error on deployment failure.""" + from gradient._exceptions import AgentDeploymentError + + agent_uuid = "test-agent-id" + + # Mock returns failed status + respx_mock.get(f"/v2/gen-ai/agents/{agent_uuid}").mock( + return_value=httpx.Response( + 200, + json={ + "agent": { + "uuid": agent_uuid, + "deployment": {"status": "STATUS_FAILED"}, + } + }, + ) + ) + + with pytest.raises(AgentDeploymentError) as exc_info: + await async_client.agents.wait_until_ready(agent_uuid, poll_interval=0.1, timeout=10.0) + + assert "deployment failed with status: STATUS_FAILED" in str(exc_info.value) + assert exc_info.value.status == "STATUS_FAILED" From 4e15db6992c12bad22e7fda73a7e82078b37ff25 Mon Sep 17 00:00:00 2001 From: Ben Batha Date: Mon, 20 Oct 2025 16:55:52 -0400 Subject: [PATCH 2/7] Update test_agents.py --- tests/api_resources/test_agents.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/api_resources/test_agents.py b/tests/api_resources/test_agents.py index 6b6bd229..5777c3ea 100644 --- a/tests/api_resources/test_agents.py +++ b/tests/api_resources/test_agents.py @@ -371,7 +371,7 @@ def test_method_wait_until_ready(self, client: Gradient, respx_mock: Any) -> Non # Create side effect that returns different responses call_count = [0] - def get_response(request: httpx.Request) -> httpx.Response: + def get_response(_: httpx.Request) -> httpx.Response: call_count[0] += 1 if call_count[0] == 1: # First call: deploying @@ -813,7 +813,7 @@ async def test_method_wait_until_ready(self, async_client: AsyncGradient, respx_ # Create side effect that returns different responses call_count = [0] - def get_response(request: httpx.Request) -> httpx.Response: + def get_response(_: httpx.Request) -> httpx.Response: call_count[0] += 1 if call_count[0] == 1: # First call: deploying From ddd579221c124cab8a81abb1142a27bd19d37be0 Mon Sep 17 00:00:00 2001 From: Ben Batha Date: Mon, 20 Oct 2025 16:57:32 -0400 Subject: [PATCH 3/7] Update agents.py --- src/gradient/resources/agents/agents.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gradient/resources/agents/agents.py b/src/gradient/resources/agents/agents.py index 40405fae..e8422c63 100644 --- a/src/gradient/resources/agents/agents.py +++ b/src/gradient/resources/agents/agents.py @@ -2,8 +2,8 @@ from __future__ import annotations -import time import httpx +import time from .routes import ( RoutesResource, @@ -1237,6 +1237,7 @@ async def wait_until_ready( ValueError: If uuid is empty """ import asyncio + from ..._exceptions import AgentDeploymentError, AgentDeploymentTimeoutError if not uuid: From da54eebabc96f8e8b0afe5089a018affcfbe8ab5 Mon Sep 17 00:00:00 2001 From: Ben Batha Date: Mon, 20 Oct 2025 17:01:35 -0400 Subject: [PATCH 4/7] Update agents.py --- src/gradient/resources/agents/agents.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gradient/resources/agents/agents.py b/src/gradient/resources/agents/agents.py index e8422c63..8a5678f4 100644 --- a/src/gradient/resources/agents/agents.py +++ b/src/gradient/resources/agents/agents.py @@ -1,5 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from __future__ import annotations import httpx From 276f574a2b294cd1d201cf1edefc3bfe0a72c618 Mon Sep 17 00:00:00 2001 From: Ben Batha Date: Mon, 20 Oct 2025 17:42:23 -0400 Subject: [PATCH 5/7] Update agents.py --- src/gradient/resources/agents/agents.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/gradient/resources/agents/agents.py b/src/gradient/resources/agents/agents.py index 8a5678f4..ffe814c4 100644 --- a/src/gradient/resources/agents/agents.py +++ b/src/gradient/resources/agents/agents.py @@ -1,9 +1,10 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations -import httpx import time +import httpx + from .routes import ( RoutesResource, AsyncRoutesResource, @@ -646,9 +647,9 @@ def wait_until_ready( AgentRetrieveResponse: The agent response when it reaches STATUS_RUNNING Raises: - AgentDeploymentError: If the agent deployment fails (STATUS_FAILED, + AgentDeploymentError: If the agent deployment fails (STATUS_FAILED, STATUS_UNDEPLOYMENT_FAILED, or STATUS_DELETED) - AgentDeploymentTimeoutError: If the agent doesn't reach STATUS_RUNNING + AgentDeploymentTimeoutError: If the agent doesn't reach STATUS_RUNNING within the timeout period ValueError: If uuid is empty """ @@ -658,7 +659,7 @@ def wait_until_ready( raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") start_time = time.time() - + while True: agent_response = self.retrieve( uuid, @@ -691,8 +692,7 @@ def wait_until_ready( else "UNKNOWN" ) raise AgentDeploymentTimeoutError( - f"Agent did not reach STATUS_RUNNING within {timeout} seconds. " - f"Current status: {current_status}", + f"Agent did not reach STATUS_RUNNING within {timeout} seconds. Current status: {current_status}", agent_id=uuid, ) @@ -1229,9 +1229,9 @@ async def wait_until_ready( AgentRetrieveResponse: The agent response when it reaches STATUS_RUNNING Raises: - AgentDeploymentError: If the agent deployment fails (STATUS_FAILED, + AgentDeploymentError: If the agent deployment fails (STATUS_FAILED, STATUS_UNDEPLOYMENT_FAILED, or STATUS_DELETED) - AgentDeploymentTimeoutError: If the agent doesn't reach STATUS_RUNNING + AgentDeploymentTimeoutError: If the agent doesn't reach STATUS_RUNNING within the timeout period ValueError: If uuid is empty """ @@ -1243,7 +1243,7 @@ async def wait_until_ready( raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") start_time = time.time() - + while True: agent_response = await self.retrieve( uuid, @@ -1276,8 +1276,7 @@ async def wait_until_ready( else "UNKNOWN" ) raise AgentDeploymentTimeoutError( - f"Agent did not reach STATUS_RUNNING within {timeout} seconds. " - f"Current status: {current_status}", + f"Agent did not reach STATUS_RUNNING within {timeout} seconds. Current status: {current_status}", agent_id=uuid, ) From 75d9000c6997fd059835be4cfba1041d7a56ac76 Mon Sep 17 00:00:00 2001 From: Ben Batha Date: Mon, 20 Oct 2025 17:44:40 -0400 Subject: [PATCH 6/7] Update agents.py --- src/gradient/resources/agents/agents.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gradient/resources/agents/agents.py b/src/gradient/resources/agents/agents.py index ffe814c4..686678ba 100644 --- a/src/gradient/resources/agents/agents.py +++ b/src/gradient/resources/agents/agents.py @@ -680,7 +680,7 @@ def wait_until_ready( if status in ("STATUS_FAILED", "STATUS_UNDEPLOYMENT_FAILED", "STATUS_DELETED"): raise AgentDeploymentError( f"Agent deployment failed with status: {status}", - status=status or "UNKNOWN", + status=status, ) # Check timeout @@ -1264,7 +1264,7 @@ async def wait_until_ready( if status in ("STATUS_FAILED", "STATUS_UNDEPLOYMENT_FAILED", "STATUS_DELETED"): raise AgentDeploymentError( f"Agent deployment failed with status: {status}", - status=status or "UNKNOWN", + status=status, ) # Check timeout From 2a5042586e2d718ef417b56937247aed66478b7a Mon Sep 17 00:00:00 2001 From: Ben Batha Date: Mon, 20 Oct 2025 17:45:00 -0400 Subject: [PATCH 7/7] Update agent_wait_until_ready.py --- examples/agent_wait_until_ready.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/agent_wait_until_ready.py b/examples/agent_wait_until_ready.py index 66d6dc75..3ea7b4a3 100644 --- a/examples/agent_wait_until_ready.py +++ b/examples/agent_wait_until_ready.py @@ -58,7 +58,7 @@ from gradient import AsyncGradient -async def main(): +async def main() -> None: async_client = AsyncGradient() # Create a new agent