@eko-ai/eko 1.0.5 → 1.0.7
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 +15 -9
- package/dist/core/eko.d.ts +2 -0
- package/dist/extension.cjs.js +1 -9
- package/dist/extension.esm.js +1 -9
- package/dist/extension_content_script.js +3 -0
- package/dist/index.cjs.js +93 -17
- package/dist/index.esm.js +93 -17
- package/dist/models/workflow.d.ts +1 -0
- package/dist/services/workflow/generator.d.ts +4 -2
- package/dist/services/workflow/templates.d.ts +1 -0
- package/dist/types/index.d.ts +0 -1
- package/dist/web.cjs.js +4 -9
- package/dist/web.esm.js +4 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
# Eko
|
|
2
2
|
|
|
3
|
-
[](LICENSE) [](https://example.com/build-status) [](LICENSE) [](https://example.com/build-status) [](https://eko.fellou.ai/docs/release/versions/)
|
|
4
4
|
|
|
5
5
|
**Eko** is a revolutionary framework designed to empower developers and users alike to program their browser and operating system using natural language. With seamless integration of browser APIs, OS-level capabilities, and cutting-edge AI tools like Claude 3.5, Eko redefines how we interact with technology, making it intuitive, powerful, and accessible.
|
|
6
6
|
|
|
7
7
|
## Key Features
|
|
8
8
|
|
|
9
|
-
- **Natural Language Programming**: Transform human instructions into executable
|
|
10
|
-
- **
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
9
|
+
- **Natural Language Programming**: Transform human instructions into e- **Natural Language Programming**: Convert natural language task descriptions into executable workflows, making agent development more intuitive
|
|
10
|
+
- **Two-Layer Execution Model**: Separate offline planning from online execution, making agent decisions more structured and explainable
|
|
11
|
+
- **Comprehensive Tooling**: Rich built-in tools for browser automation, computer control, file operations, and web interactions
|
|
12
|
+
- **Hybrid Drive System:** Combine LLM capabilities with developer control, enabling "human in the loop" and allowing interference at multiple levels of granularity
|
|
13
|
+
- **Event-Driven Automation:** Trigger workflows based on browser or system events
|
|
14
|
+
- **Environment Flexibility**: Work across different environments ( [Browser Extension](/docs/browseruse/browser-extension), [Web](/docs/browseruse/browser-web), [Node.js](/docs/computeruse/computer-node), [Next-Gen AI Browser Fellou](/docs/computeruse/computer-fellou) ) with consistent APIs
|
|
14
15
|
|
|
15
|
-
##
|
|
16
|
+
## Quickstart
|
|
16
17
|
|
|
17
18
|
```bash
|
|
18
19
|
npm install @eko-ai/eko
|
|
19
20
|
```
|
|
20
21
|
|
|
22
|
+
> The following code is for reference only. For detailed usage, please refer to the [Eko Quickstart guide](https://eko.fellou.ai/docs/getting-started/quickstart/).
|
|
23
|
+
|
|
21
24
|
```typescript
|
|
22
25
|
import { Eko } from '@eko-ai/eko';
|
|
23
26
|
|
|
@@ -26,10 +29,13 @@ const eko = new Eko({
|
|
|
26
29
|
});
|
|
27
30
|
|
|
28
31
|
// Example: Browser automation
|
|
29
|
-
await eko.
|
|
32
|
+
const extWorkflow = await eko.generate("Search for 'Eko framework' on Google and save the first result");
|
|
33
|
+
await eko.execute(extWorkflow);
|
|
30
34
|
|
|
31
35
|
// Example: System operation
|
|
32
|
-
await eko.
|
|
36
|
+
const sysWorkflow = await eko.generate("Create a new folder named 'reports' and move all PDF files there");
|
|
37
|
+
await eko.execute(sysWorkflow);
|
|
38
|
+
|
|
33
39
|
```
|
|
34
40
|
|
|
35
41
|
## Use Cases
|
package/dist/core/eko.d.ts
CHANGED
|
@@ -6,9 +6,11 @@ export declare class Eko {
|
|
|
6
6
|
static tools: Map<string, Tool<any, any>>;
|
|
7
7
|
private llmProvider;
|
|
8
8
|
private toolRegistry;
|
|
9
|
+
private workflowGeneratorMap;
|
|
9
10
|
constructor(config: EkoConfig);
|
|
10
11
|
generate(prompt: string, param?: EkoInvokeParam): Promise<Workflow>;
|
|
11
12
|
execute(workflow: Workflow, callback?: WorkflowCallback): Promise<void>;
|
|
13
|
+
modify(workflow: Workflow, prompt: string): Promise<Workflow>;
|
|
12
14
|
private getTool;
|
|
13
15
|
callTool(toolName: string, input: object, callback?: WorkflowCallback): Promise<any>;
|
|
14
16
|
callTool(tool: Tool<any, any>, input: object, callback?: WorkflowCallback): Promise<any>;
|
package/dist/extension.cjs.js
CHANGED
|
@@ -455,7 +455,6 @@ class BrowserUse {
|
|
|
455
455
|
- Screenshots help verify element positions and relationships. Labels may sometimes overlap, so extracted elements are used to verify the correct elements.
|
|
456
456
|
- In addition to screenshots, simplified information about interactive elements is returned, with element indexes corresponding to those in the screenshots.
|
|
457
457
|
* \`input_text\`: Enter a string in the interactive element.
|
|
458
|
-
* \`clear_text\`: Clear the text in the input/textarea element.
|
|
459
458
|
* \`click\`: Click to element.
|
|
460
459
|
* \`right_click\`: Right-click on the element.
|
|
461
460
|
* \`double_click\`: Double-click on the element.
|
|
@@ -466,7 +465,6 @@ class BrowserUse {
|
|
|
466
465
|
enum: [
|
|
467
466
|
'screenshot_extract_element',
|
|
468
467
|
'input_text',
|
|
469
|
-
'clear_text',
|
|
470
468
|
'click',
|
|
471
469
|
'right_click',
|
|
472
470
|
'double_click',
|
|
@@ -519,16 +517,10 @@ class BrowserUse {
|
|
|
519
517
|
if (params.text == null) {
|
|
520
518
|
throw new Error('text parameter is required');
|
|
521
519
|
}
|
|
520
|
+
await clear_input_by(tabId, selector_xpath, params.index);
|
|
522
521
|
result = await type_by(tabId, params.text, selector_xpath, params.index);
|
|
523
522
|
await sleep(200);
|
|
524
523
|
break;
|
|
525
|
-
case 'clear_text':
|
|
526
|
-
if (params.index == null) {
|
|
527
|
-
throw new Error('index parameter is required');
|
|
528
|
-
}
|
|
529
|
-
result = await clear_input_by(tabId, selector_xpath, params.index);
|
|
530
|
-
await sleep(100);
|
|
531
|
-
break;
|
|
532
524
|
case 'click':
|
|
533
525
|
if (params.index == null) {
|
|
534
526
|
throw new Error('index parameter is required');
|
package/dist/extension.esm.js
CHANGED
|
@@ -453,7 +453,6 @@ class BrowserUse {
|
|
|
453
453
|
- Screenshots help verify element positions and relationships. Labels may sometimes overlap, so extracted elements are used to verify the correct elements.
|
|
454
454
|
- In addition to screenshots, simplified information about interactive elements is returned, with element indexes corresponding to those in the screenshots.
|
|
455
455
|
* \`input_text\`: Enter a string in the interactive element.
|
|
456
|
-
* \`clear_text\`: Clear the text in the input/textarea element.
|
|
457
456
|
* \`click\`: Click to element.
|
|
458
457
|
* \`right_click\`: Right-click on the element.
|
|
459
458
|
* \`double_click\`: Double-click on the element.
|
|
@@ -464,7 +463,6 @@ class BrowserUse {
|
|
|
464
463
|
enum: [
|
|
465
464
|
'screenshot_extract_element',
|
|
466
465
|
'input_text',
|
|
467
|
-
'clear_text',
|
|
468
466
|
'click',
|
|
469
467
|
'right_click',
|
|
470
468
|
'double_click',
|
|
@@ -517,16 +515,10 @@ class BrowserUse {
|
|
|
517
515
|
if (params.text == null) {
|
|
518
516
|
throw new Error('text parameter is required');
|
|
519
517
|
}
|
|
518
|
+
await clear_input_by(tabId, selector_xpath, params.index);
|
|
520
519
|
result = await type_by(tabId, params.text, selector_xpath, params.index);
|
|
521
520
|
await sleep(200);
|
|
522
521
|
break;
|
|
523
|
-
case 'clear_text':
|
|
524
|
-
if (params.index == null) {
|
|
525
|
-
throw new Error('index parameter is required');
|
|
526
|
-
}
|
|
527
|
-
result = await clear_input_by(tabId, selector_xpath, params.index);
|
|
528
|
-
await sleep(100);
|
|
529
|
-
break;
|
|
530
522
|
case 'click':
|
|
531
523
|
if (params.index == null) {
|
|
532
524
|
throw new Error('index parameter is required');
|
package/dist/index.cjs.js
CHANGED
|
@@ -16,11 +16,15 @@ class WorkflowImpl {
|
|
|
16
16
|
if (!this.validateDAG()) {
|
|
17
17
|
throw new Error("Invalid workflow: Contains circular dependencies");
|
|
18
18
|
}
|
|
19
|
+
this.abort = false;
|
|
19
20
|
callback && await ((_b = (_a = callback.hooks).beforeWorkflow) === null || _b === void 0 ? void 0 : _b.call(_a, this));
|
|
20
21
|
const executed = new Set();
|
|
21
22
|
const executing = new Set();
|
|
22
23
|
const executeNode = async (nodeId) => {
|
|
23
24
|
var _a, _b, _c, _d, _e;
|
|
25
|
+
if (this.abort) {
|
|
26
|
+
throw new Error("Abort");
|
|
27
|
+
}
|
|
24
28
|
if (executed.has(nodeId)) {
|
|
25
29
|
return;
|
|
26
30
|
}
|
|
@@ -30,12 +34,22 @@ class WorkflowImpl {
|
|
|
30
34
|
const node = this.getNode(nodeId);
|
|
31
35
|
// Execute the node's action
|
|
32
36
|
const context = {
|
|
37
|
+
__skip: false,
|
|
38
|
+
__abort: false,
|
|
33
39
|
variables: this.variables,
|
|
34
40
|
llmProvider: this.llmProvider,
|
|
35
41
|
tools: new Map(node.action.tools.map(tool => [tool.name, tool])),
|
|
36
|
-
callback
|
|
42
|
+
callback,
|
|
43
|
+
next: () => context.__skip = true,
|
|
44
|
+
abortAll: () => this.abort = context.__abort = true,
|
|
37
45
|
};
|
|
38
46
|
callback && await ((_b = (_a = callback.hooks).beforeSubtask) === null || _b === void 0 ? void 0 : _b.call(_a, node, context));
|
|
47
|
+
if (context.__abort) {
|
|
48
|
+
throw new Error("Abort");
|
|
49
|
+
}
|
|
50
|
+
else if (context.__skip) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
39
53
|
executing.add(nodeId);
|
|
40
54
|
// Execute dependencies first
|
|
41
55
|
for (const depId of node.dependencies) {
|
|
@@ -221,12 +235,26 @@ class ActionImpl {
|
|
|
221
235
|
toolExecutionPromise = (async () => {
|
|
222
236
|
try {
|
|
223
237
|
// beforeToolUse
|
|
238
|
+
context.__skip = false;
|
|
224
239
|
if (context.callback && context.callback.hooks.beforeToolUse) {
|
|
225
240
|
let modified_input = await context.callback.hooks.beforeToolUse(tool, context, toolCall.input);
|
|
226
241
|
if (modified_input) {
|
|
227
242
|
toolCall.input = modified_input;
|
|
228
243
|
}
|
|
229
244
|
}
|
|
245
|
+
if (context.__skip || context.__abort) {
|
|
246
|
+
toolResultMessage = {
|
|
247
|
+
role: 'user',
|
|
248
|
+
content: [
|
|
249
|
+
{
|
|
250
|
+
type: 'tool_result',
|
|
251
|
+
tool_use_id: toolCall.id,
|
|
252
|
+
content: 'skip',
|
|
253
|
+
},
|
|
254
|
+
],
|
|
255
|
+
};
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
230
258
|
// Execute the tool
|
|
231
259
|
let result = await tool.execute(context, toolCall.input);
|
|
232
260
|
// afterToolUse
|
|
@@ -291,6 +319,9 @@ class ActionImpl {
|
|
|
291
319
|
if (toolExecutionPromise) {
|
|
292
320
|
await toolExecutionPromise;
|
|
293
321
|
}
|
|
322
|
+
if (context.__abort) {
|
|
323
|
+
throw new Error("Abort");
|
|
324
|
+
}
|
|
294
325
|
// Add messages in the correct order after everything is complete
|
|
295
326
|
if (assistantTextMessage) {
|
|
296
327
|
roundMessages.push({ role: 'assistant', content: assistantTextMessage });
|
|
@@ -440,11 +471,12 @@ ${toolDescriptions}
|
|
|
440
471
|
Generate a complete workflow that:
|
|
441
472
|
1. Only uses the tools listed above
|
|
442
473
|
2. Properly sequences tool usage based on dependencies
|
|
443
|
-
3. Ensures each action has appropriate input/output schemas
|
|
474
|
+
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
|
|
444
475
|
4. Creates a clear, logical flow to accomplish the user's goal
|
|
445
476
|
5. Includes detailed descriptions for each action, ensuring that the actions, when combined, is a complete solution to the user's problem`;
|
|
446
477
|
},
|
|
447
478
|
formatUserPrompt: (requirement) => `Create a workflow for the following requirement: ${requirement}`,
|
|
479
|
+
modifyUserPrompt: (prompt) => `Modify workflow: ${prompt}`,
|
|
448
480
|
};
|
|
449
481
|
}
|
|
450
482
|
function createWorkflowGenerationTool(registry) {
|
|
@@ -519,30 +551,68 @@ class WorkflowGenerator {
|
|
|
519
551
|
constructor(llmProvider, toolRegistry) {
|
|
520
552
|
this.llmProvider = llmProvider;
|
|
521
553
|
this.toolRegistry = toolRegistry;
|
|
554
|
+
this.message_history = [];
|
|
522
555
|
}
|
|
523
556
|
async generateWorkflow(prompt) {
|
|
557
|
+
return this.doGenerateWorkflow(prompt, false);
|
|
558
|
+
}
|
|
559
|
+
async modifyWorkflow(prompt) {
|
|
560
|
+
return this.doGenerateWorkflow(prompt, true);
|
|
561
|
+
}
|
|
562
|
+
async doGenerateWorkflow(prompt, modify) {
|
|
524
563
|
// Create prompts with current set of tools
|
|
525
564
|
const prompts = createWorkflowPrompts(this.toolRegistry.getToolDefinitions());
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
},
|
|
531
|
-
{
|
|
565
|
+
let messages = [];
|
|
566
|
+
if (modify) {
|
|
567
|
+
messages = this.message_history;
|
|
568
|
+
messages.push({
|
|
532
569
|
role: 'user',
|
|
533
|
-
content: prompts.
|
|
534
|
-
}
|
|
535
|
-
|
|
570
|
+
content: prompts.modifyUserPrompt(prompt),
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
else {
|
|
574
|
+
messages = this.message_history = [
|
|
575
|
+
{
|
|
576
|
+
role: 'system',
|
|
577
|
+
content: prompts.formatSystemPrompt(),
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
role: 'user',
|
|
581
|
+
content: prompts.formatUserPrompt(prompt),
|
|
582
|
+
},
|
|
583
|
+
];
|
|
584
|
+
}
|
|
536
585
|
const params = {
|
|
537
586
|
temperature: 0.7,
|
|
538
587
|
maxTokens: 8192,
|
|
539
588
|
tools: [createWorkflowGenerationTool(this.toolRegistry)],
|
|
540
|
-
toolChoice: { type: 'tool', name: 'generate_workflow' }
|
|
589
|
+
toolChoice: { type: 'tool', name: 'generate_workflow' },
|
|
541
590
|
};
|
|
542
591
|
const response = await this.llmProvider.generateText(messages, params);
|
|
543
592
|
if (!response.toolCalls.length || !response.toolCalls[0].input.workflow) {
|
|
593
|
+
messages.pop();
|
|
544
594
|
throw new Error('Failed to generate workflow: Invalid response from LLM');
|
|
545
595
|
}
|
|
596
|
+
messages.push({
|
|
597
|
+
role: 'assistant',
|
|
598
|
+
content: [
|
|
599
|
+
{
|
|
600
|
+
type: 'tool_use',
|
|
601
|
+
id: response.toolCalls[0].id,
|
|
602
|
+
name: response.toolCalls[0].name,
|
|
603
|
+
input: response.toolCalls[0].input,
|
|
604
|
+
},
|
|
605
|
+
],
|
|
606
|
+
}, {
|
|
607
|
+
role: 'user',
|
|
608
|
+
content: [
|
|
609
|
+
{
|
|
610
|
+
type: 'tool_result',
|
|
611
|
+
tool_use_id: response.toolCalls[0].id,
|
|
612
|
+
content: 'ok',
|
|
613
|
+
},
|
|
614
|
+
],
|
|
615
|
+
});
|
|
546
616
|
const workflowData = response.toolCalls[0].input.workflow;
|
|
547
617
|
// Validate all tools exist
|
|
548
618
|
for (const node of workflowData.nodes) {
|
|
@@ -569,16 +639,13 @@ class WorkflowGenerator {
|
|
|
569
639
|
input: nodeData.input || { type: 'any', schema: {}, value: undefined },
|
|
570
640
|
output: nodeData.output || { type: 'any', schema: {}, value: undefined },
|
|
571
641
|
action: action,
|
|
572
|
-
dependencies: nodeData.dependencies || []
|
|
642
|
+
dependencies: nodeData.dependencies || [],
|
|
573
643
|
};
|
|
574
644
|
workflow.addNode(node);
|
|
575
645
|
});
|
|
576
646
|
}
|
|
577
647
|
return workflow;
|
|
578
648
|
}
|
|
579
|
-
async modifyWorkflow(workflow, prompt) {
|
|
580
|
-
throw new Error('Not implemented');
|
|
581
|
-
}
|
|
582
649
|
}
|
|
583
650
|
|
|
584
651
|
const VERSION$1 = '0.33.1'; // x-release-please-version
|
|
@@ -9091,6 +9158,7 @@ class ToolRegistry {
|
|
|
9091
9158
|
class Eko {
|
|
9092
9159
|
constructor(config) {
|
|
9093
9160
|
this.toolRegistry = new ToolRegistry();
|
|
9161
|
+
this.workflowGeneratorMap = new Map();
|
|
9094
9162
|
if (typeof config == 'string') {
|
|
9095
9163
|
this.llmProvider = new ClaudeProvider(config);
|
|
9096
9164
|
}
|
|
@@ -9127,11 +9195,19 @@ class Eko {
|
|
|
9127
9195
|
}
|
|
9128
9196
|
}
|
|
9129
9197
|
const generator = new WorkflowGenerator(this.llmProvider, toolRegistry);
|
|
9130
|
-
|
|
9198
|
+
const workflow = await generator.generateWorkflow(prompt);
|
|
9199
|
+
this.workflowGeneratorMap.set(workflow, generator);
|
|
9200
|
+
return workflow;
|
|
9131
9201
|
}
|
|
9132
9202
|
async execute(workflow, callback) {
|
|
9133
9203
|
return await workflow.execute(callback);
|
|
9134
9204
|
}
|
|
9205
|
+
async modify(workflow, prompt) {
|
|
9206
|
+
const generator = this.workflowGeneratorMap.get(workflow);
|
|
9207
|
+
workflow = await generator.modifyWorkflow(prompt);
|
|
9208
|
+
this.workflowGeneratorMap.set(workflow, generator);
|
|
9209
|
+
return workflow;
|
|
9210
|
+
}
|
|
9135
9211
|
getTool(toolName) {
|
|
9136
9212
|
let tool;
|
|
9137
9213
|
if (this.toolRegistry.hasTools([toolName])) {
|
package/dist/index.esm.js
CHANGED
|
@@ -12,11 +12,15 @@ class WorkflowImpl {
|
|
|
12
12
|
if (!this.validateDAG()) {
|
|
13
13
|
throw new Error("Invalid workflow: Contains circular dependencies");
|
|
14
14
|
}
|
|
15
|
+
this.abort = false;
|
|
15
16
|
callback && await ((_b = (_a = callback.hooks).beforeWorkflow) === null || _b === void 0 ? void 0 : _b.call(_a, this));
|
|
16
17
|
const executed = new Set();
|
|
17
18
|
const executing = new Set();
|
|
18
19
|
const executeNode = async (nodeId) => {
|
|
19
20
|
var _a, _b, _c, _d, _e;
|
|
21
|
+
if (this.abort) {
|
|
22
|
+
throw new Error("Abort");
|
|
23
|
+
}
|
|
20
24
|
if (executed.has(nodeId)) {
|
|
21
25
|
return;
|
|
22
26
|
}
|
|
@@ -26,12 +30,22 @@ class WorkflowImpl {
|
|
|
26
30
|
const node = this.getNode(nodeId);
|
|
27
31
|
// Execute the node's action
|
|
28
32
|
const context = {
|
|
33
|
+
__skip: false,
|
|
34
|
+
__abort: false,
|
|
29
35
|
variables: this.variables,
|
|
30
36
|
llmProvider: this.llmProvider,
|
|
31
37
|
tools: new Map(node.action.tools.map(tool => [tool.name, tool])),
|
|
32
|
-
callback
|
|
38
|
+
callback,
|
|
39
|
+
next: () => context.__skip = true,
|
|
40
|
+
abortAll: () => this.abort = context.__abort = true,
|
|
33
41
|
};
|
|
34
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
|
+
}
|
|
35
49
|
executing.add(nodeId);
|
|
36
50
|
// Execute dependencies first
|
|
37
51
|
for (const depId of node.dependencies) {
|
|
@@ -217,12 +231,26 @@ class ActionImpl {
|
|
|
217
231
|
toolExecutionPromise = (async () => {
|
|
218
232
|
try {
|
|
219
233
|
// beforeToolUse
|
|
234
|
+
context.__skip = false;
|
|
220
235
|
if (context.callback && context.callback.hooks.beforeToolUse) {
|
|
221
236
|
let modified_input = await context.callback.hooks.beforeToolUse(tool, context, toolCall.input);
|
|
222
237
|
if (modified_input) {
|
|
223
238
|
toolCall.input = modified_input;
|
|
224
239
|
}
|
|
225
240
|
}
|
|
241
|
+
if (context.__skip || context.__abort) {
|
|
242
|
+
toolResultMessage = {
|
|
243
|
+
role: 'user',
|
|
244
|
+
content: [
|
|
245
|
+
{
|
|
246
|
+
type: 'tool_result',
|
|
247
|
+
tool_use_id: toolCall.id,
|
|
248
|
+
content: 'skip',
|
|
249
|
+
},
|
|
250
|
+
],
|
|
251
|
+
};
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
226
254
|
// Execute the tool
|
|
227
255
|
let result = await tool.execute(context, toolCall.input);
|
|
228
256
|
// afterToolUse
|
|
@@ -287,6 +315,9 @@ class ActionImpl {
|
|
|
287
315
|
if (toolExecutionPromise) {
|
|
288
316
|
await toolExecutionPromise;
|
|
289
317
|
}
|
|
318
|
+
if (context.__abort) {
|
|
319
|
+
throw new Error("Abort");
|
|
320
|
+
}
|
|
290
321
|
// Add messages in the correct order after everything is complete
|
|
291
322
|
if (assistantTextMessage) {
|
|
292
323
|
roundMessages.push({ role: 'assistant', content: assistantTextMessage });
|
|
@@ -436,11 +467,12 @@ ${toolDescriptions}
|
|
|
436
467
|
Generate a complete workflow that:
|
|
437
468
|
1. Only uses the tools listed above
|
|
438
469
|
2. Properly sequences tool usage based on dependencies
|
|
439
|
-
3. Ensures each action has appropriate input/output schemas
|
|
470
|
+
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
|
|
440
471
|
4. Creates a clear, logical flow to accomplish the user's goal
|
|
441
472
|
5. Includes detailed descriptions for each action, ensuring that the actions, when combined, is a complete solution to the user's problem`;
|
|
442
473
|
},
|
|
443
474
|
formatUserPrompt: (requirement) => `Create a workflow for the following requirement: ${requirement}`,
|
|
475
|
+
modifyUserPrompt: (prompt) => `Modify workflow: ${prompt}`,
|
|
444
476
|
};
|
|
445
477
|
}
|
|
446
478
|
function createWorkflowGenerationTool(registry) {
|
|
@@ -515,30 +547,68 @@ class WorkflowGenerator {
|
|
|
515
547
|
constructor(llmProvider, toolRegistry) {
|
|
516
548
|
this.llmProvider = llmProvider;
|
|
517
549
|
this.toolRegistry = toolRegistry;
|
|
550
|
+
this.message_history = [];
|
|
518
551
|
}
|
|
519
552
|
async generateWorkflow(prompt) {
|
|
553
|
+
return this.doGenerateWorkflow(prompt, false);
|
|
554
|
+
}
|
|
555
|
+
async modifyWorkflow(prompt) {
|
|
556
|
+
return this.doGenerateWorkflow(prompt, true);
|
|
557
|
+
}
|
|
558
|
+
async doGenerateWorkflow(prompt, modify) {
|
|
520
559
|
// Create prompts with current set of tools
|
|
521
560
|
const prompts = createWorkflowPrompts(this.toolRegistry.getToolDefinitions());
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
},
|
|
527
|
-
{
|
|
561
|
+
let messages = [];
|
|
562
|
+
if (modify) {
|
|
563
|
+
messages = this.message_history;
|
|
564
|
+
messages.push({
|
|
528
565
|
role: 'user',
|
|
529
|
-
content: prompts.
|
|
530
|
-
}
|
|
531
|
-
|
|
566
|
+
content: prompts.modifyUserPrompt(prompt),
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
else {
|
|
570
|
+
messages = this.message_history = [
|
|
571
|
+
{
|
|
572
|
+
role: 'system',
|
|
573
|
+
content: prompts.formatSystemPrompt(),
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
role: 'user',
|
|
577
|
+
content: prompts.formatUserPrompt(prompt),
|
|
578
|
+
},
|
|
579
|
+
];
|
|
580
|
+
}
|
|
532
581
|
const params = {
|
|
533
582
|
temperature: 0.7,
|
|
534
583
|
maxTokens: 8192,
|
|
535
584
|
tools: [createWorkflowGenerationTool(this.toolRegistry)],
|
|
536
|
-
toolChoice: { type: 'tool', name: 'generate_workflow' }
|
|
585
|
+
toolChoice: { type: 'tool', name: 'generate_workflow' },
|
|
537
586
|
};
|
|
538
587
|
const response = await this.llmProvider.generateText(messages, params);
|
|
539
588
|
if (!response.toolCalls.length || !response.toolCalls[0].input.workflow) {
|
|
589
|
+
messages.pop();
|
|
540
590
|
throw new Error('Failed to generate workflow: Invalid response from LLM');
|
|
541
591
|
}
|
|
592
|
+
messages.push({
|
|
593
|
+
role: 'assistant',
|
|
594
|
+
content: [
|
|
595
|
+
{
|
|
596
|
+
type: 'tool_use',
|
|
597
|
+
id: response.toolCalls[0].id,
|
|
598
|
+
name: response.toolCalls[0].name,
|
|
599
|
+
input: response.toolCalls[0].input,
|
|
600
|
+
},
|
|
601
|
+
],
|
|
602
|
+
}, {
|
|
603
|
+
role: 'user',
|
|
604
|
+
content: [
|
|
605
|
+
{
|
|
606
|
+
type: 'tool_result',
|
|
607
|
+
tool_use_id: response.toolCalls[0].id,
|
|
608
|
+
content: 'ok',
|
|
609
|
+
},
|
|
610
|
+
],
|
|
611
|
+
});
|
|
542
612
|
const workflowData = response.toolCalls[0].input.workflow;
|
|
543
613
|
// Validate all tools exist
|
|
544
614
|
for (const node of workflowData.nodes) {
|
|
@@ -565,16 +635,13 @@ class WorkflowGenerator {
|
|
|
565
635
|
input: nodeData.input || { type: 'any', schema: {}, value: undefined },
|
|
566
636
|
output: nodeData.output || { type: 'any', schema: {}, value: undefined },
|
|
567
637
|
action: action,
|
|
568
|
-
dependencies: nodeData.dependencies || []
|
|
638
|
+
dependencies: nodeData.dependencies || [],
|
|
569
639
|
};
|
|
570
640
|
workflow.addNode(node);
|
|
571
641
|
});
|
|
572
642
|
}
|
|
573
643
|
return workflow;
|
|
574
644
|
}
|
|
575
|
-
async modifyWorkflow(workflow, prompt) {
|
|
576
|
-
throw new Error('Not implemented');
|
|
577
|
-
}
|
|
578
645
|
}
|
|
579
646
|
|
|
580
647
|
const VERSION$1 = '0.33.1'; // x-release-please-version
|
|
@@ -9087,6 +9154,7 @@ class ToolRegistry {
|
|
|
9087
9154
|
class Eko {
|
|
9088
9155
|
constructor(config) {
|
|
9089
9156
|
this.toolRegistry = new ToolRegistry();
|
|
9157
|
+
this.workflowGeneratorMap = new Map();
|
|
9090
9158
|
if (typeof config == 'string') {
|
|
9091
9159
|
this.llmProvider = new ClaudeProvider(config);
|
|
9092
9160
|
}
|
|
@@ -9123,11 +9191,19 @@ class Eko {
|
|
|
9123
9191
|
}
|
|
9124
9192
|
}
|
|
9125
9193
|
const generator = new WorkflowGenerator(this.llmProvider, toolRegistry);
|
|
9126
|
-
|
|
9194
|
+
const workflow = await generator.generateWorkflow(prompt);
|
|
9195
|
+
this.workflowGeneratorMap.set(workflow, generator);
|
|
9196
|
+
return workflow;
|
|
9127
9197
|
}
|
|
9128
9198
|
async execute(workflow, callback) {
|
|
9129
9199
|
return await workflow.execute(callback);
|
|
9130
9200
|
}
|
|
9201
|
+
async modify(workflow, prompt) {
|
|
9202
|
+
const generator = this.workflowGeneratorMap.get(workflow);
|
|
9203
|
+
workflow = await generator.modifyWorkflow(prompt);
|
|
9204
|
+
this.workflowGeneratorMap.set(workflow, generator);
|
|
9205
|
+
return workflow;
|
|
9206
|
+
}
|
|
9131
9207
|
getTool(toolName) {
|
|
9132
9208
|
let tool;
|
|
9133
9209
|
if (this.toolRegistry.hasTools([toolName])) {
|
|
@@ -6,6 +6,7 @@ export declare class WorkflowImpl implements Workflow {
|
|
|
6
6
|
nodes: WorkflowNode[];
|
|
7
7
|
variables: Map<string, unknown>;
|
|
8
8
|
llmProvider?: LLMProvider | undefined;
|
|
9
|
+
abort?: boolean;
|
|
9
10
|
constructor(id: string, name: string, description?: string | undefined, nodes?: WorkflowNode[], variables?: Map<string, unknown>, llmProvider?: LLMProvider | undefined);
|
|
10
11
|
execute(callback?: WorkflowCallback): Promise<void>;
|
|
11
12
|
addNode(node: WorkflowNode): void;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { LLMProvider } from '../../types/llm.types';
|
|
1
|
+
import { LLMProvider, Message } from '../../types/llm.types';
|
|
2
2
|
import { Workflow } from '../../types/workflow.types';
|
|
3
3
|
import { ToolRegistry } from '../../core/tool-registry';
|
|
4
4
|
export declare class WorkflowGenerator {
|
|
5
5
|
private llmProvider;
|
|
6
6
|
private toolRegistry;
|
|
7
|
+
message_history: Message[];
|
|
7
8
|
constructor(llmProvider: LLMProvider, toolRegistry: ToolRegistry);
|
|
8
9
|
generateWorkflow(prompt: string): Promise<Workflow>;
|
|
10
|
+
modifyWorkflow(prompt: string): Promise<Workflow>;
|
|
11
|
+
private doGenerateWorkflow;
|
|
9
12
|
private createWorkflowFromData;
|
|
10
|
-
modifyWorkflow(workflow: Workflow, prompt: string): Promise<Workflow>;
|
|
11
13
|
}
|
|
@@ -3,5 +3,6 @@ import { ToolRegistry } from '../../core/tool-registry';
|
|
|
3
3
|
export declare function createWorkflowPrompts(tools: ToolDefinition[]): {
|
|
4
4
|
formatSystemPrompt: () => string;
|
|
5
5
|
formatUserPrompt: (requirement: string) => string;
|
|
6
|
+
modifyUserPrompt: (prompt: string) => string;
|
|
6
7
|
};
|
|
7
8
|
export declare function createWorkflowGenerationTool(registry: ToolRegistry): ToolDefinition;
|
package/dist/types/index.d.ts
CHANGED
package/dist/web.cjs.js
CHANGED
|
@@ -8581,6 +8581,9 @@ function do_input(text, xpath, highlightIndex) {
|
|
|
8581
8581
|
}
|
|
8582
8582
|
input.focus && input.focus();
|
|
8583
8583
|
if (!text) {
|
|
8584
|
+
if (input.value == '') {
|
|
8585
|
+
return true;
|
|
8586
|
+
}
|
|
8584
8587
|
input.value = '';
|
|
8585
8588
|
}
|
|
8586
8589
|
else {
|
|
@@ -8682,7 +8685,6 @@ class BrowserUse {
|
|
|
8682
8685
|
- Screenshots help verify element positions and relationships. Labels may sometimes overlap, so extracted elements are used to verify the correct elements.
|
|
8683
8686
|
- In addition to screenshots, simplified information about interactive elements is returned, with element indexes corresponding to those in the screenshots.
|
|
8684
8687
|
* \`input_text\`: Enter a string in the interactive element.
|
|
8685
|
-
* \`clear_text\`: Clear the text in the input/textarea element.
|
|
8686
8688
|
* \`click\`: Click to element.
|
|
8687
8689
|
* \`right_click\`: Right-click on the element.
|
|
8688
8690
|
* \`double_click\`: Double-click on the element.
|
|
@@ -8693,7 +8695,6 @@ class BrowserUse {
|
|
|
8693
8695
|
enum: [
|
|
8694
8696
|
'screenshot_extract_element',
|
|
8695
8697
|
'input_text',
|
|
8696
|
-
'clear_text',
|
|
8697
8698
|
'click',
|
|
8698
8699
|
'right_click',
|
|
8699
8700
|
'double_click',
|
|
@@ -8744,16 +8745,10 @@ class BrowserUse {
|
|
|
8744
8745
|
if (params.text == null) {
|
|
8745
8746
|
throw new Error('text parameter is required');
|
|
8746
8747
|
}
|
|
8748
|
+
await clear_input(selector_xpath, params.index);
|
|
8747
8749
|
result = await type(params.text, selector_xpath, params.index);
|
|
8748
8750
|
await sleep(200);
|
|
8749
8751
|
break;
|
|
8750
|
-
case 'clear_text':
|
|
8751
|
-
if (params.index == null) {
|
|
8752
|
-
throw new Error('index parameter is required');
|
|
8753
|
-
}
|
|
8754
|
-
result = await clear_input(selector_xpath, params.index);
|
|
8755
|
-
await sleep(100);
|
|
8756
|
-
break;
|
|
8757
8752
|
case 'click':
|
|
8758
8753
|
if (params.index == null) {
|
|
8759
8754
|
throw new Error('index parameter is required');
|
package/dist/web.esm.js
CHANGED
|
@@ -8579,6 +8579,9 @@ function do_input(text, xpath, highlightIndex) {
|
|
|
8579
8579
|
}
|
|
8580
8580
|
input.focus && input.focus();
|
|
8581
8581
|
if (!text) {
|
|
8582
|
+
if (input.value == '') {
|
|
8583
|
+
return true;
|
|
8584
|
+
}
|
|
8582
8585
|
input.value = '';
|
|
8583
8586
|
}
|
|
8584
8587
|
else {
|
|
@@ -8680,7 +8683,6 @@ class BrowserUse {
|
|
|
8680
8683
|
- Screenshots help verify element positions and relationships. Labels may sometimes overlap, so extracted elements are used to verify the correct elements.
|
|
8681
8684
|
- In addition to screenshots, simplified information about interactive elements is returned, with element indexes corresponding to those in the screenshots.
|
|
8682
8685
|
* \`input_text\`: Enter a string in the interactive element.
|
|
8683
|
-
* \`clear_text\`: Clear the text in the input/textarea element.
|
|
8684
8686
|
* \`click\`: Click to element.
|
|
8685
8687
|
* \`right_click\`: Right-click on the element.
|
|
8686
8688
|
* \`double_click\`: Double-click on the element.
|
|
@@ -8691,7 +8693,6 @@ class BrowserUse {
|
|
|
8691
8693
|
enum: [
|
|
8692
8694
|
'screenshot_extract_element',
|
|
8693
8695
|
'input_text',
|
|
8694
|
-
'clear_text',
|
|
8695
8696
|
'click',
|
|
8696
8697
|
'right_click',
|
|
8697
8698
|
'double_click',
|
|
@@ -8742,16 +8743,10 @@ class BrowserUse {
|
|
|
8742
8743
|
if (params.text == null) {
|
|
8743
8744
|
throw new Error('text parameter is required');
|
|
8744
8745
|
}
|
|
8746
|
+
await clear_input(selector_xpath, params.index);
|
|
8745
8747
|
result = await type(params.text, selector_xpath, params.index);
|
|
8746
8748
|
await sleep(200);
|
|
8747
8749
|
break;
|
|
8748
|
-
case 'clear_text':
|
|
8749
|
-
if (params.index == null) {
|
|
8750
|
-
throw new Error('index parameter is required');
|
|
8751
|
-
}
|
|
8752
|
-
result = await clear_input(selector_xpath, params.index);
|
|
8753
|
-
await sleep(100);
|
|
8754
|
-
break;
|
|
8755
8750
|
case 'click':
|
|
8756
8751
|
if (params.index == null) {
|
|
8757
8752
|
throw new Error('index parameter is required');
|