@geenius/ai-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.
Files changed (140) hide show
  1. package/.changeset/config.json +11 -0
  2. package/.github/CODEOWNERS +1 -0
  3. package/.github/ISSUE_TEMPLATE/bug_report.md +16 -0
  4. package/.github/ISSUE_TEMPLATE/feature_request.md +11 -0
  5. package/.github/PULL_REQUEST_TEMPLATE.md +10 -0
  6. package/.github/dependabot.yml +11 -0
  7. package/.github/workflows/ci.yml +23 -0
  8. package/.github/workflows/release.yml +29 -0
  9. package/.nvmrc +1 -0
  10. package/.project/ACCOUNT.yaml +4 -0
  11. package/.project/IDEAS.yaml +7 -0
  12. package/.project/PROJECT.yaml +11 -0
  13. package/.project/ROADMAP.yaml +15 -0
  14. package/CHANGELOG.md +11 -0
  15. package/CODE_OF_CONDUCT.md +16 -0
  16. package/CONTRIBUTING.md +26 -0
  17. package/LICENSE +21 -0
  18. package/README.md +1 -0
  19. package/SECURITY.md +15 -0
  20. package/SUPPORT.md +8 -0
  21. package/package.json +74 -0
  22. package/packages/convex/README.md +1 -0
  23. package/packages/convex/package.json +12 -0
  24. package/packages/convex/src/convex.config.ts +3 -0
  25. package/packages/convex/src/index.ts +3 -0
  26. package/packages/convex/src/mutations.ts +36 -0
  27. package/packages/convex/src/queries.ts +19 -0
  28. package/packages/convex/src/schema.ts +24 -0
  29. package/packages/convex/tsconfig.json +25 -0
  30. package/packages/react/README.md +1 -0
  31. package/packages/react/package.json +46 -0
  32. package/packages/react/src/components/ApprovalModal.tsx +47 -0
  33. package/packages/react/src/components/StepConfigPanel.tsx +67 -0
  34. package/packages/react/src/components/StepConnector.tsx +47 -0
  35. package/packages/react/src/components/StepNode.tsx +38 -0
  36. package/packages/react/src/components/StepPalette.tsx +48 -0
  37. package/packages/react/src/components/WorkflowCanvas.tsx +42 -0
  38. package/packages/react/src/components/WorkflowRunPanel.tsx +64 -0
  39. package/packages/react/src/components/WorkflowToolbar.tsx +43 -0
  40. package/packages/react/src/components/index.ts +9 -0
  41. package/packages/react/src/hooks/index.ts +10 -0
  42. package/packages/react/src/hooks/useApprovalGate.ts +59 -0
  43. package/packages/react/src/hooks/useWorkflow.ts +39 -0
  44. package/packages/react/src/hooks/useWorkflowBuilder.ts +121 -0
  45. package/packages/react/src/hooks/useWorkflowRun.ts +75 -0
  46. package/packages/react/src/hooks/useWorkflowStep.ts +52 -0
  47. package/packages/react/src/hooks/useWorkflowTemplates.ts +54 -0
  48. package/packages/react/src/index.ts +16 -0
  49. package/packages/react/src/pages/WorkflowBuilderPage.tsx +81 -0
  50. package/packages/react/src/pages/WorkflowRunsPage.tsx +59 -0
  51. package/packages/react/src/pages/index.ts +3 -0
  52. package/packages/react/tsconfig.json +1 -0
  53. package/packages/react/tsup.config.ts +7 -0
  54. package/packages/react-css/README.md +1 -0
  55. package/packages/react-css/package.json +44 -0
  56. package/packages/react-css/src/components/ApprovalModal.tsx +6 -0
  57. package/packages/react-css/src/components/StepConfigPanel.tsx +7 -0
  58. package/packages/react-css/src/components/StepConnector.tsx +6 -0
  59. package/packages/react-css/src/components/StepNode.tsx +7 -0
  60. package/packages/react-css/src/components/StepPalette.tsx +6 -0
  61. package/packages/react-css/src/components/WorkflowCanvas.tsx +6 -0
  62. package/packages/react-css/src/components/WorkflowRunPanel.tsx +9 -0
  63. package/packages/react-css/src/components/WorkflowToolbar.tsx +4 -0
  64. package/packages/react-css/src/components/index.ts +9 -0
  65. package/packages/react-css/src/hooks/index.ts +3 -0
  66. package/packages/react-css/src/hooks/useWorkflow.ts +39 -0
  67. package/packages/react-css/src/hooks/useWorkflowBuilder.ts +121 -0
  68. package/packages/react-css/src/index.ts +7 -0
  69. package/packages/react-css/src/pages/WorkflowBuilderPage.tsx +16 -0
  70. package/packages/react-css/src/pages/WorkflowRunsPage.tsx +6 -0
  71. package/packages/react-css/src/pages/index.ts +3 -0
  72. package/packages/react-css/src/styles.css +945 -0
  73. package/packages/react-css/tsconfig.json +26 -0
  74. package/packages/react-css/tsup.config.ts +2 -0
  75. package/packages/shared/README.md +1 -0
  76. package/packages/shared/package.json +56 -0
  77. package/packages/shared/src/__tests__/ai-workflow.test.ts +217 -0
  78. package/packages/shared/src/config.ts +49 -0
  79. package/packages/shared/src/convex/index.ts +2 -0
  80. package/packages/shared/src/convex/schemas.ts +42 -0
  81. package/packages/shared/src/engine.test.ts +1 -0
  82. package/packages/shared/src/engine.ts +295 -0
  83. package/packages/shared/src/index.ts +43 -0
  84. package/packages/shared/src/steps.ts +68 -0
  85. package/packages/shared/src/templates.ts +172 -0
  86. package/packages/shared/src/types.ts +237 -0
  87. package/packages/shared/src/utils/cost.ts +79 -0
  88. package/packages/shared/src/utils/dag.ts +133 -0
  89. package/packages/shared/src/utils/index.ts +5 -0
  90. package/packages/shared/src/utils/interpolation.ts +53 -0
  91. package/packages/shared/src/validators.ts +215 -0
  92. package/packages/shared/tsconfig.json +1 -0
  93. package/packages/shared/tsup.config.ts +5 -0
  94. package/packages/shared/vitest.config.ts +4 -0
  95. package/packages/solidjs/README.md +1 -0
  96. package/packages/solidjs/package.json +45 -0
  97. package/packages/solidjs/src/components/ApprovalModal.tsx +18 -0
  98. package/packages/solidjs/src/components/StepConfigPanel.tsx +14 -0
  99. package/packages/solidjs/src/components/StepConnector.tsx +11 -0
  100. package/packages/solidjs/src/components/StepNode.tsx +12 -0
  101. package/packages/solidjs/src/components/StepPalette.tsx +22 -0
  102. package/packages/solidjs/src/components/WorkflowCanvas.tsx +23 -0
  103. package/packages/solidjs/src/components/WorkflowRunPanel.tsx +18 -0
  104. package/packages/solidjs/src/components/WorkflowToolbar.tsx +13 -0
  105. package/packages/solidjs/src/components/index.ts +9 -0
  106. package/packages/solidjs/src/index.ts +7 -0
  107. package/packages/solidjs/src/pages/WorkflowBuilderPage.tsx +37 -0
  108. package/packages/solidjs/src/pages/WorkflowRunsPage.tsx +20 -0
  109. package/packages/solidjs/src/pages/index.ts +3 -0
  110. package/packages/solidjs/src/primitives/createApprovalGate.ts +29 -0
  111. package/packages/solidjs/src/primitives/createWorkflow.ts +28 -0
  112. package/packages/solidjs/src/primitives/createWorkflowBuilder.ts +56 -0
  113. package/packages/solidjs/src/primitives/createWorkflowRun.ts +32 -0
  114. package/packages/solidjs/src/primitives/createWorkflowStep.ts +23 -0
  115. package/packages/solidjs/src/primitives/createWorkflowTemplates.ts +28 -0
  116. package/packages/solidjs/src/primitives/index.ts +8 -0
  117. package/packages/solidjs/tsconfig.json +1 -0
  118. package/packages/solidjs/tsup.config.ts +7 -0
  119. package/packages/solidjs-css/README.md +1 -0
  120. package/packages/solidjs-css/package.json +43 -0
  121. package/packages/solidjs-css/src/components/ApprovalModal.tsx +6 -0
  122. package/packages/solidjs-css/src/components/StepConfigPanel.tsx +7 -0
  123. package/packages/solidjs-css/src/components/StepConnector.tsx +6 -0
  124. package/packages/solidjs-css/src/components/StepNode.tsx +7 -0
  125. package/packages/solidjs-css/src/components/StepPalette.tsx +7 -0
  126. package/packages/solidjs-css/src/components/WorkflowCanvas.tsx +7 -0
  127. package/packages/solidjs-css/src/components/WorkflowRunPanel.tsx +8 -0
  128. package/packages/solidjs-css/src/components/WorkflowToolbar.tsx +5 -0
  129. package/packages/solidjs-css/src/components/index.ts +9 -0
  130. package/packages/solidjs-css/src/index.ts +7 -0
  131. package/packages/solidjs-css/src/pages/WorkflowBuilderPage.tsx +2 -0
  132. package/packages/solidjs-css/src/pages/WorkflowRunsPage.tsx +7 -0
  133. package/packages/solidjs-css/src/pages/index.ts +3 -0
  134. package/packages/solidjs-css/src/primitives/createWorkflow.ts +28 -0
  135. package/packages/solidjs-css/src/primitives/createWorkflowBuilder.ts +56 -0
  136. package/packages/solidjs-css/src/primitives/index.ts +1 -0
  137. package/packages/solidjs-css/src/styles.css +945 -0
  138. package/packages/solidjs-css/tsconfig.json +27 -0
  139. package/packages/solidjs-css/tsup.config.ts +2 -0
  140. package/pnpm-workspace.yaml +2 -0
