@geenius/ai-workflow 0.1.0 → 0.4.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/CHANGELOG.md +7 -0
- package/README.md +69 -1
- package/package.json +60 -27
- package/packages/convex/dist/index.d.ts +363 -0
- package/packages/convex/dist/index.js +200 -0
- package/packages/convex/dist/index.js.map +1 -0
- package/packages/react/dist/index.d.ts +466 -0
- package/packages/react/dist/index.js +13914 -0
- package/packages/react/dist/index.js.map +1 -0
- package/packages/react-css/{src/styles.css → dist/index.css} +107 -253
- package/packages/react-css/dist/index.css.map +1 -0
- package/packages/react-css/dist/index.d.ts +495 -0
- package/packages/react-css/dist/index.js +13901 -0
- package/packages/react-css/dist/index.js.map +1 -0
- package/packages/shared/dist/index.d.ts +1368 -0
- package/packages/shared/dist/index.js +1681 -0
- package/packages/shared/dist/index.js.map +1 -0
- package/packages/solidjs/dist/index.d.ts +452 -0
- package/packages/solidjs/dist/index.js +13830 -0
- package/packages/solidjs/dist/index.js.map +1 -0
- package/packages/solidjs-css/{src/styles.css → dist/index.css} +107 -253
- package/packages/solidjs-css/dist/index.css.map +1 -0
- package/packages/solidjs-css/dist/index.d.ts +471 -0
- package/packages/solidjs-css/dist/index.js +13774 -0
- package/packages/solidjs-css/dist/index.js.map +1 -0
- package/.changeset/config.json +0 -11
- package/.github/CODEOWNERS +0 -1
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -16
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -11
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -10
- package/.github/dependabot.yml +0 -11
- package/.github/workflows/ci.yml +0 -23
- package/.github/workflows/release.yml +0 -29
- package/.nvmrc +0 -1
- package/.project/ACCOUNT.yaml +0 -4
- package/.project/IDEAS.yaml +0 -7
- package/.project/PROJECT.yaml +0 -11
- package/.project/ROADMAP.yaml +0 -15
- package/CODE_OF_CONDUCT.md +0 -16
- package/CONTRIBUTING.md +0 -26
- package/SECURITY.md +0 -15
- package/SUPPORT.md +0 -8
- package/packages/convex/README.md +0 -1
- package/packages/convex/package.json +0 -12
- package/packages/convex/src/convex.config.ts +0 -3
- package/packages/convex/src/index.ts +0 -3
- package/packages/convex/src/mutations.ts +0 -36
- package/packages/convex/src/queries.ts +0 -19
- package/packages/convex/src/schema.ts +0 -24
- package/packages/convex/tsconfig.json +0 -25
- package/packages/react/README.md +0 -1
- package/packages/react/package.json +0 -46
- package/packages/react/src/components/ApprovalModal.tsx +0 -47
- package/packages/react/src/components/StepConfigPanel.tsx +0 -67
- package/packages/react/src/components/StepConnector.tsx +0 -47
- package/packages/react/src/components/StepNode.tsx +0 -38
- package/packages/react/src/components/StepPalette.tsx +0 -48
- package/packages/react/src/components/WorkflowCanvas.tsx +0 -42
- package/packages/react/src/components/WorkflowRunPanel.tsx +0 -64
- package/packages/react/src/components/WorkflowToolbar.tsx +0 -43
- package/packages/react/src/components/index.ts +0 -9
- package/packages/react/src/hooks/index.ts +0 -10
- package/packages/react/src/hooks/useApprovalGate.ts +0 -59
- package/packages/react/src/hooks/useWorkflow.ts +0 -39
- package/packages/react/src/hooks/useWorkflowBuilder.ts +0 -121
- package/packages/react/src/hooks/useWorkflowRun.ts +0 -75
- package/packages/react/src/hooks/useWorkflowStep.ts +0 -52
- package/packages/react/src/hooks/useWorkflowTemplates.ts +0 -54
- package/packages/react/src/index.ts +0 -16
- package/packages/react/src/pages/WorkflowBuilderPage.tsx +0 -81
- package/packages/react/src/pages/WorkflowRunsPage.tsx +0 -59
- package/packages/react/src/pages/index.ts +0 -3
- package/packages/react/tsconfig.json +0 -1
- package/packages/react/tsup.config.ts +0 -7
- package/packages/react-css/README.md +0 -1
- package/packages/react-css/package.json +0 -44
- package/packages/react-css/src/components/ApprovalModal.tsx +0 -6
- package/packages/react-css/src/components/StepConfigPanel.tsx +0 -7
- package/packages/react-css/src/components/StepConnector.tsx +0 -6
- package/packages/react-css/src/components/StepNode.tsx +0 -7
- package/packages/react-css/src/components/StepPalette.tsx +0 -6
- package/packages/react-css/src/components/WorkflowCanvas.tsx +0 -6
- package/packages/react-css/src/components/WorkflowRunPanel.tsx +0 -9
- package/packages/react-css/src/components/WorkflowToolbar.tsx +0 -4
- package/packages/react-css/src/components/index.ts +0 -9
- package/packages/react-css/src/hooks/index.ts +0 -3
- package/packages/react-css/src/hooks/useWorkflow.ts +0 -39
- package/packages/react-css/src/hooks/useWorkflowBuilder.ts +0 -121
- package/packages/react-css/src/index.ts +0 -7
- package/packages/react-css/src/pages/WorkflowBuilderPage.tsx +0 -16
- package/packages/react-css/src/pages/WorkflowRunsPage.tsx +0 -6
- package/packages/react-css/src/pages/index.ts +0 -3
- package/packages/react-css/tsconfig.json +0 -26
- package/packages/react-css/tsup.config.ts +0 -2
- package/packages/shared/README.md +0 -1
- package/packages/shared/package.json +0 -56
- package/packages/shared/src/__tests__/ai-workflow.test.ts +0 -217
- package/packages/shared/src/config.ts +0 -49
- package/packages/shared/src/convex/index.ts +0 -2
- package/packages/shared/src/convex/schemas.ts +0 -42
- package/packages/shared/src/engine.test.ts +0 -1
- package/packages/shared/src/engine.ts +0 -295
- package/packages/shared/src/index.ts +0 -43
- package/packages/shared/src/steps.ts +0 -68
- package/packages/shared/src/templates.ts +0 -172
- package/packages/shared/src/types.ts +0 -237
- package/packages/shared/src/utils/cost.ts +0 -79
- package/packages/shared/src/utils/dag.ts +0 -133
- package/packages/shared/src/utils/index.ts +0 -5
- package/packages/shared/src/utils/interpolation.ts +0 -53
- package/packages/shared/src/validators.ts +0 -215
- package/packages/shared/tsconfig.json +0 -1
- package/packages/shared/tsup.config.ts +0 -5
- package/packages/shared/vitest.config.ts +0 -4
- package/packages/solidjs/README.md +0 -1
- package/packages/solidjs/package.json +0 -45
- package/packages/solidjs/src/components/ApprovalModal.tsx +0 -18
- package/packages/solidjs/src/components/StepConfigPanel.tsx +0 -14
- package/packages/solidjs/src/components/StepConnector.tsx +0 -11
- package/packages/solidjs/src/components/StepNode.tsx +0 -12
- package/packages/solidjs/src/components/StepPalette.tsx +0 -22
- package/packages/solidjs/src/components/WorkflowCanvas.tsx +0 -23
- package/packages/solidjs/src/components/WorkflowRunPanel.tsx +0 -18
- package/packages/solidjs/src/components/WorkflowToolbar.tsx +0 -13
- package/packages/solidjs/src/components/index.ts +0 -9
- package/packages/solidjs/src/index.ts +0 -7
- package/packages/solidjs/src/pages/WorkflowBuilderPage.tsx +0 -37
- package/packages/solidjs/src/pages/WorkflowRunsPage.tsx +0 -20
- package/packages/solidjs/src/pages/index.ts +0 -3
- package/packages/solidjs/src/primitives/createApprovalGate.ts +0 -29
- package/packages/solidjs/src/primitives/createWorkflow.ts +0 -28
- package/packages/solidjs/src/primitives/createWorkflowBuilder.ts +0 -56
- package/packages/solidjs/src/primitives/createWorkflowRun.ts +0 -32
- package/packages/solidjs/src/primitives/createWorkflowStep.ts +0 -23
- package/packages/solidjs/src/primitives/createWorkflowTemplates.ts +0 -28
- package/packages/solidjs/src/primitives/index.ts +0 -8
- package/packages/solidjs/tsconfig.json +0 -1
- package/packages/solidjs/tsup.config.ts +0 -7
- package/packages/solidjs-css/README.md +0 -1
- package/packages/solidjs-css/package.json +0 -43
- package/packages/solidjs-css/src/components/ApprovalModal.tsx +0 -6
- package/packages/solidjs-css/src/components/StepConfigPanel.tsx +0 -7
- package/packages/solidjs-css/src/components/StepConnector.tsx +0 -6
- package/packages/solidjs-css/src/components/StepNode.tsx +0 -7
- package/packages/solidjs-css/src/components/StepPalette.tsx +0 -7
- package/packages/solidjs-css/src/components/WorkflowCanvas.tsx +0 -7
- package/packages/solidjs-css/src/components/WorkflowRunPanel.tsx +0 -8
- package/packages/solidjs-css/src/components/WorkflowToolbar.tsx +0 -5
- package/packages/solidjs-css/src/components/index.ts +0 -9
- package/packages/solidjs-css/src/index.ts +0 -7
- package/packages/solidjs-css/src/pages/WorkflowBuilderPage.tsx +0 -2
- package/packages/solidjs-css/src/pages/WorkflowRunsPage.tsx +0 -7
- package/packages/solidjs-css/src/pages/index.ts +0 -3
- package/packages/solidjs-css/src/primitives/createWorkflow.ts +0 -28
- package/packages/solidjs-css/src/primitives/createWorkflowBuilder.ts +0 -56
- package/packages/solidjs-css/src/primitives/index.ts +0 -1
- package/packages/solidjs-css/tsconfig.json +0 -27
- package/packages/solidjs-css/tsup.config.ts +0 -2
- package/pnpm-workspace.yaml +0 -2
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
// @geenius-ai-workflow/shared — src/__tests__/ai-workflow.test.ts
|
|
2
|
-
import { describe, it, expect } from 'vitest'
|
|
3
|
-
import { WorkflowEngine } from '../engine'
|
|
4
|
-
import { llmStep, transformStep, conditionStep, linearWorkflow, delayStep } from '../steps'
|
|
5
|
-
import { interpolate, extractVariables, detectCycle, topoSort } from '../utils'
|
|
6
|
-
import { validateWorkflow } from '../validators'
|
|
7
|
-
import { WORKFLOW_TEMPLATES, getTemplate } from '../templates'
|
|
8
|
-
|
|
9
|
-
// ─── WorkflowEngine ───────────────────────────────────────────────────────────
|
|
10
|
-
|
|
11
|
-
describe('WorkflowEngine', () => {
|
|
12
|
-
it('executes a linear workflow', async () => {
|
|
13
|
-
const engine = new WorkflowEngine({
|
|
14
|
-
callLLM: async () => ({ content: 'AI Result' }),
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
const { steps, connections } = linearWorkflow('Test', [
|
|
18
|
-
llmStep('Step 1', { systemPrompt: 'Sys', userPromptTemplate: 'User', outputVar: 'out1' }),
|
|
19
|
-
transformStep('Step 2', { expression: 'out1.toUpperCase()', inputVars: ['out1'], outputVar: 'out2' }),
|
|
20
|
-
])
|
|
21
|
-
|
|
22
|
-
const run = await engine.execute({
|
|
23
|
-
id: 'test', name: 'Test', version: 1, status: 'active',
|
|
24
|
-
steps, connections, createdBy: 'test', createdAt: 0, updatedAt: 0,
|
|
25
|
-
}, {})
|
|
26
|
-
|
|
27
|
-
expect(run.status).toBe('completed')
|
|
28
|
-
expect(run.variables.out1).toBe('AI Result')
|
|
29
|
-
expect(run.variables.out2).toBe('AI RESULT')
|
|
30
|
-
expect(run.stepResults).toHaveLength(2)
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it('handles conditional branching (false path)', async () => {
|
|
34
|
-
const engine = new WorkflowEngine({
|
|
35
|
-
callLLM: async () => ({ content: 'Small' }),
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
const step1 = llmStep('Check', { systemPrompt: '...', userPromptTemplate: '...', outputVar: 'val' })
|
|
39
|
-
// Create branch steps first so we have their IDs
|
|
40
|
-
const bigStep = transformStep('Big Result', { expression: '"BIG"', inputVars: [], outputVar: 'res' })
|
|
41
|
-
const smallStep = transformStep('Small Result', { expression: '"SMALL"', inputVars: [], outputVar: 'res' })
|
|
42
|
-
const cond = conditionStep('Branch', {
|
|
43
|
-
expression: 'val === "Big"',
|
|
44
|
-
trueStepId: bigStep.id,
|
|
45
|
-
falseStepId: smallStep.id,
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
const run = await engine.execute({
|
|
49
|
-
id: 'test', name: 'Test', version: 1, status: 'active',
|
|
50
|
-
steps: [step1, cond, bigStep, smallStep],
|
|
51
|
-
connections: [
|
|
52
|
-
{ fromStepId: step1.id, toStepId: cond.id },
|
|
53
|
-
{ fromStepId: cond.id, toStepId: bigStep.id, label: 'true' },
|
|
54
|
-
{ fromStepId: cond.id, toStepId: smallStep.id, label: 'false' },
|
|
55
|
-
],
|
|
56
|
-
createdBy: 'test', createdAt: 0, updatedAt: 0,
|
|
57
|
-
}, {})
|
|
58
|
-
|
|
59
|
-
expect(run.status).toBe('completed')
|
|
60
|
-
expect(run.variables.res).toBe('SMALL')
|
|
61
|
-
expect(run.stepResults.find(s => s.stepId === bigStep.id)?.status).toBe('skipped')
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
it('fails with invalid workflow (no steps)', async () => {
|
|
65
|
-
const engine = new WorkflowEngine({
|
|
66
|
-
callLLM: async () => ({ content: 'x' }),
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
const run = await engine.execute({
|
|
70
|
-
id: 'empty', name: 'Empty', version: 1, status: 'active',
|
|
71
|
-
steps: [], connections: [], createdBy: 'test', createdAt: 0, updatedAt: 0,
|
|
72
|
-
}, {})
|
|
73
|
-
|
|
74
|
-
// An empty workflow completes immediately with no steps executed
|
|
75
|
-
expect(run.stepResults).toHaveLength(0)
|
|
76
|
-
})
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
// ─── Step Factories ──────────────────────────────────────────────────────────
|
|
80
|
-
|
|
81
|
-
describe('Step factories', () => {
|
|
82
|
-
it('llmStep creates a step with llm-call type', () => {
|
|
83
|
-
const step = llmStep('My LLM', { systemPrompt: 'sys', userPromptTemplate: 'user', outputVar: 'result' })
|
|
84
|
-
expect(step.type).toBe('llm-call')
|
|
85
|
-
expect(step.name).toBe('My LLM')
|
|
86
|
-
expect(typeof step.id).toBe('string')
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
it('transformStep creates a step with transform type', () => {
|
|
90
|
-
const step = transformStep('My Transform', { expression: 'x + 1', inputVars: ['x'], outputVar: 'y' })
|
|
91
|
-
expect(step.type).toBe('transform')
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
it('delayStep creates a step with delay type', () => {
|
|
95
|
-
const step = delayStep('Wait', 1000)
|
|
96
|
-
expect(step.type).toBe('delay')
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
it('linearWorkflow creates sequential connections', () => {
|
|
100
|
-
const s1 = llmStep('A', { systemPrompt: '', userPromptTemplate: '', outputVar: 'a' })
|
|
101
|
-
const s2 = llmStep('B', { systemPrompt: '', userPromptTemplate: '', outputVar: 'b' })
|
|
102
|
-
const { steps, connections } = linearWorkflow('Flow', [s1, s2])
|
|
103
|
-
expect(steps).toHaveLength(2)
|
|
104
|
-
expect(connections).toHaveLength(1)
|
|
105
|
-
expect(connections[0].fromStepId).toBe(s1.id)
|
|
106
|
-
expect(connections[0].toStepId).toBe(s2.id)
|
|
107
|
-
})
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
// ─── Interpolation ───────────────────────────────────────────────────────────
|
|
111
|
-
|
|
112
|
-
describe('interpolate', () => {
|
|
113
|
-
it('replaces {{var}} placeholders', () => {
|
|
114
|
-
const result = interpolate('Hello {{name}}!', { name: 'World' })
|
|
115
|
-
expect(result).toBe('Hello World!')
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
it('supports dot-path access', () => {
|
|
119
|
-
const result = interpolate('{{user.name}}', { user: { name: 'Mehdi' } })
|
|
120
|
-
expect(result).toBe('Mehdi')
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
it('leaves missing vars as empty string', () => {
|
|
124
|
-
const result = interpolate('{{missing}}', {})
|
|
125
|
-
expect(result).not.toContain('{{missing}}')
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
it('extractVariables finds all placeholders', () => {
|
|
129
|
-
const vars = extractVariables('Hello {{name}}, you have {{count}} messages')
|
|
130
|
-
expect(vars).toContain('name')
|
|
131
|
-
expect(vars).toContain('count')
|
|
132
|
-
expect(vars).toHaveLength(2)
|
|
133
|
-
})
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
// ─── DAG utilities ───────────────────────────────────────────────────────────
|
|
137
|
-
|
|
138
|
-
describe('DAG utilities', () => {
|
|
139
|
-
it('detectCycle returns null for a valid DAG', () => {
|
|
140
|
-
const nodeIds = ['a', 'b', 'c']
|
|
141
|
-
const edges = [{ from: 'a', to: 'b' }, { from: 'b', to: 'c' }]
|
|
142
|
-
expect(detectCycle(nodeIds, edges)).toBeNull()
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
it('detectCycle detects a cycle', () => {
|
|
146
|
-
const nodeIds = ['a', 'b', 'c']
|
|
147
|
-
const edges = [{ from: 'a', to: 'b' }, { from: 'b', to: 'c' }, { from: 'c', to: 'a' }]
|
|
148
|
-
expect(detectCycle(nodeIds, edges)).not.toBeNull()
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
it('topoSort returns valid topological order', () => {
|
|
152
|
-
const nodeIds = ['a', 'b', 'c']
|
|
153
|
-
const edges = [{ from: 'a', to: 'b' }, { from: 'b', to: 'c' }]
|
|
154
|
-
const order = topoSort(nodeIds, edges)
|
|
155
|
-
expect(order.indexOf('a')).toBeLessThan(order.indexOf('b'))
|
|
156
|
-
expect(order.indexOf('b')).toBeLessThan(order.indexOf('c'))
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
it('topoSort throws on cyclic graph', () => {
|
|
160
|
-
const nodeIds = ['a', 'b', 'c']
|
|
161
|
-
const edges = [{ from: 'a', to: 'b' }, { from: 'b', to: 'c' }, { from: 'c', to: 'a' }]
|
|
162
|
-
expect(() => topoSort(nodeIds, edges)).toThrow()
|
|
163
|
-
})
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
// ─── Validators ──────────────────────────────────────────────────────────────
|
|
167
|
-
|
|
168
|
-
describe('validateWorkflow', () => {
|
|
169
|
-
it('validates a workflow with a valid step', () => {
|
|
170
|
-
const step = llmStep('My Step', { systemPrompt: 'sys', userPromptTemplate: 'user', outputVar: 'result' })
|
|
171
|
-
const wf = {
|
|
172
|
-
id: 'test',
|
|
173
|
-
name: 'Test Workflow',
|
|
174
|
-
version: 1,
|
|
175
|
-
status: 'active' as const,
|
|
176
|
-
steps: [step],
|
|
177
|
-
connections: [],
|
|
178
|
-
createdBy: 'user',
|
|
179
|
-
createdAt: Date.now(),
|
|
180
|
-
updatedAt: Date.now(),
|
|
181
|
-
}
|
|
182
|
-
const result = validateWorkflow(wf)
|
|
183
|
-
expect(result.success).toBe(true)
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
it('rejects a workflow with no steps', () => {
|
|
187
|
-
const wf = {
|
|
188
|
-
id: 'empty',
|
|
189
|
-
name: 'Empty Workflow',
|
|
190
|
-
version: 1,
|
|
191
|
-
status: 'active' as const,
|
|
192
|
-
steps: [],
|
|
193
|
-
connections: [],
|
|
194
|
-
createdBy: 'user',
|
|
195
|
-
createdAt: Date.now(),
|
|
196
|
-
updatedAt: Date.now(),
|
|
197
|
-
}
|
|
198
|
-
const result = validateWorkflow(wf)
|
|
199
|
-
expect(result.success).toBe(false)
|
|
200
|
-
})
|
|
201
|
-
})
|
|
202
|
-
|
|
203
|
-
// ─── Templates ───────────────────────────────────────────────────────────────
|
|
204
|
-
|
|
205
|
-
describe('WORKFLOW_TEMPLATES', () => {
|
|
206
|
-
it('is a non-empty array', () => {
|
|
207
|
-
expect(Array.isArray(WORKFLOW_TEMPLATES)).toBe(true)
|
|
208
|
-
expect(WORKFLOW_TEMPLATES.length).toBeGreaterThan(0)
|
|
209
|
-
})
|
|
210
|
-
|
|
211
|
-
it('getTemplate returns a template by id', () => {
|
|
212
|
-
const first = WORKFLOW_TEMPLATES[0]
|
|
213
|
-
const found = getTemplate(first.id)
|
|
214
|
-
expect(found).toBeDefined()
|
|
215
|
-
expect(found?.id).toBe(first.id)
|
|
216
|
-
})
|
|
217
|
-
})
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Configuration factory for Geenius AI Workflow
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export interface ConfigureWorkflowOptions {
|
|
6
|
-
maxSteps?: number
|
|
7
|
-
maxExecutionTimeMs?: number
|
|
8
|
-
maxRetries?: number
|
|
9
|
-
defaultTimeout?: number
|
|
10
|
-
enableLogging?: boolean
|
|
11
|
-
enableMetrics?: boolean
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Configure the workflow system with execution options
|
|
16
|
-
* @param options Configuration options for workflows
|
|
17
|
-
* @returns WorkflowConfig object ready for use
|
|
18
|
-
* @example
|
|
19
|
-
* ```ts
|
|
20
|
-
* const config = configureWorkflow({
|
|
21
|
-
* maxSteps: 50,
|
|
22
|
-
* maxExecutionTimeMs: 3600000,
|
|
23
|
-
* maxRetries: 3,
|
|
24
|
-
* enableMetrics: true,
|
|
25
|
-
* })
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
28
|
-
export function configureWorkflow(options: ConfigureWorkflowOptions = {}) {
|
|
29
|
-
return {
|
|
30
|
-
maxSteps: options.maxSteps ?? 100,
|
|
31
|
-
maxExecutionTimeMs: options.maxExecutionTimeMs ?? 3600000, // 1 hour
|
|
32
|
-
maxRetries: options.maxRetries ?? 3,
|
|
33
|
-
defaultTimeout: options.defaultTimeout ?? 30000, // 30 seconds
|
|
34
|
-
enableLogging: options.enableLogging ?? true,
|
|
35
|
-
enableMetrics: options.enableMetrics ?? false,
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Default workflow configuration
|
|
41
|
-
*/
|
|
42
|
-
export const defaultWorkflowConfig = {
|
|
43
|
-
maxSteps: 100,
|
|
44
|
-
maxExecutionTimeMs: 3600000,
|
|
45
|
-
maxRetries: 3,
|
|
46
|
-
defaultTimeout: 30000,
|
|
47
|
-
enableLogging: true,
|
|
48
|
-
enableMetrics: false,
|
|
49
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
// @geenius-ai-workflow/shared — src/convex/schemas.ts
|
|
2
|
-
export const workflowsTable = {
|
|
3
|
-
name: { type: 'string' as const },
|
|
4
|
-
description: { type: 'string' as const, optional: true as const },
|
|
5
|
-
version: { type: 'number' as const },
|
|
6
|
-
status: { type: 'string' as const },
|
|
7
|
-
definitionJson: { type: 'string' as const },
|
|
8
|
-
createdBy: { type: 'string' as const },
|
|
9
|
-
tags: { type: 'string' as const, optional: true as const },
|
|
10
|
-
createdAt: { type: 'number' as const },
|
|
11
|
-
updatedAt: { type: 'number' as const },
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const workflowRunsTable = {
|
|
15
|
-
workflowId: { type: 'string' as const },
|
|
16
|
-
workflowVersion: { type: 'number' as const },
|
|
17
|
-
status: { type: 'string' as const },
|
|
18
|
-
inputJson: { type: 'string' as const },
|
|
19
|
-
variablesJson: { type: 'string' as const },
|
|
20
|
-
currentStepIndex: { type: 'number' as const },
|
|
21
|
-
error: { type: 'string' as const, optional: true as const },
|
|
22
|
-
triggeredBy: { type: 'string' as const },
|
|
23
|
-
startedAt: { type: 'number' as const },
|
|
24
|
-
completedAt: { type: 'number' as const, optional: true as const },
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export const workflowStepResultsTable = {
|
|
28
|
-
runId: { type: 'string' as const },
|
|
29
|
-
stepId: { type: 'string' as const },
|
|
30
|
-
stepName: { type: 'string' as const },
|
|
31
|
-
type: { type: 'string' as const },
|
|
32
|
-
status: { type: 'string' as const },
|
|
33
|
-
outputJson: { type: 'string' as const, optional: true as const },
|
|
34
|
-
error: { type: 'string' as const, optional: true as const },
|
|
35
|
-
durationMs: { type: 'number' as const },
|
|
36
|
-
tokens: { type: 'number' as const, optional: true as const },
|
|
37
|
-
costUsd: { type: 'number' as const, optional: true as const },
|
|
38
|
-
startedAt: { type: 'number' as const },
|
|
39
|
-
completedAt: { type: 'number' as const, optional: true as const },
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export const workflowTables = { workflows: workflowsTable, workflowRuns: workflowRunsTable, workflowStepResults: workflowStepResultsTable }
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
// This file has been superseded by src/__tests__/ai-workflow.test.ts — please use that file instead.
|
|
@@ -1,295 +0,0 @@
|
|
|
1
|
-
// @geenius-ai-workflow/shared — src/engine.ts
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Workflow execution engine — DAG-based step execution with variable passing.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type {
|
|
8
|
-
WorkflowDefinition,
|
|
9
|
-
WorkflowStepDef,
|
|
10
|
-
WorkflowRun,
|
|
11
|
-
StepResult,
|
|
12
|
-
RunStatus,
|
|
13
|
-
StepStatus,
|
|
14
|
-
LLMCallConfig,
|
|
15
|
-
TransformConfig,
|
|
16
|
-
ConditionConfig,
|
|
17
|
-
DelayConfig,
|
|
18
|
-
} from './types'
|
|
19
|
-
|
|
20
|
-
export interface WorkflowEngineOptions {
|
|
21
|
-
/** LLM call function for llm-call steps */
|
|
22
|
-
callLLM?: (systemPrompt: string, userPrompt: string, model?: string) => Promise<{
|
|
23
|
-
content: string; tokens?: number; costUsd?: number
|
|
24
|
-
}>
|
|
25
|
-
/** Webhook call function */
|
|
26
|
-
callWebhook?: (url: string, method: string, body?: string, headers?: Record<string, string>) => Promise<string>
|
|
27
|
-
/** Human approval callback */
|
|
28
|
-
onApprovalRequired?: (message: string, stepId: string) => Promise<boolean>
|
|
29
|
-
/** Step event callback */
|
|
30
|
-
onStepComplete?: (result: StepResult) => void
|
|
31
|
-
/** Custom step handlers */
|
|
32
|
-
customHandlers?: Record<string, (params: Record<string, unknown>, vars: Record<string, unknown>) => Promise<unknown>>
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export class WorkflowEngine {
|
|
36
|
-
private options: WorkflowEngineOptions
|
|
37
|
-
private cancelled = false
|
|
38
|
-
|
|
39
|
-
constructor(options: WorkflowEngineOptions) {
|
|
40
|
-
this.options = options
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
cancel(): void {
|
|
44
|
-
this.cancelled = true
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async execute(
|
|
48
|
-
definition: WorkflowDefinition,
|
|
49
|
-
input: Record<string, unknown> = {},
|
|
50
|
-
): Promise<WorkflowRun> {
|
|
51
|
-
this.cancelled = false
|
|
52
|
-
|
|
53
|
-
const run: WorkflowRun = {
|
|
54
|
-
id: crypto.randomUUID?.() ?? String(Date.now()),
|
|
55
|
-
workflowId: definition.id,
|
|
56
|
-
workflowVersion: definition.version,
|
|
57
|
-
status: 'running',
|
|
58
|
-
input,
|
|
59
|
-
variables: { ...definition.variables, ...input },
|
|
60
|
-
stepResults: [],
|
|
61
|
-
currentStepIndex: 0,
|
|
62
|
-
triggeredBy: '',
|
|
63
|
-
startedAt: Date.now(),
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Topologically sort steps based on connections
|
|
67
|
-
const sortedSteps = this.topoSort(definition)
|
|
68
|
-
|
|
69
|
-
for (let i = 0; i < sortedSteps.length; i++) {
|
|
70
|
-
if (this.cancelled) {
|
|
71
|
-
run.status = 'cancelled'
|
|
72
|
-
break
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
run.currentStepIndex = i
|
|
76
|
-
const stepDef = sortedSteps[i]
|
|
77
|
-
const result = await this.executeStep(stepDef, run.variables, definition)
|
|
78
|
-
|
|
79
|
-
run.stepResults.push(result)
|
|
80
|
-
this.options.onStepComplete?.(result)
|
|
81
|
-
|
|
82
|
-
if (result.status === 'failed' && !stepDef.optional) {
|
|
83
|
-
run.status = 'failed'
|
|
84
|
-
run.error = result.error
|
|
85
|
-
break
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Store output in variables
|
|
89
|
-
if (result.output !== undefined && result.output !== null) {
|
|
90
|
-
const config = stepDef.config as { outputVar?: string }
|
|
91
|
-
if (config.outputVar) {
|
|
92
|
-
run.variables[config.outputVar] = result.output
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Handle condition branching
|
|
97
|
-
if (stepDef.config.type === 'condition') {
|
|
98
|
-
const condConfig = stepDef.config as ConditionConfig
|
|
99
|
-
const condResult = result.output as boolean
|
|
100
|
-
const nextStepId = condResult ? condConfig.trueStepId : condConfig.falseStepId
|
|
101
|
-
|
|
102
|
-
// Skip to the target step
|
|
103
|
-
const targetIndex = sortedSteps.findIndex(s => s.id === nextStepId)
|
|
104
|
-
if (targetIndex > i) {
|
|
105
|
-
// Mark skipped steps
|
|
106
|
-
for (let j = i + 1; j < targetIndex; j++) {
|
|
107
|
-
run.stepResults.push({
|
|
108
|
-
stepId: sortedSteps[j].id,
|
|
109
|
-
stepName: sortedSteps[j].name,
|
|
110
|
-
type: sortedSteps[j].type,
|
|
111
|
-
status: 'skipped',
|
|
112
|
-
durationMs: 0,
|
|
113
|
-
startedAt: Date.now(),
|
|
114
|
-
})
|
|
115
|
-
}
|
|
116
|
-
i = targetIndex - 1 // Will be incremented by loop
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (run.status === 'running') {
|
|
122
|
-
run.status = 'completed'
|
|
123
|
-
}
|
|
124
|
-
run.completedAt = Date.now()
|
|
125
|
-
|
|
126
|
-
return run
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
private async executeStep(
|
|
130
|
-
stepDef: WorkflowStepDef,
|
|
131
|
-
variables: Record<string, unknown>,
|
|
132
|
-
_definition: WorkflowDefinition,
|
|
133
|
-
): Promise<StepResult> {
|
|
134
|
-
const start = Date.now()
|
|
135
|
-
const baseResult: StepResult = {
|
|
136
|
-
stepId: stepDef.id,
|
|
137
|
-
stepName: stepDef.name,
|
|
138
|
-
type: stepDef.type,
|
|
139
|
-
status: 'running',
|
|
140
|
-
durationMs: 0,
|
|
141
|
-
startedAt: start,
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
let attempts = 0
|
|
145
|
-
const maxAttempts = stepDef.retries?.maxAttempts ?? 1
|
|
146
|
-
|
|
147
|
-
while (attempts < maxAttempts) {
|
|
148
|
-
attempts++
|
|
149
|
-
try {
|
|
150
|
-
const output = await this.runStepLogic(stepDef, variables)
|
|
151
|
-
return {
|
|
152
|
-
...baseResult,
|
|
153
|
-
status: 'completed',
|
|
154
|
-
output,
|
|
155
|
-
durationMs: Date.now() - start,
|
|
156
|
-
completedAt: Date.now(),
|
|
157
|
-
}
|
|
158
|
-
} catch (err) {
|
|
159
|
-
if (attempts >= maxAttempts) {
|
|
160
|
-
return {
|
|
161
|
-
...baseResult,
|
|
162
|
-
status: 'failed',
|
|
163
|
-
error: err instanceof Error ? err.message : String(err),
|
|
164
|
-
durationMs: Date.now() - start,
|
|
165
|
-
completedAt: Date.now(),
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
// Wait before retry
|
|
169
|
-
const backoff = stepDef.retries?.backoffMs ?? 1000
|
|
170
|
-
await new Promise(r => setTimeout(r, backoff * attempts))
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return { ...baseResult, status: 'failed', error: 'Max retries exceeded', durationMs: Date.now() - start }
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
private async runStepLogic(
|
|
178
|
-
stepDef: WorkflowStepDef,
|
|
179
|
-
variables: Record<string, unknown>,
|
|
180
|
-
): Promise<unknown> {
|
|
181
|
-
switch (stepDef.config.type) {
|
|
182
|
-
case 'llm-call': {
|
|
183
|
-
if (!this.options.callLLM) throw new Error('No LLM function provided')
|
|
184
|
-
const config = stepDef.config as LLMCallConfig
|
|
185
|
-
const userPrompt = this.interpolate(config.userPromptTemplate, variables)
|
|
186
|
-
const result = await this.options.callLLM(config.systemPrompt, userPrompt, config.model)
|
|
187
|
-
if (config.parseJson) {
|
|
188
|
-
try { return JSON.parse(result.content) }
|
|
189
|
-
catch { return result.content }
|
|
190
|
-
}
|
|
191
|
-
return result.content
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
case 'transform': {
|
|
195
|
-
const config = stepDef.config as TransformConfig
|
|
196
|
-
// Simple expression evaluation with variable access
|
|
197
|
-
const varEntries = config.inputVars.map(v => [v, variables[v]])
|
|
198
|
-
const fn = new Function(
|
|
199
|
-
...config.inputVars,
|
|
200
|
-
`"use strict"; return (${config.expression})`
|
|
201
|
-
)
|
|
202
|
-
return fn(...varEntries.map(([_, v]) => v))
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
case 'condition': {
|
|
206
|
-
const config = stepDef.config as ConditionConfig
|
|
207
|
-
const fn = new Function(
|
|
208
|
-
'vars',
|
|
209
|
-
`"use strict"; with(vars) { return Boolean(${config.expression}) }`
|
|
210
|
-
)
|
|
211
|
-
return fn(variables)
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
case 'human-approval': {
|
|
215
|
-
if (!this.options.onApprovalRequired) {
|
|
216
|
-
// Auto-approve if no handler
|
|
217
|
-
return true
|
|
218
|
-
}
|
|
219
|
-
const config = stepDef.config
|
|
220
|
-
return this.options.onApprovalRequired(
|
|
221
|
-
(config as { message: string }).message,
|
|
222
|
-
stepDef.id,
|
|
223
|
-
)
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
case 'webhook': {
|
|
227
|
-
if (!this.options.callWebhook) throw new Error('No webhook function provided')
|
|
228
|
-
const config = stepDef.config as { url: string; method: string; bodyTemplate?: string; headers?: Record<string, string> }
|
|
229
|
-
const body = config.bodyTemplate ? this.interpolate(config.bodyTemplate, variables) : undefined
|
|
230
|
-
return this.options.callWebhook(config.url, config.method, body, config.headers)
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
case 'delay': {
|
|
234
|
-
const config = stepDef.config as DelayConfig
|
|
235
|
-
await new Promise(r => setTimeout(r, config.durationMs))
|
|
236
|
-
return null
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
case 'custom': {
|
|
240
|
-
const config = stepDef.config as { handler: string; params: Record<string, unknown> }
|
|
241
|
-
const handler = this.options.customHandlers?.[config.handler]
|
|
242
|
-
if (!handler) throw new Error(`No handler for custom step: ${config.handler}`)
|
|
243
|
-
return handler(config.params, variables)
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
default:
|
|
247
|
-
throw new Error(`Unsupported step type: ${stepDef.config.type}`)
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/** Interpolate {{variable}} references in a string */
|
|
252
|
-
private interpolate(template: string, vars: Record<string, unknown>): string {
|
|
253
|
-
return template.replace(/\{\{(\w+)\}\}/g, (_, key) => {
|
|
254
|
-
const val = vars[key]
|
|
255
|
-
return val !== undefined ? String(val) : `{{${key}}}`
|
|
256
|
-
})
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/** Topological sort of steps based on connections */
|
|
260
|
-
private topoSort(def: WorkflowDefinition): WorkflowStepDef[] {
|
|
261
|
-
const stepMap = new Map(def.steps.map(s => [s.id, s]))
|
|
262
|
-
const inDegree = new Map<string, number>()
|
|
263
|
-
const adjacency = new Map<string, string[]>()
|
|
264
|
-
|
|
265
|
-
for (const step of def.steps) {
|
|
266
|
-
inDegree.set(step.id, 0)
|
|
267
|
-
adjacency.set(step.id, [])
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
for (const conn of def.connections) {
|
|
271
|
-
adjacency.get(conn.fromStepId)?.push(conn.toStepId)
|
|
272
|
-
inDegree.set(conn.toStepId, (inDegree.get(conn.toStepId) ?? 0) + 1)
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const queue: string[] = []
|
|
276
|
-
for (const [id, degree] of inDegree) {
|
|
277
|
-
if (degree === 0) queue.push(id)
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
const sorted: WorkflowStepDef[] = []
|
|
281
|
-
while (queue.length > 0) {
|
|
282
|
-
const id = queue.shift()!
|
|
283
|
-
const step = stepMap.get(id)
|
|
284
|
-
if (step) sorted.push(step)
|
|
285
|
-
|
|
286
|
-
for (const neighbor of adjacency.get(id) ?? []) {
|
|
287
|
-
const newDegree = (inDegree.get(neighbor) ?? 1) - 1
|
|
288
|
-
inDegree.set(neighbor, newDegree)
|
|
289
|
-
if (newDegree === 0) queue.push(neighbor)
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
return sorted
|
|
294
|
-
}
|
|
295
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
// @geenius-ai-workflow/shared — src/index.ts
|
|
2
|
-
|
|
3
|
-
// Config
|
|
4
|
-
export { configureWorkflow, defaultWorkflowConfig } from './config'
|
|
5
|
-
export type { ConfigureWorkflowOptions } from './config'
|
|
6
|
-
|
|
7
|
-
// Types
|
|
8
|
-
export type {
|
|
9
|
-
WorkflowStatus, RunStatus, StepStatus, StepType,
|
|
10
|
-
WorkflowDefinition, WorkflowStepDef, StepConfig,
|
|
11
|
-
LLMCallConfig, TransformConfig, ConditionConfig, HumanApprovalConfig,
|
|
12
|
-
WebhookConfig, DelayConfig, ParallelConfig, LoopConfig, SubWorkflowConfig, CustomStepConfig,
|
|
13
|
-
StepConnection, WorkflowVariable, WorkflowRun, StepResult, WorkflowBuilderState,
|
|
14
|
-
} from './types'
|
|
15
|
-
|
|
16
|
-
// Engine
|
|
17
|
-
export { WorkflowEngine } from './engine'
|
|
18
|
-
export type { WorkflowEngineOptions } from './engine'
|
|
19
|
-
|
|
20
|
-
// Step factories
|
|
21
|
-
export { llmStep, transformStep, conditionStep, approvalStep, webhookStep, delayStep, connect, linearWorkflow } from './steps'
|
|
22
|
-
|
|
23
|
-
// Validators
|
|
24
|
-
export {
|
|
25
|
-
workflowDefinitionSchema, workflowStepDefSchema, stepConfigSchema, stepConnectionSchema,
|
|
26
|
-
workflowRunSchema, stepResultSchema, workflowVariableSchema,
|
|
27
|
-
validateWorkflow, validateRun, validateConnectionIntegrity,
|
|
28
|
-
} from './validators'
|
|
29
|
-
|
|
30
|
-
// Templates
|
|
31
|
-
export { WORKFLOW_TEMPLATES, getTemplate, getTemplatesByCategory } from './templates'
|
|
32
|
-
export type { WorkflowTemplate } from './templates'
|
|
33
|
-
|
|
34
|
-
// Utilities
|
|
35
|
-
export {
|
|
36
|
-
interpolate, extractVariables, findMissingVariables,
|
|
37
|
-
topoSort, detectCycle, findPaths, findEntryNodes, findExitNodes,
|
|
38
|
-
estimateTokens, estimateStepTokens, estimateStepCost, estimateWorkflowCost, calculateRunCost, formatCost, MODEL_RATES,
|
|
39
|
-
} from './utils'
|
|
40
|
-
export type { DAGNode, DAGEdge } from './utils'
|
|
41
|
-
|
|
42
|
-
// Convex schemas
|
|
43
|
-
export { workflowTables, workflowsTable, workflowRunsTable, workflowStepResultsTable } from './convex/index'
|