@graph-compose/client 1.0.0 → 1.0.3

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.
Files changed (49) hide show
  1. package/README.md +949 -476
  2. package/dist/core/adk-helpers.d.ts +94 -0
  3. package/dist/core/adk-helpers.d.ts.map +1 -0
  4. package/dist/core/adk-helpers.js +134 -0
  5. package/dist/core/adk-helpers.js.map +1 -0
  6. package/dist/core/adk-node-builder.d.ts +128 -0
  7. package/dist/core/adk-node-builder.d.ts.map +1 -0
  8. package/dist/core/adk-node-builder.js +175 -0
  9. package/dist/core/adk-node-builder.js.map +1 -0
  10. package/dist/core/adk-workflow-builder.d.ts +74 -0
  11. package/dist/core/adk-workflow-builder.d.ts.map +1 -0
  12. package/dist/core/adk-workflow-builder.js +138 -0
  13. package/dist/core/adk-workflow-builder.js.map +1 -0
  14. package/dist/core/base-builder.d.ts +80 -0
  15. package/dist/core/base-builder.d.ts.map +1 -0
  16. package/dist/core/base-builder.js +63 -0
  17. package/dist/core/base-builder.js.map +1 -0
  18. package/dist/core/builder.d.ts +35 -67
  19. package/dist/core/builder.d.ts.map +1 -1
  20. package/dist/core/builder.js +45 -144
  21. package/dist/core/builder.js.map +1 -1
  22. package/dist/core/node-builder.d.ts +15 -59
  23. package/dist/core/node-builder.d.ts.map +1 -1
  24. package/dist/core/node-builder.js +36 -106
  25. package/dist/core/node-builder.js.map +1 -1
  26. package/dist/index.d.ts +5 -1
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +7 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/types.d.ts +50 -4
  31. package/dist/types.d.ts.map +1 -1
  32. package/dist/types.js.map +1 -1
  33. package/dist/validation/adk-validation.d.ts +10 -0
  34. package/dist/validation/adk-validation.d.ts.map +1 -0
  35. package/dist/validation/adk-validation.js +362 -0
  36. package/dist/validation/adk-validation.js.map +1 -0
  37. package/dist/validation/index.d.ts +34 -9
  38. package/dist/validation/index.d.ts.map +1 -1
  39. package/dist/validation/index.js +40 -131
  40. package/dist/validation/index.js.map +1 -1
  41. package/package.json +4 -2
  42. package/dist/core/tool-builder.d.ts +0 -130
  43. package/dist/core/tool-builder.d.ts.map +0 -1
  44. package/dist/core/tool-builder.js +0 -343
  45. package/dist/core/tool-builder.js.map +0 -1
  46. package/dist/node-builder.d.ts +0 -64
  47. package/dist/node-builder.d.ts.map +0 -1
  48. package/dist/node-builder.js +0 -261
  49. package/dist/node-builder.js.map +0 -1
package/README.md CHANGED
@@ -1,646 +1,1119 @@
1
1
  # @graph-compose/client
2
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
-
3
+ A fluent TypeScript client for building declarative, Temporal-orchestrated workflows. Compose HTTP-based microservices or multi-agent AI systems with a simple, type-safe API.
7
4
 
8
5
  ## Features
9
6
 
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)
7
+ - 🔗 **HTTP Workflow Orchestration** - Chain HTTP services with dependency management
8
+ - 🤖 **Multi-Agent AI Workflows** - Build ADK (Agent Development Kit) hierarchies with LLMs, tools, and orchestrators
9
+ - 🎯 **Type-Safe Builder Pattern** - Fluent API with full TypeScript support
10
+ - **JSONata Templating** - Dynamic parameter resolution with `{{ }}` expressions
11
+ - 🔄 **Automatic Retry & Error Handling** - Configure retry policies and error boundaries
12
+ - **Built-in Validation** - Zod-based schema validation for workflows
13
+ - 🧪 **Temporal Integration** - Native support for Temporal workflow execution
47
14
 
48
15
  ## Installation
49
16
 
50
17
  ```bash
51
18
  npm install @graph-compose/client @graph-compose/core
52
19
  # or
20
+ pnpm add @graph-compose/client @graph-compose/core
21
+ # or
53
22
  yarn add @graph-compose/client @graph-compose/core
54
23
  ```
55
- *(Note: `@graph-compose/core` is a required peer dependency.)*
56
24
 
57
- ## Quick Start: Building a Simple HTTP Workflow
25
+ ## Authentication
58
26
 
59
- ```typescript
60
- import { GraphCompose } from '@graph-compose/client';
61
- import { z } from 'zod'; // Often used with core schemas
27
+ The client requires an API token to execute workflows against the GraphCompose API:
62
28
 
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',
29
+ ```typescript
30
+ // Option 1: Pass token in constructor
31
+ const workflow = new GraphCompose({
32
+ token: 'your-api-token'
67
33
  });
68
34
 
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
35
+ // Option 2: Set environment variable
36
+ // GRAPH_COMPOSE_TOKEN=your-api-token
37
+ const workflow = new GraphCompose();
38
+ ```
93
39
 
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
- }
40
+ **API Endpoint:** `https://api.graphcompose.io/api/v1`
103
41
 
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
- });
42
+ Get your API token from the [GraphCompose Dashboard](https://app.graphcompose.io)
113
43
 
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
- }
44
+ ## Quick Start
120
45
 
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);
46
+ ### End-to-End Example
47
+
48
+ ```typescript
49
+ import { GraphCompose } from '@graph-compose/client';
50
+
51
+ // 1. Build your workflow
52
+ const workflow = new GraphCompose({
53
+ token: process.env.GRAPH_COMPOSE_TOKEN
54
+ })
55
+ .http('fetchData')
56
+ .get('https://api.example.com/data')
57
+ .end()
58
+ .http('processData')
59
+ .post('https://api.example.com/process')
60
+ .withBody({ data: '{{results.fetchData}}' })
61
+ .dependsOn(['fetchData'])
62
+ .end();
63
+
64
+ // 2. Validate it
65
+ const validation = workflow.validate();
66
+ if (!validation.isValid) {
67
+ throw new Error('Invalid workflow');
68
+ }
69
+
70
+ // 3. Execute it
71
+ const result = await workflow.execute({
72
+ context: { userId: '123' }
73
+ });
74
+
75
+ // 4. Poll for completion and get results
76
+ if (result.success && result.data) {
77
+ const workflowId = result.data.workflowId;
78
+
79
+ // Poll until complete
80
+ let status = 'RUNNING';
81
+ while (status === 'RUNNING') {
82
+ await new Promise(resolve => setTimeout(resolve, 1000));
83
+ const statusResult = await workflow.getWorkflowStatus(workflowId);
84
+ if (statusResult.success && statusResult.data) {
85
+ status = statusResult.data.status;
86
+ }
87
+ }
88
+
89
+ // Get final results
90
+ const finalResult = await workflow.getWorkflowStatus(workflowId);
91
+ if (finalResult.success && finalResult.data?.status === 'COMPLETED') {
92
+ console.log('Results:', finalResult.data.execution_state);
126
93
  }
127
94
  }
128
95
  ```
