@happyrobot-ai/sdk 0.1.8 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/helpers/index.d.ts +2 -0
- package/helpers/index.js +3 -1
- package/helpers/index.mjs +2 -2
- package/helpers/trigger-and-wait-for-node-output.d.ts +18 -0
- package/helpers/trigger-and-wait-for-node-output.js +110 -0
- package/helpers/trigger-and-wait-for-node-output.mjs +3 -0
- package/package.json +2 -2
- package/resources/runs.d.ts +7 -1
- package/resources/runs.js +17 -0
- package/types/runs.types.d.ts +5 -1
- package/types/workflows.types.d.ts +1 -1
package/helpers/index.d.ts
CHANGED
|
@@ -10,5 +10,7 @@ export { createVoiceAgent } from "./voice-agent";
|
|
|
10
10
|
export type { CreateVoiceAgentOptions, CreateVoiceAgentResult } from "./voice-agent";
|
|
11
11
|
export { triggerAndWait } from "./trigger-and-wait";
|
|
12
12
|
export type { TriggerAndWaitOptions, TriggerAndWaitResult } from "./trigger-and-wait";
|
|
13
|
+
export { triggerAndWaitForNodeOutput } from "./trigger-and-wait-for-node-output";
|
|
14
|
+
export type { TriggerAndWaitForNodeOutputOptions, TriggerAndWaitForNodeOutputResult, } from "./trigger-and-wait-for-node-output";
|
|
13
15
|
export { createFromTemplate } from "./template-workflows";
|
|
14
16
|
export type { CreateFromTemplateOptions, CreateFromTemplateResult } from "./template-workflows";
|
package/helpers/index.js
CHANGED
|
@@ -8,11 +8,13 @@
|
|
|
8
8
|
* ```
|
|
9
9
|
*/
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
-
exports.createFromTemplate = exports.triggerAndWait = exports.createVoiceAgent = void 0;
|
|
11
|
+
exports.createFromTemplate = exports.triggerAndWaitForNodeOutput = exports.triggerAndWait = exports.createVoiceAgent = void 0;
|
|
12
12
|
var voice_agent_1 = require("./voice-agent");
|
|
13
13
|
Object.defineProperty(exports, "createVoiceAgent", { enumerable: true, get: function () { return voice_agent_1.createVoiceAgent; } });
|
|
14
14
|
var trigger_and_wait_1 = require("./trigger-and-wait");
|
|
15
15
|
Object.defineProperty(exports, "triggerAndWait", { enumerable: true, get: function () { return trigger_and_wait_1.triggerAndWait; } });
|
|
16
|
+
var trigger_and_wait_for_node_output_1 = require("./trigger-and-wait-for-node-output");
|
|
17
|
+
Object.defineProperty(exports, "triggerAndWaitForNodeOutput", { enumerable: true, get: function () { return trigger_and_wait_for_node_output_1.triggerAndWaitForNodeOutput; } });
|
|
16
18
|
var template_workflows_1 = require("./template-workflows");
|
|
17
19
|
Object.defineProperty(exports, "createFromTemplate", { enumerable: true, get: function () { return template_workflows_1.createFromTemplate; } });
|
|
18
20
|
//# sourceMappingURL=index.js.map
|
package/helpers/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import * as _cjs from "./index.js";
|
|
2
|
-
const { createVoiceAgent, triggerAndWait, createFromTemplate } = _cjs;
|
|
3
|
-
export { createVoiceAgent, triggerAndWait, createFromTemplate };
|
|
2
|
+
const { createVoiceAgent, triggerAndWait, triggerAndWaitForNodeOutput, createFromTemplate } = _cjs;
|
|
3
|
+
export { createVoiceAgent, triggerAndWait, triggerAndWaitForNodeOutput, createFromTemplate };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { HappyRobotClient } from "../client";
|
|
2
|
+
import type { RunNodeOutput } from "../types/runs.types";
|
|
3
|
+
export interface TriggerAndWaitForNodeOutputOptions {
|
|
4
|
+
workflowId: string;
|
|
5
|
+
payload?: Record<string, unknown>;
|
|
6
|
+
environment?: "production" | "staging" | "development";
|
|
7
|
+
nodePersistentId: string;
|
|
8
|
+
timeoutMs?: number;
|
|
9
|
+
pollIntervalMs?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface TriggerAndWaitForNodeOutputResult {
|
|
12
|
+
ok: boolean;
|
|
13
|
+
runId: string;
|
|
14
|
+
status: string;
|
|
15
|
+
nodeOutput: RunNodeOutput;
|
|
16
|
+
completedAt: string | null;
|
|
17
|
+
}
|
|
18
|
+
export declare function triggerAndWaitForNodeOutput(client: HappyRobotClient, options: TriggerAndWaitForNodeOutputOptions): Promise<TriggerAndWaitForNodeOutputResult>;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.triggerAndWaitForNodeOutput = triggerAndWaitForNodeOutput;
|
|
4
|
+
const errors_1 = require("../core/errors");
|
|
5
|
+
const RUN_TERMINAL_STATUSES = new Set([
|
|
6
|
+
"completed",
|
|
7
|
+
"succeeded",
|
|
8
|
+
"failed",
|
|
9
|
+
"canceled",
|
|
10
|
+
"skipped",
|
|
11
|
+
]);
|
|
12
|
+
const NODE_READY_STATUSES = new Set([
|
|
13
|
+
"completed",
|
|
14
|
+
"succeeded",
|
|
15
|
+
"failed",
|
|
16
|
+
"canceled",
|
|
17
|
+
"skipped",
|
|
18
|
+
]);
|
|
19
|
+
async function triggerAndWaitForNodeOutput(client, options) {
|
|
20
|
+
const { workflowId, payload, environment = "production", nodePersistentId, timeoutMs = 300_000, pollIntervalMs = 2_000, } = options;
|
|
21
|
+
const triggerResponse = await client.workflows.triggerRun(workflowId, {
|
|
22
|
+
payload,
|
|
23
|
+
environment,
|
|
24
|
+
});
|
|
25
|
+
// NOTE: V3 trigger responses use `run_id`, while some V2 workflows return
|
|
26
|
+
// `queued_run_ids`. We support both for now and can remove the fallback once
|
|
27
|
+
// all workflows are upgraded.
|
|
28
|
+
const run_id = triggerResponse.run_id ??
|
|
29
|
+
(Array.isArray(triggerResponse.queued_run_ids)
|
|
30
|
+
? triggerResponse.queued_run_ids[0]
|
|
31
|
+
: undefined);
|
|
32
|
+
if (!run_id) {
|
|
33
|
+
throw new Error("No run_id returned from trigger");
|
|
34
|
+
}
|
|
35
|
+
const deadline = Date.now() + timeoutMs;
|
|
36
|
+
let lastRunStatus = null;
|
|
37
|
+
let lastRunCompletedAt = null;
|
|
38
|
+
while (Date.now() < deadline) {
|
|
39
|
+
const nodes = await client.runs.listNodes(run_id, {
|
|
40
|
+
node_persistent_id: nodePersistentId,
|
|
41
|
+
sort: "asc",
|
|
42
|
+
});
|
|
43
|
+
const matchingNodes = nodes.data.filter((item) => item.node_persistent_id === nodePersistentId);
|
|
44
|
+
const node = pickLatestNodeExecution(matchingNodes);
|
|
45
|
+
if (node?.output_id && node.status && NODE_READY_STATUSES.has(node.status)) {
|
|
46
|
+
const output = await client.runs.getOutput(run_id, node.output_id);
|
|
47
|
+
return {
|
|
48
|
+
ok: true,
|
|
49
|
+
runId: run_id,
|
|
50
|
+
status: lastRunStatus ?? node.status,
|
|
51
|
+
nodeOutput: output.data,
|
|
52
|
+
completedAt: lastRunCompletedAt,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
// TODO: 2 HTTP calls per poll (listNodes + get(run)). Future fix: include
|
|
56
|
+
// run_status in listNodes response to eliminate get(run) entirely.
|
|
57
|
+
const run = await client.runs.get(run_id);
|
|
58
|
+
lastRunStatus = run.status;
|
|
59
|
+
lastRunCompletedAt = run.completed_at;
|
|
60
|
+
if (RUN_TERMINAL_STATUSES.has(run.status)) {
|
|
61
|
+
const finalNodes = await client.runs.listNodes(run_id, {
|
|
62
|
+
node_persistent_id: nodePersistentId,
|
|
63
|
+
sort: "asc",
|
|
64
|
+
});
|
|
65
|
+
const finalMatchingNodes = finalNodes.data.filter((item) => item.node_persistent_id === nodePersistentId);
|
|
66
|
+
const finalNode = pickLatestNodeExecution(finalMatchingNodes);
|
|
67
|
+
if (finalNode?.output_id &&
|
|
68
|
+
finalNode.status &&
|
|
69
|
+
NODE_READY_STATUSES.has(finalNode.status)) {
|
|
70
|
+
const output = await client.runs.getOutput(run_id, finalNode.output_id);
|
|
71
|
+
return {
|
|
72
|
+
ok: true,
|
|
73
|
+
runId: run_id,
|
|
74
|
+
status: run.status,
|
|
75
|
+
nodeOutput: output.data,
|
|
76
|
+
completedAt: run.completed_at,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
throw new Error(`Run reached terminal status '${run.status}' before node '${nodePersistentId}' became ready`);
|
|
80
|
+
}
|
|
81
|
+
// TODO: add response-node mode when is_response_node filtering is implemented server-side.
|
|
82
|
+
await sleep(pollIntervalMs);
|
|
83
|
+
}
|
|
84
|
+
throw new errors_1.TimeoutError(timeoutMs);
|
|
85
|
+
}
|
|
86
|
+
function sleep(ms) {
|
|
87
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
88
|
+
}
|
|
89
|
+
function pickLatestNodeExecution(nodes) {
|
|
90
|
+
// TODO: should return FIRST completed execution by default.
|
|
91
|
+
// Consider adding executionStrategy: "first" | "latest" to options.
|
|
92
|
+
if (nodes.length === 0) {
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
return nodes.reduce((latest, current) => {
|
|
96
|
+
const latestTs = Date.parse(latest.timestamp);
|
|
97
|
+
const currentTs = Date.parse(current.timestamp);
|
|
98
|
+
if (Number.isNaN(latestTs) || Number.isNaN(currentTs)) {
|
|
99
|
+
return current.output_id > latest.output_id ? current : latest;
|
|
100
|
+
}
|
|
101
|
+
if (currentTs > latestTs) {
|
|
102
|
+
return current;
|
|
103
|
+
}
|
|
104
|
+
if (currentTs < latestTs) {
|
|
105
|
+
return latest;
|
|
106
|
+
}
|
|
107
|
+
return current.output_id > latest.output_id ? current : latest;
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=trigger-and-wait-for-node-output.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyrobot-ai/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "TypeScript SDK for the HappyRobot Public API",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"module": "./index.mjs",
|
|
@@ -59,4 +59,4 @@
|
|
|
59
59
|
"directory": "packages/public_api"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {}
|
|
62
|
-
}
|
|
62
|
+
}
|
package/resources/runs.d.ts
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* Maps to:
|
|
5
5
|
* GET /runs
|
|
6
6
|
* GET /runs/:run_id
|
|
7
|
+
* GET /runs/:run_id/nodes
|
|
8
|
+
* GET /runs/:run_id/outputs/:output_id
|
|
7
9
|
* GET /runs/:run_id/sessions
|
|
8
10
|
* GET /runs/:run_id/recordings
|
|
9
11
|
* GET /runs/:run_id/flags
|
|
@@ -11,13 +13,17 @@
|
|
|
11
13
|
* POST /runs/:run_id/mark
|
|
12
14
|
*/
|
|
13
15
|
import type { HttpClient } from "../core/http";
|
|
14
|
-
import type { RunDetail, MarkRunBody, MarkRunResponse, ListFlagsQuery, PaginatedFlagsResponse } from "../types/runs.types";
|
|
16
|
+
import type { RunDetail, MarkRunBody, MarkRunResponse, ListFlagsQuery, PaginatedFlagsResponse, GetRunOutputResponse, ListRunNodesQuery, RunNodesResponse } from "../types/runs.types";
|
|
15
17
|
import type { PaginatedSessionsResponse, ListSessionsQuery } from "../types/sessions.types";
|
|
16
18
|
export declare class RunsResource {
|
|
17
19
|
private readonly http;
|
|
18
20
|
constructor(http: HttpClient);
|
|
19
21
|
/** Get a run by ID. */
|
|
20
22
|
get(runId: string): Promise<RunDetail>;
|
|
23
|
+
/** List node executions for a run in timestamp order. */
|
|
24
|
+
listNodes(runId: string, query?: ListRunNodesQuery): Promise<RunNodesResponse>;
|
|
25
|
+
/** Get one output payload by output ID within a run. */
|
|
26
|
+
getOutput(runId: string, outputId: string): Promise<GetRunOutputResponse>;
|
|
21
27
|
/** Get sessions for a run. */
|
|
22
28
|
getSessions(runId: string, query?: ListSessionsQuery): Promise<PaginatedSessionsResponse>;
|
|
23
29
|
/** Get recordings for a run. */
|
package/resources/runs.js
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* Maps to:
|
|
6
6
|
* GET /runs
|
|
7
7
|
* GET /runs/:run_id
|
|
8
|
+
* GET /runs/:run_id/nodes
|
|
9
|
+
* GET /runs/:run_id/outputs/:output_id
|
|
8
10
|
* GET /runs/:run_id/sessions
|
|
9
11
|
* GET /runs/:run_id/recordings
|
|
10
12
|
* GET /runs/:run_id/flags
|
|
@@ -25,6 +27,21 @@ class RunsResource {
|
|
|
25
27
|
path: `/runs/${enc(runId)}`,
|
|
26
28
|
});
|
|
27
29
|
}
|
|
30
|
+
/** List node executions for a run in timestamp order. */
|
|
31
|
+
async listNodes(runId, query) {
|
|
32
|
+
return this.http.request({
|
|
33
|
+
method: "GET",
|
|
34
|
+
path: `/runs/${enc(runId)}/nodes`,
|
|
35
|
+
query: query,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/** Get one output payload by output ID within a run. */
|
|
39
|
+
async getOutput(runId, outputId) {
|
|
40
|
+
return this.http.request({
|
|
41
|
+
method: "GET",
|
|
42
|
+
path: `/runs/${enc(runId)}/outputs/${enc(outputId)}`,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
28
45
|
/** Get sessions for a run. */
|
|
29
46
|
async getSessions(runId, query) {
|
|
30
47
|
return this.http.request({
|
package/types/runs.types.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Run types extracted from Zod schemas.
|
|
3
3
|
*/
|
|
4
4
|
import type { z } from "zod";
|
|
5
|
-
import type { RunDetailSchema, MarkRunBodySchema, MarkRunResponseSchema, ListFlagsQuerySchema, PaginatedFlagsResponseSchema, FlagItemSchema } from "../../routes/runs/schemas";
|
|
5
|
+
import type { RunDetailSchema, MarkRunBodySchema, MarkRunResponseSchema, ListFlagsQuerySchema, PaginatedFlagsResponseSchema, FlagItemSchema, GetRunOutputResponseSchema, ListRunNodesQuerySchema, ListRunNodesResponseSchema, RunNodeOutputSchema } from "../../routes/runs/schemas";
|
|
6
6
|
import type { WorkflowRunsQuerySchema, PaginatedWorkflowRunsResponseSchema, TriggerRunBodySchema, TriggerRunResponseSchema } from "../../routes/workflows/:workflow_id/runs/schemas";
|
|
7
7
|
export type WorkflowRunsQuery = z.input<typeof WorkflowRunsQuerySchema>;
|
|
8
8
|
export type PaginatedWorkflowRunsResponse = z.infer<typeof PaginatedWorkflowRunsResponseSchema>;
|
|
@@ -14,3 +14,7 @@ export type MarkRunResponse = z.infer<typeof MarkRunResponseSchema>;
|
|
|
14
14
|
export type ListFlagsQuery = z.input<typeof ListFlagsQuerySchema>;
|
|
15
15
|
export type PaginatedFlagsResponse = z.infer<typeof PaginatedFlagsResponseSchema>;
|
|
16
16
|
export type FlagItem = z.infer<typeof FlagItemSchema>;
|
|
17
|
+
export type ListRunNodesQuery = z.input<typeof ListRunNodesQuerySchema>;
|
|
18
|
+
export type RunNodesResponse = z.infer<typeof ListRunNodesResponseSchema>;
|
|
19
|
+
export type GetRunOutputResponse = z.infer<typeof GetRunOutputResponseSchema>;
|
|
20
|
+
export type RunNodeOutput = z.infer<typeof RunNodeOutputSchema>;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { z } from "zod";
|
|
6
6
|
import type { ListWorkflowsQuerySchema, WorkflowListItemSchema, PaginatedWorkflowsResponseSchema, CreateWorkflowBodySchema, CreateWorkflowResponseSchema, WorkflowDetailResponseSchema, UpdateWorkflowBodySchema, UpdateWorkflowResponseSchema, DuplicateWorkflowBodySchema, DuplicateWorkflowResponseSchema, PaginatedTemplatesResponseSchema, ListTemplatesQuerySchema, WorkflowPublishResponseSchema, WorkflowPublishErrorResponseSchema, WorkflowUnpublishResponseSchema, CancelRunsResponseSchema, ListWorkflowVersionsQuerySchema, PaginatedWorkflowVersionsResponseSchema } from "../../routes/workflows/schemas";
|
|
7
|
-
export type ListWorkflowsQuery = z.
|
|
7
|
+
export type ListWorkflowsQuery = z.output<typeof ListWorkflowsQuerySchema>;
|
|
8
8
|
export type WorkflowListItem = z.infer<typeof WorkflowListItemSchema>;
|
|
9
9
|
export type PaginatedWorkflowsResponse = z.infer<typeof PaginatedWorkflowsResponseSchema>;
|
|
10
10
|
export type CreateWorkflowBody = z.input<typeof CreateWorkflowBodySchema>;
|