Skip to content

Commit 48a9366

Browse files
authored
feat: add waitForAgentReady helper for agent deployment polling (#9)
1 parent 04e35f0 commit 48a9366

File tree

2 files changed

+111
-0
lines changed

2 files changed

+111
-0
lines changed

src/resources/agents/agents.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ import {
106106
import { APIPromise } from '../../core/api-promise';
107107
import { RequestOptions } from '../../internal/request-options';
108108
import { path } from '../../internal/utils/path';
109+
import { waitForAgentReady } from './wait-for-agent';
109110

110111
export class Agents extends APIResource {
111112
apiKeys: APIKeysAPI.APIKeys = new APIKeysAPI.APIKeys(this._client);
@@ -266,6 +267,23 @@ export class Agents extends APIResource {
266267
...options,
267268
});
268269
}
270+
271+
/**
272+
* Polls for agent readiness.
273+
*
274+
* @example
275+
* ```typescript
276+
* const agent = await waitForAgentReady(client, 'agent-123', {
277+
* timeout: 60000, // 1 minute
278+
* });
279+
* ```
280+
*/
281+
waitForReady(
282+
uuid: string,
283+
options: import('./wait-for-agent').WaitForAgentOptions,
284+
): Promise<AgentReadinessResponse> {
285+
return waitForAgentReady(this._client, uuid, options);
286+
}
269287
}
270288

271289
/**
@@ -1067,6 +1085,16 @@ export interface AgentRetrieveResponse {
10671085
agent?: APIAgent;
10681086
}
10691087

1088+
/**
1089+
* One Agent
1090+
*/
1091+
export interface AgentReadinessResponse {
1092+
/**
1093+
* An Agent
1094+
*/
1095+
agent?: APIAgent;
1096+
}
1097+
10701098
/**
10711099
* Information about an updated agent
10721100
*/
@@ -1758,6 +1786,7 @@ export declare namespace Agents {
17581786
type APIWorkspace as APIWorkspace,
17591787
type AgentCreateResponse as AgentCreateResponse,
17601788
type AgentRetrieveResponse as AgentRetrieveResponse,
1789+
type AgentReadinessResponse as AgentReadinessResponse,
17611790
type AgentUpdateResponse as AgentUpdateResponse,
17621791
type AgentListResponse as AgentListResponse,
17631792
type AgentDeleteResponse as AgentDeleteResponse,
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { Gradient } from '../../client';
2+
import { RequestOptions } from '../../internal/request-options';
3+
import { GradientError } from '../../core/error';
4+
import { sleep } from '../../internal/utils';
5+
import { AgentReadinessResponse, APIAgent } from './agents';
6+
7+
type AgentStatus = NonNullable<APIAgent.Deployment['status']>;
8+
9+
export interface WaitForAgentOptions extends RequestOptions {
10+
/** Check interval in ms (default: 3000) */
11+
interval?: number;
12+
}
13+
14+
export class AgentTimeoutError extends GradientError {
15+
constructor(
16+
public agentId: string,
17+
public timeoutMs: number,
18+
) {
19+
super(`Agent ${agentId} did not become ready within ${timeoutMs}ms`);
20+
this.name = 'AgentTimeoutError';
21+
}
22+
}
23+
24+
export class AgentDeploymentError extends GradientError {
25+
constructor(
26+
public agentId: string,
27+
public status: AgentStatus,
28+
) {
29+
super(`Agent ${agentId} deployment failed with status: ${status}`);
30+
this.name = 'AgentDeploymentError';
31+
}
32+
}
33+
34+
/**
35+
* Polls an agent until it reaches STATUS_RUNNING.
36+
*
37+
* @example
38+
* ```typescript
39+
* const agent = await waitForAgentReady(client, 'agent-123', {
40+
* timeout: 60000, // 1 minute
41+
* });
42+
* ```
43+
*
44+
* @param client - Gradient API client instance
45+
* @param uuid - UUID of the agent to monitor
46+
* @param options - Polling configuration
47+
* @returns The agent object once STATUS_RUNNING is reached
48+
* @throws {AgentTimeoutError} When polling exceeds timeout duration
49+
* @throws {AgentDeploymentError} When agent enters a failure state
50+
*/
51+
export async function waitForAgentReady(
52+
client: Gradient,
53+
uuid: string,
54+
options: WaitForAgentOptions = {},
55+
): Promise<AgentReadinessResponse> {
56+
const { interval = 3000, timeout = 180000, signal } = options;
57+
const start = Date.now();
58+
59+
while (true) {
60+
signal?.throwIfAborted();
61+
62+
const elapsed = Date.now() - start;
63+
64+
// ⏰ Check timeout BEFORE making the API call
65+
if (elapsed > timeout) {
66+
throw new AgentTimeoutError(uuid, timeout);
67+
}
68+
69+
const agent = await client.agents.retrieve(uuid, options);
70+
const status = (agent.agent?.deployment?.status as AgentStatus) || 'STATUS_UNKNOWN';
71+
72+
if (status === 'STATUS_RUNNING') {
73+
return agent;
74+
}
75+
76+
if (status === 'STATUS_FAILED' || status === 'STATUS_UNDEPLOYMENT_FAILED') {
77+
throw new AgentDeploymentError(uuid, status);
78+
}
79+
80+
await sleep(interval);
81+
}
82+
}

0 commit comments

Comments
 (0)