129
96
 
97
+ ### Traditional HTTP Workflow
130
98
 
131
- ## Method Summary
132
-
133
- This section provides a quick overview of the primary methods available on the `GraphCompose` instance, grouped by purpose.
99
+ Build a workflow that chains multiple HTTP services:
134
100
 
135
- ### Initialization
101
+ ```typescript
102
+ import { GraphCompose } from '@graph-compose/client';
136
103
 
137
- | Method | Purpose |
138
- | :-------------------------- | :----------------------------------------------------- |
139
- | `new GraphCompose(options?)` | Creates a new workflow builder instance. Requires API token. |
104
+ const workflow = new GraphCompose({
105
+ token: 'your-api-token'
106
+ })
107
+ .http('fetchUser')
108
+ .get('https://api.example.com/users/{{userId}}')
109
+ .withHeaders({
110
+ 'Authorization': 'Bearer {{$secret("api_token")}}'
111
+ })
112
+ .end()
113
+ .http('enrichProfile')
114
+ .post('https://api.example.com/enrich')
115
+ .withBody({
116
+ userId: '{{results.fetchUser.id}}',
117
+ email: '{{results.fetchUser.email}}'
118
+ })
119
+ .dependsOn(['fetchUser'])
120
+ .end()
121
+ .build();
122
+ ```
140
123
 
141
- ### Component Definition
124
+ ### ADK Multi-Agent Workflow
142
125
 
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.
126
+ Build an AI agent workflow with tools and orchestration:
144
127
 
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). |
128
+ ```typescript
129
+ import {
130
+ GraphCompose,
131
+ AdkWorkflowBuilder,
132
+ createLlmAgent,
133
+ createHttpTool
134
+ } from '@graph-compose/client';
135
+
136
+ // Define the agent workflow
137
+ const agentWorkflow = new AdkWorkflowBuilder()
138
+ .agent(createLlmAgent({
139
+ id: 'assistant',
140
+ httpConfig: {
141
+ url: 'https://api.example.com/llm',
142
+ method: 'POST'
143
+ },
144
+ tools: ['searchTool'],
145
+ outputKey: 'assistant_response'
146
+ }))
147
+ .httpTool(createHttpTool({
148
+ id: 'searchTool',
149
+ httpConfig: {
150
+ url: 'https://api.example.com/search',
151
+ method: 'POST',
152
+ body: {
153
+ query: '{{state.user_query}}'
154
+ }
155
+ }
156
+ }))
157
+ .setRootAgent('assistant')
158
+ .build();
159
+
160
+ // Create the workflow node
161
+ const workflow = new GraphCompose()
162
+ .adk('aiAgent')
163
+ .withWorkflow(agentWorkflow)
164
+ .withInitialPrompt('Hello! How can I help you today?')
165
+ .withMaxCycles(10)
166
+ .end()
167
+ .build();
168
+ ```
151
169
 
152
- ### Node/Tool Configuration (on Builder)
170
+ ## HTTP Workflow Builder
153
171
 
154
- These are examples of methods called on the **builder instance** returned by the definition methods above to configure the specific component.
172
+ ### Basic HTTP Methods
155
173
 
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). |
174
+ ```typescript
175
+ const builder = new GraphCompose();
176
+
177
+ // GET request
178
+ builder.http('getUser')
179
+ .get('https://api.example.com/users/123')
180
+ .end();
181
+
182
+ // POST request
183
+ builder.http('createUser')
184
+ .post('https://api.example.com/users')
185
+ .withBody({ name: 'John', email: 'john@example.com' })
186
+ .end();
187
+
188
+ // PUT, PATCH, DELETE
189
+ builder.http('updateUser').put('...').end();
190
+ builder.http('patchUser').patch('...').end();
191
+ builder.http('deleteUser').delete('...').end();
192
+ ```
172
193
 
173
- ### Finalization (on Builder)
194
+ ### Headers and Authentication
174
195
 
175
- This method is called on the **builder instance** to finalize the component's definition.
196
+ ```typescript
197
+ builder.http('secureRequest')
198
+ .post('https://api.example.com/data')
199
+ .withHeaders({
200
+ 'Authorization': 'Bearer {{$secret("api_token")}}',
201
+ 'X-Request-ID': '{{context.requestId}}',
202
+ 'Content-Type': 'application/json'
203
+ })
204
+ .end();
205
+ ```
176
206
 
177
- | Method | Purpose |
178
- | :------- | :-------------------------------------------------------------------- |
179
- | `.end()` | Finalizes the current node/tool definition and adds it to the graph. |
207
+ ### Dynamic Body and Parameters
180
208
 
181
- ### Workflow Configuration
209
+ ```typescript
210
+ builder.http('processOrder')
211
+ .post('https://api.example.com/orders')
212
+ .withBody({
213
+ orderId: '{{results.createOrder.id}}',
214
+ userId: '{{context.userId}}',
215
+ items: '{{results.fetchCart.items}}',
216
+ total: '{{results.calculateTotal.amount}}'
217
+ })
218
+ .withParams({
219
+ async: 'true',
220
+ priority: '{{context.priority}}'
221
+ })
222
+ .end();
223
+ ```
182
224
 
