footprintjs 0.2.1 → 0.2.3
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/README.md +85 -32
- package/dist/esm/lib/builder/FlowChartBuilder.js +54 -91
- package/dist/esm/lib/builder/types.js +1 -1
- package/dist/esm/lib/engine/graph/StageNode.js +1 -1
- package/dist/esm/lib/engine/handlers/ChildrenExecutor.js +5 -5
- package/dist/esm/lib/engine/handlers/ContinuationResolver.js +6 -6
- package/dist/esm/lib/engine/handlers/DeciderHandler.js +4 -4
- package/dist/esm/lib/engine/handlers/NodeResolver.js +1 -2
- package/dist/esm/lib/engine/handlers/RuntimeStructureManager.js +1 -2
- package/dist/esm/lib/engine/handlers/SelectorHandler.js +6 -6
- package/dist/esm/lib/engine/narrative/ControlFlowNarrativeGenerator.js +13 -18
- package/dist/esm/lib/engine/narrative/types.js +1 -1
- package/dist/esm/lib/engine/traversal/FlowchartTraverser.js +10 -11
- package/dist/esm/lib/engine/types.js +1 -1
- package/dist/esm/lib/runner/ExecutionRuntime.js +1 -1
- package/dist/esm/types/lib/builder/FlowChartBuilder.d.ts +8 -10
- package/dist/esm/types/lib/builder/types.d.ts +0 -6
- package/dist/esm/types/lib/engine/graph/StageNode.d.ts +0 -2
- package/dist/esm/types/lib/engine/narrative/ControlFlowNarrativeGenerator.d.ts +6 -6
- package/dist/esm/types/lib/engine/narrative/types.d.ts +6 -6
- package/dist/esm/types/lib/engine/types.d.ts +0 -1
- package/dist/esm/types/lib/runner/ExecutionRuntime.d.ts +0 -1
- package/dist/lib/builder/FlowChartBuilder.js +54 -91
- package/dist/lib/builder/types.js +1 -1
- package/dist/lib/engine/graph/StageNode.js +1 -1
- package/dist/lib/engine/handlers/ChildrenExecutor.js +5 -5
- package/dist/lib/engine/handlers/ContinuationResolver.js +6 -6
- package/dist/lib/engine/handlers/DeciderHandler.js +4 -4
- package/dist/lib/engine/handlers/NodeResolver.js +1 -2
- package/dist/lib/engine/handlers/RuntimeStructureManager.js +1 -2
- package/dist/lib/engine/handlers/SelectorHandler.js +6 -6
- package/dist/lib/engine/narrative/ControlFlowNarrativeGenerator.js +13 -18
- package/dist/lib/engine/narrative/types.js +1 -1
- package/dist/lib/engine/traversal/FlowchartTraverser.js +10 -11
- package/dist/lib/engine/types.js +1 -1
- package/dist/lib/runner/ExecutionRuntime.js +1 -1
- package/dist/types/lib/builder/FlowChartBuilder.d.ts +8 -10
- package/dist/types/lib/builder/types.d.ts +0 -6
- package/dist/types/lib/engine/graph/StageNode.d.ts +0 -2
- package/dist/types/lib/engine/narrative/ControlFlowNarrativeGenerator.d.ts +6 -6
- package/dist/types/lib/engine/narrative/types.d.ts +6 -6
- package/dist/types/lib/engine/types.d.ts +0 -1
- package/dist/types/lib/runner/ExecutionRuntime.d.ts +0 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,9 +21,48 @@ FootPrint is a runtime for building **flowchart pipelines** where each node is j
|
|
|
21
21
|
npm install footprintjs
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { flowChart, FlowChartExecutor, ScopeFacade, toScopeFactory } from 'footprintjs';
|
|
28
|
+
|
|
29
|
+
const chart = flowChart('Greet', (scope) => {
|
|
30
|
+
scope.setValue('name', 'Alice');
|
|
31
|
+
})
|
|
32
|
+
.addFunction('Personalize', (scope) => {
|
|
33
|
+
const name = scope.getValue('name');
|
|
34
|
+
scope.setValue('message', `Hello, ${name}!`);
|
|
35
|
+
})
|
|
36
|
+
.setEnableNarrative()
|
|
37
|
+
.build();
|
|
38
|
+
|
|
39
|
+
const executor = new FlowChartExecutor(chart, toScopeFactory(ScopeFacade));
|
|
40
|
+
const result = await executor.run();
|
|
41
|
+
|
|
42
|
+
console.log(executor.getNarrative());
|
|
43
|
+
// Stage 1: The process began with Greet.
|
|
44
|
+
// Step 1: Write name = "Alice"
|
|
45
|
+
// Stage 2: Next, it moved on to Personalize.
|
|
46
|
+
// Step 1: Read name = "Alice"
|
|
47
|
+
// Step 2: Write message = "Hello, Alice!"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
> **[Try it in the browser](https://footprintjs.github.io/footprint-playground/)** — no install needed
|
|
51
|
+
>
|
|
52
|
+
> **[Browse 20+ examples](https://github.com/footprintjs/footPrint-samples)** — features, flowchart patterns, and a full loan underwriting demo
|
|
53
|
+
|
|
24
54
|
---
|
|
25
55
|
|
|
26
|
-
## Why
|
|
56
|
+
## Why FootPrint?
|
|
57
|
+
|
|
58
|
+
| | Without FootPrint | With FootPrint |
|
|
59
|
+
|---|---|---|
|
|
60
|
+
| **LLM explains a decision** | Reconstruct from scattered logs; expensive, slow, hallucination-prone | Read the causal trace; cheap model, fewer tokens, zero hallucination |
|
|
61
|
+
| **Tool descriptions** | Write and maintain them by hand | Auto-generated from the flowchart structure |
|
|
62
|
+
| **Debugging** | `console.log` + guesswork | Time-travel replay to any stage |
|
|
63
|
+
| **State management** | Global/manual, race-prone | Transactional scope with atomic commits |
|
|
64
|
+
|
|
65
|
+
### Example: Loan rejection
|
|
27
66
|
|
|
28
67
|
A loan application pipeline rejects Bob. The user asks: **"Why was I rejected?"**
|
|
29
68
|
|
|
@@ -69,67 +108,81 @@ No one wrote those trace sentences. Stage functions just read and write scope &m
|
|
|
69
108
|
|
|
70
109
|
```typescript
|
|
71
110
|
import {
|
|
72
|
-
|
|
111
|
+
flowChart, FlowChartExecutor, ScopeFacade, toScopeFactory,
|
|
73
112
|
} from 'footprintjs';
|
|
74
113
|
|
|
75
|
-
// ── Stage functions: just
|
|
76
|
-
|
|
77
|
-
const receiveApplication =
|
|
78
|
-
scope.setValue('app',
|
|
114
|
+
// ── Stage functions: just read and write scope ─────────────────────────
|
|
115
|
+
|
|
116
|
+
const receiveApplication = (scope: ScopeFacade) => {
|
|
117
|
+
scope.setValue('app', {
|
|
118
|
+
applicantName: 'Bob',
|
|
119
|
+
annualIncome: 42_000,
|
|
120
|
+
monthlyDebts: 2_100,
|
|
121
|
+
creditScore: 580,
|
|
122
|
+
employmentType: 'self-employed',
|
|
123
|
+
employmentYears: 1,
|
|
124
|
+
});
|
|
79
125
|
};
|
|
80
126
|
|
|
81
|
-
const pullCreditReport =
|
|
127
|
+
const pullCreditReport = (scope: ScopeFacade) => {
|
|
82
128
|
const { creditScore } = scope.getValue('app') as any;
|
|
83
|
-
const tier = creditScore >= 740 ? 'excellent'
|
|
84
|
-
|
|
129
|
+
const tier = creditScore >= 740 ? 'excellent'
|
|
130
|
+
: creditScore >= 670 ? 'good'
|
|
131
|
+
: creditScore >= 580 ? 'fair' : 'poor';
|
|
132
|
+
|
|
85
133
|
scope.setValue('creditTier', tier);
|
|
86
134
|
scope.setValue('creditFlags', tier === 'fair' ? ['below-average credit'] : []);
|
|
87
135
|
};
|
|
88
136
|
|
|
89
|
-
const calculateDTI =
|
|
137
|
+
const calculateDTI = (scope: ScopeFacade) => {
|
|
90
138
|
const { annualIncome, monthlyDebts } = scope.getValue('app') as any;
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
scope.setValue('
|
|
94
|
-
scope.setValue('
|
|
95
|
-
scope.setValue('dtiFlags',
|
|
96
|
-
dtiRatio > 0.43 ? [`DTI at ${Math.round(dtiRatio * 100)}% exceeds 43%`] : []);
|
|
139
|
+
const ratio = Math.round((monthlyDebts / (annualIncome / 12)) * 100) / 100;
|
|
140
|
+
|
|
141
|
+
scope.setValue('dtiRatio', ratio);
|
|
142
|
+
scope.setValue('dtiFlags', ratio > 0.43 ? [`DTI at ${Math.round(ratio * 100)}% exceeds 43%`] : []);
|
|
97
143
|
};
|
|
98
144
|
|
|
99
|
-
const
|
|
100
|
-
const
|
|
101
|
-
const
|
|
145
|
+
const verifyEmployment = (scope: ScopeFacade) => {
|
|
146
|
+
const { employmentType, employmentYears } = scope.getValue('app') as any;
|
|
147
|
+
const verified = employmentType !== 'self-employed' || employmentYears >= 2;
|
|
148
|
+
|
|
149
|
+
scope.setValue('employmentVerified', verified);
|
|
150
|
+
scope.setValue('employmentFlags', !verified
|
|
151
|
+
? [`${employmentType}, ${employmentYears}yr < 2yr minimum`] : []);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const assessRisk = (scope: ScopeFacade) => {
|
|
155
|
+
const tier = scope.getValue('creditTier') as string;
|
|
156
|
+
const ratio = scope.getValue('dtiRatio') as number;
|
|
102
157
|
const verified = scope.getValue('employmentVerified') as boolean;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
scope.setValue('riskTier', riskTier);
|
|
106
|
-
scope.setValue('riskFactors', [/* collected flags */]);
|
|
158
|
+
|
|
159
|
+
scope.setValue('riskTier', (!verified || ratio > 0.43 || tier === 'poor') ? 'high' : 'low');
|
|
107
160
|
};
|
|
108
161
|
|
|
162
|
+
// Deciders return a branch ID — the only stage that needs a return value
|
|
109
163
|
const loanDecider = (scope: ScopeFacade): string => {
|
|
110
164
|
const tier = scope.getValue('riskTier') as string;
|
|
111
|
-
|
|
165
|
+
if (tier === 'low') return 'approved';
|
|
166
|
+
if (tier === 'high') return 'rejected';
|
|
167
|
+
return 'manual-review';
|
|
112
168
|
};
|
|
113
169
|
|
|
114
|
-
// ── Build
|
|
170
|
+
// ── Build → Run → Narrative (D3-style chaining) ──────────────────────
|
|
115
171
|
|
|
116
|
-
const chart =
|
|
172
|
+
const chart = flowChart('ReceiveApplication', receiveApplication)
|
|
117
173
|
.setEnableNarrative()
|
|
118
|
-
.start('ReceiveApplication', receiveApplication)
|
|
119
174
|
.addFunction('PullCreditReport', pullCreditReport)
|
|
120
175
|
.addFunction('CalculateDTI', calculateDTI)
|
|
121
176
|
.addFunction('VerifyEmployment', verifyEmployment)
|
|
122
177
|
.addFunction('AssessRisk', assessRisk)
|
|
123
178
|
.addDeciderFunction('LoanDecision', loanDecider as any)
|
|
124
|
-
.addFunctionBranch('approved', '
|
|
125
|
-
.addFunctionBranch('rejected', '
|
|
126
|
-
.addFunctionBranch('manual-review', 'ManualReview',
|
|
179
|
+
.addFunctionBranch('approved', 'Approve', () => {})
|
|
180
|
+
.addFunctionBranch('rejected', 'Reject', () => {})
|
|
181
|
+
.addFunctionBranch('manual-review', 'ManualReview', () => {})
|
|
127
182
|
.setDefault('manual-review')
|
|
128
183
|
.end()
|
|
129
184
|
.build();
|
|
130
185
|
|
|
131
|
-
// ── Run and get the narrative ──────────────────────────────────────────
|
|
132
|
-
|
|
133
186
|
const executor = new FlowChartExecutor(chart, toScopeFactory(ScopeFacade));
|
|
134
187
|
await executor.run();
|
|
135
188
|
|