web-ai-service 1.0.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 (118) hide show
  1. package/README.md +287 -0
  2. package/dist/engine/cli.d.ts +3 -0
  3. package/dist/engine/cli.d.ts.map +1 -0
  4. package/dist/engine/cli.js +4 -0
  5. package/dist/engine/cli.js.map +1 -0
  6. package/dist/engine/config/llm-costs.d.ts +16 -0
  7. package/dist/engine/config/llm-costs.d.ts.map +1 -0
  8. package/dist/engine/config/llm-costs.js +38 -0
  9. package/dist/engine/config/llm-costs.js.map +1 -0
  10. package/dist/engine/core/node-executor.d.ts +8 -0
  11. package/dist/engine/core/node-executor.d.ts.map +1 -0
  12. package/dist/engine/core/node-executor.js +150 -0
  13. package/dist/engine/core/node-executor.js.map +1 -0
  14. package/dist/engine/core/validator.d.ts +6 -0
  15. package/dist/engine/core/validator.d.ts.map +1 -0
  16. package/dist/engine/core/validator.js +498 -0
  17. package/dist/engine/core/validator.js.map +1 -0
  18. package/dist/engine/core/workflow-executor.d.ts +6 -0
  19. package/dist/engine/core/workflow-executor.d.ts.map +1 -0
  20. package/dist/engine/core/workflow-executor.js +187 -0
  21. package/dist/engine/core/workflow-executor.js.map +1 -0
  22. package/dist/engine/core/workflow-loader.d.ts +26 -0
  23. package/dist/engine/core/workflow-loader.d.ts.map +1 -0
  24. package/dist/engine/core/workflow-loader.js +265 -0
  25. package/dist/engine/core/workflow-loader.js.map +1 -0
  26. package/dist/engine/index.d.ts +5 -0
  27. package/dist/engine/index.d.ts.map +1 -0
  28. package/dist/engine/index.js +70 -0
  29. package/dist/engine/index.js.map +1 -0
  30. package/dist/engine/nodes/code-node.d.ts +6 -0
  31. package/dist/engine/nodes/code-node.d.ts.map +1 -0
  32. package/dist/engine/nodes/code-node.js +70 -0
  33. package/dist/engine/nodes/code-node.js.map +1 -0
  34. package/dist/engine/nodes/endpoint-node.d.ts +6 -0
  35. package/dist/engine/nodes/endpoint-node.d.ts.map +1 -0
  36. package/dist/engine/nodes/endpoint-node.js +47 -0
  37. package/dist/engine/nodes/endpoint-node.js.map +1 -0
  38. package/dist/engine/nodes/llm-node.d.ts +6 -0
  39. package/dist/engine/nodes/llm-node.d.ts.map +1 -0
  40. package/dist/engine/nodes/llm-node.js +164 -0
  41. package/dist/engine/nodes/llm-node.js.map +1 -0
  42. package/dist/engine/nodes/passthrough-node.d.ts +6 -0
  43. package/dist/engine/nodes/passthrough-node.d.ts.map +1 -0
  44. package/dist/engine/nodes/passthrough-node.js +13 -0
  45. package/dist/engine/nodes/passthrough-node.js.map +1 -0
  46. package/dist/engine/nodes/reduce-node.d.ts +6 -0
  47. package/dist/engine/nodes/reduce-node.d.ts.map +1 -0
  48. package/dist/engine/nodes/reduce-node.js +44 -0
  49. package/dist/engine/nodes/reduce-node.js.map +1 -0
  50. package/dist/engine/nodes/split-node.d.ts +10 -0
  51. package/dist/engine/nodes/split-node.d.ts.map +1 -0
  52. package/dist/engine/nodes/split-node.js +51 -0
  53. package/dist/engine/nodes/split-node.js.map +1 -0
  54. package/dist/engine/providers/gemini.d.ts +27 -0
  55. package/dist/engine/providers/gemini.d.ts.map +1 -0
  56. package/dist/engine/providers/gemini.js +163 -0
  57. package/dist/engine/providers/gemini.js.map +1 -0
  58. package/dist/engine/providers/grok.d.ts +28 -0
  59. package/dist/engine/providers/grok.d.ts.map +1 -0
  60. package/dist/engine/providers/grok.js +164 -0
  61. package/dist/engine/providers/grok.js.map +1 -0
  62. package/dist/engine/providers/registry.d.ts +33 -0
  63. package/dist/engine/providers/registry.d.ts.map +1 -0
  64. package/dist/engine/providers/registry.js +51 -0
  65. package/dist/engine/providers/registry.js.map +1 -0
  66. package/dist/engine/router.d.ts +8 -0
  67. package/dist/engine/router.d.ts.map +1 -0
  68. package/dist/engine/router.js +79 -0
  69. package/dist/engine/router.js.map +1 -0
  70. package/dist/engine/scripts/cleanup-port.d.ts +2 -0
  71. package/dist/engine/scripts/cleanup-port.d.ts.map +1 -0
  72. package/dist/engine/scripts/cleanup-port.js +54 -0
  73. package/dist/engine/scripts/cleanup-port.js.map +1 -0
  74. package/dist/engine/scripts/create-endpoint.d.ts +2 -0
  75. package/dist/engine/scripts/create-endpoint.d.ts.map +1 -0
  76. package/dist/engine/scripts/create-endpoint.js +83 -0
  77. package/dist/engine/scripts/create-endpoint.js.map +1 -0
  78. package/dist/engine/scripts/scan-deps.d.ts +2 -0
  79. package/dist/engine/scripts/scan-deps.d.ts.map +1 -0
  80. package/dist/engine/scripts/scan-deps.js +112 -0
  81. package/dist/engine/scripts/scan-deps.js.map +1 -0
  82. package/dist/engine/scripts/validate-workflows.d.ts +3 -0
  83. package/dist/engine/scripts/validate-workflows.d.ts.map +1 -0
  84. package/dist/engine/scripts/validate-workflows.js +75 -0
  85. package/dist/engine/scripts/validate-workflows.js.map +1 -0
  86. package/dist/engine/server.d.ts +10 -0
  87. package/dist/engine/server.d.ts.map +1 -0
  88. package/dist/engine/server.js +92 -0
  89. package/dist/engine/server.js.map +1 -0
  90. package/dist/engine/types/index.d.ts +320 -0
  91. package/dist/engine/types/index.d.ts.map +1 -0
  92. package/dist/engine/types/index.js +24 -0
  93. package/dist/engine/types/index.js.map +1 -0
  94. package/dist/engine/utils/file-cache.d.ts +31 -0
  95. package/dist/engine/utils/file-cache.d.ts.map +1 -0
  96. package/dist/engine/utils/file-cache.js +68 -0
  97. package/dist/engine/utils/file-cache.js.map +1 -0
  98. package/dist/engine/utils/file-resolver.d.ts +21 -0
  99. package/dist/engine/utils/file-resolver.d.ts.map +1 -0
  100. package/dist/engine/utils/file-resolver.js +33 -0
  101. package/dist/engine/utils/file-resolver.js.map +1 -0
  102. package/dist/engine/utils/jsonpath.d.ts +13 -0
  103. package/dist/engine/utils/jsonpath.d.ts.map +1 -0
  104. package/dist/engine/utils/jsonpath.js +38 -0
  105. package/dist/engine/utils/jsonpath.js.map +1 -0
  106. package/dist/engine/utils/logger.d.ts +11 -0
  107. package/dist/engine/utils/logger.d.ts.map +1 -0
  108. package/dist/engine/utils/logger.js +24 -0
  109. package/dist/engine/utils/logger.js.map +1 -0
  110. package/dist/engine/utils/metrics.d.ts +6 -0
  111. package/dist/engine/utils/metrics.d.ts.map +1 -0
  112. package/dist/engine/utils/metrics.js +82 -0
  113. package/dist/engine/utils/metrics.js.map +1 -0
  114. package/dist/engine/utils/schema-validator.d.ts +10 -0
  115. package/dist/engine/utils/schema-validator.d.ts.map +1 -0
  116. package/dist/engine/utils/schema-validator.js +61 -0
  117. package/dist/engine/utils/schema-validator.js.map +1 -0
  118. package/package.json +53 -0
