@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.
Files changed (159) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +69 -1
  3. package/package.json +60 -27
  4. package/packages/convex/dist/index.d.ts +363 -0
  5. package/packages/convex/dist/index.js +200 -0
  6. package/packages/convex/dist/index.js.map +1 -0
  7. package/packages/react/dist/index.d.ts +466 -0
  8. package/packages/react/dist/index.js +13914 -0
  9. package/packages/react/dist/index.js.map +1 -0
  10. package/packages/react-css/{src/styles.css → dist/index.css} +107 -253
  11. package/packages/react-css/dist/index.css.map +1 -0
  12. package/packages/react-css/dist/index.d.ts +495 -0
  13. package/packages/react-css/dist/index.js +13901 -0
  14. package/packages/react-css/dist/index.js.map +1 -0
  15. package/packages/shared/dist/index.d.ts +1368 -0
  16. package/packages/shared/dist/index.js +1681 -0
  17. package/packages/shared/dist/index.js.map +1 -0
  18. package/packages/solidjs/dist/index.d.ts +452 -0
  19. package/packages/solidjs/dist/index.js +13830 -0
  20. package/packages/solidjs/dist/index.js.map +1 -0
  21. package/packages/solidjs-css/{src/styles.css → dist/index.css} +107 -253
  22. package/packages/solidjs-css/dist/index.css.map +1 -0
  23. package/packages/solidjs-css/dist/index.d.ts +471 -0
  24. package/packages/solidjs-css/dist/index.js +13774 -0
  25. package/packages/solidjs-css/dist/index.js.map +1 -0
  26. package/.changeset/config.json +0 -11
  27. package/.github/CODEOWNERS +0 -1
  28. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -16
  29. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -11
  30. package/.github/PULL_REQUEST_TEMPLATE.md +0 -10
  31. package/.github/dependabot.yml +0 -11
  32. package/.github/workflows/ci.yml +0 -23
  33. package/.github/workflows/release.yml +0 -29
  34. package/.nvmrc +0 -1
  35. package/.project/ACCOUNT.yaml +0 -4
  36. package/.project/IDEAS.yaml +0 -7
  37. package/.project/PROJECT.yaml +0 -11
  38. package/.project/ROADMAP.yaml +0 -15
  39. package/CODE_OF_CONDUCT.md +0 -16
  40. package/CONTRIBUTING.md +0 -26
  41. package/SECURITY.md +0 -15
  42. package/SUPPORT.md +0 -8
  43. package/packages/convex/README.md +0 -1
  44. package/packages/convex/package.json +0 -12
  45. package/packages/convex/src/convex.config.ts +0 -3
  46. package/packages/convex/src/index.ts +0 -3
  47. package/packages/convex/src/mutations.ts +0 -36
  48. package/packages/convex/src/queries.ts +0 -19
  49. package/packages/convex/src/schema.ts +0 -24
  50. package/packages/convex/tsconfig.json +0 -25
  51. package/packages/react/README.md +0 -1
  52. package/packages/react/package.json +0 -46
  53. package/packages/react/src/components/ApprovalModal.tsx +0 -47
  54. package/packages/react/src/components/StepConfigPanel.tsx +0 -67
  55. package/packages/react/src/components/StepConnector.tsx +0 -47
  56. package/packages/react/src/components/StepNode.tsx +0 -38
  57. package/packages/react/src/components/StepPalette.tsx +0 -48
  58. package/packages/react/src/components/WorkflowCanvas.tsx +0 -42
  59. package/packages/react/src/components/WorkflowRunPanel.tsx +0 -64
  60. package/packages/react/src/components/WorkflowToolbar.tsx +0 -43
  61. package/packages/react/src/components/index.ts +0 -9
  62. package/packages/react/src/hooks/index.ts +0 -10
  63. package/packages/react/src/hooks/useApprovalGate.ts +0 -59
  64. package/packages/react/src/hooks/useWorkflow.ts +0 -39
  65. package/packages/react/src/hooks/useWorkflowBuilder.ts +0 -121
  66. package/packages/react/src/hooks/useWorkflowRun.ts +0 -75
  67. package/packages/react/src/hooks/useWorkflowStep.ts +0 -52
  68. package/packages/react/src/hooks/useWorkflowTemplates.ts +0 -54
  69. package/packages/react/src/index.ts +0 -16
  70. package/packages/react/src/pages/WorkflowBuilderPage.tsx +0 -81
  71. package/packages/react/src/pages/WorkflowRunsPage.tsx +0 -59
  72. package/packages/react/src/pages/index.ts +0 -3
  73. package/packages/react/tsconfig.json +0 -1
  74. package/packages/react/tsup.config.ts +0 -7
  75. package/packages/react-css/README.md +0 -1
  76. package/packages/react-css/package.json +0 -44
  77. package/packages/react-css/src/components/ApprovalModal.tsx +0 -6
  78. package/packages/react-css/src/components/StepConfigPanel.tsx +0 -7
  79. package/packages/react-css/src/components/StepConnector.tsx +0 -6
  80. package/packages/react-css/src/components/StepNode.tsx +0 -7
  81. package/packages/react-css/src/components/StepPalette.tsx +0 -6
  82. package/packages/react-css/src/components/WorkflowCanvas.tsx +0 -6
  83. package/packages/react-css/src/components/WorkflowRunPanel.tsx +0 -9
  84. package/packages/react-css/src/components/WorkflowToolbar.tsx +0 -4
  85. package/packages/react-css/src/components/index.ts +0 -9
  86. package/packages/react-css/src/hooks/index.ts +0 -3
  87. package/packages/react-css/src/hooks/useWorkflow.ts +0 -39
  88. package/packages/react-css/src/hooks/useWorkflowBuilder.ts +0 -121
  89. package/packages/react-css/src/index.ts +0 -7
  90. package/packages/react-css/src/pages/WorkflowBuilderPage.tsx +0 -16
  91. package/packages/react-css/src/pages/WorkflowRunsPage.tsx +0 -6
  92. package/packages/react-css/src/pages/index.ts +0 -3
  93. package/packages/react-css/tsconfig.json +0 -26
  94. package/packages/react-css/tsup.config.ts +0 -2
  95. package/packages/shared/README.md +0 -1
  96. package/packages/shared/package.json +0 -56
  97. package/packages/shared/src/__tests__/ai-workflow.test.ts +0 -217
  98. package/packages/shared/src/config.ts +0 -49
  99. package/packages/shared/src/convex/index.ts +0 -2
  100. package/packages/shared/src/convex/schemas.ts +0 -42
  101. package/packages/shared/src/engine.test.ts +0 -1
  102. package/packages/shared/src/engine.ts +0 -295
  103. package/packages/shared/src/index.ts +0 -43
  104. package/packages/shared/src/steps.ts +0 -68
  105. package/packages/shared/src/templates.ts +0 -172
  106. package/packages/shared/src/types.ts +0 -237
  107. package/packages/shared/src/utils/cost.ts +0 -79
  108. package/packages/shared/src/utils/dag.ts +0 -133
  109. package/packages/shared/src/utils/index.ts +0 -5
  110. package/packages/shared/src/utils/interpolation.ts +0 -53
  111. package/packages/shared/src/validators.ts +0 -215
  112. package/packages/shared/tsconfig.json +0 -1
  113. package/packages/shared/tsup.config.ts +0 -5
  114. package/packages/shared/vitest.config.ts +0 -4
  115. package/packages/solidjs/README.md +0 -1
  116. package/packages/solidjs/package.json +0 -45
  117. package/packages/solidjs/src/components/ApprovalModal.tsx +0 -18
  118. package/packages/solidjs/src/components/StepConfigPanel.tsx +0 -14
  119. package/packages/solidjs/src/components/StepConnector.tsx +0 -11
  120. package/packages/solidjs/src/components/StepNode.tsx +0 -12
  121. package/packages/solidjs/src/components/StepPalette.tsx +0 -22
  122. package/packages/solidjs/src/components/WorkflowCanvas.tsx +0 -23
  123. package/packages/solidjs/src/components/WorkflowRunPanel.tsx +0 -18
  124. package/packages/solidjs/src/components/WorkflowToolbar.tsx +0 -13
  125. package/packages/solidjs/src/components/index.ts +0 -9
  126. package/packages/solidjs/src/index.ts +0 -7
  127. package/packages/solidjs/src/pages/WorkflowBuilderPage.tsx +0 -37
  128. package/packages/solidjs/src/pages/WorkflowRunsPage.tsx +0 -20
  129. package/packages/solidjs/src/pages/index.ts +0 -3
  130. package/packages/solidjs/src/primitives/createApprovalGate.ts +0 -29
  131. package/packages/solidjs/src/primitives/createWorkflow.ts +0 -28
  132. package/packages/solidjs/src/primitives/createWorkflowBuilder.ts +0 -56
  133. package/packages/solidjs/src/primitives/createWorkflowRun.ts +0 -32
  134. package/packages/solidjs/src/primitives/createWorkflowStep.ts +0 -23
  135. package/packages/solidjs/src/primitives/createWorkflowTemplates.ts +0 -28
  136. package/packages/solidjs/src/primitives/index.ts +0 -8
  137. package/packages/solidjs/tsconfig.json +0 -1
  138. package/packages/solidjs/tsup.config.ts +0 -7
  139. package/packages/solidjs-css/README.md +0 -1
  140. package/packages/solidjs-css/package.json +0 -43
  141. package/packages/solidjs-css/src/components/ApprovalModal.tsx +0 -6
  142. package/packages/solidjs-css/src/components/StepConfigPanel.tsx +0 -7
  143. package/packages/solidjs-css/src/components/StepConnector.tsx +0 -6
  144. package/packages/solidjs-css/src/components/StepNode.tsx +0 -7
  145. package/packages/solidjs-css/src/components/StepPalette.tsx +0 -7
  146. package/packages/solidjs-css/src/components/WorkflowCanvas.tsx +0 -7
  147. package/packages/solidjs-css/src/components/WorkflowRunPanel.tsx +0 -8
  148. package/packages/solidjs-css/src/components/WorkflowToolbar.tsx +0 -5
  149. package/packages/solidjs-css/src/components/index.ts +0 -9
  150. package/packages/solidjs-css/src/index.ts +0 -7
  151. package/packages/solidjs-css/src/pages/WorkflowBuilderPage.tsx +0 -2
  152. package/packages/solidjs-css/src/pages/WorkflowRunsPage.tsx +0 -7
  153. package/packages/solidjs-css/src/pages/index.ts +0 -3
  154. package/packages/solidjs-css/src/primitives/createWorkflow.ts +0 -28
  155. package/packages/solidjs-css/src/primitives/createWorkflowBuilder.ts +0 -56
  156. package/packages/solidjs-css/src/primitives/index.ts +0 -1
  157. package/packages/solidjs-css/tsconfig.json +0 -27
  158. package/packages/solidjs-css/tsup.config.ts +0 -2
  159. 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,2 +0,0 @@
1
- // @geenius-ai-workflow/shared — src/convex/index.ts
2
- export { workflowTables, workflowsTable, workflowRunsTable, workflowStepResultsTable } from './schemas'
@@ -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'