xcomponent-ai 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +5 -5
- package/EVENT-ACCUMULATION-GUIDE.md +93 -75
- package/EXTERNAL-API.md +1 -1
- package/LLM-GUIDE.md +129 -103
- package/QUICKSTART.md +18 -1
- package/README.md +154 -79
- package/SCALABILITY.md +1 -1
- package/dist/agents.js +3 -3
- package/dist/agents.js.map +1 -1
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +81 -1
- package/dist/api.js.map +1 -1
- package/dist/cli.js +56 -13
- package/dist/cli.js.map +1 -1
- package/dist/component-registry.d.ts.map +1 -1
- package/dist/component-registry.js +0 -1
- package/dist/component-registry.js.map +1 -1
- package/dist/dashboard-server.d.ts +75 -0
- package/dist/dashboard-server.d.ts.map +1 -0
- package/dist/dashboard-server.js +793 -0
- package/dist/dashboard-server.js.map +1 -0
- package/dist/external-broker-api.d.ts +90 -4
- package/dist/external-broker-api.d.ts.map +1 -1
- package/dist/external-broker-api.js +90 -4
- package/dist/external-broker-api.js.map +1 -1
- package/dist/fsm-runtime.d.ts +44 -2
- package/dist/fsm-runtime.d.ts.map +1 -1
- package/dist/fsm-runtime.js +354 -44
- package/dist/fsm-runtime.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -1
- package/dist/index.js.map +1 -1
- package/dist/mermaid-generator.d.ts +9 -0
- package/dist/mermaid-generator.d.ts.map +1 -1
- package/dist/mermaid-generator.js +72 -29
- package/dist/mermaid-generator.js.map +1 -1
- package/dist/message-broker.d.ts +63 -6
- package/dist/message-broker.d.ts.map +1 -1
- package/dist/message-broker.js +257 -28
- package/dist/message-broker.js.map +1 -1
- package/dist/monitoring.js +1 -1
- package/dist/monitoring.js.map +1 -1
- package/dist/persistence.d.ts +1 -1
- package/dist/persistence.d.ts.map +1 -1
- package/dist/persistence.js +4 -2
- package/dist/persistence.js.map +1 -1
- package/dist/postgres-persistence.d.ts +114 -0
- package/dist/postgres-persistence.d.ts.map +1 -0
- package/dist/postgres-persistence.js +438 -0
- package/dist/postgres-persistence.js.map +1 -0
- package/dist/redis-persistence.d.ts +87 -0
- package/dist/redis-persistence.d.ts.map +1 -0
- package/dist/redis-persistence.js +279 -0
- package/dist/redis-persistence.js.map +1 -0
- package/dist/runtime-broadcaster.d.ts +67 -0
- package/dist/runtime-broadcaster.d.ts.map +1 -0
- package/dist/runtime-broadcaster.js +427 -0
- package/dist/runtime-broadcaster.js.map +1 -0
- package/dist/swagger-spec.js +1 -1
- package/dist/swagger-spec.js.map +1 -1
- package/dist/types.d.ts +106 -13
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +3 -1
- package/dist/types.js.map +1 -1
- package/dist/websockets.d.ts +9 -0
- package/dist/websockets.d.ts.map +1 -1
- package/dist/websockets.js +22 -3
- package/dist/websockets.js.map +1 -1
- package/examples/advanced-patterns-demo.yaml +2 -132
- package/examples/complete-workflow-all-features.yaml +0 -9
- package/examples/cross-component-demo.yaml +3 -0
- package/examples/distributed/Dockerfile.dashboard +27 -0
- package/examples/distributed/Dockerfile.runtime +28 -0
- package/examples/distributed/README.md +349 -0
- package/examples/distributed/docker-compose.yml +103 -0
- package/examples/distributed/e2e-test.js +468 -0
- package/examples/distributed/init-db.sql +104 -0
- package/examples/distributed/order-component.yaml +97 -0
- package/examples/distributed/payment-component.yaml +96 -0
- package/examples/distributed/runtime.js +182 -0
- package/examples/distributed-redis/Dockerfile.dashboard +27 -0
- package/examples/distributed-redis/Dockerfile.runtime +28 -0
- package/examples/distributed-redis/docker-compose.yml +97 -0
- package/examples/distributed-redis/e2e-test.js +448 -0
- package/examples/distributed-redis/init-db.sql +104 -0
- package/examples/distributed-redis/order-component.yaml +97 -0
- package/examples/distributed-redis/payment-component.yaml +96 -0
- package/examples/distributed-redis/runtime.js +182 -0
- package/examples/event-accumulation-demo.yaml +2 -54
- package/examples/explicit-transitions-demo.yaml +2 -93
- package/examples/full-project-structure.md +0 -14
- package/examples/kyc.yaml +0 -16
- package/examples/monolith-postgres/Dockerfile +28 -0
- package/examples/monolith-postgres/docker-compose.yml +42 -0
- package/examples/monolith-postgres/e2e-test.js +428 -0
- package/examples/monolith-postgres/runtime.js +200 -0
- package/examples/order-processing-xcomponent.yaml +0 -13
- package/examples/payment.yaml +0 -20
- package/examples/simple-xcomponent-demo.yaml +94 -0
- package/examples/trading-complete.yaml +0 -8
- package/examples/trading-with-schema.yaml +0 -6
- package/examples/trading.yaml +0 -8
- package/package.json +14 -4
- package/public/dashboard-old.html +1299 -0
- package/public/dashboard.html +3115 -1152
- package/examples/distributed-demo/README.md +0 -234
- package/examples/distributed-demo/order.yaml +0 -71
- package/examples/distributed-demo/payment.yaml +0 -60
package/CONTRIBUTING.md
CHANGED
|
@@ -10,7 +10,7 @@ By participating in this project, you agree to maintain a respectful and inclusi
|
|
|
10
10
|
|
|
11
11
|
### Reporting Bugs
|
|
12
12
|
|
|
13
|
-
1. Check if the bug has already been reported in [Issues](https://github.com/fredericcarre/
|
|
13
|
+
1. Check if the bug has already been reported in [Issues](https://github.com/fredericcarre/xcomponent-ai/issues)
|
|
14
14
|
2. If not, create a new issue with:
|
|
15
15
|
- Clear, descriptive title
|
|
16
16
|
- Steps to reproduce
|
|
@@ -20,7 +20,7 @@ By participating in this project, you agree to maintain a respectful and inclusi
|
|
|
20
20
|
|
|
21
21
|
### Suggesting Features
|
|
22
22
|
|
|
23
|
-
1. Check existing [Discussions](https://github.com/fredericcarre/
|
|
23
|
+
1. Check existing [Discussions](https://github.com/fredericcarre/xcomponent-ai/discussions) and Issues
|
|
24
24
|
2. Create a new discussion with:
|
|
25
25
|
- Use case description
|
|
26
26
|
- Proposed solution
|
|
@@ -31,8 +31,8 @@ By participating in this project, you agree to maintain a respectful and inclusi
|
|
|
31
31
|
|
|
32
32
|
1. **Fork and Clone**
|
|
33
33
|
```bash
|
|
34
|
-
git clone https://github.com/YOUR_USERNAME/
|
|
35
|
-
cd
|
|
34
|
+
git clone https://github.com/YOUR_USERNAME/xcomponent-ai.git
|
|
35
|
+
cd xcomponent-ai
|
|
36
36
|
npm install
|
|
37
37
|
```
|
|
38
38
|
|
|
@@ -186,7 +186,7 @@ npm run cli -- load examples/trading.yaml
|
|
|
186
186
|
|
|
187
187
|
## Questions?
|
|
188
188
|
|
|
189
|
-
- Open a [Discussion](https://github.com/fredericcarre/
|
|
189
|
+
- Open a [Discussion](https://github.com/fredericcarre/xcomponent-ai/discussions)
|
|
190
190
|
- Join our community chat (coming soon)
|
|
191
191
|
- Email: contributors@xcomponent.com
|
|
192
192
|
|
|
@@ -53,46 +53,59 @@ stateMachines:
|
|
|
53
53
|
|
|
54
54
|
### Step 2: Create Accumulation Method with Explicit Control
|
|
55
55
|
|
|
56
|
+
**YAML** -- declare the method name on the transition:
|
|
57
|
+
|
|
56
58
|
```yaml
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if (!context.executions) {
|
|
65
|
-
context.executions = [];
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Accumulate quantity from event
|
|
69
|
-
const qty = event.payload.quantity || 0;
|
|
70
|
-
context.executedQuantity += qty;
|
|
71
|
-
|
|
72
|
-
// Track individual executions
|
|
73
|
-
context.executions.push({
|
|
74
|
-
quantity: qty,
|
|
75
|
-
price: event.payload.price,
|
|
76
|
-
executionId: event.payload.executionId,
|
|
77
|
-
timestamp: event.timestamp
|
|
78
|
-
});
|
|
59
|
+
transitions:
|
|
60
|
+
- from: PartiallyExecuted
|
|
61
|
+
to: PartiallyExecuted
|
|
62
|
+
event: EXECUTION_NOTIFICATION
|
|
63
|
+
type: triggerable
|
|
64
|
+
triggeredMethod: accumulateExecution
|
|
65
|
+
```
|
|
79
66
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
},
|
|
92
|
-
timestamp: Date.now()
|
|
93
|
-
});
|
|
94
|
-
}
|
|
67
|
+
**TypeScript** -- implement the handler:
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
runtime.on('triggered_method', async ({ method, event, context, sender }) => {
|
|
71
|
+
if (method === 'accumulateExecution') {
|
|
72
|
+
// Initialize counters
|
|
73
|
+
if (!context.executedQuantity) {
|
|
74
|
+
context.executedQuantity = 0;
|
|
75
|
+
}
|
|
76
|
+
if (!context.executions) {
|
|
77
|
+
context.executions = [];
|
|
95
78
|
}
|
|
79
|
+
|
|
80
|
+
// Accumulate quantity from event
|
|
81
|
+
const qty = event.payload.quantity || 0;
|
|
82
|
+
context.executedQuantity += qty;
|
|
83
|
+
|
|
84
|
+
// Track individual executions
|
|
85
|
+
context.executions.push({
|
|
86
|
+
quantity: qty,
|
|
87
|
+
price: event.payload.price,
|
|
88
|
+
executionId: event.payload.executionId,
|
|
89
|
+
timestamp: event.timestamp
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
console.log(`Executed: ${context.executedQuantity}/${context.totalQuantity}`);
|
|
93
|
+
|
|
94
|
+
// EXPLICIT CONTROL: Decide when to transition
|
|
95
|
+
if (context.executedQuantity >= context.totalQuantity) {
|
|
96
|
+
// Send event to self to trigger completion
|
|
97
|
+
await sender.sendToSelf({
|
|
98
|
+
type: 'FULLY_EXECUTED',
|
|
99
|
+
payload: {
|
|
100
|
+
totalExecuted: context.executedQuantity,
|
|
101
|
+
executionCount: context.executions.length,
|
|
102
|
+
averagePrice: context.executions.reduce((sum, e) => sum + e.price, 0) / context.executions.length
|
|
103
|
+
},
|
|
104
|
+
timestamp: Date.now()
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
});
|
|
96
109
|
```
|
|
97
110
|
|
|
98
111
|
**Key Points:**
|
|
@@ -146,48 +159,12 @@ transitions:
|
|
|
146
159
|
|
|
147
160
|
## 📝 Complete Example
|
|
148
161
|
|
|
162
|
+
**YAML** (`trading.yaml`):
|
|
163
|
+
|
|
149
164
|
```yaml
|
|
150
165
|
name: TradingComponent
|
|
151
166
|
version: 1.0.0
|
|
152
167
|
|
|
153
|
-
triggeredMethods:
|
|
154
|
-
accumulateExecution: |
|
|
155
|
-
async function(event, context, sender) {
|
|
156
|
-
if (!context.executedQuantity) context.executedQuantity = 0;
|
|
157
|
-
if (!context.executions) context.executions = [];
|
|
158
|
-
|
|
159
|
-
const qty = event.payload.quantity || 0;
|
|
160
|
-
context.executedQuantity += qty;
|
|
161
|
-
context.executions.push({
|
|
162
|
-
quantity: qty,
|
|
163
|
-
price: event.payload.price,
|
|
164
|
-
executionId: event.payload.executionId,
|
|
165
|
-
timestamp: event.timestamp
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
console.log(`Executed: ${context.executedQuantity}/${context.totalQuantity}`);
|
|
169
|
-
|
|
170
|
-
// EXPLICIT CONTROL
|
|
171
|
-
if (context.executedQuantity >= context.totalQuantity) {
|
|
172
|
-
await sender.sendToSelf({
|
|
173
|
-
type: 'FULLY_EXECUTED',
|
|
174
|
-
payload: {
|
|
175
|
-
totalExecuted: context.executedQuantity,
|
|
176
|
-
executionCount: context.executions.length
|
|
177
|
-
},
|
|
178
|
-
timestamp: Date.now()
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
handleCompletion: |
|
|
184
|
-
async function(event, context, sender) {
|
|
185
|
-
console.log(`Order completed!`);
|
|
186
|
-
console.log(` Total: ${event.payload.totalExecuted}`);
|
|
187
|
-
console.log(` Executions: ${event.payload.executionCount}`);
|
|
188
|
-
context.stats = event.payload;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
168
|
stateMachines:
|
|
192
169
|
- name: TradingOrder
|
|
193
170
|
initialState: Created
|
|
@@ -242,6 +219,47 @@ stateMachines:
|
|
|
242
219
|
type: triggerable
|
|
243
220
|
```
|
|
244
221
|
|
|
222
|
+
**TypeScript** -- implement the handlers:
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
runtime.on('triggered_method', async ({ method, event, context, sender }) => {
|
|
226
|
+
if (method === 'accumulateExecution') {
|
|
227
|
+
if (!context.executedQuantity) context.executedQuantity = 0;
|
|
228
|
+
if (!context.executions) context.executions = [];
|
|
229
|
+
|
|
230
|
+
const qty = event.payload.quantity || 0;
|
|
231
|
+
context.executedQuantity += qty;
|
|
232
|
+
context.executions.push({
|
|
233
|
+
quantity: qty,
|
|
234
|
+
price: event.payload.price,
|
|
235
|
+
executionId: event.payload.executionId,
|
|
236
|
+
timestamp: event.timestamp
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
console.log(`Executed: ${context.executedQuantity}/${context.totalQuantity}`);
|
|
240
|
+
|
|
241
|
+
// EXPLICIT CONTROL
|
|
242
|
+
if (context.executedQuantity >= context.totalQuantity) {
|
|
243
|
+
await sender.sendToSelf({
|
|
244
|
+
type: 'FULLY_EXECUTED',
|
|
245
|
+
payload: {
|
|
246
|
+
totalExecuted: context.executedQuantity,
|
|
247
|
+
executionCount: context.executions.length
|
|
248
|
+
},
|
|
249
|
+
timestamp: Date.now()
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (method === 'handleCompletion') {
|
|
255
|
+
console.log(`Order completed!`);
|
|
256
|
+
console.log(` Total: ${event.payload.totalExecuted}`);
|
|
257
|
+
console.log(` Executions: ${event.payload.executionCount}`);
|
|
258
|
+
context.stats = event.payload;
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
```
|
|
262
|
+
|
|
245
263
|
---
|
|
246
264
|
|
|
247
265
|
## 🧪 Testing
|
package/EXTERNAL-API.md
CHANGED
|
@@ -714,6 +714,6 @@ public void onOrderValidated(String orderId) {
|
|
|
714
714
|
|
|
715
715
|
- See [SCALABILITY.md](./SCALABILITY.md) for production deployment
|
|
716
716
|
- See [LLM-GUIDE.md](./LLM-GUIDE.md) for YAML FSM design patterns
|
|
717
|
-
- See `examples/distributed-
|
|
717
|
+
- See `examples/distributed/` (RabbitMQ) or `examples/distributed-redis/` (Redis) for working examples
|
|
718
718
|
|
|
719
719
|
**Built for interoperability.** Any language, any platform. 🌍
|
package/LLM-GUIDE.md
CHANGED
|
@@ -50,8 +50,6 @@ stateMachines:
|
|
|
50
50
|
to: Validated
|
|
51
51
|
event: VALIDATE
|
|
52
52
|
type: triggerable
|
|
53
|
-
guards:
|
|
54
|
-
- keys: [orderId, amount]
|
|
55
53
|
- from: Validated
|
|
56
54
|
to: Executed
|
|
57
55
|
event: EXECUTE
|
|
@@ -197,7 +195,7 @@ GET /api/components/:componentName/diagrams/:machineName
|
|
|
197
195
|
|
|
198
196
|
Returns Mermaid `stateDiagram-v2` syntax with:
|
|
199
197
|
- State styling (entry/orange, final/green, error/red)
|
|
200
|
-
- Transitions with
|
|
198
|
+
- Transitions with event labels
|
|
201
199
|
- State descriptions as notes
|
|
202
200
|
|
|
203
201
|
### 6. Distributed Mode (Multi-Process)
|
|
@@ -233,97 +231,153 @@ states:
|
|
|
233
231
|
|
|
234
232
|
When Order transitions to "Validated" in Process 1, Redis automatically delivers the PROCESS event to PaymentComponent in Process 2.
|
|
235
233
|
|
|
236
|
-
See: `examples/distributed-
|
|
234
|
+
See: `examples/distributed/` (RabbitMQ) or `examples/distributed-redis/` (Redis) for complete working examples.
|
|
237
235
|
|
|
238
|
-
###
|
|
236
|
+
### 6b. Entry Point Modes (Singleton vs Multiple)
|
|
239
237
|
|
|
240
|
-
|
|
238
|
+
**Entry point machines** can operate in two modes, controlling how instances are created:
|
|
239
|
+
|
|
240
|
+
#### Configuration Options
|
|
241
241
|
|
|
242
242
|
```yaml
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
'Monitoring', // Target state
|
|
253
|
-
{
|
|
254
|
-
type: 'ORDER_UPDATE',
|
|
255
|
-
payload: {
|
|
256
|
-
orderId: context.orderId,
|
|
257
|
-
executedQuantity: context.executedQuantity
|
|
258
|
-
},
|
|
259
|
-
timestamp: Date.now()
|
|
260
|
-
},
|
|
261
|
-
[
|
|
262
|
-
// FILTERS: Property-based targeting
|
|
263
|
-
{ property: 'customerId', value: context.customerId },
|
|
264
|
-
{ property: 'assetClass', operator: '===', value: 'EQUITY' }
|
|
265
|
-
]
|
|
266
|
-
);
|
|
267
|
-
|
|
268
|
-
console.log(`Notified ${count} risk monitor(s)`);
|
|
269
|
-
}
|
|
243
|
+
name: OrderComponent
|
|
244
|
+
entryMachine: Order # Which machine is the entry point
|
|
245
|
+
entryMachineMode: multiple # 'singleton' or 'multiple' (default: 'singleton')
|
|
246
|
+
autoCreateEntryPoint: false # Auto-create instance on startup? (default: true for singleton, false for multiple)
|
|
247
|
+
|
|
248
|
+
stateMachines:
|
|
249
|
+
- name: Order
|
|
250
|
+
initialState: Created
|
|
251
|
+
# ...
|
|
270
252
|
```
|
|
271
253
|
|
|
272
|
-
|
|
273
|
-
- `sender.sendTo(instanceId, event)` - Send to specific instance
|
|
274
|
-
- `sender.broadcast(machine, state, event, filters?)` - Broadcast with optional filters
|
|
275
|
-
- `sender.broadcastToComponent(component, machine, state, event, filters?)` - Cross-component broadcast
|
|
276
|
-
- `sender.createInstance(machine, context)` - Create new instance
|
|
254
|
+
#### Singleton Mode (Default)
|
|
277
255
|
|
|
278
|
-
|
|
256
|
+
Best for: Monitors, supervisors, background processors
|
|
279
257
|
|
|
280
|
-
|
|
258
|
+
```yaml
|
|
259
|
+
name: MonitoringComponent
|
|
260
|
+
entryMachine: SystemMonitor
|
|
261
|
+
entryMachineMode: singleton # Only ONE instance allowed
|
|
262
|
+
autoCreateEntryPoint: true # Created automatically on startup
|
|
281
263
|
|
|
282
|
-
|
|
264
|
+
stateMachines:
|
|
265
|
+
- name: SystemMonitor
|
|
266
|
+
initialState: Idle
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
**Behavior:**
|
|
270
|
+
- ✅ Runtime auto-creates the instance on startup
|
|
271
|
+
- ❌ API calls to create additional instances are rejected
|
|
272
|
+
- ✅ Instance recreated automatically if component restarts
|
|
273
|
+
|
|
274
|
+
#### Multiple Mode
|
|
275
|
+
|
|
276
|
+
Best for: Orders, payments, user workflows - entities created by users
|
|
277
|
+
|
|
278
|
+
```yaml
|
|
279
|
+
name: OrderComponent
|
|
280
|
+
entryMachine: Order
|
|
281
|
+
entryMachineMode: multiple # Multiple instances allowed
|
|
282
|
+
autoCreateEntryPoint: false # Don't auto-create (user creates via API)
|
|
283
|
+
|
|
284
|
+
stateMachines:
|
|
285
|
+
- name: Order
|
|
286
|
+
initialState: Created
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
**Behavior:**
|
|
290
|
+
- ❌ No instance created on startup (unless autoCreateEntryPoint: true)
|
|
291
|
+
- ✅ Create instances via API: `POST /api/components/OrderComponent/instances`
|
|
292
|
+
- ✅ Dashboard "New Instance" button available for manual creation
|
|
293
|
+
|
|
294
|
+
#### Creating Instances via API
|
|
295
|
+
|
|
296
|
+
For **multiple mode** components, create instances programmatically:
|
|
283
297
|
|
|
284
|
-
|
|
298
|
+
```bash
|
|
299
|
+
# Create Order instance with context
|
|
300
|
+
curl -X POST http://localhost:3000/api/components/OrderComponent/instances \
|
|
301
|
+
-H "Content-Type: application/json" \
|
|
302
|
+
-d '{
|
|
303
|
+
"machineName": "Order",
|
|
304
|
+
"context": {
|
|
305
|
+
"orderId": "ORD-123",
|
|
306
|
+
"amount": 99.99,
|
|
307
|
+
"customerId": "CUST-456"
|
|
308
|
+
}
|
|
309
|
+
}'
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
Or via the dashboard UI: Click the **"+ New"** button in the Instances sidebar.
|
|
285
313
|
|
|
286
|
-
|
|
314
|
+
#### Summary Table
|
|
287
315
|
|
|
288
|
-
|
|
316
|
+
| Mode | autoCreateEntryPoint | Behavior |
|
|
317
|
+
|------|---------------------|----------|
|
|
318
|
+
| `singleton` | `true` (default) | One instance auto-created, API rejects new ones |
|
|
319
|
+
| `singleton` | `false` | One instance allowed, created via API |
|
|
320
|
+
| `multiple` | `true` | One instance auto-created, more via API |
|
|
321
|
+
| `multiple` | `false` (default) | No auto-create, all via API or dashboard |
|
|
322
|
+
|
|
323
|
+
### 7. Broadcast with Property Filters (from Triggered Methods)
|
|
324
|
+
|
|
325
|
+
Triggered methods can send events to **specific instances** using property filters:
|
|
326
|
+
|
|
327
|
+
**YAML** — declare the method name on the transition:
|
|
289
328
|
|
|
290
329
|
```yaml
|
|
291
330
|
transitions:
|
|
292
|
-
# TRANSITION 1: Stay in PartiallyExecuted
|
|
293
331
|
- from: PartiallyExecuted
|
|
294
332
|
to: PartiallyExecuted
|
|
295
333
|
event: EXECUTION_NOTIFICATION
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
condition: "context.executedQuantity < context.totalQuantity"
|
|
334
|
+
type: triggerable
|
|
335
|
+
triggeredMethod: notifyRiskMonitors
|
|
336
|
+
```
|
|
300
337
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
338
|
+
**TypeScript** — implement the handler:
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
runtime.on('triggered_method', async ({ method, event, context, sender }) => {
|
|
342
|
+
if (method === 'notifyRiskMonitors') {
|
|
343
|
+
// Update local context
|
|
344
|
+
context.executedQuantity += event.payload.quantity;
|
|
345
|
+
|
|
346
|
+
// BROADCAST to risk monitors for THIS CUSTOMER ONLY
|
|
347
|
+
const count = await sender.broadcast(
|
|
348
|
+
'RiskMonitor', // Target machine
|
|
349
|
+
{
|
|
350
|
+
type: 'ORDER_UPDATE',
|
|
351
|
+
payload: {
|
|
352
|
+
orderId: context.orderId,
|
|
353
|
+
executedQuantity: context.executedQuantity
|
|
354
|
+
},
|
|
355
|
+
timestamp: Date.now()
|
|
356
|
+
},
|
|
357
|
+
[
|
|
358
|
+
// FILTERS: Property-based targeting
|
|
359
|
+
{ property: 'customerId', value: context.customerId },
|
|
360
|
+
{ property: 'assetClass', operator: '===', value: 'EQUITY' }
|
|
361
|
+
],
|
|
362
|
+
'Monitoring' // Target state (optional)
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
console.log(`Notified ${count} risk monitor(s)`);
|
|
366
|
+
}
|
|
367
|
+
});
|
|
311
368
|
```
|
|
312
369
|
|
|
313
|
-
**
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
370
|
+
**Available sender methods:**
|
|
371
|
+
- `sender.sendToSelf(event)` - Send event to current instance
|
|
372
|
+
- `sender.sendTo(instanceId, event)` - Send to specific instance
|
|
373
|
+
- `sender.sendToComponent(componentName, instanceId, event)` - Cross-component to specific instance
|
|
374
|
+
- `sender.broadcast(machineName, event, currentState?, componentName?)` - Broadcast to instances
|
|
375
|
+
- `sender.createInstance(machineName, context)` - Create new instance
|
|
376
|
+
- `sender.createInstanceInComponent(componentName, machineName, context)` - Cross-component instance creation
|
|
319
377
|
|
|
320
|
-
|
|
321
|
-
- Triggered method runs **before** guards (can update context)
|
|
322
|
-
- Transitions defined in YAML are evaluated in order
|
|
323
|
-
- First match wins - other transitions not tried
|
|
324
|
-
- Useful for accumulation patterns (partial vs. full execution)
|
|
378
|
+
Instance filtering is done via `matchingRules` on the target transition in YAML, not in the sender call.
|
|
325
379
|
|
|
326
|
-
See: `
|
|
380
|
+
See: `examples/advanced-patterns-demo.yaml`
|
|
327
381
|
|
|
328
382
|
---
|
|
329
383
|
|
|
@@ -379,20 +433,6 @@ stateMachines:
|
|
|
379
433
|
xcomponent-ai serve order.yaml payment.yaml
|
|
380
434
|
```
|
|
381
435
|
|
|
382
|
-
### Guards
|
|
383
|
-
|
|
384
|
-
Conditional transitions:
|
|
385
|
-
|
|
386
|
-
```yaml
|
|
387
|
-
transitions:
|
|
388
|
-
- from: Pending
|
|
389
|
-
to: Approved
|
|
390
|
-
event: APPROVE
|
|
391
|
-
guards:
|
|
392
|
-
- keys: [amount, clientId] # Required fields
|
|
393
|
-
- customFunction: "event.payload.amount <= 100000" # Max limit
|
|
394
|
-
```
|
|
395
|
-
|
|
396
436
|
### Payload Templating
|
|
397
437
|
|
|
398
438
|
Pass context data between machines:
|
|
@@ -444,20 +484,7 @@ states:
|
|
|
444
484
|
address: "{{shippingAddress}}"
|
|
445
485
|
```
|
|
446
486
|
|
|
447
|
-
### Pattern 3:
|
|
448
|
-
|
|
449
|
-
```yaml
|
|
450
|
-
transitions:
|
|
451
|
-
- from: Submitted
|
|
452
|
-
to: Approved
|
|
453
|
-
event: APPROVE
|
|
454
|
-
guards:
|
|
455
|
-
- keys: [complianceCheck, riskScore]
|
|
456
|
-
- customFunction: "event.payload.riskScore < 70"
|
|
457
|
-
- customFunction: "event.payload.complianceCheck === 'PASSED'"
|
|
458
|
-
```
|
|
459
|
-
|
|
460
|
-
### Pattern 4: Timeout Transitions
|
|
487
|
+
### Pattern 3: Timeout Transitions
|
|
461
488
|
|
|
462
489
|
```yaml
|
|
463
490
|
transitions:
|
|
@@ -538,10 +565,9 @@ describe('OrderEntry FSM', () => {
|
|
|
538
565
|
1. **Start with YAML** - Define FSM first, code second
|
|
539
566
|
2. **Use contextSchema** - Let dashboard generate forms automatically
|
|
540
567
|
3. **Leverage cascadingRules** - Reduce orchestration code
|
|
541
|
-
4. **
|
|
542
|
-
5. **
|
|
543
|
-
6. **Use
|
|
544
|
-
7. **Use programmatic mode for production** - More control, better scaling
|
|
568
|
+
4. **Test FSM** - Write tests for state transitions
|
|
569
|
+
5. **Use serve for demos** - Quick prototypes with dashboard
|
|
570
|
+
6. **Use programmatic mode for production** - More control, better scaling
|
|
545
571
|
|
|
546
572
|
---
|
|
547
573
|
|
package/QUICKSTART.md
CHANGED
|
@@ -199,12 +199,29 @@ xcomponent-ai init loan-approval
|
|
|
199
199
|
cd loan-approval
|
|
200
200
|
|
|
201
201
|
# Edit fsm/LoanApprovalComponent.yaml
|
|
202
|
-
# (Add your states, transitions
|
|
202
|
+
# (Add your states, transitions)
|
|
203
203
|
|
|
204
204
|
# Test your FSM
|
|
205
205
|
xcomponent-ai serve fsm/LoanApprovalComponent.yaml
|
|
206
206
|
```
|
|
207
207
|
|
|
208
|
+
## 🏗️ XComponent Pattern (Advanced)
|
|
209
|
+
|
|
210
|
+
For orchestrating multiple state machines with automatic instance creation:
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
# Use the XComponent pattern demo
|
|
214
|
+
xcomponent-ai serve examples/xcomponent-pattern-demo.yaml
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
This demonstrates:
|
|
218
|
+
- **Entry Point** auto-created on startup (⭐ OrderManager)
|
|
219
|
+
- **Inter-machine transitions** creating new instances (green arrows in dashboard)
|
|
220
|
+
- **Auto-deallocation** of completed instances
|
|
221
|
+
- **Component View** showing all machines and their connections
|
|
222
|
+
|
|
223
|
+
See [XCOMPONENT-PATTERN.md](./XCOMPONENT-PATTERN.md) for complete guide.
|
|
224
|
+
|
|
208
225
|
## 🎓 Next Steps
|
|
209
226
|
|
|
210
227
|
- 📖 Read the [Framework Guide](./LLM_FRAMEWORK_GUIDE.md) to understand concepts
|