@lupaflow/workflow 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +32 -0
- package/src/engine.ts +100 -0
- package/src/index.ts +3 -0
- package/src/pipeline.ts +49 -0
- package/src/step.ts +77 -0
- package/tsconfig.json +8 -0
- package/tsup.config.ts +9 -0
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lupaflow/workflow",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"module": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"require": "./dist/index.js",
|
|
16
|
+
"import": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup",
|
|
21
|
+
"dev": "tsup --watch"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@lupaflow/core": "workspace:*",
|
|
25
|
+
"@lupaflow/types": "workspace:*",
|
|
26
|
+
"@lupaflow/agent": "workspace:*"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"tsup": "^8.3.0",
|
|
30
|
+
"typescript": "^5.6.0"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/engine.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { WorkflowConfig, WorkflowResult } from "@lupaflow/types"
|
|
2
|
+
import { WorkflowStep } from "./step"
|
|
3
|
+
import { Pipeline } from "./pipeline"
|
|
4
|
+
import { generateId } from "@lupaflow/core"
|
|
5
|
+
import { globalEventBus } from "@lupaflow/core"
|
|
6
|
+
import { WorkflowError } from "@lupaflow/core"
|
|
7
|
+
|
|
8
|
+
export class WorkflowEngine {
|
|
9
|
+
public config: WorkflowConfig
|
|
10
|
+
public results: WorkflowResult[] = []
|
|
11
|
+
|
|
12
|
+
constructor(config: WorkflowConfig) {
|
|
13
|
+
this.config = { ...config, id: config.id || generateId() }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
static fromPipeline(name: string, pipeline: Pipeline, description?: string): WorkflowEngine {
|
|
17
|
+
return new WorkflowEngine({
|
|
18
|
+
name,
|
|
19
|
+
description,
|
|
20
|
+
steps: pipeline.getSteps(),
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async run(initialInput?: unknown): Promise<WorkflowResult> {
|
|
25
|
+
const start = Date.now()
|
|
26
|
+
const stepResults = []
|
|
27
|
+
|
|
28
|
+
await globalEventBus.emit("workflow:start", {
|
|
29
|
+
steps: this.config.steps.length,
|
|
30
|
+
} as any)
|
|
31
|
+
|
|
32
|
+
let currentInput = initialInput
|
|
33
|
+
|
|
34
|
+
for (let i = 0; i < this.config.steps.length; i++) {
|
|
35
|
+
const stepConfig = this.config.steps[i]
|
|
36
|
+
const step = new WorkflowStep(stepConfig)
|
|
37
|
+
|
|
38
|
+
await globalEventBus.emit("workflow:step", {
|
|
39
|
+
step: i + 1,
|
|
40
|
+
stepName: stepConfig.name,
|
|
41
|
+
status: "running",
|
|
42
|
+
} as any)
|
|
43
|
+
|
|
44
|
+
const stepResult = await step.execute(currentInput)
|
|
45
|
+
stepResults.push(stepResult)
|
|
46
|
+
|
|
47
|
+
await globalEventBus.emit("workflow:step", {
|
|
48
|
+
step: i + 1,
|
|
49
|
+
stepName: stepConfig.name,
|
|
50
|
+
status: stepResult.status === "success" ? "completed" : "failed",
|
|
51
|
+
} as any)
|
|
52
|
+
|
|
53
|
+
if (stepResult.status === "failed") {
|
|
54
|
+
if (this.config.maxRetries && i < this.config.steps.length - 1) {
|
|
55
|
+
stepResult.status = "success"
|
|
56
|
+
} else {
|
|
57
|
+
const result: WorkflowResult = {
|
|
58
|
+
id: this.config.id!,
|
|
59
|
+
name: this.config.name,
|
|
60
|
+
success: false,
|
|
61
|
+
steps: stepResults,
|
|
62
|
+
durationMs: Date.now() - start,
|
|
63
|
+
error: stepResult.error,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
await globalEventBus.emit("workflow:complete", {
|
|
67
|
+
totalSteps: this.config.steps.length,
|
|
68
|
+
failedSteps: stepResults.filter((s) => s.status === "failed").length,
|
|
69
|
+
durationMs: result.durationMs,
|
|
70
|
+
} as any)
|
|
71
|
+
|
|
72
|
+
this.config.onError?.(new WorkflowError(stepResult.error!, stepConfig.id))
|
|
73
|
+
this.results.push(result)
|
|
74
|
+
return result
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
currentInput = stepResult.output
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const durationMs = Date.now() - start
|
|
82
|
+
const result: WorkflowResult = {
|
|
83
|
+
id: this.config.id!,
|
|
84
|
+
name: this.config.name,
|
|
85
|
+
success: true,
|
|
86
|
+
steps: stepResults,
|
|
87
|
+
durationMs,
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
await globalEventBus.emit("workflow:complete", {
|
|
91
|
+
totalSteps: this.config.steps.length,
|
|
92
|
+
failedSteps: 0,
|
|
93
|
+
durationMs,
|
|
94
|
+
} as any)
|
|
95
|
+
|
|
96
|
+
this.config.onComplete?.(stepResults.map((s) => s.output))
|
|
97
|
+
this.results.push(result)
|
|
98
|
+
return result
|
|
99
|
+
}
|
|
100
|
+
}
|
package/src/index.ts
ADDED
package/src/pipeline.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { WorkflowStepConfig } from "@lupaflow/types"
|
|
2
|
+
|
|
3
|
+
export class Pipeline {
|
|
4
|
+
public steps: WorkflowStepConfig[] = []
|
|
5
|
+
|
|
6
|
+
addAgent(name: string, config: Partial<WorkflowStepConfig>): this {
|
|
7
|
+
this.steps.push({
|
|
8
|
+
id: `step-${this.steps.length + 1}`,
|
|
9
|
+
name,
|
|
10
|
+
type: "agent",
|
|
11
|
+
...config,
|
|
12
|
+
})
|
|
13
|
+
return this
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
addTool(name: string, config: Partial<WorkflowStepConfig>): this {
|
|
17
|
+
this.steps.push({
|
|
18
|
+
id: `step-${this.steps.length + 1}`,
|
|
19
|
+
name,
|
|
20
|
+
type: "tool",
|
|
21
|
+
...config,
|
|
22
|
+
})
|
|
23
|
+
return this
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
addTransform(name: string, transform: (input: unknown) => unknown): this {
|
|
27
|
+
this.steps.push({
|
|
28
|
+
id: `step-${this.steps.length + 1}`,
|
|
29
|
+
name,
|
|
30
|
+
type: "transform",
|
|
31
|
+
transform,
|
|
32
|
+
})
|
|
33
|
+
return this
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
addCondition(name: string, condition: (input: unknown) => boolean): this {
|
|
37
|
+
this.steps.push({
|
|
38
|
+
id: `step-${this.steps.length + 1}`,
|
|
39
|
+
name,
|
|
40
|
+
type: "condition",
|
|
41
|
+
condition,
|
|
42
|
+
})
|
|
43
|
+
return this
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
getSteps(): WorkflowStepConfig[] {
|
|
47
|
+
return [...this.steps]
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/step.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { WorkflowStepConfig, WorkflowStepResult } from "@lupaflow/types"
|
|
2
|
+
import { Agent } from "@lupaflow/agent"
|
|
3
|
+
import { generateId } from "@lupaflow/core"
|
|
4
|
+
|
|
5
|
+
export class WorkflowStep {
|
|
6
|
+
public config: WorkflowStepConfig
|
|
7
|
+
public result?: WorkflowStepResult
|
|
8
|
+
|
|
9
|
+
constructor(config: WorkflowStepConfig) {
|
|
10
|
+
this.config = { ...config, id: config.id || generateId() }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async execute(input?: unknown): Promise<WorkflowStepResult> {
|
|
14
|
+
const start = Date.now()
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
let output: unknown
|
|
18
|
+
|
|
19
|
+
switch (this.config.type) {
|
|
20
|
+
case "agent": {
|
|
21
|
+
if (!this.config.agent) throw new Error("Agent config required for agent step")
|
|
22
|
+
const agent = new Agent(this.config.agent)
|
|
23
|
+
const agentInput = this.config.input || input
|
|
24
|
+
const result = await agent.run(typeof agentInput === "string" ? agentInput : JSON.stringify(agentInput))
|
|
25
|
+
output = result.output
|
|
26
|
+
break
|
|
27
|
+
}
|
|
28
|
+
case "tool": {
|
|
29
|
+
if (!this.config.tool) throw new Error("Tool required for tool step")
|
|
30
|
+
const toolInput = this.config.input || input
|
|
31
|
+
output = await this.config.tool.execute(toolInput as Record<string, unknown>)
|
|
32
|
+
break
|
|
33
|
+
}
|
|
34
|
+
case "transform": {
|
|
35
|
+
if (!this.config.transform) throw new Error("Transform function required for transform step")
|
|
36
|
+
output = this.config.transform(input)
|
|
37
|
+
break
|
|
38
|
+
}
|
|
39
|
+
case "condition": {
|
|
40
|
+
if (!this.config.condition) throw new Error("Condition function required for condition step")
|
|
41
|
+
output = this.config.condition(input)
|
|
42
|
+
break
|
|
43
|
+
}
|
|
44
|
+
default: {
|
|
45
|
+
output = input
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const durationMs = Date.now() - start
|
|
50
|
+
this.result = {
|
|
51
|
+
id: this.config.id,
|
|
52
|
+
name: this.config.name,
|
|
53
|
+
type: this.config.type,
|
|
54
|
+
status: "success",
|
|
55
|
+
input,
|
|
56
|
+
output,
|
|
57
|
+
durationMs,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
this.config.onComplete?.(output)
|
|
61
|
+
return this.result!
|
|
62
|
+
} catch (err: any) {
|
|
63
|
+
const durationMs = Date.now() - start
|
|
64
|
+
this.result = {
|
|
65
|
+
id: this.config.id,
|
|
66
|
+
name: this.config.name,
|
|
67
|
+
type: this.config.type,
|
|
68
|
+
status: "failed",
|
|
69
|
+
input,
|
|
70
|
+
durationMs,
|
|
71
|
+
error: err.message,
|
|
72
|
+
}
|
|
73
|
+
this.config.onError?.(err)
|
|
74
|
+
return this.result!
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
package/tsconfig.json
ADDED