package/README.md ADDED
@@ -0,0 +1,287 @@
1
+ # Web AI Service
2
+
3
+ A TypeScript-based workflow engine that creates dynamic API endpoints from YAML workflow definitions. Build powerful API pipelines with LLM calls, custom code execution, and data transformations without writing server code.
4
+
5
+ ## Features
6
+
7
+ - 🚀 **YAML-Based Configuration** - Define API endpoints declaratively
8
+ - 🤖 **LLM Integration** - Built-in support for Gemini and Grok
9
+ - 📝 **Custom Code Nodes** - Execute TypeScript functions in your workflows
10
+ - ⚡ **Parallel Execution** - Run multiple nodes concurrently with error strategies
11
+ - 🔄 **Data Transformation** - Reduce, split, and map data with JSONPath
12
+ - ✅ **Input Validation** - JSON Schema validation on request inputs
13
+ - 🎯 **Type-Safe** - Full TypeScript support with strict typing
14
+ - 🌐 **Auto-Routing** - Endpoint folders automatically become API routes
15
+
16
+ ---
17
+
18
+ ## Quick Start
19
+
20
+ ### 1. Installation
21
+
22
+ You can install the engine globally or in your project:
23
+
24
+ ```bash
25
+ npm install -g web-ai-service
26
+ ```
27
+
28
+ Or just use `npx`:
29
+
30
+ ```bash
31
+ npx web-ai-service
32
+ ```
33
+
34
+ ### 2. Setup (if running from source)
35
+
36
+ ```bash
37
+ # Clone or navigate to your project directory
38
+ cd API-Workflow
39
+
40
+ # Install dependencies
41
+ npm install
42
+
43
+ # Copy environment template
44
+ cp .env.example .env
45
+ ```
46
+
47
+ ### 2. Configuration
48
+
49
+ Edit `.env` with your API keys:
50
+
51
+ ```bash
52
+ PORT=3000
53
+ GEMINI_API_KEY=your-gemini-api-key-here
54
+ GROK_API_KEY=your-grok-api-key-here
55
+ ```
56
+
57
+ ### 3. Start the Server
58
+
59
+ ```bash
60
+ # Development (with hot reload)
61
+ npm run dev
62
+
63
+ # Production
64
+ npm run build && npm start
65
+ ```
66
+
67
+ The server starts at `http://localhost:3000`.
68
+
69
+ ---
70
+
71
+ ## Project Structure
72
+
73
+ ```
74
+ API-Workflow/
75
+ ├── engine/ # Core workflow engine (framework code)
76
+ │ ├── core/ # Core execution logic
77
+ │ ├── nodes/ # Node type executors
78
+ │ ├── providers/ # LLM provider implementations
79
+ │ ├── types/ # TypeScript type definitions
80
+ │ └── utils/ # Utility functions
81
+
82
+ ├── src/ # User content (you edit this)
83
+ │ ├── endpoints/ # Your API endpoints
84
+ │ │ └── {endpoint-name}/ # One folder per endpoint route
85
+ │ │ ├── POST.yaml # Workflow definition (method = filename)
86
+ │ │ ├── codes/ # Endpoint-specific code files
87
+ │ │ └── prompts/ # Endpoint-specific prompt files
88
+ │ │
89
+ │ └── plugins/ # Shared code modules (via @code-plugins/*)
90
+
91
+ └── .env # API keys and configuration
92
+ ```
93
+
94
+ ### Key Concepts
95
+
96
+ | Concept | Description |
97
+ |---------|-------------|
98
+ | **Endpoint** | A folder in `src/endpoints/` that becomes an API route (e.g., `summarize/` → `/summarize`) |
99
+ | **Workflow** | A YAML file (e.g., `POST.yaml`) defining the request processing pipeline |
100
+ | **Stage** | A sequential step in the workflow containing one or more nodes |
101
+ | **Node** | An individual processing unit (LLM call, code execution, etc.) |
102
+
103
+ ---
104
+
105
+ ## Creating Your First Endpoint
106
+
107
+ ### Example: Text Summarization API
108
+
109
+ **Goal**: Create `POST /summarize` that takes text and returns a summary.
110
+
111
+ #### Step 1: Create the Endpoint Folder
112
+
113
+ ```bash
114
+ mkdir -p src/endpoints/summarize/prompts
115
+ ```
116
+
117
+ #### Step 2: Create the System Prompt
118
+
119
+ Create `src/endpoints/summarize/prompts/summarizer-system.txt`:
120
+
121
+ ```text
122
+ You are a concise summarization assistant. Summarize the provided text clearly.
123
+
124
+ Guidelines:
125
+ - Capture the main points and key information
126
+ - Use clear, professional language
127
+ - Keep the summary to 2-3 paragraphs
128
+ ```
129
+
130
+ #### Step 3: Create the Workflow
131
+
132
+ Create `src/endpoints/summarize/POST.yaml`:
133
+
134
+ ```yaml
135
+ version: "1.0"
136
+
137
+ stages:
138
+ - name: main
139
+ nodes:
140
+ summarize:
141
+ type: llm
142
+ input: $input
143
+ provider: gemini
144
+ model: gemini-2.0-flash-lite
145
+ temperature: 0.3
146
+ maxTokens: 1024
147
+ systemMessages:
148
+ - file: summarizer-system.txt
149
+ cache: true
150
+ ```
151
+
152
+ #### Step 4: Test It
153
+
154
+ ```bash
155
+ npm run dev
156
+
157
+ curl -X POST http://localhost:3000/summarize \
158
+ -H "Content-Type: application/json" \
159
+ -d '{"text": "Your long text to summarize goes here..."}'
160
+ ```
161
+
162
+ ---
163
+
164
+ ## Adding Custom Code Nodes
165
+
166
+ For custom processing logic, add TypeScript files to your endpoint's `codes/` folder.
167
+
168
+ ### Example: Validation + Processing
169
+
170
+ Create `src/endpoints/summarize/codes/validate-input.ts`:
171
+
172
+ ```typescript
173
+ import type { NodeOutput } from '@workflow/types';
174
+
175
+ export default async function(input: unknown): Promise<NodeOutput> {
176
+ const body = input as { text?: string };
177
+
178
+ if (!body.text || typeof body.text !== 'string') {
179
+ throw new Error('Missing required field: text');
180
+ }
181
+
182
+ if (body.text.length < 10) {
183
+ throw new Error('Text must be at least 10 characters');
184
+ }
185
+
186
+ return { type: 'string', value: body.text };
187
+ }
188
+ ```
189
+
190
+ Update `src/endpoints/summarize/POST.yaml`:
191
+
192
+ ```yaml
193
+ version: "1.0"
194
+
195
+ stages:
196
+ - name: entry
197
+ nodes:
198
+ validate:
199
+ type: code
200
+ input: $input
201
+ file: validate-input.ts
202
+
203
+ - name: summarization
204
+ nodes:
205
+ summarize:
206
+ type: llm
207
+ input: entry.validate
208
+ provider: gemini
209
+ model: gemini-2.0-flash-lite
210
+ temperature: 0.3
211
+ maxTokens: 1024
212
+ systemMessages:
213
+ - file: summarizer-system.txt
214
+ cache: true
215
+ ```
216
+
217
+ ---
218
+
219
+ ## Shared Plugins
220
+
221
+ For reusable code across endpoints, add files to `src/plugins/` and import via `@code-plugins/*`:
222
+
223
+ ```typescript
224
+ // In any code file
225
+ import { myHelper } from '@code-plugins/my-shared-lib.js';
226
+ ```
227
+
228
+ ---
229
+
230
+ ## Node Types Reference
231
+
232
+ | Type | Purpose | Key Properties |
233
+ |------|---------|----------------|
234
+ | `llm` | Call LLM provider | `provider`, `model`, `systemMessages`, `temperature`, `maxTokens` |
235
+ | `code` | Execute TypeScript | `file` |
236
+ | `reduce` | Combine outputs | `inputs`, `mapping` |
237
+ | `split` | Divide output | `mapping` |
238
+ | `passthrough` | Pass input unchanged | (none) |
239
+
240
+ ---
241
+
242
+ ## Available Commands
243
+
244
+ | Command | Description |
245
+ |---------|-------------|
246
+ | `npm run dev` | Start development server with hot reload |
247
+ | `npm run create-endpoint` | Scaffold a new API endpoint interactively |
248
+ | `npm run build` | Compile TypeScript to JavaScript |
249
+ | `npm start` | Start production server |
250
+ | `npm run validate` | Validate all workflows |
251
+ | `npm run scan-deps` | Scan and install code node dependencies |
252
+ | `npm run lint` | Run ESLint |
253
+ | `npm run format` | Format code with Prettier |
254
+
255
+ ---
256
+
257
+ ## Environment Variables
258
+
259
+ | Variable | Default | Description |
260
+ |----------|---------|-------------|
261
+ | `PORT` | `3000` | Server port |
262
+ | `GEMINI_API_KEY` | - | Gemini API key (required for Gemini) |
263
+ | `GROK_API_KEY` | - | Grok API key (required for Grok) |
264
+ | `LLM_TIMEOUT_MS` | `30000` | LLM call timeout |
265
+ | `LOG_LEVEL` | `info` | Logging level (`debug`, `info`, `warn`, `error`) |
266
+
267
+ ---
268
+
269
+ ## Troubleshooting
270
+
271
+ | Error | Solution |
272
+ |-------|----------|
273
+ | "Provider not found" | Check `provider` is `gemini` or `grok` and API key is set in `.env` |
274
+ | "Code node file not found" | Verify file exists in `codes/` folder with correct filename |
275
+ | "Cannot find module '@workflow/types'" | Run `npm run build` or restart TypeScript server |
276
+ | LLM Timeout | Increase `LLM_TIMEOUT_MS` in `.env` or use a faster model |
277
+
278
+ ---
279
+
280
+ ## Documentation
281
+
282
+ - [Full Specification](__dev_guide__/Dynamic-API-Service-SPEC.md) - Complete technical spec
283
+ - [Development Notes](_dev_notes_/plan.md) - Implementation roadmap
284
+
285
+ ## License
286
+
287
+ ISC
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../engine/cli.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { main } from './index.js';
3
+ main();
4
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../engine/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAElC,IAAI,EAAE,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * LLM Cost Configuration
3
+ * Costs are in USD per 1 million tokens
4
+ */
5
+ export interface ModelCost {
6
+ inputCostPer1M: number;
7
+ outputCostPer1M: number;
8
+ cachedInputCostPer1M?: number;
9
+ }
10
+ export declare const defaultLLMCosts: Record<string, ModelCost>;
11
+ /**
12
+ * Get cost for a specific model
13
+ * Falls back to 0 if model not found
14
+ */
15
+ export declare function getModelCost(model: string): ModelCost;
16
+ //# sourceMappingURL=llm-costs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm-costs.d.ts","sourceRoot":"","sources":["../../../engine/config/llm-costs.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,SAAS;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAiBrD,CAAC;AAEF;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAerD"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * LLM Cost Configuration
3
+ * Costs are in USD per 1 million tokens
4
+ */
5
+ export const defaultLLMCosts = {
6
+ // Gemini 3 Preview Models
7
+ 'gemini-3-pro-preview': { inputCostPer1M: 2.00, cachedInputCostPer1M: 0.20, outputCostPer1M: 12.00 },
8
+ 'gemini-3-flash-preview': { inputCostPer1M: 0.50, cachedInputCostPer1M: 0.05, outputCostPer1M: 3.00 },
9
+ // Gemini 2.5 Models
10
+ 'gemini-2.5-pro': { inputCostPer1M: 1.25, cachedInputCostPer1M: 0.125, outputCostPer1M: 10.00 }, // Corrected swapped values
11
+ 'gemini-2.5-flash': { inputCostPer1M: 0.30, cachedInputCostPer1M: 0.03, outputCostPer1M: 2.50 },
12
+ 'gemini-2.5-flash-lite': { inputCostPer1M: 0.10, cachedInputCostPer1M: 0.01, outputCostPer1M: 0.40 },
13
+ // Gemini 2.0 Models
14
+ 'gemini-2.0-flash': { inputCostPer1M: 0.10, cachedInputCostPer1M: 0.025, outputCostPer1M: 0.40 },
15
+ 'gemini-2.0-flash-lite': { inputCostPer1M: 0.075, cachedInputCostPer1M: 0.075, outputCostPer1M: 0.30 }, // User specified cached=input
16
+ // Grok Models
17
+ 'grok-4-1-fast-reasoning': { inputCostPer1M: 0.20, cachedInputCostPer1M: 0.20, outputCostPer1M: 0.50 },
18
+ 'grok-4-1-fast-non-reasoning': { inputCostPer1M: 0.20, cachedInputCostPer1M: 0.20, outputCostPer1M: 0.50 },
19
+ };
20
+ /**
21
+ * Get cost for a specific model
22
+ * Falls back to 0 if model not found
23
+ */
24
+ export function getModelCost(model) {
25
+ // Exact match
26
+ if (defaultLLMCosts[model]) {
27
+ return defaultLLMCosts[model];
28
+ }
29
+ // Try finding matching prefix (e.g. gemini-1.5-flash-001 matches gemini-1.5-flash)
30
+ // This is a simple heuristic, might need refinement
31
+ const knownModels = Object.keys(defaultLLMCosts);
32
+ const match = knownModels.find(m => model.startsWith(m));
33
+ if (match) {
34
+ return defaultLLMCosts[match];
35
+ }
36
+ return { inputCostPer1M: 0, outputCostPer1M: 0 };
37
+ }
38
+ //# sourceMappingURL=llm-costs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm-costs.js","sourceRoot":"","sources":["../../../engine/config/llm-costs.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,MAAM,CAAC,MAAM,eAAe,GAA8B;IACtD,0BAA0B;IAC1B,sBAAsB,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,oBAAoB,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE;IACpG,wBAAwB,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,oBAAoB,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE;IAErG,oBAAoB;IACpB,gBAAgB,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,oBAAoB,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,EAAE,2BAA2B;IAC5H,kBAAkB,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,oBAAoB,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE;IAC/F,uBAAuB,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,oBAAoB,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE;IAEpG,oBAAoB;IACpB,kBAAkB,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,oBAAoB,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE;IAChG,uBAAuB,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,oBAAoB,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,EAAE,8BAA8B;IAEtI,cAAc;IACd,yBAAyB,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,oBAAoB,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE;IACtG,6BAA6B,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,oBAAoB,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE;CAC7G,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa;IACtC,cAAc;IACd,IAAI,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;IAED,mFAAmF;IACnF,oDAAoD;IACpD,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IACzD,IAAI,KAAK,EAAE,CAAC;QACR,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;IAED,OAAO,EAAE,cAAc,EAAE,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,CAAC;AACrD,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { NodeDefinition, NodeOutput, ExecutionContext, WorkflowDefinition, StageDefinition } from '../types/index.js';
2
+ /**
3
+ * Execute a single node within a stage
4
+ * Outputs are stored as stageName.nodeId to allow stage-scoped references
5
+ * Input is passed from stage at runtime (nodes: { nodeId: input })
6
+ */
7
+ export declare function executeNode(node: NodeDefinition, inputRef: string, stage: StageDefinition, context: ExecutionContext, workflow: WorkflowDefinition): Promise<NodeOutput>;
8
+ //# sourceMappingURL=node-executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-executor.d.ts","sourceRoot":"","sources":["../../../engine/core/node-executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EAClB,MAAM,mBAAmB,CAAC;AAU3B;;;;GAIG;AACH,wBAAsB,WAAW,CAC7B,IAAI,EAAE,cAAc,EACpB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,eAAe,EACtB,OAAO,EAAE,gBAAgB,EACzB,QAAQ,EAAE,kBAAkB,GAC7B,OAAO,CAAC,UAAU,CAAC,CA6FrB"}
@@ -0,0 +1,150 @@
1
+ import { executeCodeNode } from '../nodes/code-node.js';
2
+ import { executeLLMNode } from '../nodes/llm-node.js';
3
+ import { executeReduceNode } from '../nodes/reduce-node.js';
4
+ import { executeSplitNode } from '../nodes/split-node.js';
5
+ import { executePassthroughNode } from '../nodes/passthrough-node.js';
6
+ import { executeEndpointNode } from '../nodes/endpoint-node.js';
7
+ import logger from '../utils/logger.js';
8
+ import { ErrorCode } from '../types/index.js';
9
+ /**
10
+ * Execute a single node within a stage
11
+ * Outputs are stored as stageName.nodeId to allow stage-scoped references
12
+ * Input is passed from stage at runtime (nodes: { nodeId: input })
13
+ */
14
+ export async function executeNode(node, inputRef, stage, context, workflow) {
15
+ const outputKey = `${stage.name}.${node.id}`;
16
+ const startTime = Date.now();
17
+ try {
18
+ let output;
19
+ switch (node.type) {
20
+ case 'code': {
21
+ const input = resolveInput(inputRef, context);
22
+ output = await executeCodeNode(node, input, context, workflow);
23
+ break;
24
+ }
25
+ case 'llm': {
26
+ const input = resolveInput(inputRef, context);
27
+ output = await executeLLMNode(node, input, workflow, context);
28
+ break;
29
+ }
30
+ case 'reduce': {
31
+ output = await executeReduceNode(node, context);
32
+ break;
33
+ }
34
+ case 'split': {
35
+ const input = resolveInput(inputRef, context);
36
+ const splitOutputs = await executeSplitNode(node, input);
37
+ // Store split outputs with stage prefix
38
+ const stageKey = `${stage.name}.${node.id}`;
39
+ context.splitOutputs.set(stageKey, splitOutputs);
40
+ // Return a placeholder output (split doesn't have a single output)
41
+ output = {
42
+ type: 'json',
43
+ value: Object.fromEntries(Array.from(splitOutputs.entries()).map(([key, val]) => [key, val.value])),
44
+ };
45
+ break;
46
+ }
47
+ case 'passthrough': {
48
+ const input = resolveInput(inputRef, context);
49
+ if (!input) {
50
+ throw new Error(`${ErrorCode.NODE_NOT_FOUND}: Input '${inputRef}' not found`);
51
+ }
52
+ output = await executePassthroughNode(node, { type: 'json', value: input });
53
+ break;
54
+ }
55
+ case 'endpoint': {
56
+ const input = resolveInput(inputRef, context);
57
+ output = await executeEndpointNode(node, input, context);
58
+ break;
59
+ }
60
+ default:
61
+ throw new Error(`Unknown node type: ${node.type}`);
62
+ }
63
+ // Store output with stage-scoped key (no caching, always execute)
64
+ context.nodeOutputs.set(outputKey, output);
65
+ // Record timing with stage-scoped key
66
+ const duration = Date.now() - startTime;
67
+ context.timing.set(outputKey, duration);
68
+ logger.info('Node executed successfully', {
69
+ stage: stage.name,
70
+ nodeId: node.id,
71
+ outputKey,
72
+ type: node.type,
73
+ duration: `${duration}ms`,
74
+ outputType: output.type,
75
+ });
76
+ return output;
77
+ }
78
+ catch (error) {
79
+ const duration = Date.now() - startTime;
80
+ context.timing.set(outputKey, duration);
81
+ logger.error('Node execution failed', {
82
+ stage: stage.name,
83
+ nodeId: node.id,
84
+ type: node.type,
85
+ duration: `${duration}ms`,
86
+ error: error instanceof Error ? error.message : String(error),
87
+ });
88
+ throw error;
89
+ }
90
+ }
91
+ /**
92
+ * Resolve input value from context
93
+ * Supports: $input, $params.paramName, stageName.nodeId
94
+ */
95
+ function resolveInput(inputRef, context) {
96
+ // Special case: $input refers to raw request body
97
+ if (inputRef === '$input') {
98
+ return context.input;
99
+ }
100
+ // Special case: $params.paramName refers to route parameters
101
+ if (inputRef.startsWith('$params.')) {
102
+ const paramName = inputRef.slice(8); // Remove '$params.'
103
+ const paramValue = context.params[paramName];
104
+ if (paramValue === undefined) {
105
+ throw new Error(`${ErrorCode.NODE_NOT_FOUND}: Route parameter '${paramName}' not found`);
106
+ }
107
+ return paramValue;
108
+ }
109
+ // Support $input.property access
110
+ if (inputRef.startsWith('$input.')) {
111
+ const key = inputRef.replace('$input.', '');
112
+ if (typeof context.input === 'object' && context.input !== null) {
113
+ return resolveObjectPath(context.input, key);
114
+ }
115
+ return undefined;
116
+ }
117
+ // Check for stage.node reference (e.g., "entry.summarize")
118
+ if (inputRef.includes('.')) {
119
+ const parts = inputRef.split('.');
120
+ // Handle split output reference (stageName.nodeId.outputName)
121
+ if (parts.length === 3) {
122
+ const [stageName, nodeId, outputName] = parts;
123
+ const stageKey = `${stageName}.${nodeId}`;
124
+ const splitOutputs = context.splitOutputs.get(stageKey);
125
+ if (splitOutputs) {
126
+ const splitOutput = splitOutputs.get(outputName);
127
+ if (splitOutput) {
128
+ return splitOutput.value;
129
+ }
130
+ }
131
+ throw new Error(`${ErrorCode.NODE_NOT_FOUND}: Split output '${inputRef}' not found`);
132
+ }
133
+ // Standard stage.node reference (stageName.nodeId)
134
+ const nodeOutput = context.nodeOutputs.get(inputRef);
135
+ if (nodeOutput) {
136
+ return nodeOutput.value;
137
+ }
138
+ throw new Error(`${ErrorCode.NODE_NOT_FOUND}: Output '${inputRef}' not found. Input must be in format 'stageName.nodeId'`);
139
+ }
140
+ throw new Error(`${ErrorCode.NODE_NOT_FOUND}: Invalid input reference '${inputRef}'. Must be '$input' or 'stageName.nodeId'`);
141
+ }
142
+ /**
143
+ * Helper to resolve object path (e.g., "prop.subprop")
144
+ */
145
+ function resolveObjectPath(obj, path) {
146
+ return path.split('.').reduce((prev, curr) => {
147
+ return prev ? prev[curr] : undefined;
148
+ }, obj);
149
+ }
150
+ //# sourceMappingURL=node-executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-executor.js","sourceRoot":"","sources":["../../../engine/core/node-executor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { WorkflowDefinition, ValidationResult } from '../types/index.js';
2
+ /**
3
+ * Validate a workflow definition
4
+ */
5
+ export declare function validateWorkflow(workflow: WorkflowDefinition): Promise<ValidationResult>;
6
+ //# sourceMappingURL=validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../../engine/core/validator.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAsC,MAAM,mBAAmB,CAAC;AAOlH;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAwC9F"}