183
- These methods configure the overall workflow. They must be called when no node or tool builder is active.
225
+ ### Dependencies and Ordering
184
226
 
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). |
227
+ ```typescript
228
+ // Sequential execution
229
+ builder
230
+ .http('step1').get('...').end()
231
+ .http('step2').post('...').dependsOn(['step1']).end()
232
+ .http('step3').post('...').dependsOn(['step2']).end();
233
+
234
+ // Parallel execution (no dependencies)
235
+ builder
236
+ .http('fetchUserData').get('...').end()
237
+ .http('fetchOrderData').get('...').end()
238
+ .http('fetchPreferences').get('...').end();
239
+
240
+ // Fan-out then fan-in
241
+ builder
242
+ .http('fetchData1').get('...').end()
243
+ .http('fetchData2').get('...').end()
244
+ .http('aggregate')
245
+ .post('...')
246
+ .dependsOn(['fetchData1', 'fetchData2'])
247
+ .end();
248
+ ```
190
249
 
191
- ### Validation
250
+ ### Activity Configuration
192
251
 
193
- These methods validate the workflow definition. They must be called when no node or tool builder is active.
252
+ Configure Temporal activity behavior:
194
253
 
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. |
254
+ ```typescript
255
+ builder.http('reliableCall')
256
+ .post('https://api.example.com/process')
257
+ .withStartToCloseTimeout('30s')
258
+ .withScheduleToCloseTimeout('5m')
259
+ .withRetries({
260
+ maximumAttempts: 5,
261
+ initialInterval: '1s',
262
+ backoffCoefficient: 2.0,
263
+ maximumInterval: '30s'
264
+ })
265
+ .end();
266
+ ```
199
267
 
200
- ### Execution
268
+ ### Error Boundaries
201
269
 
202
- These methods execute the workflow. They must be called when no node or tool builder is active.
270
+ Wrap nodes with error handling:
203
271
 
204
- | Method | Purpose |
205
- | :--------------------- | :-------------------------------------------------------------------- |
206
- | `execute(options?)` | Executes the workflow asynchronously, returning immediately. |
207
- | `executeSync(options?)`| Executes the workflow synchronously, waiting for completion. |
272
+ ```typescript
273
+ builder
274
+ .http('riskyOperation')
275
+ .post('https://api.example.com/risky')
276
+ .end()
277
+ .errorBoundary('handleError')
278
+ .protects(['riskyOperation'])
279
+ .onError(
280
+ builder => builder
281
+ .http('fallback')
282
+ .post('https://api.example.com/fallback')
283
+ .end()
284
+ )
285
+ .end();
286
+ ```
208
287
 
209
- ### Retrieving Definition
288
+ ### Conditional Branching
210
289
 
211
- These methods retrieve the built workflow definition. They must be called when no node or tool builder is active.
290
+ ```typescript
291
+ builder
292
+ .http('checkStatus')
293
+ .get('https://api.example.com/status')
294
+ .end()
295
+ .http('handleSuccess')
296
+ .post('...')
297
+ .runWhen({
298
+ condition: '{{results.checkStatus.state = "completed"}}'
299
+ })
300
+ .dependsOn(['checkStatus'])
301
+ .end()
302
+ .http('handleFailure')
303
+ .post('...')
304
+ .runWhen({
305
+ condition: '{{results.checkStatus.state = "failed"}}'
306
+ })
307
+ .dependsOn(['checkStatus'])
308
+ .end();
309
+ ```
212
310
 
213
- | Method | Purpose |
214
- | :-------------- | :------------------------------------------------------------------------ |
215
- | `getWorkflow()` | Returns the complete workflow definition object. |
216
- | `toJSON()` | Alias for `getWorkflow()`; standard method for `JSON.stringify()`. |
311
+ ## ADK Agent Workflows
217
312
 
218
- ### API Interaction
313
+ ### Agent Types
219
314
 
220
- These methods interact with existing workflow instances via the API.
315
+ #### LLM Agent
316
+ The primary agent that interacts with language models:
221
317
 
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. |
318
+ ```typescript
319
+ import { createLlmAgent } from '@graph-compose/client';
320
+
321
+ createLlmAgent({
322
+ id: 'assistant',
323
+ httpConfig: {
324
+ url: 'https://api.example.com/llm',
325
+ method: 'POST'
326
+ },
327
+ tools: ['searchTool', 'calculatorTool'],
328
+ outputKey: 'assistant_response',
329
+ activityConfig: {
330
+ startToCloseTimeout: '30s',
331
+ retryPolicy: {
332
+ maximumAttempts: 3
333
+ }
334
+ }
335
+ })
336
+ ```
226
337
 
227
- ## Core Concepts
338
+ #### Sequential Agent
339
+ Executes sub-agents in order:
228
340
 
229
- ### The `GraphCompose` Builder
341
+ ```typescript
342
+ import { createSequentialAgent, createSubAgentReference } from '@graph-compose/client';
343
+
344
+ createSequentialAgent({
345
+ id: 'pipeline',
346
+ httpConfig: {
347
+ url: 'https://api.example.com/orchestrate',
348
+ method: 'POST'
349
+ },
350
+ subAgents: [
351
+ createSubAgentReference('extractor'),
352
+ createSubAgentReference('transformer'),
353
+ createSubAgentReference('loader')
354
+ ]
355
+ })
356
+ ```
230
357
 
231
- This is the main class you'll instantiate. It holds the state of the workflow being built.
358
+ #### Parallel Agent
359
+ Executes sub-agents concurrently:
232
360
 
