@eko-ai/eko 1.0.7 → 1.0.9
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 +72 -21
- package/dist/core/eko.d.ts +3 -2
- package/dist/extension/content/index.d.ts +1 -0
- package/dist/extension/tools/browser.d.ts +2 -1
- package/dist/extension/tools/get_all_tabs.d.ts +9 -0
- package/dist/extension/tools/index.d.ts +4 -1
- package/dist/extension/tools/request_login.d.ts +10 -0
- package/dist/extension/tools/tab_management.d.ts +1 -1
- package/dist/extension/utils.d.ts +2 -1
- package/dist/extension.cjs.js +797 -209
- package/dist/extension.esm.js +797 -209
- package/dist/extension_content_script.js +129 -2
- package/dist/index.cjs.js +518 -114
- package/dist/index.d.ts +2 -1
- package/dist/index.esm.js +518 -115
- package/dist/models/action.d.ts +9 -4
- package/dist/models/workflow.d.ts +8 -3
- package/dist/nodejs/script/build_dom_tree.d.ts +1 -0
- package/dist/nodejs/tools/browser_use.d.ts +28 -0
- package/dist/nodejs/tools/index.d.ts +2 -0
- package/dist/nodejs.cjs.js +71638 -12
- package/dist/nodejs.esm.js +71632 -6
- package/dist/schemas/workflow.schema.d.ts +2 -13
- package/dist/services/llm/claude-provider.d.ts +2 -1
- package/dist/services/llm/openai-provider.d.ts +2 -1
- package/dist/services/parser/workflow-parser.d.ts +0 -7
- package/dist/types/action.types.d.ts +8 -3
- package/dist/types/tools.types.d.ts +44 -1
- package/dist/types/workflow.types.d.ts +22 -9
- package/dist/universal_tools/cancel_workflow.d.ts +9 -0
- package/dist/universal_tools/human.d.ts +30 -0
- package/dist/universal_tools/index.d.ts +4 -0
- package/dist/universal_tools/summary_workflow.d.ts +9 -0
- package/dist/utils/execution-logger.d.ts +69 -0
- package/dist/web/tools/browser.d.ts +2 -1
- package/dist/web.cjs.js +29 -17
- package/dist/web.esm.js +29 -17
- package/package.json +6 -9
package/dist/index.esm.js
CHANGED
|
@@ -1,11 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manages logging for action execution, providing a cleaner view of the execution
|
|
3
|
+
* flow while maintaining important context and history.
|
|
4
|
+
*/
|
|
5
|
+
class ExecutionLogger {
|
|
6
|
+
constructor(options = {}) {
|
|
7
|
+
var _a;
|
|
8
|
+
this.history = [];
|
|
9
|
+
this.maxHistoryLength = options.maxHistoryLength || 10;
|
|
10
|
+
this.logLevel = options.logLevel || 'info';
|
|
11
|
+
this.includeTimestamp = (_a = options.includeTimestamp) !== null && _a !== void 0 ? _a : true;
|
|
12
|
+
this.debugImagePath = options.debugImagePath;
|
|
13
|
+
this.imageSaver = options.imageSaver;
|
|
14
|
+
// Check if running in Node.js environment
|
|
15
|
+
this.isNode =
|
|
16
|
+
typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Logs a message with execution context
|
|
20
|
+
*/
|
|
21
|
+
log(level, message, context) {
|
|
22
|
+
if (this.shouldLog(level)) {
|
|
23
|
+
const timestamp = this.includeTimestamp ? new Date().toISOString() : '';
|
|
24
|
+
const contextSummary = this.summarizeContext(context);
|
|
25
|
+
console.log(`${timestamp} [${level.toUpperCase()}] ${message}${contextSummary}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Updates conversation history while maintaining size limit
|
|
30
|
+
*/
|
|
31
|
+
updateHistory(messages) {
|
|
32
|
+
// Keep system messages and last N messages
|
|
33
|
+
const systemMessages = messages.filter((m) => m.role === 'system');
|
|
34
|
+
const nonSystemMessages = messages.filter((m) => m.role !== 'system');
|
|
35
|
+
const recentMessages = nonSystemMessages.slice(-this.maxHistoryLength);
|
|
36
|
+
this.history = [...systemMessages, ...recentMessages];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Gets current conversation history
|
|
40
|
+
*/
|
|
41
|
+
getHistory() {
|
|
42
|
+
return this.history;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Summarizes the execution context for logging
|
|
46
|
+
*/
|
|
47
|
+
summarizeContext(context) {
|
|
48
|
+
if (!context)
|
|
49
|
+
return '';
|
|
50
|
+
const summary = {
|
|
51
|
+
variables: Object.fromEntries(context.variables),
|
|
52
|
+
tools: context.tools ? Array.from(context.tools.keys()) : [],
|
|
53
|
+
};
|
|
54
|
+
return `\nContext: ${JSON.stringify(summary, null, 2)}`;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Checks if message should be logged based on log level
|
|
58
|
+
*/
|
|
59
|
+
shouldLog(level) {
|
|
60
|
+
const levels = {
|
|
61
|
+
error: 0,
|
|
62
|
+
warn: 1,
|
|
63
|
+
info: 2,
|
|
64
|
+
debug: 3,
|
|
65
|
+
};
|
|
66
|
+
return levels[level] <= levels[this.logLevel];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Logs the start of an action execution
|
|
70
|
+
*/
|
|
71
|
+
logActionStart(actionName, input, context) {
|
|
72
|
+
this.log('info', `Starting action: ${actionName}`, context);
|
|
73
|
+
this.log('info', `Input: ${JSON.stringify(input, null, 2)}`);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Logs the completion of an action execution
|
|
77
|
+
*/
|
|
78
|
+
logActionComplete(actionName, result, context) {
|
|
79
|
+
this.log('info', `Completed action: ${actionName}`, context);
|
|
80
|
+
this.log('info', `Result: ${JSON.stringify(result, null, 2)}`);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Logs a tool execution
|
|
84
|
+
*/
|
|
85
|
+
logToolExecution(toolName, input, context) {
|
|
86
|
+
this.log('info', `Executing tool: ${toolName}`);
|
|
87
|
+
this.log('info', `Tool input: ${JSON.stringify(input, null, 2)}`);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Logs an error that occurred during execution
|
|
91
|
+
*/
|
|
92
|
+
logError(error, context) {
|
|
93
|
+
this.log('error', `Error occurred: ${error.message}`, context);
|
|
94
|
+
if (error.stack) {
|
|
95
|
+
this.log('debug', `Stack trace: ${error.stack}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
extractFromDataUrl(dataUrl) {
|
|
99
|
+
const matches = dataUrl.match(/^data:image\/([a-zA-Z0-9]+);base64,(.+)$/);
|
|
100
|
+
if (!matches) {
|
|
101
|
+
throw new Error('Invalid data URL format');
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
extension: matches[1],
|
|
105
|
+
base64Data: matches[2],
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
async saveDebugImage(imageData, toolName) {
|
|
109
|
+
try {
|
|
110
|
+
let extension;
|
|
111
|
+
let base64Data;
|
|
112
|
+
// Handle both data URL strings and ImageData objects
|
|
113
|
+
if (typeof imageData === 'string' && imageData.startsWith('data:')) {
|
|
114
|
+
const extracted = this.extractFromDataUrl(imageData);
|
|
115
|
+
extension = extracted.extension;
|
|
116
|
+
base64Data = extracted.base64Data;
|
|
117
|
+
}
|
|
118
|
+
else if (typeof imageData === 'object' && 'type' in imageData) {
|
|
119
|
+
extension = imageData.media_type.split('/')[1] || 'png';
|
|
120
|
+
base64Data = imageData.data;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
return '[image]';
|
|
124
|
+
}
|
|
125
|
+
// If custom image saver is provided, use it
|
|
126
|
+
if (this.imageSaver) {
|
|
127
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
128
|
+
const filename = `${toolName}_${timestamp}.${extension}`;
|
|
129
|
+
return await this.imageSaver({ type: 'base64', media_type: `image/${extension}`, data: base64Data }, filename);
|
|
130
|
+
}
|
|
131
|
+
// If in Node.js environment and debugImagePath is set
|
|
132
|
+
if (this.isNode && this.debugImagePath) {
|
|
133
|
+
// Dynamically import Node.js modules only when needed
|
|
134
|
+
const { promises: fs } = await import('fs');
|
|
135
|
+
const { join } = await import('path');
|
|
136
|
+
await fs.mkdir(this.debugImagePath, { recursive: true });
|
|
137
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
138
|
+
const filename = `${toolName}_${timestamp}.${extension}`;
|
|
139
|
+
const filepath = join(this.debugImagePath, filename);
|
|
140
|
+
const buffer = Buffer.from(base64Data, 'base64');
|
|
141
|
+
await fs.writeFile(filepath, buffer);
|
|
142
|
+
return `[image saved to: ${filepath}]`;
|
|
143
|
+
}
|
|
144
|
+
// Default case - just return placeholder
|
|
145
|
+
return '[image]';
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
console.warn('Failed to save debug image:', error);
|
|
149
|
+
return '[image]';
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async formatToolResult(result) {
|
|
153
|
+
// Handle null/undefined
|
|
154
|
+
if (result == null) {
|
|
155
|
+
return 'null';
|
|
156
|
+
}
|
|
157
|
+
// Handle direct image result
|
|
158
|
+
if (result.image) {
|
|
159
|
+
const imagePlaceholder = await this.saveDebugImage(result.image, 'tool');
|
|
160
|
+
const modifiedResult = { ...result, image: imagePlaceholder };
|
|
161
|
+
return JSON.stringify(modifiedResult);
|
|
162
|
+
}
|
|
163
|
+
// Handle nested images in result object
|
|
164
|
+
if (typeof result === 'object') {
|
|
165
|
+
const formatted = { ...result };
|
|
166
|
+
for (const [key, value] of Object.entries(formatted)) {
|
|
167
|
+
if (value && typeof value === 'string' && value.startsWith('data:image/')) {
|
|
168
|
+
formatted[key] = await this.saveDebugImage(value, key);
|
|
169
|
+
}
|
|
170
|
+
else if (value &&
|
|
171
|
+
typeof value === 'object' &&
|
|
172
|
+
'type' in value &&
|
|
173
|
+
value.type === 'base64') {
|
|
174
|
+
formatted[key] = await this.saveDebugImage(value, key);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return JSON.stringify(formatted);
|
|
178
|
+
}
|
|
179
|
+
// Handle primitive values
|
|
180
|
+
return String(result);
|
|
181
|
+
}
|
|
182
|
+
async logToolResult(toolName, result, context) {
|
|
183
|
+
if (this.shouldLog('info')) {
|
|
184
|
+
const timestamp = this.includeTimestamp ? new Date().toISOString() : '';
|
|
185
|
+
const contextSummary = this.summarizeContext(context);
|
|
186
|
+
const formattedResult = await this.formatToolResult(result);
|
|
187
|
+
console.log(`${timestamp} [INFO] Tool executed: ${toolName}\n` +
|
|
188
|
+
`${timestamp} [INFO] Tool result: ${formattedResult}${contextSummary}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
1
193
|
class WorkflowImpl {
|
|
2
|
-
constructor(id, name, description, nodes = [], variables = new Map(), llmProvider) {
|
|
194
|
+
constructor(id, name, description, nodes = [], variables = new Map(), llmProvider, loggerOptions) {
|
|
3
195
|
this.id = id;
|
|
4
196
|
this.name = name;
|
|
5
197
|
this.description = description;
|
|
6
198
|
this.nodes = nodes;
|
|
7
199
|
this.variables = variables;
|
|
8
200
|
this.llmProvider = llmProvider;
|
|
201
|
+
this.abortControllers = new Map();
|
|
202
|
+
if (loggerOptions) {
|
|
203
|
+
this.logger = new ExecutionLogger(loggerOptions);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
setLogger(logger) {
|
|
207
|
+
this.logger = logger;
|
|
208
|
+
}
|
|
209
|
+
async cancel() {
|
|
210
|
+
this.abort = true;
|
|
211
|
+
for (const controller of this.abortControllers.values()) {
|
|
212
|
+
controller.abort("Workflow cancelled");
|
|
213
|
+
}
|
|
9
214
|
}
|
|
10
215
|
async execute(callback) {
|
|
11
216
|
var _a, _b, _c, _d;
|
|
@@ -28,37 +233,49 @@ class WorkflowImpl {
|
|
|
28
233
|
throw new Error(`Circular dependency detected at node: ${nodeId}`);
|
|
29
234
|
}
|
|
30
235
|
const node = this.getNode(nodeId);
|
|
236
|
+
const abortController = new AbortController();
|
|
237
|
+
this.abortControllers.set(nodeId, abortController);
|
|
31
238
|
// Execute the node's action
|
|
32
239
|
const context = {
|
|
33
240
|
__skip: false,
|
|
34
241
|
__abort: false,
|
|
242
|
+
workflow: this,
|
|
35
243
|
variables: this.variables,
|
|
36
244
|
llmProvider: this.llmProvider,
|
|
37
245
|
tools: new Map(node.action.tools.map(tool => [tool.name, tool])),
|
|
38
246
|
callback,
|
|
247
|
+
logger: this.logger,
|
|
39
248
|
next: () => context.__skip = true,
|
|
40
|
-
abortAll: () =>
|
|
249
|
+
abortAll: () => {
|
|
250
|
+
this.abort = context.__abort = true;
|
|
251
|
+
// Abort all running tasks
|
|
252
|
+
for (const controller of this.abortControllers.values()) {
|
|
253
|
+
controller.abort("Workflow cancelled");
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
signal: abortController.signal
|
|
41
257
|
};
|
|
42
|
-
callback && await ((_b = (_a = callback.hooks).beforeSubtask) === null || _b === void 0 ? void 0 : _b.call(_a, node, context));
|
|
43
|
-
if (context.__abort) {
|
|
44
|
-
throw new Error("Abort");
|
|
45
|
-
}
|
|
46
|
-
else if (context.__skip) {
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
258
|
executing.add(nodeId);
|
|
50
259
|
// Execute dependencies first
|
|
51
260
|
for (const depId of node.dependencies) {
|
|
52
261
|
await executeNode(depId);
|
|
53
262
|
}
|
|
54
263
|
// Prepare input by gathering outputs from dependencies
|
|
55
|
-
const input = {};
|
|
264
|
+
const input = { items: [] };
|
|
56
265
|
for (const depId of node.dependencies) {
|
|
57
266
|
const depNode = this.getNode(depId);
|
|
58
|
-
input
|
|
267
|
+
input.items.push(depNode.output);
|
|
268
|
+
}
|
|
269
|
+
node.input = input;
|
|
270
|
+
// Run pre-execution hooks and execute action
|
|
271
|
+
callback && await ((_b = (_a = callback.hooks).beforeSubtask) === null || _b === void 0 ? void 0 : _b.call(_a, node, context));
|
|
272
|
+
if (context.__abort) {
|
|
273
|
+
throw new Error("Abort");
|
|
59
274
|
}
|
|
60
|
-
|
|
61
|
-
|
|
275
|
+
else if (context.__skip) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
node.output.value = await node.action.execute(node.input, node.output, context);
|
|
62
279
|
executing.delete(nodeId);
|
|
63
280
|
executed.add(nodeId);
|
|
64
281
|
callback && await ((_d = (_c = callback.hooks).afterSubtask) === null || _d === void 0 ? void 0 : _d.call(_c, node, context, (_e = node.output) === null || _e === void 0 ? void 0 : _e.value));
|
|
@@ -67,6 +284,7 @@ class WorkflowImpl {
|
|
|
67
284
|
const terminalNodes = this.nodes.filter(node => !this.nodes.some(n => n.dependencies.includes(node.id)));
|
|
68
285
|
await Promise.all(terminalNodes.map(node => executeNode(node.id)));
|
|
69
286
|
callback && await ((_d = (_c = callback.hooks).afterWorkflow) === null || _d === void 0 ? void 0 : _d.call(_c, this, this.variables));
|
|
287
|
+
return terminalNodes.map(node => node.output);
|
|
70
288
|
}
|
|
71
289
|
addNode(node) {
|
|
72
290
|
if (this.nodes.some(n => n.id === node.id)) {
|
|
@@ -125,7 +343,7 @@ class WorkflowImpl {
|
|
|
125
343
|
class WriteContextTool {
|
|
126
344
|
constructor() {
|
|
127
345
|
this.name = 'write_context';
|
|
128
|
-
this.description = 'Write a value to the workflow context. Use this to store intermediate results
|
|
346
|
+
this.description = 'Write a value to the global workflow context. Use this to store important intermediate results, but only when a piece of information is essential for future reference but missing from the final output specification of the current action.';
|
|
129
347
|
this.input_schema = {
|
|
130
348
|
type: 'object',
|
|
131
349
|
properties: {
|
|
@@ -155,25 +373,32 @@ class WriteContextTool {
|
|
|
155
373
|
return { success: true, key, value };
|
|
156
374
|
}
|
|
157
375
|
}
|
|
158
|
-
function createReturnTool(outputSchema) {
|
|
376
|
+
function createReturnTool(actionName, outputDescription, outputSchema) {
|
|
159
377
|
return {
|
|
160
378
|
name: 'return_output',
|
|
161
|
-
description:
|
|
379
|
+
description: `Return the final output of this action. Use this to return a value matching the required output schema (if specified) and the following description:
|
|
380
|
+
${outputDescription}
|
|
381
|
+
|
|
382
|
+
You can either set 'use_tool_result=true' to return the result of a previous tool call, or explicitly specify 'value' with 'use_tool_result=false' to return a value according to your own understanding. Whenever possible, reuse tool results to avoid redundancy.
|
|
383
|
+
`,
|
|
162
384
|
input_schema: {
|
|
163
385
|
type: 'object',
|
|
164
386
|
properties: {
|
|
387
|
+
use_tool_result: {
|
|
388
|
+
type: ['boolean'],
|
|
389
|
+
description: `Whether to use the latest tool result as output. When set to true, the 'value' parameter is ignored.`,
|
|
390
|
+
},
|
|
165
391
|
value: outputSchema || {
|
|
166
392
|
// Default to accepting any JSON value
|
|
167
393
|
type: ['string', 'number', 'boolean', 'object', 'null'],
|
|
168
|
-
description: 'The output value',
|
|
394
|
+
description: 'The output value. Only provide a value if the previous tool result is not suitable for the output description. Otherwise, leave this as null.',
|
|
169
395
|
},
|
|
170
396
|
},
|
|
171
|
-
required: ['value'],
|
|
397
|
+
required: ['use_tool_result', 'value'],
|
|
172
398
|
},
|
|
173
399
|
async execute(context, params) {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
return { returned: value };
|
|
400
|
+
context.variables.set(`__action_${actionName}_output`, params);
|
|
401
|
+
return { success: true };
|
|
177
402
|
},
|
|
178
403
|
};
|
|
179
404
|
}
|
|
@@ -187,6 +412,8 @@ class ActionImpl {
|
|
|
187
412
|
this.llmProvider = llmProvider;
|
|
188
413
|
this.llmConfig = llmConfig;
|
|
189
414
|
this.maxRounds = 10; // Default max rounds
|
|
415
|
+
this.toolResults = new Map();
|
|
416
|
+
this.logger = new ExecutionLogger();
|
|
190
417
|
this.writeContextTool = new WriteContextTool();
|
|
191
418
|
this.tools = [...tools, this.writeContextTool];
|
|
192
419
|
if (config === null || config === void 0 ? void 0 : config.maxRounds) {
|
|
@@ -194,6 +421,7 @@ class ActionImpl {
|
|
|
194
421
|
}
|
|
195
422
|
}
|
|
196
423
|
async executeSingleRound(messages, params, toolMap, context) {
|
|
424
|
+
this.logger = context.logger;
|
|
197
425
|
const roundMessages = [];
|
|
198
426
|
let hasToolUse = false;
|
|
199
427
|
let response = null;
|
|
@@ -203,6 +431,12 @@ class ActionImpl {
|
|
|
203
431
|
let toolResultMessage = null;
|
|
204
432
|
// Track tool execution promise
|
|
205
433
|
let toolExecutionPromise = null;
|
|
434
|
+
// Listen for abort signal
|
|
435
|
+
if (context.signal) {
|
|
436
|
+
context.signal.addEventListener('abort', () => {
|
|
437
|
+
context.__abort = true;
|
|
438
|
+
});
|
|
439
|
+
}
|
|
206
440
|
const handler = {
|
|
207
441
|
onContent: (content) => {
|
|
208
442
|
if (content.trim()) {
|
|
@@ -210,7 +444,8 @@ class ActionImpl {
|
|
|
210
444
|
}
|
|
211
445
|
},
|
|
212
446
|
onToolUse: async (toolCall) => {
|
|
213
|
-
|
|
447
|
+
this.logger.log('info', `Assistant: ${assistantTextMessage}`);
|
|
448
|
+
this.logger.logToolExecution(toolCall.name, toolCall.input, context);
|
|
214
449
|
hasToolUse = true;
|
|
215
450
|
const tool = toolMap.get(toolCall.name);
|
|
216
451
|
if (!tool) {
|
|
@@ -229,6 +464,7 @@ class ActionImpl {
|
|
|
229
464
|
};
|
|
230
465
|
// Store the promise of tool execution
|
|
231
466
|
toolExecutionPromise = (async () => {
|
|
467
|
+
var _a;
|
|
232
468
|
try {
|
|
233
469
|
// beforeToolUse
|
|
234
470
|
context.__skip = false;
|
|
@@ -238,7 +474,7 @@ class ActionImpl {
|
|
|
238
474
|
toolCall.input = modified_input;
|
|
239
475
|
}
|
|
240
476
|
}
|
|
241
|
-
if (context.__skip || context.__abort) {
|
|
477
|
+
if (context.__skip || context.__abort || ((_a = context.signal) === null || _a === void 0 ? void 0 : _a.aborted)) {
|
|
242
478
|
toolResultMessage = {
|
|
243
479
|
role: 'user',
|
|
244
480
|
content: [
|
|
@@ -260,31 +496,42 @@ class ActionImpl {
|
|
|
260
496
|
result = modified_result;
|
|
261
497
|
}
|
|
262
498
|
}
|
|
499
|
+
const result_has_image = result && "image" in result;
|
|
500
|
+
const resultContent = result_has_image
|
|
501
|
+
? {
|
|
502
|
+
type: 'tool_result',
|
|
503
|
+
tool_use_id: toolCall.id,
|
|
504
|
+
content: result.text
|
|
505
|
+
? [
|
|
506
|
+
{ type: 'image', source: result.image },
|
|
507
|
+
{ type: 'text', text: result.text },
|
|
508
|
+
]
|
|
509
|
+
: [{ type: 'image', source: result.image }],
|
|
510
|
+
}
|
|
511
|
+
: {
|
|
512
|
+
type: 'tool_result',
|
|
513
|
+
tool_use_id: toolCall.id,
|
|
514
|
+
content: [{ type: 'text', text: JSON.stringify(result) }],
|
|
515
|
+
};
|
|
516
|
+
const resultContentText = result_has_image
|
|
517
|
+
? result.text
|
|
518
|
+
? result.text + ' [Image]'
|
|
519
|
+
: '[Image]'
|
|
520
|
+
: JSON.stringify(result);
|
|
263
521
|
const resultMessage = {
|
|
264
522
|
role: 'user',
|
|
265
|
-
content: [
|
|
266
|
-
result.image && result.image.type
|
|
267
|
-
? {
|
|
268
|
-
type: 'tool_result',
|
|
269
|
-
tool_use_id: toolCall.id,
|
|
270
|
-
content: result.text
|
|
271
|
-
? [
|
|
272
|
-
{ type: 'image', source: result.image },
|
|
273
|
-
{ type: 'text', text: result.text },
|
|
274
|
-
]
|
|
275
|
-
: [{ type: 'image', source: result.image }],
|
|
276
|
-
}
|
|
277
|
-
: {
|
|
278
|
-
type: 'tool_result',
|
|
279
|
-
tool_use_id: toolCall.id,
|
|
280
|
-
content: [{ type: 'text', text: JSON.stringify(result) }],
|
|
281
|
-
},
|
|
282
|
-
],
|
|
523
|
+
content: [resultContent],
|
|
283
524
|
};
|
|
284
525
|
toolResultMessage = resultMessage;
|
|
285
|
-
|
|
526
|
+
this.logger.logToolResult(tool.name, result, context);
|
|
527
|
+
// Store tool results except for the return_output tool
|
|
528
|
+
if (tool.name !== 'return_output') {
|
|
529
|
+
this.toolResults.set(toolCall.id, resultContentText);
|
|
530
|
+
}
|
|
286
531
|
}
|
|
287
532
|
catch (err) {
|
|
533
|
+
console.log("An error occurred when calling tool:");
|
|
534
|
+
console.log(err);
|
|
288
535
|
const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';
|
|
289
536
|
const errorResult = {
|
|
290
537
|
role: 'user',
|
|
@@ -298,7 +545,7 @@ class ActionImpl {
|
|
|
298
545
|
],
|
|
299
546
|
};
|
|
300
547
|
toolResultMessage = errorResult;
|
|
301
|
-
|
|
548
|
+
this.logger.logError(err, context);
|
|
302
549
|
}
|
|
303
550
|
})();
|
|
304
551
|
},
|
|
@@ -307,16 +554,21 @@ class ActionImpl {
|
|
|
307
554
|
},
|
|
308
555
|
onError: (error) => {
|
|
309
556
|
console.error('Stream Error:', error);
|
|
557
|
+
console.log('Last message array sent to LLM:', JSON.stringify(messages, null, 2));
|
|
310
558
|
},
|
|
311
559
|
};
|
|
560
|
+
this.handleHistoryImageMessages(messages);
|
|
312
561
|
// Wait for stream to complete
|
|
562
|
+
if (!this.llmProvider) {
|
|
563
|
+
throw new Error('LLM provider not set');
|
|
564
|
+
}
|
|
313
565
|
await this.llmProvider.generateStream(messages, params, handler);
|
|
314
566
|
// Wait for tool execution to complete if it was started
|
|
315
567
|
if (toolExecutionPromise) {
|
|
316
568
|
await toolExecutionPromise;
|
|
317
569
|
}
|
|
318
570
|
if (context.__abort) {
|
|
319
|
-
throw new Error(
|
|
571
|
+
throw new Error('Abort');
|
|
320
572
|
}
|
|
321
573
|
// Add messages in the correct order after everything is complete
|
|
322
574
|
if (assistantTextMessage) {
|
|
@@ -330,10 +582,59 @@ class ActionImpl {
|
|
|
330
582
|
}
|
|
331
583
|
return { response, hasToolUse, roundMessages };
|
|
332
584
|
}
|
|
333
|
-
|
|
334
|
-
|
|
585
|
+
handleHistoryImageMessages(messages) {
|
|
586
|
+
// Remove all images from historical tool results except the most recent user message
|
|
587
|
+
const initialImageCount = this.countImages(messages);
|
|
588
|
+
let foundFirstUser = false;
|
|
589
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
590
|
+
const message = messages[i];
|
|
591
|
+
if (message.role === 'user') {
|
|
592
|
+
if (!foundFirstUser) {
|
|
593
|
+
foundFirstUser = true;
|
|
594
|
+
continue;
|
|
595
|
+
}
|
|
596
|
+
if (Array.isArray(message.content)) {
|
|
597
|
+
// Directly modify the message content array
|
|
598
|
+
message.content = message.content.map((item) => {
|
|
599
|
+
if (item.type === 'tool_result' && Array.isArray(item.content)) {
|
|
600
|
+
// Create a new content array without images
|
|
601
|
+
if (item.content.length > 0) {
|
|
602
|
+
item.content = item.content.filter((c) => c.type !== 'image');
|
|
603
|
+
// If all content was images and got filtered out, replace with ok message
|
|
604
|
+
if (item.content.length === 0) {
|
|
605
|
+
item.content = [{ type: 'text', text: 'ok' }];
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
return item;
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
const finalImageCount = this.countImages(messages);
|
|
615
|
+
if (initialImageCount !== finalImageCount) {
|
|
616
|
+
this.logger.log("info", `Removed ${initialImageCount - finalImageCount} images from history`);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
countImages(messages) {
|
|
620
|
+
let count = 0;
|
|
621
|
+
messages.forEach(msg => {
|
|
622
|
+
if (Array.isArray(msg.content)) {
|
|
623
|
+
msg.content.forEach((item) => {
|
|
624
|
+
if (item.type === 'tool_result' && Array.isArray(item.content)) {
|
|
625
|
+
count += item.content.filter((c) => c.type === 'image').length;
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
});
|
|
630
|
+
return count;
|
|
631
|
+
}
|
|
632
|
+
async execute(input, output, context, outputSchema) {
|
|
633
|
+
var _a, _b, _c, _d, _e;
|
|
634
|
+
this.logger = context.logger;
|
|
635
|
+
console.log(`Executing action started: ${this.name}`);
|
|
335
636
|
// Create return tool with output schema
|
|
336
|
-
const returnTool = createReturnTool(outputSchema);
|
|
637
|
+
const returnTool = createReturnTool(this.name, output.description, outputSchema);
|
|
337
638
|
// Create tool map combining context tools, action tools, and return tool
|
|
338
639
|
const toolMap = new Map();
|
|
339
640
|
this.tools.forEach((tool) => toolMap.set(tool.name, tool));
|
|
@@ -344,9 +645,7 @@ class ActionImpl {
|
|
|
344
645
|
{ role: 'system', content: this.formatSystemPrompt() },
|
|
345
646
|
{ role: 'user', content: this.formatUserPrompt(context, input) },
|
|
346
647
|
];
|
|
347
|
-
|
|
348
|
-
console.log('Initial messages:', messages);
|
|
349
|
-
console.log('Output schema:', outputSchema);
|
|
648
|
+
this.logger.logActionStart(this.name, input, context);
|
|
350
649
|
// Configure tool parameters
|
|
351
650
|
const params = {
|
|
352
651
|
...this.llmConfig,
|
|
@@ -358,17 +657,24 @@ class ActionImpl {
|
|
|
358
657
|
};
|
|
359
658
|
let roundCount = 0;
|
|
360
659
|
while (roundCount < this.maxRounds) {
|
|
660
|
+
// Check for abort signal
|
|
661
|
+
if ((_b = context.signal) === null || _b === void 0 ? void 0 : _b.aborted) {
|
|
662
|
+
throw new Error('Workflow cancelled');
|
|
663
|
+
}
|
|
361
664
|
roundCount++;
|
|
362
|
-
|
|
363
|
-
console.log('Current conversation status:', JSON.stringify(messages, null, 2));
|
|
665
|
+
this.logger.log('info', `Starting round ${roundCount} of ${this.maxRounds}`, context);
|
|
364
666
|
const { response, hasToolUse, roundMessages } = await this.executeSingleRound(messages, params, toolMap, context);
|
|
667
|
+
if (response === null || response === void 0 ? void 0 : response.textContent) {
|
|
668
|
+
(_e = (_d = (_c = context.callback) === null || _c === void 0 ? void 0 : _c.hooks) === null || _d === void 0 ? void 0 : _d.onLlmMessage) === null || _e === void 0 ? void 0 : _e.call(_d, response.textContent);
|
|
669
|
+
}
|
|
365
670
|
// Add round messages to conversation history
|
|
366
671
|
messages.push(...roundMessages);
|
|
672
|
+
this.logger.log('debug', `Round ${roundCount} messages: ${JSON.stringify(roundMessages)}`, context);
|
|
367
673
|
// Check termination conditions
|
|
368
674
|
if (!hasToolUse && response) {
|
|
369
675
|
// LLM sent a message without using tools - request explicit return
|
|
370
|
-
|
|
371
|
-
|
|
676
|
+
this.logger.log('info', `Assistant: ${response.textContent}`);
|
|
677
|
+
this.logger.log('warn', 'LLM sent a message without using tools; requesting explicit return');
|
|
372
678
|
const returnOnlyParams = {
|
|
373
679
|
...params,
|
|
374
680
|
tools: [
|
|
@@ -388,12 +694,11 @@ class ActionImpl {
|
|
|
388
694
|
break;
|
|
389
695
|
}
|
|
390
696
|
if (response === null || response === void 0 ? void 0 : response.toolCalls.some((call) => call.name === 'return_output')) {
|
|
391
|
-
console.log('Task completed with return_output tool');
|
|
392
697
|
break;
|
|
393
698
|
}
|
|
394
699
|
// If this is the last round, force an explicit return
|
|
395
700
|
if (roundCount === this.maxRounds) {
|
|
396
|
-
|
|
701
|
+
this.logger.log('warn', 'Max rounds reached, requesting explicit return');
|
|
397
702
|
const returnOnlyParams = {
|
|
398
703
|
...params,
|
|
399
704
|
tools: [
|
|
@@ -413,33 +718,50 @@ class ActionImpl {
|
|
|
413
718
|
}
|
|
414
719
|
}
|
|
415
720
|
// Get and clean up output value
|
|
416
|
-
const
|
|
417
|
-
context.variables.
|
|
418
|
-
|
|
721
|
+
const outputKey = `__action_${this.name}_output`;
|
|
722
|
+
const outputParams = context.variables.get(outputKey);
|
|
723
|
+
context.variables.delete(outputKey);
|
|
724
|
+
// Get output value, first checking for use_tool_result
|
|
725
|
+
const outputValue = outputParams.use_tool_result
|
|
726
|
+
? Array.from(this.toolResults.values()).pop()
|
|
727
|
+
: outputParams === null || outputParams === void 0 ? void 0 : outputParams.value;
|
|
728
|
+
if (outputValue === undefined) {
|
|
419
729
|
console.warn('Action completed without returning a value');
|
|
420
730
|
return {};
|
|
421
731
|
}
|
|
422
|
-
return
|
|
732
|
+
return outputValue;
|
|
423
733
|
}
|
|
424
734
|
formatSystemPrompt() {
|
|
425
|
-
return `You are a
|
|
735
|
+
return `You are a subtask executor. You need to complete the subtask specified by the user, which is a consisting part of the overall task. Help the user by calling the tools provided.
|
|
426
736
|
|
|
427
737
|
Remember to:
|
|
428
738
|
1. Use tools when needed to accomplish the task
|
|
429
|
-
2.
|
|
430
|
-
3.
|
|
739
|
+
2. Think step by step about what needs to be done
|
|
740
|
+
3. Return the output of the subtask using the 'return_output' tool when you are done; prefer using the 'tool_use_id' parameter to refer to the output of a tool call over providing a long text as the value
|
|
741
|
+
4. Use the context to store important information for later reference, but use it sparingly: most of the time, the output of the subtask should be sufficient for the next steps
|
|
742
|
+
5. If there are any unclear points during the task execution, please use the human-related tool to inquire with the user
|
|
743
|
+
6. If user intervention is required during the task execution, please use the human-related tool to transfer the operation rights to the user
|
|
744
|
+
`;
|
|
431
745
|
}
|
|
432
746
|
formatUserPrompt(context, input) {
|
|
433
|
-
|
|
434
|
-
const
|
|
747
|
+
var _a;
|
|
748
|
+
const workflowDescription = ((_a = context.workflow) === null || _a === void 0 ? void 0 : _a.description) || null;
|
|
749
|
+
const actionDescription = `${this.name} -- ${this.description}`;
|
|
750
|
+
const inputDescription = JSON.stringify(input, null, 2) || null;
|
|
751
|
+
const contextVariables = Array.from(context.variables.entries())
|
|
435
752
|
.map(([key, value]) => `${key}: ${JSON.stringify(value)}`)
|
|
436
753
|
.join('\n');
|
|
437
|
-
return `You are executing the
|
|
754
|
+
return `You are executing a subtask in the workflow. The workflow description is as follows:
|
|
755
|
+
${workflowDescription}
|
|
438
756
|
|
|
439
|
-
|
|
757
|
+
The subtask description is as follows:
|
|
758
|
+
${actionDescription}
|
|
440
759
|
|
|
441
|
-
|
|
442
|
-
${
|
|
760
|
+
The input to the subtask is as follows:
|
|
761
|
+
${inputDescription}
|
|
762
|
+
|
|
763
|
+
There are some variables stored in the context that you can use for reference:
|
|
764
|
+
${contextVariables}
|
|
443
765
|
`;
|
|
444
766
|
}
|
|
445
767
|
// Static factory method
|
|
@@ -469,7 +791,8 @@ Generate a complete workflow that:
|
|
|
469
791
|
2. Properly sequences tool usage based on dependencies
|
|
470
792
|
3. Ensures each action has appropriate input/output schemas, and that the "tools" field in each action is populated with the sufficient subset of all available tools needed to complete the action
|
|
471
793
|
4. Creates a clear, logical flow to accomplish the user's goal
|
|
472
|
-
5. Includes detailed descriptions for each action, ensuring that the actions, when combined, is a complete solution to the user's problem
|
|
794
|
+
5. Includes detailed descriptions for each action, ensuring that the actions, when combined, is a complete solution to the user's problem
|
|
795
|
+
6. You should always add a SubTask at the end of the workflow to summarize it, and this SubTask should always call the "summary_workflow" tool. It's dependencies should be all of the SubTasks`;
|
|
473
796
|
},
|
|
474
797
|
formatUserPrompt: (requirement) => `Create a workflow for the following requirement: ${requirement}`,
|
|
475
798
|
modifyUserPrompt: (prompt) => `Modify workflow: ${prompt}`,
|
|
@@ -610,6 +933,19 @@ class WorkflowGenerator {
|
|
|
610
933
|
],
|
|
611
934
|
});
|
|
612
935
|
const workflowData = response.toolCalls[0].input.workflow;
|
|
936
|
+
// Forcibly add special tools
|
|
937
|
+
const specialTools = [
|
|
938
|
+
"cancel_workflow",
|
|
939
|
+
"human_input_text",
|
|
940
|
+
"human_operate",
|
|
941
|
+
];
|
|
942
|
+
for (const node of workflowData.nodes) {
|
|
943
|
+
for (const tool of specialTools) {
|
|
944
|
+
if (!node.action.tools.includes(tool)) {
|
|
945
|
+
node.action.tools.push(tool);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
}
|
|
613
949
|
// Validate all tools exist
|
|
614
950
|
for (const node of workflowData.nodes) {
|
|
615
951
|
if (!this.toolRegistry.hasTools(node.action.tools)) {
|
|
@@ -620,15 +956,22 @@ class WorkflowGenerator {
|
|
|
620
956
|
if (!workflowData.id) {
|
|
621
957
|
workflowData.id = v4();
|
|
622
958
|
}
|
|
959
|
+
// debug
|
|
960
|
+
console.log("Debug the workflow...");
|
|
961
|
+
console.log(workflowData);
|
|
962
|
+
console.log("Debug the workflow...Done");
|
|
623
963
|
return this.createWorkflowFromData(workflowData);
|
|
624
964
|
}
|
|
625
965
|
createWorkflowFromData(data) {
|
|
626
|
-
const workflow = new WorkflowImpl(data.id, data.name, data.description || '', [], new Map(Object.entries(data.variables || {})), this.llmProvider
|
|
966
|
+
const workflow = new WorkflowImpl(data.id, data.name, data.description || '', [], new Map(Object.entries(data.variables || {})), this.llmProvider, {
|
|
967
|
+
logLevel: 'info',
|
|
968
|
+
includeTimestamp: true,
|
|
969
|
+
});
|
|
627
970
|
// Add nodes to workflow
|
|
628
971
|
if (Array.isArray(data.nodes)) {
|
|
629
972
|
data.nodes.forEach((nodeData) => {
|
|
630
973
|
const tools = nodeData.action.tools.map((toolName) => this.toolRegistry.getTool(toolName));
|
|
631
|
-
const action = ActionImpl.createPromptAction(nodeData.action.name, nodeData.action.description, tools, this.llmProvider, { maxTokens:
|
|
974
|
+
const action = ActionImpl.createPromptAction(nodeData.action.name, nodeData.action.description, tools, this.llmProvider, { maxTokens: 8192 });
|
|
632
975
|
const node = {
|
|
633
976
|
id: nodeData.id,
|
|
634
977
|
name: nodeData.name || nodeData.id,
|
|
@@ -3372,8 +3715,13 @@ class ClaudeProvider {
|
|
|
3372
3715
|
...options,
|
|
3373
3716
|
});
|
|
3374
3717
|
}
|
|
3718
|
+
else if (param.messages && param.completions) {
|
|
3719
|
+
this.client = param;
|
|
3720
|
+
}
|
|
3375
3721
|
else {
|
|
3376
|
-
|
|
3722
|
+
let options = param;
|
|
3723
|
+
options.dangerouslyAllowBrowser = true;
|
|
3724
|
+
this.client = new Anthropic(options);
|
|
3377
3725
|
}
|
|
3378
3726
|
}
|
|
3379
3727
|
processResponse(response) {
|
|
@@ -8792,8 +9140,13 @@ class OpenaiProvider {
|
|
|
8792
9140
|
...options,
|
|
8793
9141
|
});
|
|
8794
9142
|
}
|
|
9143
|
+
else if (param.chat && param.chat.completions) {
|
|
9144
|
+
this.client = param;
|
|
9145
|
+
}
|
|
8795
9146
|
else {
|
|
8796
|
-
|
|
9147
|
+
let options = param;
|
|
9148
|
+
options.dangerouslyAllowBrowser = true;
|
|
9149
|
+
this.client = new OpenAI(options);
|
|
8797
9150
|
}
|
|
8798
9151
|
}
|
|
8799
9152
|
buildParams(messages, params, stream) {
|
|
@@ -8872,13 +9225,61 @@ class OpenaiProvider {
|
|
|
8872
9225
|
content: content.text,
|
|
8873
9226
|
});
|
|
8874
9227
|
}
|
|
8875
|
-
else if (content.type == '
|
|
9228
|
+
else if (content.type == 'image') {
|
|
8876
9229
|
_messages.push({
|
|
8877
|
-
role: '
|
|
8878
|
-
content:
|
|
8879
|
-
|
|
9230
|
+
role: 'user',
|
|
9231
|
+
content: [
|
|
9232
|
+
{
|
|
9233
|
+
type: 'image_url',
|
|
9234
|
+
image_url: {
|
|
9235
|
+
url: `data:${content.source.media_type};base64,${content.source.data}`,
|
|
9236
|
+
},
|
|
9237
|
+
},
|
|
9238
|
+
],
|
|
8880
9239
|
});
|
|
8881
9240
|
}
|
|
9241
|
+
else if (content.type == 'tool_result') {
|
|
9242
|
+
let _content = [];
|
|
9243
|
+
if (content.content == 'string') {
|
|
9244
|
+
_content.push({ type: 'text', text: content.content });
|
|
9245
|
+
}
|
|
9246
|
+
else {
|
|
9247
|
+
for (let k = 0; k < content.content.length; k++) {
|
|
9248
|
+
let item = content.content[k];
|
|
9249
|
+
if (item.type == 'text') {
|
|
9250
|
+
_content.push({ ...item });
|
|
9251
|
+
}
|
|
9252
|
+
else if (item.type == 'image') {
|
|
9253
|
+
_content.push({
|
|
9254
|
+
type: 'image_url',
|
|
9255
|
+
image_url: {
|
|
9256
|
+
url: `data:${item.source.media_type};base64,${item.source.data}`,
|
|
9257
|
+
},
|
|
9258
|
+
});
|
|
9259
|
+
}
|
|
9260
|
+
}
|
|
9261
|
+
}
|
|
9262
|
+
let hasImage = _content.filter((s) => s.type == 'image_url').length > 0;
|
|
9263
|
+
if (hasImage) {
|
|
9264
|
+
// OpenAI does not support images returned by the tool.
|
|
9265
|
+
_messages.push({
|
|
9266
|
+
role: 'tool',
|
|
9267
|
+
content: 'ok',
|
|
9268
|
+
tool_call_id: content.tool_call_id || content.tool_use_id,
|
|
9269
|
+
});
|
|
9270
|
+
_messages.push({
|
|
9271
|
+
role: 'user',
|
|
9272
|
+
content: _content,
|
|
9273
|
+
});
|
|
9274
|
+
}
|
|
9275
|
+
else {
|
|
9276
|
+
_messages.push({
|
|
9277
|
+
role: 'tool',
|
|
9278
|
+
content: _content,
|
|
9279
|
+
tool_call_id: content.tool_call_id || content.tool_use_id,
|
|
9280
|
+
});
|
|
9281
|
+
}
|
|
9282
|
+
}
|
|
8882
9283
|
}
|
|
8883
9284
|
}
|
|
8884
9285
|
else {
|
|
@@ -9056,18 +9457,11 @@ const workflowSchema = {
|
|
|
9056
9457
|
type: "array",
|
|
9057
9458
|
items: { type: "string" },
|
|
9058
9459
|
},
|
|
9059
|
-
input: {
|
|
9060
|
-
type: "object",
|
|
9061
|
-
properties: {
|
|
9062
|
-
type: { type: "string" },
|
|
9063
|
-
schema: { type: "object" },
|
|
9064
|
-
},
|
|
9065
|
-
},
|
|
9066
9460
|
output: {
|
|
9067
9461
|
type: "object",
|
|
9068
9462
|
properties: {
|
|
9069
|
-
|
|
9070
|
-
|
|
9463
|
+
name: { type: "string" },
|
|
9464
|
+
description: { type: "string" },
|
|
9071
9465
|
},
|
|
9072
9466
|
},
|
|
9073
9467
|
action: {
|
|
@@ -9196,8 +9590,27 @@ class Eko {
|
|
|
9196
9590
|
return workflow;
|
|
9197
9591
|
}
|
|
9198
9592
|
async execute(workflow, callback) {
|
|
9593
|
+
// Inject LLM provider at workflow level
|
|
9594
|
+
workflow.llmProvider = this.llmProvider;
|
|
9595
|
+
// Process each node's action
|
|
9596
|
+
for (const node of workflow.nodes) {
|
|
9597
|
+
if (node.action.type === 'prompt') {
|
|
9598
|
+
// Inject LLM provider
|
|
9599
|
+
node.action.llmProvider = this.llmProvider;
|
|
9600
|
+
// Resolve tools
|
|
9601
|
+
node.action.tools = node.action.tools.map(tool => {
|
|
9602
|
+
if (typeof tool === 'string') {
|
|
9603
|
+
return this.toolRegistry.getTool(tool);
|
|
9604
|
+
}
|
|
9605
|
+
return tool;
|
|
9606
|
+
});
|
|
9607
|
+
}
|
|
9608
|
+
}
|
|
9199
9609
|
return await workflow.execute(callback);
|
|
9200
9610
|
}
|
|
9611
|
+
async cancel(workflow) {
|
|
9612
|
+
return await workflow.cancel();
|
|
9613
|
+
}
|
|
9201
9614
|
async modify(workflow, prompt) {
|
|
9202
9615
|
const generator = this.workflowGeneratorMap.get(workflow);
|
|
9203
9616
|
workflow = await generator.modifyWorkflow(prompt);
|
|
@@ -9381,31 +9794,30 @@ class WorkflowParser {
|
|
|
9381
9794
|
errors,
|
|
9382
9795
|
};
|
|
9383
9796
|
}
|
|
9384
|
-
/**
|
|
9385
|
-
* Convert parsed JSON to runtime Workflow object
|
|
9386
|
-
*/
|
|
9387
9797
|
static toRuntime(json) {
|
|
9388
9798
|
const variables = new Map(Object.entries(json.variables || {}));
|
|
9389
|
-
const workflow = new WorkflowImpl(json.id, json.name, json.description, [], variables
|
|
9799
|
+
const workflow = new WorkflowImpl(json.id, json.name, json.description, [], variables, undefined, {
|
|
9800
|
+
logLevel: 'info',
|
|
9801
|
+
includeTimestamp: true,
|
|
9802
|
+
});
|
|
9390
9803
|
// Convert nodes
|
|
9391
9804
|
json.nodes.forEach((nodeJson) => {
|
|
9805
|
+
const action = ActionImpl.createPromptAction(nodeJson.action.name, nodeJson.action.description,
|
|
9806
|
+
// Pass tool names as strings, they'll be resolved at execution time
|
|
9807
|
+
nodeJson.action.tools || [], undefined, // LLM provider will be injected at execution time
|
|
9808
|
+
{ maxTokens: 1000 });
|
|
9392
9809
|
const node = {
|
|
9393
9810
|
id: nodeJson.id,
|
|
9394
9811
|
name: nodeJson.name || nodeJson.id,
|
|
9395
9812
|
description: nodeJson.description,
|
|
9396
9813
|
dependencies: nodeJson.dependencies || [],
|
|
9397
|
-
input:
|
|
9398
|
-
output:
|
|
9399
|
-
|
|
9400
|
-
|
|
9401
|
-
|
|
9402
|
-
description: nodeJson.action.description,
|
|
9403
|
-
tools: nodeJson.action.tools || [],
|
|
9404
|
-
execute: async (input, context) => {
|
|
9405
|
-
// Default implementation - should be overridden by specific action types
|
|
9406
|
-
return input;
|
|
9407
|
-
},
|
|
9814
|
+
input: { items: [] },
|
|
9815
|
+
output: nodeJson.output || {
|
|
9816
|
+
name: `${nodeJson.name || nodeJson.id}_output`,
|
|
9817
|
+
description: `Output of node ${nodeJson.name || nodeJson.id}`,
|
|
9818
|
+
value: null,
|
|
9408
9819
|
},
|
|
9820
|
+
action: action,
|
|
9409
9821
|
};
|
|
9410
9822
|
workflow.addNode(node);
|
|
9411
9823
|
});
|
|
@@ -9425,28 +9837,19 @@ class WorkflowParser {
|
|
|
9425
9837
|
name: node.name,
|
|
9426
9838
|
description: node.description,
|
|
9427
9839
|
dependencies: node.dependencies,
|
|
9428
|
-
input: node.input,
|
|
9429
9840
|
output: node.output,
|
|
9430
9841
|
action: {
|
|
9431
9842
|
type: node.action.type,
|
|
9432
9843
|
name: node.action.name,
|
|
9433
9844
|
description: node.action.description,
|
|
9434
|
-
tools: node.action.tools
|
|
9845
|
+
tools: node.action.tools
|
|
9846
|
+
.map((tool) => (typeof tool === 'string' ? tool : tool.name))
|
|
9847
|
+
.filter((tool) => tool !== 'write_context'),
|
|
9435
9848
|
},
|
|
9436
9849
|
})),
|
|
9437
9850
|
variables: Object.fromEntries(workflow.variables),
|
|
9438
9851
|
};
|
|
9439
9852
|
}
|
|
9440
|
-
/**
|
|
9441
|
-
* Helper to convert IO definitions
|
|
9442
|
-
*/
|
|
9443
|
-
static convertIO(io) {
|
|
9444
|
-
return {
|
|
9445
|
-
type: (io === null || io === void 0 ? void 0 : io.type) || 'object',
|
|
9446
|
-
schema: (io === null || io === void 0 ? void 0 : io.schema) || {},
|
|
9447
|
-
value: null,
|
|
9448
|
-
};
|
|
9449
|
-
}
|
|
9450
9853
|
}
|
|
9451
9854
|
|
|
9452
|
-
export { ClaudeProvider, Eko, OpenaiProvider, ToolRegistry, WorkflowGenerator, WorkflowParser, Eko as default };
|
|
9855
|
+
export { ClaudeProvider, Eko, ExecutionLogger, OpenaiProvider, ToolRegistry, WorkflowGenerator, WorkflowParser, Eko as default };
|