Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions src/resources/agents/agents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ import {
import { APIPromise } from '../../core/api-promise';
import { RequestOptions } from '../../internal/request-options';
import { path } from '../../internal/utils/path';
import { waitForAgentReady } from './wait-for-agent';

export class Agents extends APIResource {
apiKeys: APIKeysAPI.APIKeys = new APIKeysAPI.APIKeys(this._client);
Expand Down Expand Up @@ -266,6 +267,23 @@ export class Agents extends APIResource {
...options,
});
}

/**
* Polls for agent readiness.
*
* @example
* ```typescript
* const agent = await waitForAgentReady(client, 'agent-123', {
* timeout: 60000, // 1 minute
* });
* ```
*/
waitForReady(
uuid: string,
options: import('./wait-for-agent').WaitForAgentOptions,
): Promise<AgentReadinessResponse> {
return waitForAgentReady(this._client, uuid, options);
}
}

/**
Expand Down Expand Up @@ -1067,6 +1085,16 @@ export interface AgentRetrieveResponse {
agent?: APIAgent;
}

/**
* One Agent
*/
export interface AgentReadinessResponse {
/**
* An Agent
*/
agent?: APIAgent;
}

/**
* Information about an updated agent
*/
Expand Down Expand Up @@ -1758,6 +1786,7 @@ export declare namespace Agents {
type APIWorkspace as APIWorkspace,
type AgentCreateResponse as AgentCreateResponse,
type AgentRetrieveResponse as AgentRetrieveResponse,
type AgentReadinessResponse as AgentReadinessResponse,
type AgentUpdateResponse as AgentUpdateResponse,
type AgentListResponse as AgentListResponse,
type AgentDeleteResponse as AgentDeleteResponse,
Expand Down
82 changes: 82 additions & 0 deletions src/resources/agents/wait-for-agent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { Gradient } from '../../client';
import { RequestOptions } from '../../internal/request-options';
import { GradientError } from '../../core/error';
import { sleep } from '../../internal/utils';
import { AgentReadinessResponse, APIAgent } from './agents';

type AgentStatus = NonNullable<APIAgent.Deployment['status']>;

export interface WaitForAgentOptions extends RequestOptions {
/** Check interval in ms (default: 3000) */
interval?: number;
}

export class AgentTimeoutError extends GradientError {
constructor(
public agentId: string,
public timeoutMs: number,
) {
super(`Agent ${agentId} did not become ready within ${timeoutMs}ms`);
this.name = 'AgentTimeoutError';
}
}

export class AgentDeploymentError extends GradientError {
constructor(
public agentId: string,
public status: AgentStatus,
) {
super(`Agent ${agentId} deployment failed with status: ${status}`);
this.name = 'AgentDeploymentError';
}
}

/**
* Polls an agent until it reaches STATUS_RUNNING.
*
* @example
* ```typescript
* const agent = await waitForAgentReady(client, 'agent-123', {
* timeout: 60000, // 1 minute
* });
* ```
*
* @param client - Gradient API client instance
* @param uuid - UUID of the agent to monitor
* @param options - Polling configuration
* @returns The agent object once STATUS_RUNNING is reached
* @throws {AgentTimeoutError} When polling exceeds timeout duration
* @throws {AgentDeploymentError} When agent enters a failure state
*/
export async function waitForAgentReady(
client: Gradient,
uuid: string,
options: WaitForAgentOptions = {},
): Promise<AgentReadinessResponse> {
const { interval = 3000, timeout = 180000, signal } = options;
const start = Date.now();

while (true) {
signal?.throwIfAborted();

const elapsed = Date.now() - start;

// ⏰ Check timeout BEFORE making the API call
if (elapsed > timeout) {
throw new AgentTimeoutError(uuid, timeout);
}

const agent = await client.agents.retrieve(uuid, options);
const status = (agent.agent?.deployment?.status as AgentStatus) || 'STATUS_UNKNOWN';

if (status === 'STATUS_RUNNING') {
return agent;
}

if (status === 'STATUS_FAILED' || status === 'STATUS_UNDEPLOYMENT_FAILED') {
throw new AgentDeploymentError(uuid, status);
}

await sleep(interval);
}
}
Loading