233
361
  ```typescript
234
- import { GraphCompose } from '@graph-compose/client';
362
+ import { createParallelAgent } from '@graph-compose/client';
363
+
364
+ createParallelAgent({
365
+ id: 'fanOut',
366
+ httpConfig: {
367
+ url: 'https://api.example.com/orchestrate',
368
+ method: 'POST'
369
+ },
370
+ subAgents: [
371
+ createSubAgentReference('analyzer1'),
372
+ createSubAgentReference('analyzer2'),
373
+ createSubAgentReference('analyzer3')
374
+ ]
375
+ // Note: ParallelAgent does not support outputKey
376
+ // Individual sub-agents should have their own outputKeys
377
+ })
378
+ ```
235
379
 
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
- });
380
+ #### Loop Agent
381
+ Repeats sub-agents with iteration limits:
382
+
383
+ ```typescript
384
+ import { createLoopAgent } from '@graph-compose/client';
385
+
386
+ createLoopAgent({
387
+ id: 'retryLoop',
388
+ httpConfig: {
389
+ url: 'https://api.example.com/orchestrate',
390
+ method: 'POST'
391
+ },
392
+ subAgents: [
393
+ createSubAgentReference('worker')
394
+ ],
395
+ maxAgentLoopIterations: 5,
396
+ loopExitCondition: '{{state.task_completed = true}}',
397
+ outputKey: 'loop_result'
398
+ })
240
399
  ```
241
400
 
242
- ### Defining Nodes and Tools - The `.end()` Method
401
+ ### Tools
243
402
 
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')`.
403
+ #### HTTP Tool
404
+ Execute external HTTP services:
245
405
 
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.
406
+ ```typescript
407
+ import { createHttpTool } from '@graph-compose/client';
408
+
409
+ createHttpTool({
410
+ id: 'searchTool',
411
+ httpConfig: {
412
+ url: 'https://api.example.com/search',
413
+ method: 'POST',
414
+ body: {
415
+ query: '{{state.user_query}}',
416
+ filters: '{{state.filters}}'
417
+ },
418
+ headers: {
419
+ 'Authorization': 'Bearer {{$secret("search_api_key")}}'
420
+ }
421
+ },
422
+ outputKey: 'search_results'
423
+ })
424
+ ```
425
+
426
+ #### Agent Tool
427
+ Delegate to another agent (specialist pattern):
248
428
 
249
429
  ```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
430
+ import { createAgentTool } from '@graph-compose/client';
431
+
432
+ createAgentTool({
433
+ id: 'specialistTool',
434
+ targetAgentId: 'specialist',
435
+ skipSummarization: false,
436
+ outputKey: 'specialist_result'
437
+ })
438
+ ```
256
439
 
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'
440
+ ### Complete ADK Example
263
441
 
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();
442
+ ```typescript
443
+ import {
444
+ GraphCompose,
445
+ AdkWorkflowBuilder,
446
+ createLlmAgent,
447
+ createSequentialAgent,
448
+ createHttpTool,
449
+ createAgentTool,
450
+ createSubAgentReference
451
+ } from '@graph-compose/client';
452
+
453
+ // Build the agent hierarchy
454
+ const workflow = new AdkWorkflowBuilder()
455
+ // Main coordinator agent
456
+ .agent(createLlmAgent({
457
+ id: 'coordinator',
458
+ httpConfig: {
459
+ url: 'https://api.example.com/llm',
460
+ method: 'POST'
461
+ },
462
+ tools: ['researchTool', 'analysisTool'],
463
+ outputKey: 'coordinator_response'
464
+ }))
465
+
466
+ // Research specialist
467
+ .agent(createLlmAgent({
468
+ id: 'researcher',
469
+ httpConfig: {
470
+ url: 'https://api.example.com/llm',
471
+ method: 'POST'
472
+ },
473
+ tools: ['searchTool'],
474
+ outputKey: 'research_results'
475
+ }))
476
+
477
+ // Analysis specialist
478
+ .agent(createLlmAgent({
479
+ id: 'analyst',
480
+ httpConfig: {
481
+ url: 'https://api.example.com/llm',
482
+ method: 'POST'
483
+ },
484
+ outputKey: 'analysis_results'
485
+ }))
486
+
487
+ // HTTP search tool
488
+ .httpTool(createHttpTool({
489
+ id: 'searchTool',
490
+ httpConfig: {
491
+ url: 'https://api.example.com/search',
492
+ method: 'POST'
493
+ },
494
+ outputKey: 'search_data'
495
+ }))
496
+
497
+ // Agent tools for delegation
498
+ .agentTool(createAgentTool({
499
+ id: 'researchTool',
500
+ targetAgentId: 'researcher',
501
+ skipSummarization: false
502
+ }))
503
+
504
+ .agentTool(createAgentTool({
505
+ id: 'analysisTool',
506
+ targetAgentId: 'analyst',
507
+ skipSummarization: false
508
+ }))
509
+
510
+ // Set the entry point
511
+ .setRootAgent('coordinator')
512
+
513
+ // Configure workflow-level settings
514
+ .withMaxCycles(20)
515
+
516
+ .build();
517
+
518
+ // Create the workflow
519
+ const graph = new GraphCompose()
520
+ .adk('aiWorkflow')
521
+ .withWorkflow(workflow)
522
+ .withInitialPrompt('Research and analyze recent AI trends')
523
+ .withState({
524
+ user_id: 'user_123',
525
+ context: 'technology'
526
+ })
527
+ .end()
528
+ .build();
267
529
  ```
268
530
 
269
- ## Defining Node Types
531
+ ## Advanced Patterns
270
532
 
271
- Nodes represent the primary steps or actions within your workflow. Remember to always finish defining a node with `.end()`.
533
+ ### Agent Handoff Pattern
272
534
 
273
- ### HTTP Nodes
535
+ Nested sub-agents for dynamic routing:
274
536
 
275
- These are the most basic type, used for making HTTP requests.
537
+ ```typescript
538
+ createLlmAgent({
539
+ id: 'router',
540
+ httpConfig: { url: '...', method: 'POST' },
541
+ subAgents: [
542
+ // Nested agents for handoff
543
+ createLlmAgent({
544
+ id: 'sales_specialist',
545
+ httpConfig: { url: '...', method: 'POST' }
546
+ }),
547
+ createLlmAgent({
548
+ id: 'support_specialist',
549
+ httpConfig: { url: '...', method: 'POST' }
550
+ })
551
+ ]
552
+ })
553
+ ```
554
+
555
+ ### Human-in-the-Loop (HITL)
556
+
557
+ Agents can request human approval:
276
558
 
