@graph-compose/client 1.0.0
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/README.md +646 -0
- package/dist/core/builder.d.ts +313 -0
- package/dist/core/builder.d.ts.map +1 -0
- package/dist/core/builder.js +538 -0
- package/dist/core/builder.js.map +1 -0
- package/dist/core/node-builder.d.ts +180 -0
- package/dist/core/node-builder.d.ts.map +1 -0
- package/dist/core/node-builder.js +387 -0
- package/dist/core/node-builder.js.map +1 -0
- package/dist/core/tool-builder.d.ts +130 -0
- package/dist/core/tool-builder.d.ts.map +1 -0
- package/dist/core/tool-builder.js +343 -0
- package/dist/core/tool-builder.js.map +1 -0
- package/dist/errors.d.ts +16 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +39 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -0
- package/dist/node-builder.d.ts +64 -0
- package/dist/node-builder.d.ts.map +1 -0
- package/dist/node-builder.js +261 -0
- package/dist/node-builder.js.map +1 -0
- package/dist/types.d.ts +65 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -0
- package/dist/validation/index.d.ts +34 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/index.js +304 -0
- package/dist/validation/index.js.map +1 -0
- package/package.json +93 -0
package/README.md
ADDED
|
@@ -0,0 +1,646 @@
|
|
|
1
|
+
# @graph-compose/client
|
|
2
|
+
|
|
3
|
+
Welcome! 👋 This is the Graph Compose client SDK, designed to help you build, validate, and execute workflow graphs using a fluent, type-safe builder pattern in TypeScript.
|
|
4
|
+
|
|
5
|
+
This package works hand-in-hand with `@graph-compose/core`, which defines the underlying structure and validation schemas for workflows. You'll use `@graph-compose/client` to programmatically construct these workflow graphs, starting with basic HTTP operations and dependencies.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
* 🏗️ **Fluent Builder API:** Easily define nodes (like `http` requests) and their configurations using method chaining.
|
|
11
|
+
* 🔗 **Dependency Management:** Explicitly define dependencies between nodes.
|
|
12
|
+
* ✅ **Built-in Validation:** Leverages `@graph-compose/core` schemas to validate your graph definition locally.
|
|
13
|
+
* 🚀 **Workflow Execution:** Provides methods to run your defined workflows asynchronously (`execute`, recommended) or synchronously (`executeSync`).
|
|
14
|
+
* 🔒 **Type Safety:** Full TypeScript support for confidence in your workflow definitions.
|
|
15
|
+
* 🔄 **Expression Support:** Utilize `{{ }}` expressions within node configurations (e.g., URLs, headers), powered by `@graph-compose/core`.
|
|
16
|
+
* *(Support for Agents and Tools is also available - see advanced documentation)*
|
|
17
|
+
* ⏱️ **Timeouts & Retries:** Configure detailed retry policies and timeouts (`startToClose`, `scheduleToClose`) for nodes and tools.
|
|
18
|
+
* ⚙️ **Workflow Configuration:** Set global workflow settings like execution timeouts (`withWorkflowConfig`).
|
|
19
|
+
* 🔀 **Conditional Execution:** Define complex branching logic for HTTP nodes (`withConditions`).
|
|
20
|
+
* 🛡️ **Schema Validation:** Apply Zod-based input/output validation schemas to HTTP nodes (`withValidation`).
|
|
21
|
+
|
|
22
|
+
## API Reference & Documentation
|
|
23
|
+
|
|
24
|
+
> **Note:** This document focuses specifically on the `@graph-compose/client` SDK for building workflows programmatically. For broader platform documentation, conceptual guides, API schemas, and examples, please visit the main Graph Compose site: [graphcompose.io](https://graphcompose.io).
|
|
25
|
+
|
|
26
|
+
* **Guides & Overviews:** For conceptual guides, integration examples, and broader platform documentation, visit the main Graph Compose documentation site: [graphcompose.io/docs](https://graphcompose.io/docs).
|
|
27
|
+
* **Workflow API Schemas:** For the definitive source of truth on the backend Workflow API request/response schemas (based on Zod/OpenAPI), see: [graphcompose.io/workflows](https://graphcompose.io/workflows).
|
|
28
|
+
|
|
29
|
+
## Table of Contents
|
|
30
|
+
|
|
31
|
+
- [Features](#features)
|
|
32
|
+
- [API Reference & Documentation](#api-reference--documentation)
|
|
33
|
+
- [Installation](#installation)
|
|
34
|
+
- [Quick Start: Building a Simple HTTP Workflow](#quick-start-building-a-simple-http-workflow)
|
|
35
|
+
- [Method Summary](#method-summary)
|
|
36
|
+
- [Core Concepts](#core-concepts)
|
|
37
|
+
- [Defining Node Types](#defining-node-types)
|
|
38
|
+
- [Defining Tool Types (for Agents)](#defining-tool-types-for-agents)
|
|
39
|
+
- [Workflow Configuration](#workflow-configuration)
|
|
40
|
+
- [Validation](#validation)
|
|
41
|
+
- [Execution](#execution)
|
|
42
|
+
- [Getting the Workflow Definition](#getting-the-workflow-definition)
|
|
43
|
+
- [Interacting with Existing Workflows (API)](#interacting-with-existing-workflows-api)
|
|
44
|
+
- [Full Example: Putting it Together](#full-example-putting-it-together)
|
|
45
|
+
- [Contributing](#contributing)
|
|
46
|
+
- [License](#license)
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npm install @graph-compose/client @graph-compose/core
|
|
52
|
+
# or
|
|
53
|
+
yarn add @graph-compose/client @graph-compose/core
|
|
54
|
+
```
|
|
55
|
+
*(Note: `@graph-compose/core` is a required peer dependency.)*
|
|
56
|
+
|
|
57
|
+
## Quick Start: Building a Simple HTTP Workflow
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { GraphCompose } from '@graph-compose/client';
|
|
61
|
+
import { z } from 'zod'; // Often used with core schemas
|
|
62
|
+
|
|
63
|
+
// Initialize the client (token is required)
|
|
64
|
+
// Reads from process.env.GRAPH_COMPOSE_TOKEN if not provided
|
|
65
|
+
const graph = new GraphCompose({
|
|
66
|
+
token: process.env.GRAPH_COMPOSE_TOKEN || 'your-api-token',
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Define a simple workflow with three dependent HTTP steps
|
|
70
|
+
try {
|
|
71
|
+
// Node 1: Fetch initial user data
|
|
72
|
+
graph.node('fetch_user')
|
|
73
|
+
.get('https://api.example.com/users/{{ context.userId }}')
|
|
74
|
+
.withHeaders({ 'X-Request-ID': '{{ $uuid() }}' })
|
|
75
|
+
.withStartToCloseTimeout('5s')
|
|
76
|
+
.end(); // <-- Required: Finalize the 'fetch_user' node definition
|
|
77
|
+
|
|
78
|
+
// Node 2: Fetch user's orders, depending on the first node
|
|
79
|
+
graph.node('fetch_orders')
|
|
80
|
+
.withDependencies(['fetch_user']) // This node runs after 'fetch_user' completes
|
|
81
|
+
.get('https://api.example.com/orders?userId={{ results.fetch_user.id }}')
|
|
82
|
+
.end(); // <-- Required: Finalize the 'fetch_orders' node definition
|
|
83
|
+
|
|
84
|
+
// Node 3: Post-process orders
|
|
85
|
+
graph.node('process_orders')
|
|
86
|
+
.withDependencies(['fetch_orders']) // Depends on 'fetch_orders'
|
|
87
|
+
.post('https://api.example.com/process')
|
|
88
|
+
.withBody({
|
|
89
|
+
orderData: '{{ results.fetch_orders }}',
|
|
90
|
+
processingTimestamp: '{{ $now() }}'
|
|
91
|
+
})
|
|
92
|
+
.end(); // <-- Required: Finalize the 'process_orders' node definition
|
|
93
|
+
|
|
94
|
+
// (Optional) Explicitly validate the workflow definition locally
|
|
95
|
+
// Requires all nodes/tools to be finalized with .end()
|
|
96
|
+
const validation = graph.validate();
|
|
97
|
+
if (!validation.isValid) {
|
|
98
|
+
console.error("Workflow validation failed:", validation.errors);
|
|
99
|
+
throw new Error("Invalid workflow definition");
|
|
100
|
+
} else {
|
|
101
|
+
console.log("Local validation successful!");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Execute the workflow
|
|
105
|
+
// Requires all nodes/tools to be finalized with .end()
|
|
106
|
+
console.log('Executing workflow...');
|
|
107
|
+
const apiResponse = await graph.execute({
|
|
108
|
+
context: { // Initial data for the workflow run
|
|
109
|
+
userId: 'user-789'
|
|
110
|
+
},
|
|
111
|
+
// webhookUrl: 'https://my-service.com/updates' // Optional webhook
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
if (apiResponse.success && apiResponse.data) {
|
|
115
|
+
console.log('Workflow execution started successfully:', apiResponse.data);
|
|
116
|
+
// Access workflowId via apiResponse.data.workflowId
|
|
117
|
+
} else {
|
|
118
|
+
console.error(`Workflow execution failed: ${apiResponse.message}`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.error('Error building or executing workflow:', error);
|
|
123
|
+
// Handle potential ZodErrors during build if schemas are used directly
|
|
124
|
+
if (error instanceof z.ZodError) {
|
|
125
|
+
console.error("Detailed Zod Errors:", error.errors);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
## Method Summary
|
|
132
|
+
|
|
133
|
+
This section provides a quick overview of the primary methods available on the `GraphCompose` instance, grouped by purpose.
|
|
134
|
+
|
|
135
|
+
### Initialization
|
|
136
|
+
|
|
137
|
+
| Method | Purpose |
|
|
138
|
+
| :-------------------------- | :----------------------------------------------------- |
|
|
139
|
+
| `new GraphCompose(options?)` | Creates a new workflow builder instance. Requires API token. |
|
|
140
|
+
|
|
141
|
+
### Component Definition
|
|
142
|
+
|
|
143
|
+
These methods start the definition of a new workflow component and return a **builder instance**. You **must** call `.end()` on the builder to finalize the component.
|
|
144
|
+
|
|
145
|
+
| Method | Purpose |
|
|
146
|
+
| :---------------------------------- | :------------------------------------------------ |
|
|
147
|
+
| `node(id)` | Starts defining a standard HTTP node. |
|
|
148
|
+
| `agent(id)` | Starts defining an Agent node, which orchestrates calls to your custom HTTP endpoint. |
|
|
149
|
+
| `errorBoundary(id, protectedNodes)` | Starts defining an Error Boundary node. |
|
|
150
|
+
| `tool(id)` | Starts defining a Tool (HTTP or Graph based). |
|
|
151
|
+
|
|
152
|
+
### Node/Tool Configuration (on Builder)
|
|
153
|
+
|
|
154
|
+
These are examples of methods called on the **builder instance** returned by the definition methods above to configure the specific component.
|
|
155
|
+
|
|
156
|
+
| Method | Purpose |
|
|
157
|
+
| :---------------------------- | :--------------------------------------------------------------------------- |
|
|
158
|
+
| `.get(url)`, `.post(url)`, etc. | Sets the HTTP method and URL (HTTP Nodes/Tools). |
|
|
159
|
+
| `.withHeaders(headers)` | Adds HTTP headers (HTTP Nodes/Tools). |
|
|
160
|
+
| `.withBody(body)` | Sets the request body (HTTP Nodes/Tools). |
|
|
161
|
+
| `.withDependencies(ids)` | Specifies prerequisite nodes (HTTP & Agent Nodes). |
|
|
162
|
+
| `.withRetries(policy)` | Configures automatic retry behavior (HTTP Nodes/Tools). |
|
|
163
|
+
| `.withTools(ids)` | (Agent builder) Declares tools available for the agent's logic to call. |
|
|
164
|
+
| `.addTool(tool)` | (Agent builder) Adds a single tool available for the agent's logic to call. |
|
|
165
|
+
| `.withMaxIterations(n)` | (Agent builder) Sets the maximum number of agent iterations. |
|
|
166
|
+
| `.graph(callback)` | (Tool builder) Defines a sub-graph for a Graph tool. |
|
|
167
|
+
| `.description(text)` | (Tool builder) Sets the description used by agents to select the tool. |
|
|
168
|
+
| `.withConditions(conds)` | (Node builder) Adds conditional execution logic (HTTP Nodes only). |
|
|
169
|
+
| `.withValidation(schema)` | (Node builder) Adds input/output schema validation (HTTP Nodes only). |
|
|
170
|
+
| `.withStartToCloseTimeout(d)` | Sets max time for one execution attempt (HTTP Nodes/Tools). |
|
|
171
|
+
| `.withScheduleToCloseTimeout(d)`| Sets max time from schedule to completion (incl. queue) (HTTP Nodes/Tools). |
|
|
172
|
+
|
|
173
|
+
### Finalization (on Builder)
|
|
174
|
+
|
|
175
|
+
This method is called on the **builder instance** to finalize the component's definition.
|
|
176
|
+
|
|
177
|
+
| Method | Purpose |
|
|
178
|
+
| :------- | :-------------------------------------------------------------------- |
|
|
179
|
+
| `.end()` | Finalizes the current node/tool definition and adds it to the graph. |
|
|
180
|
+
|
|
181
|
+
### Workflow Configuration
|
|
182
|
+
|
|
183
|
+
These methods configure the overall workflow. They must be called when no node or tool builder is active.
|
|
184
|
+
|
|
185
|
+
| Method | Purpose |
|
|
186
|
+
| :--------------------------- | :------------------------------------------------ |
|
|
187
|
+
| `withContext(context)` | Sets global context data accessible to all nodes. |
|
|
188
|
+
| `withWebhookUrl(url)` | Sets a webhook URL for workflow event updates. |
|
|
189
|
+
| `withWorkflowConfig(config)` | Sets global workflow settings (e.g., timeouts). |
|
|
190
|
+
|
|
191
|
+
### Validation
|
|
192
|
+
|
|
193
|
+
These methods validate the workflow definition. They must be called when no node or tool builder is active.
|
|
194
|
+
|
|
195
|
+
| Method | Purpose |
|
|
196
|
+
| :-------------- | :------------------------------------------------------- |
|
|
197
|
+
| `validate()` | Performs local, client-side validation of the structure. |
|
|
198
|
+
| `validateApi()` | Sends the definition to the API for server-side validation. |
|
|
199
|
+
|
|
200
|
+
### Execution
|
|
201
|
+
|
|
202
|
+
These methods execute the workflow. They must be called when no node or tool builder is active.
|
|
203
|
+
|
|
204
|
+
| Method | Purpose |
|
|
205
|
+
| :--------------------- | :-------------------------------------------------------------------- |
|
|
206
|
+
| `execute(options?)` | Executes the workflow asynchronously, returning immediately. |
|
|
207
|
+
| `executeSync(options?)`| Executes the workflow synchronously, waiting for completion. |
|
|
208
|
+
|
|
209
|
+
### Retrieving Definition
|
|
210
|
+
|
|
211
|
+
These methods retrieve the built workflow definition. They must be called when no node or tool builder is active.
|
|
212
|
+
|
|
213
|
+
| Method | Purpose |
|
|
214
|
+
| :-------------- | :------------------------------------------------------------------------ |
|
|
215
|
+
| `getWorkflow()` | Returns the complete workflow definition object. |
|
|
216
|
+
| `toJSON()` | Alias for `getWorkflow()`; standard method for `JSON.stringify()`. |
|
|
217
|
+
|
|
218
|
+
### API Interaction
|
|
219
|
+
|
|
220
|
+
These methods interact with existing workflow instances via the API.
|
|
221
|
+
|
|
222
|
+
| Method | Purpose |
|
|
223
|
+
| :------------------------------------ | :--------------------------------------------------- |
|
|
224
|
+
| `getWorkflowStatus(workflowId)` | Retrieves the current status and details of a workflow. |
|
|
225
|
+
| `terminateWorkflow(workflowId, opts?)`| Requests termination of a running workflow. |
|
|
226
|
+
|
|
227
|
+
## Core Concepts
|
|
228
|
+
|
|
229
|
+
### The `GraphCompose` Builder
|
|
230
|
+
|
|
231
|
+
This is the main class you'll instantiate. It holds the state of the workflow being built.
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
import { GraphCompose } from '@graph-compose/client';
|
|
235
|
+
|
|
236
|
+
// Reads from process.env.GRAPH_COMPOSE_TOKEN if not provided
|
|
237
|
+
const graph = new GraphCompose({
|
|
238
|
+
token: process.env.GRAPH_COMPOSE_TOKEN || 'your-api-token',
|
|
239
|
+
});
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Defining Nodes and Tools - The `.end()` Method
|
|
243
|
+
|
|
244
|
+
You define the components of your workflow (nodes and tools) by chaining methods starting with `graph.node('node_id')`, `graph.agent('agent_id')`, `graph.errorBoundary('boundary_id', [...])`, or `graph.tool('tool_id')`.
|
|
245
|
+
|
|
246
|
+
* **Configuration:** Chain methods like `.get()`, `.post()`, `.withHeaders()`, `.withBody()`, `.withRetries()`, `.withDependencies()`, etc., to configure the node or tool.
|
|
247
|
+
* **Finalization:** **Crucially, you MUST call `.end()`** after configuring each node or tool. This finalizes its definition, adds it to the graph, and returns control to the main `GraphCompose` instance. Attempting to start defining a new component or perform graph-level operations (like `validate` or `execute`) before calling `.end()` on the *current* component builder will result in an error.
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
// Correct usage with .end()
|
|
251
|
+
graph
|
|
252
|
+
.node('fetch_data') // Start defining node 'fetch_data'
|
|
253
|
+
.get('https://api.example.com/data')
|
|
254
|
+
.withStartToCloseTimeout('10s')
|
|
255
|
+
.end(); // Finalize 'fetch_data' and return to the main graph builder
|
|
256
|
+
|
|
257
|
+
graph
|
|
258
|
+
.node('process_data') // Start defining node 'process_data'
|
|
259
|
+
.withDependencies(['fetch_data']) // Depends on 'fetch_data'
|
|
260
|
+
.post('https://api.example.com/process')
|
|
261
|
+
.withBody({ input: '{{ results.fetch_data }}' })
|
|
262
|
+
.end(); // Finalize 'process_data'
|
|
263
|
+
|
|
264
|
+
// Now it's safe to perform graph-level operations like validate or execute
|
|
265
|
+
// const validation = graph.validate();
|
|
266
|
+
// const result = await graph.execute();
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Defining Node Types
|
|
270
|
+
|
|
271
|
+
Nodes represent the primary steps or actions within your workflow. Remember to always finish defining a node with `.end()`.
|
|
272
|
+
|
|
273
|
+
### HTTP Nodes
|
|
274
|
+
|
|
275
|
+
These are the most basic type, used for making HTTP requests.
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
graph.node("fetch_user_data")
|
|
279
|
+
.description("Retrieves user details based on context ID.") // Optional description
|
|
280
|
+
.get("https://api.example.com/users/{{context.userId}}")
|
|
281
|
+
.withHeaders({ "Authorization": "Bearer YOUR_SERVICE_TOKEN" })
|
|
282
|
+
.withStartToCloseTimeout('5s') // Example timeout
|
|
283
|
+
.end(); // Required!
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Agent Nodes
|
|
287
|
+
|
|
288
|
+
Agent nodes orchestrate iterative calls to a user-defined HTTP endpoint. This endpoint implements the agent's logic (which might involve AI models) to process inputs, make decisions, and determine which Tools (other workflow nodes) to use.
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
graph.agent("process_user_request")
|
|
292
|
+
.description("Handles user queries using available tools.") // Optional
|
|
293
|
+
.withMaxIterations(5) // Limit the number of tool calls/LLM turns
|
|
294
|
+
.withTools(["search_web", "summarize_content"]) // Reference tool IDs (defined separately)
|
|
295
|
+
.post("https://ai.example.com/process") // The endpoint hosting the agent logic
|
|
296
|
+
.withBody({ query: "{{context.userInput}}" })
|
|
297
|
+
.withDependencies(["fetch_user_data"]) // Can depend on other nodes
|
|
298
|
+
.end(); // Required!
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Error Boundary Nodes
|
|
302
|
+
|
|
303
|
+
These nodes execute *only* if one of the nodes they monitor fails. They are used for error handling and cleanup.
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
graph.errorBoundary("api_error_handler", ["fetch_user_data", "process_user_request"]) // List of node IDs to monitor
|
|
307
|
+
.description("Logs errors from API calls.") // Optional
|
|
308
|
+
.post("https://my-service.com/log-error")
|
|
309
|
+
.withBody({
|
|
310
|
+
failedNodeId: '{{ error.nodeId }}',
|
|
311
|
+
errorMessage: '{{ error.message }}',
|
|
312
|
+
timestamp: '{{ $now() }}'
|
|
313
|
+
})
|
|
314
|
+
.end(); // Required!
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
## Defining Tool Types (for Agents)
|
|
318
|
+
|
|
319
|
+
Tools are functions or services that Agent Nodes can call to perform specific actions. Remember to always finish defining a tool with `.end()`.
|
|
320
|
+
|
|
321
|
+
### HTTP Tools
|
|
322
|
+
|
|
323
|
+
These are simple tools that make an HTTP request when called by an agent.
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
graph.tool("search_web")
|
|
327
|
+
.description("Searches the web for a given query.") // Crucial for the agent to understand the tool
|
|
328
|
+
.post("https://api.search-provider.com/search")
|
|
329
|
+
.withBody({ query: "{{input.query}}" }) // 'input' contains agent-provided parameters
|
|
330
|
+
.withScheduleToCloseTimeout('15s') // Example timeout
|
|
331
|
+
.end(); // Required!
|
|
332
|
+
|
|
333
|
+
graph.tool("summarize_content")
|
|
334
|
+
.description("Summarizes a provided block of text.")
|
|
335
|
+
.post("https://ai.example.com/summarize")
|
|
336
|
+
.withBody({ text: "{{input.text_to_summarize}}" })
|
|
337
|
+
.end(); // Required!
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Graph Tools
|
|
341
|
+
|
|
342
|
+
These allow encapsulating a *sub-graph* as a tool. This sub-graph can contain multiple HTTP nodes.
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
graph.tool('data_processor')
|
|
346
|
+
.description('Validates and transforms input data using a multi-step process.')
|
|
347
|
+
.graph((subGraph) => { // Define the sub-graph structure
|
|
348
|
+
subGraph
|
|
349
|
+
.node('validate') // Nodes within the tool's sub-graph
|
|
350
|
+
.post('https://api.example.com/validate')
|
|
351
|
+
.withBody('{{ context.example_context }}')
|
|
352
|
+
.end(); // End sub-graph node
|
|
353
|
+
subGraph
|
|
354
|
+
.node('transform')
|
|
355
|
+
.withDependencies(['validate']) // Dependencies within the sub-graph
|
|
356
|
+
.post('https://api.example.com/transform')
|
|
357
|
+
.withBody('{{ results.validate }}') // Results refer to nodes within the sub-graph
|
|
358
|
+
.end(); // End sub-graph node
|
|
359
|
+
}) // End of sub-graph definition
|
|
360
|
+
.end(); // <-- Required: Finalize the tool itself
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
**Graph Tool Validation Rules:**
|
|
364
|
+
- Graph tools can only contain HTTP nodes.
|
|
365
|
+
- Graph tools must have at least one node defined (and ended).
|
|
366
|
+
- Each graph tool runs as an isolated sub-workflow when invoked by an agent.
|
|
367
|
+
|
|
368
|
+
### Assigning Tools to Agents
|
|
369
|
+
|
|
370
|
+
You declare which Tools (defined elsewhere in the workflow) are available for your agent logic to request by referencing their IDs when defining the agent node.
|
|
371
|
+
|
|
372
|
+
```typescript
|
|
373
|
+
graph.agent('assistant')
|
|
374
|
+
.withMaxIterations(5)
|
|
375
|
+
.withTools(['search_web', 'data_processor']) // Reference the tools by their defined IDs
|
|
376
|
+
.post('https://api.example.com/agent')
|
|
377
|
+
// ... other agent configurations ...
|
|
378
|
+
.end(); // Required!
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
## Workflow Configuration
|
|
382
|
+
|
|
383
|
+
These methods modify the overall graph configuration and require that no node or tool builder is currently active (i.e., the previous builder must have called `.end()`).
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
// Assuming previous node/tool builders have called .end()
|
|
387
|
+
|
|
388
|
+
// Set initial data available to all nodes via 'context'
|
|
389
|
+
graph.withContext({
|
|
390
|
+
userId: "user-123",
|
|
391
|
+
userInput: "Tell me about GraphCompose",
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
// Specify a URL to receive webhook updates about the workflow's progress
|
|
395
|
+
graph.withWebhookUrl("https://my-app.com/graph-compose-webhook");
|
|
396
|
+
|
|
397
|
+
// Set global workflow limits
|
|
398
|
+
graph.withWorkflowConfig({
|
|
399
|
+
workflowExecutionTimeout: "1 hour" // Max total execution time
|
|
400
|
+
// workflowTaskTimeout: "1m" // Max time for a single step/task
|
|
401
|
+
});
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
## Validation
|
|
405
|
+
|
|
406
|
+
Ensuring your workflow definition is correct before execution is crucial.
|
|
407
|
+
|
|
408
|
+
### Local Validation
|
|
409
|
+
|
|
410
|
+
The client performs structural and configuration validation locally when you call `graph.validate()`. This includes checking for:
|
|
411
|
+
* Valid node/tool IDs and names.
|
|
412
|
+
* Dependency cycles.
|
|
413
|
+
* Correct agent configuration (like `max_iterations`).
|
|
414
|
+
* Valid JSONata expressions used in configurations.
|
|
415
|
+
* Graph Tool structure rules.
|
|
416
|
+
|
|
417
|
+
This method requires all nodes and tools to have been finalized with `.end()`. Execution methods (`.execute()`, `.executeSync()`) also perform this validation internally.
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
// Assuming all node/tool builders have called .end()
|
|
421
|
+
const validationResult = graph.validate();
|
|
422
|
+
if (!validationResult.isValid) {
|
|
423
|
+
console.error("Workflow validation failed locally:", validationResult.errors);
|
|
424
|
+
} else {
|
|
425
|
+
console.log("Local validation successful!");
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### API Validation
|
|
430
|
+
|
|
431
|
+
You can also send the *current* workflow definition to the Graph Compose API for validation. This might catch additional issues specific to the execution environment.
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
// Assuming all node/tool builders have called .end()
|
|
435
|
+
async function validateWithApi() {
|
|
436
|
+
try {
|
|
437
|
+
// validateApi returns ApiResponse<ApiValidationResult | null>
|
|
438
|
+
const apiResponse = await graph.validateApi();
|
|
439
|
+
|
|
440
|
+
if (apiResponse.success && apiResponse.data) {
|
|
441
|
+
// Check the validation result within the data field
|
|
442
|
+
if (apiResponse.data.isValid) {
|
|
443
|
+
console.log("API validation successful!");
|
|
444
|
+
} else {
|
|
445
|
+
console.error("API validation failed:", apiResponse.data.errors);
|
|
446
|
+
}
|
|
447
|
+
} else {
|
|
448
|
+
// Handle cases where the API call itself failed (e.g., network error, auth error)
|
|
449
|
+
console.error(`API validation request failed: ${apiResponse.message}`);
|
|
450
|
+
}
|
|
451
|
+
} catch (error) {
|
|
452
|
+
console.error("Failed to call validation API:", error);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
validateWithApi();
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
## Execution
|
|
459
|
+
|
|
460
|
+
Once defined and validated, you can run your workflow. Execution methods require all nodes and tools to have been finalized with `.end()`.
|
|
461
|
+
|
|
462
|
+
* **Asynchronous (`execute`)**: This is the recommended method. It starts the workflow on the Graph Compose platform and immediately returns a `workflowId`. You can monitor progress via webhooks or by polling the status API.
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
// Assuming all node/tool builders have called .end()
|
|
466
|
+
async function startWorkflowAsync() {
|
|
467
|
+
try {
|
|
468
|
+
// Pass any runtime context needed
|
|
469
|
+
// execute returns ApiResponse<WorkflowResponse | null>
|
|
470
|
+
const apiResponse = await graph.execute({ context: { batchId: "batch-789" } });
|
|
471
|
+
|
|
472
|
+
if (apiResponse.success && apiResponse.data) {
|
|
473
|
+
console.log("Workflow started successfully with ID:", apiResponse.data.workflowId);
|
|
474
|
+
// Use apiResponse.data.workflowId and apiResponse.data.runId
|
|
475
|
+
} else {
|
|
476
|
+
console.error(`Failed to start async workflow: ${apiResponse.message}`);
|
|
477
|
+
}
|
|
478
|
+
// Use workflowId to track status or receive webhook updates
|
|
479
|
+
} catch (error) {
|
|
480
|
+
console.error("Failed to start async workflow:", error);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
startWorkflowAsync();
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
* **Synchronous (`executeSync`)**: This method starts the workflow and *waits* for it to complete (or fail) before returning the final result or error. This is simpler for short-running workflows but can time out for longer processes.
|
|
487
|
+
|
|
488
|
+
```typescript
|
|
489
|
+
// Assuming all node/tool builders have called .end()
|
|
490
|
+
async function runWorkflowAndWait() {
|
|
491
|
+
try {
|
|
492
|
+
// Pass any runtime context needed
|
|
493
|
+
// executeSync returns ApiResponse<WorkflowResponse | null>
|
|
494
|
+
const apiResponse = await graph.executeSync({ context: { overrideUserId: "new-user-456" } });
|
|
495
|
+
|
|
496
|
+
if (apiResponse.success && apiResponse.data) {
|
|
497
|
+
console.log("Workflow completed synchronously:", apiResponse.data);
|
|
498
|
+
// apiResponse.data contains status, executionState, error etc.
|
|
499
|
+
} else {
|
|
500
|
+
console.error(`Synchronous workflow execution failed: ${apiResponse.message}`);
|
|
501
|
+
}
|
|
502
|
+
} catch (error) {
|
|
503
|
+
console.error("Synchronous workflow execution failed:", error);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
runWorkflowAndWait();
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
## Getting the Workflow Definition
|
|
510
|
+
|
|
511
|
+
After finalizing all nodes and tools with `.end()`, you can retrieve the raw workflow definition object that the client has built. This is useful for debugging, saving, or inspecting the structure.
|
|
512
|
+
|
|
513
|
+
* `graph.getWorkflow()`: Returns the complete `WorkflowGraph` object.
|
|
514
|
+
* `graph.toJSON()`: An alias for `getWorkflow()`, providing the standard JavaScript method for serialization.
|
|
515
|
+
|
|
516
|
+
Both methods will throw an error if any node or tool builder is still active (i.e., missing a final `.end()` call).
|
|
517
|
+
|
|
518
|
+
```typescript
|
|
519
|
+
// Assuming all builders are finalized
|
|
520
|
+
|
|
521
|
+
const workflowDefinition = graph.getWorkflow();
|
|
522
|
+
console.log("Raw Workflow Definition Object:", workflowDefinition);
|
|
523
|
+
|
|
524
|
+
// Equivalent using toJSON() for easy serialization
|
|
525
|
+
const workflowJsonString = JSON.stringify(graph.toJSON(), null, 2);
|
|
526
|
+
console.log("Workflow JSON:", workflowJsonString);
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
## Interacting with Existing Workflows (API)
|
|
530
|
+
|
|
531
|
+
These methods interact directly with the Graph Compose API using a workflow ID, and don't depend on the local builder state.
|
|
532
|
+
|
|
533
|
+
* **Get Workflow Status:** Check the current status (running, completed, failed, etc.) of a previously started workflow.
|
|
534
|
+
```typescript
|
|
535
|
+
// Does not depend on local builder state
|
|
536
|
+
async function checkStatus(workflowId: string) {
|
|
537
|
+
try {
|
|
538
|
+
const statusResult = await graph.getWorkflowStatus(workflowId);
|
|
539
|
+
console.log(`Status for ${workflowId}:`, statusResult.status);
|
|
540
|
+
// Handle other details in statusResult like results or errors
|
|
541
|
+
} catch (error) {
|
|
542
|
+
console.error("Failed to get workflow status:", error);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
checkStatus("existing-workflow-id-123");
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
* **Terminate Workflow:** Stop a currently running workflow.
|
|
549
|
+
```typescript
|
|
550
|
+
// Does not depend on local builder state
|
|
551
|
+
async function stopWorkflow(workflowId: string, reason?: string) {
|
|
552
|
+
try {
|
|
553
|
+
const terminateResult = await graph.terminateWorkflow(workflowId, { reason });
|
|
554
|
+
console.log(`Termination request for ${workflowId}:`, terminateResult);
|
|
555
|
+
} catch (error) {
|
|
556
|
+
console.error("Failed to terminate workflow:", error);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
stopWorkflow("running-workflow-id-456", "User cancelled operation");
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
## Full Example: Putting it Together
|
|
563
|
+
|
|
564
|
+
This example demonstrates defining nodes, setting context, validating, and executing.
|
|
565
|
+
|
|
566
|
+
```typescript
|
|
567
|
+
import { GraphCompose } from "@graph-compose/client";
|
|
568
|
+
|
|
569
|
+
async function main() {
|
|
570
|
+
const graph = new GraphCompose({ token: process.env.GRAPH_COMPOSE_TOKEN || "" });
|
|
571
|
+
|
|
572
|
+
// Define Node 1
|
|
573
|
+
graph.node("get_ip")
|
|
574
|
+
.description("Fetches the public IP address of the caller.")
|
|
575
|
+
.get("https://httpbin.org/ip")
|
|
576
|
+
.end(); // Required!
|
|
577
|
+
|
|
578
|
+
// Define Node 2 (depends on Node 1)
|
|
579
|
+
graph.node("echo_ip")
|
|
580
|
+
.description("Echoes the IP address back.")
|
|
581
|
+
.post("https://httpbin.org/post")
|
|
582
|
+
.withBody({ theIpAddress: "{{results.get_ip.origin}}" }) // Access results from 'get_ip'
|
|
583
|
+
.withDependencies(["get_ip"])
|
|
584
|
+
.end(); // Required!
|
|
585
|
+
|
|
586
|
+
// Set context *after* ending the last node builder
|
|
587
|
+
graph.withContext({ requestSource: "client-example-readme" });
|
|
588
|
+
|
|
589
|
+
console.log("Validating workflow locally...");
|
|
590
|
+
// Validate *after* ending all node builders and setting context/config
|
|
591
|
+
const validation = graph.validate();
|
|
592
|
+
if (!validation.isValid) {
|
|
593
|
+
console.error("Local Validation Failed:", validation.errors);
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
console.log("Local Validation Successful!");
|
|
597
|
+
|
|
598
|
+
console.log("Executing workflow asynchronously...");
|
|
599
|
+
// Execute *after* ending all node builders and setting context/config
|
|
600
|
+
try {
|
|
601
|
+
// execute returns ApiResponse<WorkflowResponse | null>
|
|
602
|
+
const apiResponse = await graph.execute(); // Using async execution
|
|
603
|
+
|
|
604
|
+
if (apiResponse.success && apiResponse.data) {
|
|
605
|
+
const workflowId = apiResponse.data.workflowId;
|
|
606
|
+
console.log("Workflow started with ID:", workflowId);
|
|
607
|
+
console.log("Monitor status using `getWorkflowStatus(workflowId)` or webhooks.");
|
|
608
|
+
// Example: Poll status after a short delay
|
|
609
|
+
// setTimeout(async () => {
|
|
610
|
+
// await checkStatus(workflowId); // Assumes checkStatus function from above is defined
|
|
611
|
+
// }, 5000);
|
|
612
|
+
} else {
|
|
613
|
+
console.error(`Failed to start workflow: ${apiResponse.message}`);
|
|
614
|
+
}
|
|
615
|
+
} catch (error) {
|
|
616
|
+
console.error("Execution Failed:", error);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// Helper function for the example (copy from 'Interacting with Existing Workflows' section)
|
|
621
|
+
async function checkStatus(workflowId: string) {
|
|
622
|
+
// Re-initialize graph object or ensure it's accessible in this scope
|
|
623
|
+
const graph = new GraphCompose({ token: process.env.GRAPH_COMPOSE_TOKEN || "" });
|
|
624
|
+
try {
|
|
625
|
+
const statusResult = await graph.getWorkflowStatus(workflowId);
|
|
626
|
+
console.log(`[Status Check] Status for ${workflowId}:`, statusResult.status);
|
|
627
|
+
if (statusResult.status === 'COMPLETED') {
|
|
628
|
+
console.log('[Status Check] Results:', statusResult.results);
|
|
629
|
+
} else if (statusResult.status === 'FAILED') {
|
|
630
|
+
console.error('[Status Check] Error:', statusResult.error);
|
|
631
|
+
}
|
|
632
|
+
} catch (error) {
|
|
633
|
+
console.error("[Status Check] Failed to get workflow status:", error);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
main();
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
## Contributing
|
|
641
|
+
|
|
642
|
+
Contributions are welcome! Please refer to the main repository's contribution guidelines.
|
|
643
|
+
|
|
644
|
+
## License
|
|
645
|
+
|
|
646
|
+
This project is licensed under the MIT License. See the LICENSE file in the main repository for details.
|