@@ -0,0 +1,295 @@
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
+ }
@@ -0,0 +1,43 @@
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'
@@ -0,0 +1,68 @@
1
+ // @geenius-ai-workflow/shared — src/steps.ts
2
+
3
+ /**
4
+ * Built-in step factory helpers for easy workflow construction.
5
+ */
6
+
7
+ import type { WorkflowStepDef, LLMCallConfig, TransformConfig, ConditionConfig, HumanApprovalConfig, WebhookConfig, DelayConfig } from './types'
8
+
9
+ let stepCounter = 0
10
+ function nextId(): string {
11
+ return `step_${++stepCounter}_${Date.now()}`
12
+ }
13
+
14
+ /** Create an LLM call step */
15
+ export function llmStep(name: string, config: Omit<LLMCallConfig, 'type'>): WorkflowStepDef {
16
+ return { id: nextId(), name, type: 'llm-call', config: { type: 'llm-call', ...config } }
17
+ }
18
+
19
+ /** Create a data transform step */
20
+ export function transformStep(name: string, config: Omit<TransformConfig, 'type'>): WorkflowStepDef {
21
+ return { id: nextId(), name, type: 'transform', config: { type: 'transform', ...config } }
22
+ }
23
+
24
+ /** Create a condition (if/else) step */
25
+ export function conditionStep(name: string, config: Omit<ConditionConfig, 'type'>): WorkflowStepDef {
26
+ return { id: nextId(), name, type: 'condition', config: { type: 'condition', ...config } }
27
+ }
28
+
29
+ /** Create a human approval gate */
30
+ export function approvalStep(name: string, config: Omit<HumanApprovalConfig, 'type'>): WorkflowStepDef {
31
+ return { id: nextId(), name, type: 'human-approval', config: { type: 'human-approval', ...config } }
32
+ }
33
+
34
+ /** Create a webhook call step */
35
+ export function webhookStep(name: string, config: Omit<WebhookConfig, 'type'>): WorkflowStepDef {
36
+ return { id: nextId(), name, type: 'webhook', config: { type: 'webhook', ...config } }
37
+ }
38
+
39
+ /** Create a delay step */
40
+ export function delayStep(name: string, durationMs: number): WorkflowStepDef {
41
+ return { id: nextId(), name, type: 'delay', config: { type: 'delay', durationMs } }
42
+ }
43
+
44
+ /**
45
+ * Connect two steps (create a DAG edge).
46
+ */
47
+ export function connect(fromStepId: string, toStepId: string, label?: string) {
48
+ return { fromStepId, toStepId, label }
49
+ }
50
+
51
+ /**
52
+ * Build a linear workflow from an ordered list of steps.
53
+ */
54
+ export function linearWorkflow(
55
+ name: string,
56
+ steps: WorkflowStepDef[],
57
+ ): { steps: WorkflowStepDef[]; connections: Array<{ fromStepId: string; toStepId: string }> } {
58
+ const connections = []
59
+ for (let i = 0; i < steps.length - 1; i++) {
60
+ connections.push({ fromStepId: steps[i].id, toStepId: steps[i + 1].id })
61
+ }
62
+ return { steps, connections }
63
+ }
64
+
65
+ /** Reset the step counter (for testing) */
66
+ export function resetStepCounter(): void {
67
+ stepCounter = 0
68
+ }
@@ -0,0 +1,172 @@
1
+ // @geenius-ai-workflow/shared — src/templates.ts
2
+ /**
3
+ * Pre-built workflow templates for common AI/business patterns.
4
+ */
5
+ import type { WorkflowDefinition, WorkflowStepDef, StepConnection } from './types'
6
+
7
+ export interface WorkflowTemplate {
8
+ id: string
9
+ name: string
10
+ description: string
11
+ category: 'content' | 'data' | 'approval' | 'research' | 'devops'
12
+ tags: string[]
13
+ /** Factory that creates a fresh WorkflowDefinition from this template */
14
+ create: (createdBy: string) => WorkflowDefinition
15
+ }
16
+
17
+ function uid(): string { return `step_tpl_${Date.now()}_${Math.random().toString(36).slice(2, 8)}` }
18
+ function wfId(): string { return `wf_${Date.now()}_${Math.random().toString(36).slice(2, 8)}` }
19
+
20
+ // ============================================================================
21
+ // 1. Content Pipeline
22
+ // ============================================================================
23
+
24
+ const contentPipeline: WorkflowTemplate = {
25
+ id: 'tpl-content-pipeline',
26
+ name: 'Content Pipeline',
27
+ description: 'Generate, review, and publish content with AI refinement and human approval.',
28
+ category: 'content',
29
+ tags: ['content', 'blog', 'marketing'],
30
+ create: (createdBy) => {
31
+ const draft = uid(), refine = uid(), approve = uid(), publish = uid()
32
+ const steps: WorkflowStepDef[] = [
33
+ { id: draft, name: 'Generate Draft', type: 'llm-call', config: { type: 'llm-call', systemPrompt: 'You are a content writer.', userPromptTemplate: 'Write a blog post about {{topic}}. Tone: {{tone}}.', outputVar: 'draft' }, position: { x: 100, y: 200 } },
34
+ { id: refine, name: 'Refine Content', type: 'llm-call', config: { type: 'llm-call', systemPrompt: 'You are an editor. Improve clarity, grammar, and engagement.', userPromptTemplate: 'Polish this draft:\n\n{{draft}}', outputVar: 'polished' }, position: { x: 350, y: 200 } },
35
+ { id: approve, name: 'Human Review', type: 'human-approval', config: { type: 'human-approval', message: 'Please review the polished content before publishing.' }, position: { x: 600, y: 200 } },
36
+ { id: publish, name: 'Format for Publishing', type: 'transform', config: { type: 'transform', expression: '({ title: polished.split("\\n")[0], body: polished, publishedAt: Date.now() })', inputVars: ['polished'], outputVar: 'publishData' }, position: { x: 850, y: 200 } },
37
+ ]
38
+ const connections: StepConnection[] = [
39
+ { fromStepId: draft, toStepId: refine },
40
+ { fromStepId: refine, toStepId: approve },
41
+ { fromStepId: approve, toStepId: publish },
42
+ ]
43
+ return { id: wfId(), name: 'Content Pipeline', description: 'AI-powered content generation with editorial review.', version: 1, status: 'draft', steps, connections, inputSchema: [{ name: 'topic', type: 'string', required: true }, { name: 'tone', type: 'string', required: false, default: 'professional' }], tags: ['content'], createdBy, createdAt: Date.now(), updatedAt: Date.now() }
44
+ },
45
+ }
46
+
47
+ // ============================================================================
48
+ // 2. Data Enrichment
49
+ // ============================================================================
50
+
51
+ const dataEnrichment: WorkflowTemplate = {
52
+ id: 'tpl-data-enrichment',
53
+ name: 'Data Enrichment',
54
+ description: 'Fetch external data, enrich with AI analysis, and transform for downstream use.',
55
+ category: 'data',
56
+ tags: ['data', 'analytics', 'enrichment'],
57
+ create: (createdBy) => {
58
+ const fetch_ = uid(), analyze = uid(), transform = uid()
59
+ const steps: WorkflowStepDef[] = [
60
+ { id: fetch_, name: 'Fetch Data', type: 'webhook', config: { type: 'webhook', url: '{{dataSourceUrl}}', method: 'GET', outputVar: 'rawData' }, position: { x: 100, y: 200 } },
61
+ { id: analyze, name: 'AI Analysis', type: 'llm-call', config: { type: 'llm-call', systemPrompt: 'You are a data analyst. Analyze the data and extract key insights.', userPromptTemplate: 'Analyze this dataset and return a JSON summary with key metrics:\n\n{{rawData}}', parseJson: true, outputVar: 'analysis' }, position: { x: 400, y: 200 } },
62
+ { id: transform, name: 'Transform Output', type: 'transform', config: { type: 'transform', expression: '({ ...analysis, enrichedAt: Date.now(), source: dataSourceUrl })', inputVars: ['analysis', 'dataSourceUrl'], outputVar: 'enrichedData' }, position: { x: 700, y: 200 } },
63
+ ]
64
+ const connections: StepConnection[] = [
65
+ { fromStepId: fetch_, toStepId: analyze },
66
+ { fromStepId: analyze, toStepId: transform },
67
+ ]
68
+ return { id: wfId(), name: 'Data Enrichment', version: 1, status: 'draft', steps, connections, inputSchema: [{ name: 'dataSourceUrl', type: 'string', required: true }], tags: ['data'], createdBy, createdAt: Date.now(), updatedAt: Date.now() }
69
+ },
70
+ }
71
+
72
+ // ============================================================================
73
+ // 3. Approval Chain
74
+ // ============================================================================
75
+
76
+ const approvalChain: WorkflowTemplate = {
77
+ id: 'tpl-approval-chain',
78
+ name: 'Approval Chain',
79
+ description: 'Multi-level approval workflow with conditional escalation.',
80
+ category: 'approval',
81
+ tags: ['approval', 'governance', 'compliance'],
82
+ create: (createdBy) => {
83
+ const checkRisk = uid(), lowApproval = uid(), highApproval = uid(), finalize = uid()
84
+ const steps: WorkflowStepDef[] = [
85
+ { id: checkRisk, name: 'Assess Risk Level', type: 'llm-call', config: { type: 'llm-call', systemPrompt: 'Assess the risk level of this request. Respond with JSON: {"riskLevel": "low"|"medium"|"high", "reason": "..."}', userPromptTemplate: '{{request}}', parseJson: true, outputVar: 'riskAssessment' }, position: { x: 100, y: 200 } },
86
+ { id: lowApproval, name: 'Manager Approval', type: 'human-approval', config: { type: 'human-approval', message: 'Low/medium risk: Manager approval needed for: {{request}}' }, position: { x: 400, y: 100 } },
87
+ { id: highApproval, name: 'Executive Approval', type: 'human-approval', config: { type: 'human-approval', message: 'High risk: Executive approval required for: {{request}}' }, position: { x: 400, y: 300 } },
88
+ { id: finalize, name: 'Record Decision', type: 'transform', config: { type: 'transform', expression: '({ approved: true, approvedAt: Date.now(), riskLevel: riskAssessment.riskLevel })', inputVars: ['riskAssessment'], outputVar: 'decision' }, position: { x: 700, y: 200 } },
89
+ ]
90
+ const connections: StepConnection[] = [
91
+ { fromStepId: checkRisk, toStepId: lowApproval, condition: 'riskAssessment.riskLevel !== "high"' },
92
+ { fromStepId: checkRisk, toStepId: highApproval, condition: 'riskAssessment.riskLevel === "high"' },
93
+ { fromStepId: lowApproval, toStepId: finalize },
94
+ { fromStepId: highApproval, toStepId: finalize },
95
+ ]
96
+ return { id: wfId(), name: 'Approval Chain', version: 1, status: 'draft', steps, connections, inputSchema: [{ name: 'request', type: 'string', required: true }], tags: ['approval'], createdBy, createdAt: Date.now(), updatedAt: Date.now() }
97
+ },
98
+ }
99
+
100
+ // ============================================================================
101
+ // 4. Research Pipeline
102
+ // ============================================================================
103
+
104
+ const researchPipeline: WorkflowTemplate = {
105
+ id: 'tpl-research',
106
+ name: 'Research Pipeline',
107
+ description: 'Multi-source research with AI synthesis and summary generation.',
108
+ category: 'research',
109
+ tags: ['research', 'analysis', 'summary'],
110
+ create: (createdBy) => {
111
+ const research = uid(), synthesize = uid(), summarize = uid()
112
+ const steps: WorkflowStepDef[] = [
113
+ { id: research, name: 'Deep Research', type: 'llm-call', config: { type: 'llm-call', systemPrompt: 'You are a research assistant. Provide comprehensive, well-sourced research on the given topic.', userPromptTemplate: 'Research the following topic in depth: {{topic}}\n\nFocus areas: {{focusAreas}}', outputVar: 'researchOutput' }, position: { x: 100, y: 200 } },
114
+ { id: synthesize, name: 'Synthesize Findings', type: 'llm-call', config: { type: 'llm-call', systemPrompt: 'You are a synthesis expert. Combine research findings into structured insights with citations.', userPromptTemplate: 'Synthesize these research findings into key themes and actionable insights:\n\n{{researchOutput}}', parseJson: true, outputVar: 'synthesis' }, position: { x: 400, y: 200 } },
115
+ { id: summarize, name: 'Executive Summary', type: 'llm-call', config: { type: 'llm-call', systemPrompt: 'Write a concise executive summary (3-5 paragraphs) suitable for senior leadership.', userPromptTemplate: 'Create an executive summary from:\n\n{{synthesis}}', outputVar: 'executiveSummary' }, position: { x: 700, y: 200 } },
116
+ ]
117
+ const connections: StepConnection[] = [
118
+ { fromStepId: research, toStepId: synthesize },
119
+ { fromStepId: synthesize, toStepId: summarize },
120
+ ]
121
+ return { id: wfId(), name: 'Research Pipeline', version: 1, status: 'draft', steps, connections, inputSchema: [{ name: 'topic', type: 'string', required: true }, { name: 'focusAreas', type: 'string', required: false, default: 'general overview' }], tags: ['research'], createdBy, createdAt: Date.now(), updatedAt: Date.now() }
122
+ },
123
+ }
124
+
125
+ // ============================================================================
126
+ // 5. Code Review Pipeline
127
+ // ============================================================================
128
+
129
+ const codeReviewPipeline: WorkflowTemplate = {
130
+ id: 'tpl-code-review',
131
+ name: 'Code Review Pipeline',
132
+ description: 'Automated code review with AI analysis, security check, and human approval.',
133
+ category: 'devops',
134
+ tags: ['code', 'review', 'security', 'devops'],
135
+ create: (createdBy) => {
136
+ const analyze = uid(), security = uid(), approve = uid(), report = uid()
137
+ const steps: WorkflowStepDef[] = [
138
+ { id: analyze, name: 'AI Code Review', type: 'llm-call', config: { type: 'llm-call', systemPrompt: 'You are a senior code reviewer. Analyze the code for bugs, performance issues, and best practices. Return JSON: {"issues": [], "suggestions": [], "quality": 1-10}', userPromptTemplate: 'Review this code:\n\n```{{language}}\n{{code}}\n```', parseJson: true, outputVar: 'codeReview' }, position: { x: 100, y: 200 } },
139
+ { id: security, name: 'Security Scan', type: 'llm-call', config: { type: 'llm-call', systemPrompt: 'You are a security auditor. Check for vulnerabilities (XSS, SQL injection, secrets exposure, etc). Return JSON: {"vulnerabilities": [], "riskLevel": "low"|"medium"|"high"}', userPromptTemplate: 'Security audit for:\n\n```{{language}}\n{{code}}\n```', parseJson: true, outputVar: 'securityReport' }, position: { x: 100, y: 400 } },
140
+ { id: approve, name: 'Developer Approval', type: 'human-approval', config: { type: 'human-approval', message: 'Review the AI code analysis and security scan before merging.' }, position: { x: 450, y: 300 } },
141
+ { id: report, name: 'Generate Report', type: 'transform', config: { type: 'transform', expression: '({ codeQuality: codeReview.quality, issues: codeReview.issues.length, vulnerabilities: securityReport.vulnerabilities.length, riskLevel: securityReport.riskLevel, reviewedAt: Date.now() })', inputVars: ['codeReview', 'securityReport'], outputVar: 'finalReport' }, position: { x: 700, y: 300 } },
142
+ ]
143
+ const connections: StepConnection[] = [
144
+ { fromStepId: analyze, toStepId: approve },
145
+ { fromStepId: security, toStepId: approve },
146
+ { fromStepId: approve, toStepId: report },
147
+ ]
148
+ return { id: wfId(), name: 'Code Review Pipeline', version: 1, status: 'draft', steps, connections, inputSchema: [{ name: 'code', type: 'string', required: true }, { name: 'language', type: 'string', required: false, default: 'typescript' }], tags: ['devops', 'review'], createdBy, createdAt: Date.now(), updatedAt: Date.now() }
149
+ },
150
+ }
151
+
152
+ // ============================================================================
153
+ // Template Registry
154
+ // ============================================================================
155
+
156
+ export const WORKFLOW_TEMPLATES: WorkflowTemplate[] = [
157
+ contentPipeline,
158
+ dataEnrichment,
159
+ approvalChain,
160
+ researchPipeline,
161
+ codeReviewPipeline,
162
+ ]
163
+
164
+ /** Look up a template by ID */
165
+ export function getTemplate(id: string): WorkflowTemplate | undefined {
166
+ return WORKFLOW_TEMPLATES.find(t => t.id === id)
167
+ }
168
+
169
+ /** Filter templates by category */
170
+ export function getTemplatesByCategory(category: WorkflowTemplate['category']): WorkflowTemplate[] {
171
+ return WORKFLOW_TEMPLATES.filter(t => t.category === category)
172
+ }