277
559
  ```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!
560
+ // Your agent HTTP service returns:
561
+ {
562
+ "content": [{"type": "text", "text": "Requesting approval..."}],
563
+ "hitl_request": {
564
+ "type": "approval",
565
+ "message": "Should I proceed with this action?",
566
+ "options": ["approve", "reject", "modify"]
567
+ }
568
+ }
284
569
  ```
285
570
 
286
- ### Agent Nodes
571
+ ### State Management
287
572
 
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.
573
+ Access shared state across agents:
289
574
 
290
575
  ```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!
576
+ // Initialize state
577
+ .withState({
578
+ user_preferences: { theme: 'dark' },
579
+ session_data: { authenticated: true }
580
+ })
581
+
582
+ // Access in agent HTTP configs
583
+ .withBody({
584
+ user_theme: '{{state.user_preferences.theme}}',
585
+ auth_status: '{{state.session_data.authenticated}}'
586
+ })
587
+
588
+ // Agents save to state via outputKey
589
+ .agent(createLlmAgent({
590
+ id: 'processor',
591
+ outputKey: 'processed_data', // Saved to state.processed_data
592
+ // ...
593
+ }))
299
594
  ```
300
595
 
301
- ### Error Boundary Nodes
596
+ ### Exit Flow Control
302
597
 
303
- These nodes execute *only* if one of the nodes they monitor fails. They are used for error handling and cleanup.
598
+ Agents can terminate the workflow early:
304
599
 
305
600
  ```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!
601
+ // Your agent HTTP service returns:
602
+ {
603
+ "content": [{"type": "text", "text": "Task completed!"}],
604
+ "exit_flow": true // Stops workflow execution
605
+ }
315
606
  ```
316
607
 
317
- ## Defining Tool Types (for Agents)
608
+ ## JSONata Templating
318
609
 
319
- Tools are functions or services that Agent Nodes can call to perform specific actions. Remember to always finish defining a tool with `.end()`.
610
+ All string fields support JSONata expressions with `{{ }}` delimiters:
320
611
 
321
- ### HTTP Tools
612
+ ### Accessing Results
322
613
 
323
- These are simple tools that make an HTTP request when called by an agent.
614
+ ```typescript
615
+ '{{results.fetchUser.id}}' // Node result
616
+ '{{results.fetchUser.profile.name}}' // Nested property
617
+ ```
618
+
619
+ ### Accessing Context
324
620
 
325
621
  ```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!
622
+ '{{context.userId}}' // Workflow context
623
+ '{{context.tenantId}}' // Any context value
624
+ ```
332
625
 
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!
626
+ ### Accessing State (ADK)
627
+
628
+ ```typescript
629
+ '{{state.user_query}}' // Session state
630
+ '{{state.preferences.language}}' // Nested state
338
631
  ```
339
632
 
340
- ### Graph Tools
633
+ ### Secrets
341
634
 
342
- These allow encapsulating a *sub-graph* as a tool. This sub-graph can contain multiple HTTP nodes.
635
+ ```typescript
636
+ '{{$secret("api_key")}}' // Secret reference
637
+ 'Bearer {{$secret("auth_token")}}' // In strings
638
+ ```
639
+
640
+ ### JSONata Operators
343
641
 
344
642
  ```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
643
+ // Conditionals
644
+ '{{results.status.code = 200 ? "success" : "failure"}}'
645
+
646
+ // Arithmetic
647
+ '{{results.price.amount * 1.1}}'
648
+
649
+ // String operations
650
+ '{{$uppercase(results.user.name)}}'
651
+ '{{$substring(results.text, 0, 100)}}'
652
+
653
+ // Array operations
654
+ '{{$count(results.items)}}'
655
+ '{{results.items[0].id}}'
361
656
  ```
362
657
 
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.
658
+ ## Validation
367
659
 
368
- ### Assigning Tools to Agents
660
+ ### Automatic Validation
369
661
 
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.
662
+ The builder performs comprehensive validation:
371
663
 
372
664
  ```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!
665
+ const workflow = builder.build();
666
+ const validation = builder.validate();
667
+
668
+ if (!validation.isValid) {
669
+ validation.errors.forEach(err => {
670
+ console.error(err.message);
671
+ });
672
+ }
379
673
  ```
380
674
 
381
- ## Workflow Configuration
675
+ ### Validation Checks
382
676
 
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()`).
677
+ - **Node IDs**: Unique and valid format
678
+ - ✅ **Dependencies**: Valid references, no circular deps
679
+ - ✅ **HTTP Config**: Valid URLs, methods, headers
680
+ - ✅ **JSONata**: Syntax validation in templates
681
+ - ✅ **ADK Structure**: Agent/tool references, type constraints
682
+ - ✅ **Schema Compliance**: Zod validation against core schemas
384
683
 
385
- ```typescript
386
- // Assuming previous node/tool builders have called .end()
684
+ ### Manual Validation
387
685
 
388
- // Set initial data available to all nodes via 'context'
389
- graph.withContext({
390
- userId: "user-123",
391
- userInput: "Tell me about GraphCompose",
392
- });
686
+ ```typescript
687
+ import { validateWorkflow } from '@graph-compose/client';
393
688
 
394
- // Specify a URL to receive webhook updates about the workflow's progress
395
- graph.withWebhookUrl("https://my-app.com/graph-compose-webhook");
689
+ const result = validateWorkflow(workflow);
396
690
 
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
- });
691
+ if (!result.isValid) {
692
+ console.error('Validation failed:', result.errors);
693
+ }
402
694
  ```
403
695
 
404
- ## Validation
696
+ ## Executing Workflows
405
697
 
406
- Ensuring your workflow definition is correct before execution is crucial.
698
+ ### Local Validation (Client-Side)
407
699
 
