-
Notifications
You must be signed in to change notification settings - Fork 20
Expand file tree
/
Copy path33-external-workers.ts
More file actions
176 lines (160 loc) · 5.36 KB
/
Copy path33-external-workers.ts
File metadata and controls
176 lines (160 loc) · 5.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
/**
* 33 - External Worker Tools
*
* Demonstrates tool({ external: true }) for referencing Conductor workers that
* exist in another repository, service, or language. The function stub provides
* the schema (via Zod) and description, but no local worker is started --
* Conductor dispatches the task to whatever worker is polling for that task
* definition name.
*
* This is useful when:
* - Workers are written in Java, Go, or another language
* - Workers run in a separate microservice
* - You want to reuse existing Conductor task definitions
*
* Requirements:
* - Conductor server with LLM support
* - The referenced workers must be running somewhere
* - CONDUCTOR_SERVER_URL=http://localhost:8080/api
* - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini
*/
import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents';
import { llmModel } from './settings';
// -- Example 1: Basic external worker reference ------------------------------
// The function stub defines the schema; no implementation needed.
// Conductor dispatches "process_order" tasks to whatever worker is polling.
const processOrder = tool(
async (_args: { orderId: string; action: string }) => {
// This function body is never called for external tools.
return {};
},
{
name: 'process_order',
description: 'Process a customer order. Actions: refund, cancel, update.',
inputSchema: {
type: 'object',
properties: {
orderId: { type: 'string', description: 'The order ID' },
action: { type: 'string', description: 'Action to take: refund, cancel, or update' },
},
required: ['orderId', 'action'],
},
external: true,
},
);
// -- Example 2: External worker with approval gate ---------------------------
// Dangerous operations can require human approval before execution.
const deleteAccount = tool(
async (_args: { userId: string; reason: string }) => {
return {};
},
{
name: 'delete_account',
description: 'Permanently delete a user account. Requires manager approval.',
inputSchema: {
type: 'object',
properties: {
userId: { type: 'string', description: 'The user ID to delete' },
reason: { type: 'string', description: 'Reason for deletion' },
},
required: ['userId', 'reason'],
},
external: true,
approvalRequired: true,
},
);
// -- Example 3: Mix local and external tools ---------------------------------
const formatResponse = tool(
async (args: { data: Record<string, unknown> }) => {
return Object.entries(args.data)
.map(([k, v]) => ` ${k}: ${v}`)
.join('\n');
},
{
name: 'format_response',
description: 'Format a data dictionary into a human-readable string.',
inputSchema: {
type: 'object',
properties: {
data: { type: 'object', additionalProperties: true, description: 'Data to format' },
},
required: ['data'],
},
},
);
const getCustomer = tool(
async (_args: { customerId: string }) => {
return {};
},
{
name: 'get_customer',
description: 'Look up customer details from the CRM system.',
inputSchema: {
type: 'object',
properties: {
customerId: { type: 'string', description: 'The customer ID' },
},
required: ['customerId'],
},
external: true,
},
);
const checkInventory = tool(
async (_args: { productId: string; warehouse?: string }) => {
return {};
},
{
name: 'check_inventory',
description: 'Check product availability in a warehouse.',
inputSchema: {
type: 'object',
properties: {
productId: { type: 'string', description: 'The product ID' },
warehouse: { type: 'string', description: 'Warehouse name' },
},
required: ['productId'],
},
external: true,
},
);
// -- Agent: combines local + external tools ----------------------------------
export const supportAgent = new Agent({
name: 'support_agent',
model: llmModel,
instructions:
'You are a customer support agent. Use the available tools to ' +
'look up customers, check inventory, process orders, and format ' +
'responses for the customer.',
tools: [
formatResponse, // Local -- runs in this process
getCustomer, // External -- runs in CRM service
checkInventory, // External -- runs in inventory service
processOrder, // External -- runs in order service
],
});
// -- Run ---------------------------------------------------------------------
async function main() {
const runtime = new AgentRuntime();
try {
console.log('=== External Worker Tools ===');
console.log('Agent has 1 local tool + 3 external worker references.\n');
const result = await runtime.run(
supportAgent,
'Customer C-1234 wants to cancel order ORD-5678. ' +
'Look up the customer, check if we have the product in stock, ' +
'and process the cancellation.',
);
result.printResult();
// Production pattern:
// 1. Deploy once during CI/CD (optional -- serve() below also deploys):
// await runtime.deploy(supportAgent);
// CLI alternative:
// conductor deploy --package examples/agents --agents support_agent
//
// 2. In a separate long-lived worker process (deploys + registers workers + starts polling):
// await runtime.serve(supportAgent);
} finally {
await runtime.shutdown();
}
}
main().catch(console.error);