408
- ### Local Validation
700
+ Validate workflow structure without making API calls:
409
701
 
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.
702
+ ```typescript
703
+ const workflow = new GraphCompose({ token: 'your-token' })
704
+ .http('fetchData')
705
+ .get('https://api.example.com/data')
706
+ .end()
707
+ .build();
416
708
 
417
- This method requires all nodes and tools to have been finalized with `.end()`. Execution methods (`.execute()`, `.executeSync()`) also perform this validation internally.
709
+ // Client-side validation (fast, no API call)
710
+ const validation = workflow.validate();
418
711
 
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);
712
+ if (!validation.isValid) {
713
+ console.error('Validation errors:', validation.errors);
424
714
  } else {
425
- console.log("Local validation successful!");
715
+ console.log('Workflow is valid!');
426
716
  }
427
717
  ```
428
718
 
429
- ### API Validation
719
+ ### Server-Side Validation
430
720
 
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.
721
+ Validate against the GraphCompose API (includes tier limits and quota checks):
432
722
 
433
723
  ```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();
724
+ const workflow = new GraphCompose({ token: 'your-token' })
725
+ .http('fetchData')
726
+ .get('https://api.example.com/data')
727
+ .end();
439
728
 
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);
729
+ try {
730
+ const apiResult = await workflow.validateApi();
731
+
732
+ if (apiResult.success && apiResult.data?.isValid) {
733
+ console.log('✅ Workflow passed server validation');
734
+ } else {
735
+ console.error('❌ Validation failed:', apiResult.data?.errors);
453
736
  }
737
+ } catch (error) {
738
+ console.error('API error:', error);
454
739
  }
455
- validateWithApi();
456
740
  ```
457
741
 
458
- ## Execution
742
+ **Endpoint:** `POST /workflows/validate`
459
743
 
460
- Once defined and validated, you can run your workflow. Execution methods require all nodes and tools to have been finalized with `.end()`.
744
+ ### Async Execution
461
745
 
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.
746
+ Start workflow execution and return immediately (recommended for long-running workflows):
463
747
 
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" } });
748
+ ```typescript
749
+ const workflow = new GraphCompose({ token: 'your-token' })
750
+ .http('processData')
751
+ .post('https://api.example.com/process')
752
+ .withBody({ data: 'example' })
753
+ .end();
471
754
 
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
- ```
755
+ try {
756
+ const result = await workflow.execute({
757
+ context: {
758
+ userId: '123',
759
+ requestId: 'req-456'
760
+ },
761
+ webhookUrl: 'https://your-app.com/webhook' // Optional: receive completion notification
762
+ });
508
763
 
509
- ## Getting the Workflow Definition
764
+ if (result.success && result.data) {
765
+ console.log('Workflow started!');
766
+ console.log('Workflow ID:', result.data.workflowId);
767
+ console.log('Run ID:', result.data.runId);
768
+ console.log('Status:', result.data.status); // "RUNNING"
769
+
770
+ // Poll for status or wait for webhook
771
+ }
772
+ } catch (error) {
773
+ console.error('Execution failed:', error);
774
+ }
775
+ ```
510
776
 
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.
777
+ **Endpoint:** `POST /workflows`
512
778
 
513
- * `graph.getWorkflow()`: Returns the complete `WorkflowGraph` object.
514
- * `graph.toJSON()`: An alias for `getWorkflow()`, providing the standard JavaScript method for serialization.
779
+ ### Check Workflow Status
515
780
 
516
- Both methods will throw an error if any node or tool builder is still active (i.e., missing a final `.end()` call).
781
+ Poll for workflow status after async execution:
517
782
 
518
783
  ```typescript
519
- // Assuming all builders are finalized
784
+ const workflowId = 'wf_abc123';
785
+
786
+ try {
787
+ const statusResult = await workflow.getWorkflowStatus(workflowId);
788
+
789
+ if (statusResult.success && statusResult.data) {
790
+ console.log('Status:', statusResult.data.status);
791
+ // "RUNNING" | "COMPLETED" | "FAILED" | "TERMINATED"
792
+
793
+ console.log('Started at:', statusResult.data.startTime);
794
+
795
+ if (statusResult.data.status === 'COMPLETED') {
796
+ console.log('Results:', statusResult.data.results);
797
+ } else if (statusResult.data.status === 'FAILED') {
798
+ console.log('Error:', statusResult.data.error);
799
+ }
800
+ }
801
+ } catch (error) {
802
+ console.error('Failed to get status:', error);
803
+ }
804
+ ```
805
+
806
+ **Endpoint:** `GET /workflows/{workflowId}`
520
807
 
521
- const workflowDefinition = graph.getWorkflow();
522
- console.log("Raw Workflow Definition Object:", workflowDefinition);
808
+ ### Terminate Running Workflow
523
809
 
524
- // Equivalent using toJSON() for easy serialization
525
- const workflowJsonString = JSON.stringify(graph.toJSON(), null, 2);
526
- console.log("Workflow JSON:", workflowJsonString);
810
+ Stop a workflow that's currently executing:
811
+
812
+ ```typescript
813
+ const workflowId = 'wf_abc123';
814
+
815
+ try {
816
+ const result = await workflow.terminateWorkflow(workflowId, {
817
+ runId: 'run_xyz789', // Optional: specific run ID
818
+ reason: 'User requested cancellation' // Optional
819
+ });
820
+
821
+ if (result.success) {
822
+ console.log('Workflow terminated successfully');
823
+ }
824
+ } catch (error) {
825
+ console.error('Failed to terminate:', error);
826
+ }
527
827
  ```
528
828
 
529
- ## Interacting with Existing Workflows (API)
829
+ **Endpoint:** `POST /workflows/{workflowId}/terminate`
530
830
 
531
- These methods interact directly with the Graph Compose API using a workflow ID, and don't depend on the local builder state.
831
+ ### Webhook Notifications
532
832
 
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
- ```
833
+ Receive notifications when async workflows complete:
834
+
835
+ ```typescript
836
+ const workflow = new GraphCompose({ token: 'your-token' })
837
+ .http('longProcess')
838
+ .post('https://api.example.com/long-process')
839
+ .end()
840
+ .withWebhookUrl('https://your-app.com/webhook');
841
+
842
+ // Your webhook will receive:
843
+ // POST https://your-app.com/webhook
844
+ // {
845
+ // "workflowId": "wf_abc123",
846
+ // "runId": "run_xyz789",
847
+ // "status": "COMPLETED",
848
+ // "results": { ... },
849
+ // "completedAt": "2024-01-15T10:30:00Z"
850
+ // }
851
+ ```
561
852
 
562
- ## Full Example: Putting it Together
853
+ ### API Response Format
563
854
 
564
- This example demonstrates defining nodes, setting context, validating, and executing.
855
+ All API methods return a consistent response structure:
565
856
 
566
857
  ```typescript
567
- import { GraphCompose } from "@graph-compose/client";
858
+ interface ApiResponse<T> {
859
+ success: boolean;
860
+ data: T | null;
861
+ error?: {
862
+ code: string;
863
+ message: string;
864
+ details?: any;
865
+ };
866
+ metadata?: {
867
+ requestId: string;
868
+ timestamp: string;
869
+ version: string;
870
+ };
871
+ }
568
872
 
569
- async function main() {
570
- const graph = new GraphCompose({ token: process.env.GRAPH_COMPOSE_TOKEN || "" });
873
+ // Example: Successful execution
874
+ {
875
+ "success": true,
876
+ "data": {
877
+ "workflowId": "wf_abc123",
878
+ "runId": "run_xyz789",
879
+ "status": "RUNNING",
880
+ "startTime": "2024-01-15T10:00:00Z"
881
+ },
882
+ "metadata": {
883
+ "requestId": "req_456",
884
+ "timestamp": "2024-01-15T10:00:00Z",
885
+ "version": "v1"
886
+ }
887
+ }
571
888
 
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!
889
+ // Example: Error response
890
+ {
891
+ "success": false,
892
+ "data": null,
893
+ "error": {
894
+ "code": "VALIDATION_ERROR",
895
+ "message": "Invalid node configuration",
896
+ "details": {
897
+ "nodeId": "fetchData",
898
+ "issue": "Missing required field: url"
899
+ }
900
+ }
901
+ }
902
+ ```
903
+
904
+ ### Workflow Status Types
577
905
 
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!
906
+ ```typescript
907
+ type WorkflowStatus =
908
+ | "RUNNING" // Workflow is currently executing
909
+ | "COMPLETED" // Workflow finished successfully
910
+ | "FAILED" // Workflow encountered an error
911
+ | "TERMINATED"; // Workflow was manually stopped
912
+ ```
585
913
 
586
- // Set context *after* ending the last node builder
587
- graph.withContext({ requestSource: "client-example-readme" });
914
+ ### Complete Execution Example
915
+
916
+ ```typescript
917
+ import { GraphCompose } from '@graph-compose/client';
588
918
 
589
- console.log("Validating workflow locally...");
590
- // Validate *after* ending all node builders and setting context/config
591
- const validation = graph.validate();
919
+ async function runWorkflow() {
920
+ // Build the workflow
921
+ const workflow = new GraphCompose({ token: process.env.GRAPH_COMPOSE_TOKEN })
922
+ .http('fetchUser')
923
+ .get('https://api.example.com/users/{{context.userId}}')
924
+ .end()
925
+ .http('enrichProfile')
926
+ .post('https://api.example.com/enrich')
927
+ .withBody({
928
+ userId: '{{results.fetchUser.id}}',
929
+ email: '{{results.fetchUser.email}}'
930
+ })
931
+ .dependsOn(['fetchUser'])
932
+ .end();
933
+
934
+ // Validate locally
935
+ const validation = workflow.validate();
592
936
  if (!validation.isValid) {
593
- console.error("Local Validation Failed:", validation.errors);
594
- return;
937
+ throw new Error(`Invalid workflow: ${validation.errors.map(e => e.message).join(', ')}`);
595
938
  }
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}`);
939
+
940
+ // Execute async
941
+ const execution = await workflow.execute({
942
+ context: { userId: '123' },
943
+ webhookUrl: 'https://myapp.com/webhook'
944
+ });
945
+
946
+ if (!execution.success || !execution.data) {
947
+ throw new Error('Execution failed');
948
+ }
949
+
950
+ const workflowId = execution.data.workflowId;
951
+ console.log('Workflow started:', workflowId);
952
+
953
+ // Poll for completion
954
+ let status = 'RUNNING';
955
+ while (status === 'RUNNING') {
956
+ await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2s
957
+
958
+ const statusResult = await workflow.getWorkflowStatus(workflowId);
959
+ if (statusResult.success && statusResult.data) {
960
+ status = statusResult.data.status;
961
+ console.log('Current status:', status);
614
962
  }
615
- } catch (error) {
616
- console.error("Execution Failed:", error);
617
963
  }
618
- }
619
964
 
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);
965
+ // Get final results
966
+ const finalResult = await workflow.getWorkflowStatus(workflowId);
967
+ if (finalResult.success && finalResult.data) {
968
+ if (finalResult.data.status === 'COMPLETED') {
969
+ console.log('✅ Workflow completed successfully!');
970
+ console.log('Results:', finalResult.data.results);
971
+ } else {
972
+ console.error('❌ Workflow failed:', finalResult.data.error);
631
973
  }
632
- } catch (error) {
633
- console.error("[Status Check] Failed to get workflow status:", error);
634
974
  }
635
975
  }
636
976
 
637
- main();
977
+ runWorkflow().catch(console.error);
978
+ ```
979
+
980
+ ## API Reference
981
+
982
+ ### GraphCompose
983
+
984
+ Main workflow builder class.
985
+
986
+ #### Constructor
987
+
988
+ ```typescript
989
+ new GraphCompose(options?: { token?: string })
638
990
  ```
639
991
 
992
+ - `options.token` - API token (or set `GRAPH_COMPOSE_TOKEN` env var)
993
+
994
+ #### Building Methods
995
+
996
+ - `http(id: string): NodeBuilder` - Create HTTP node
997
+ - `adk(id: string): AdkNodeBuilder` - Create ADK agent node
998
+ - `errorBoundary(id: string): ErrorBoundaryBuilder` - Create error boundary
999
+ - `build(): WorkflowGraph` - Build the workflow definition
1000
+
1001
+ #### Configuration Methods
1002
+
1003
+ - `withContext(context: Record<string, any>): GraphCompose` - Set workflow context
1004
+ - `withWebhookUrl(url: string): GraphCompose` - Set webhook for async notifications
1005
+ - `withWorkflowConfig(config: WorkflowConfig): GraphCompose` - Set workflow-level config
1006
+
1007
+ #### Validation Methods
1008
+
1009
+ - `validate(): ValidationResult` - Client-side validation
1010
+ - `validateApi(): Promise<ApiResponse>` - Server-side validation with tier checks
1011
+
1012
+ #### Execution Methods
1013
+
1014
+ - `execute(options?): Promise<ApiResponse>` - Execute workflow (returns immediately with workflow ID)
1015
+ - `getWorkflowStatus(workflowId: string): Promise<ApiResponse>` - Check workflow status
1016
+ - `terminateWorkflow(workflowId: string, options?): Promise<ApiResponse>` - Stop workflow
1017
+
1018
+ ### NodeBuilder
1019
+
1020
+ Builder for HTTP nodes.
1021
+
1022
+ #### HTTP Methods
1023
+ - `get(url: string)` - GET request
1024
+ - `post(url: string)` - POST request
1025
+ - `put(url: string)` - PUT request
1026
+ - `patch(url: string)` - PATCH request
1027
+ - `delete(url: string)` - DELETE request
1028
+
1029
+ #### Configuration
1030
+ - `withHeaders(headers: Record<string, string>)` - Set headers
1031
+ - `withBody(body: any)` - Set request body
1032
+ - `withParams(params: Record<string, string>)` - Set URL params
1033
+ - `withRetries(policy: RetryPolicy)` - Configure retry policy
1034
+ - `withStartToCloseTimeout(timeout: string)` - Set activity timeout
1035
+ - `dependsOn(nodeIds: string[])` - Set dependencies
1036
+ - `runWhen(condition: Condition)` - Conditional execution
1037
+
1038
+ #### Completion
1039
+ - `end(): GraphCompose` - Finalize node
1040
+
1041
+ ### AdkNodeBuilder
1042
+
1043
+ Builder for ADK agent nodes.
1044
+
1045
+ #### Methods
1046
+ - `withWorkflow(workflow: ADKWorkflowDefinition)` - Set agent workflow
1047
+ - `withInitialPrompt(prompt: string)` - Set initial prompt
1048
+ - `withMaxCycles(max: number)` - Set max orchestration cycles
1049
+ - `withState(state: Record<string, any>)` - Seed initial state
1050
+ - `withRetries(policy: RetryPolicy)` - Configure retry policy
1051
+ - `dependsOn(nodeIds: string[])` - Set dependencies
1052
+ - `end(): GraphCompose` - Finalize node
1053
+
1054
+ ### AdkWorkflowBuilder
1055
+
1056
+ Builder for agent workflow definitions.
1057
+
1058
+ #### Methods
1059
+ - `agent(config: AgentConfig)` - Add agent
1060
+ - `httpTool(config: HttpToolConfig)` - Add HTTP tool
1061
+ - `agentTool(config: AgentToolConfig)` - Add agent tool
1062
+ - `setRootAgent(agentId: string)` - Set entry point agent
1063
+ - `withMaxCycles(max: number)` - Set workflow-level max cycles
1064
+ - `build(): ADKWorkflowDefinition` - Build the workflow
1065
+
1066
+ ### Helper Functions
1067
+
1068
+ #### Agent Helpers
1069
+ - `createLlmAgent(config)` - Create LLM agent config
1070
+ - `createSequentialAgent(config)` - Create sequential orchestrator
1071
+ - `createParallelAgent(config)` - Create parallel orchestrator
1072
+ - `createLoopAgent(config)` - Create loop orchestrator
1073
+ - `createSubAgentReference(agentId)` - Create agent reference
1074
+
1075
+ #### Tool Helpers
1076
+ - `createHttpTool(config)` - Create HTTP tool config
1077
+ - `createAgentTool(config)` - Create agent tool config
1078
+
1079
+ ## TypeScript Types
1080
+
1081
+ Full TypeScript support with exported types:
1082
+
1083
+ ```typescript
1084
+ import type {
1085
+ WorkflowGraph,
1086
+ HttpNode,
1087
+ AdkNode,
1088
+ RetryPolicy,
1089
+ ADKWorkflowDefinition,
1090
+ AgentConfig,
1091
+ ToolConfig,
1092
+ ValidationResult
1093
+ } from '@graph-compose/client';
1094
+ ```
1095
+
1096
+ ## Examples
1097
+
1098
+ See the `examples/` directory for complete working examples:
1099
+
1100
+ - **HTTP Workflow**: Service orchestration
1101
+ - **ADK Basic**: Simple agent with tools
1102
+ - **ADK Advanced**: Multi-agent hierarchies
1103
+ - **Error Handling**: Retry and fallback patterns
1104
+ - **Conditional**: Branch-based workflows
1105
+
640
1106
  ## Contributing
641
1107
 
642
- Contributions are welcome! Please refer to the main repository's contribution guidelines.
1108
+ Contributions are welcome! Please see [CONTRIBUTING.md](../../CONTRIBUTING.md) for guidelines.
643
1109
 
644
1110
  ## License
645
1111
 
646
- This project is licensed under the MIT License. See the LICENSE file in the main repository for details.
1112
+ MIT License - see [LICENSE](../../LICENSE) for details.
1113
+
1114
+ ## Support
1115
+
1116
+ - 📖 [Documentation](https://github.com/your-org/graph-compose)
1117
+ - 💬 [Discord Community](https://discord.gg/your-server)
1118
+ - 🐛 [Issue Tracker](https://github.com/your-org/graph-compose/issues)
1119
+ - 📧 [Email Support](mailto:support@example.com)