flowneer 0.9.1 → 0.9.4
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/dist/{FlowBuilder-CqWK42bA.d.ts → FlowBuilder-8kwREeyD.d.ts} +81 -14
- package/dist/index.d.ts +1 -1
- package/dist/index.js +45 -5
- package/dist/plugins/agent/index.d.ts +7 -1
- package/dist/plugins/dev/index.d.ts +103 -24
- package/dist/plugins/dev/index.js +117 -27
- package/dist/plugins/eval/index.d.ts +1 -1
- package/dist/plugins/graph/index.d.ts +1 -1
- package/dist/plugins/graph/index.js +486 -44
- package/dist/plugins/index.d.ts +2 -2
- package/dist/plugins/index.js +584 -126
- package/dist/plugins/llm/index.d.ts +29 -1
- package/dist/plugins/memory/index.d.ts +5 -1
- package/dist/plugins/messaging/index.d.ts +9 -1
- package/dist/plugins/observability/index.d.ts +13 -1
- package/dist/plugins/output/index.d.ts +1 -1
- package/dist/plugins/persistence/index.d.ts +168 -34
- package/dist/plugins/persistence/index.js +201 -62
- package/dist/plugins/resilience/index.d.ts +14 -1
- package/dist/plugins/telemetry/index.d.ts +1 -1
- package/dist/plugins/tools/index.d.ts +8 -1
- package/dist/presets/agent/index.d.ts +7 -1
- package/dist/presets/agent/index.js +45 -5
- package/dist/presets/config/index.d.ts +1 -1
- package/dist/presets/config/index.js +45 -5
- package/dist/presets/index.d.ts +1 -1
- package/dist/presets/index.js +45 -5
- package/dist/presets/pipeline/index.d.ts +1 -1
- package/dist/presets/pipeline/index.js +45 -5
- package/dist/presets/rag/index.d.ts +1 -1
- package/dist/presets/rag/index.js +45 -5
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +45 -5
- package/package.json +1 -1
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extendable shared-state interface augmented by each plugin via declaration
|
|
3
|
+
* merging. Extend your state type with this to get all plugin-provided keys
|
|
4
|
+
* typed and documented automatically — no manual `__*` declarations needed.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* import type { AugmentedState } from "flowneer";
|
|
8
|
+
* interface MyState extends AugmentedState {
|
|
9
|
+
* topic: string;
|
|
10
|
+
* results: string[];
|
|
11
|
+
* }
|
|
12
|
+
*/
|
|
13
|
+
interface AugmentedState {
|
|
14
|
+
}
|
|
1
15
|
/**
|
|
2
16
|
* Generic validator interface — structurally compatible with Zod, ArkType,
|
|
3
17
|
* Valibot, or any custom implementation that exposes `.parse(input)`.
|
|
@@ -62,7 +76,7 @@ interface RunOptions {
|
|
|
62
76
|
/** Metadata exposed to hooks — intentionally minimal to avoid coupling. */
|
|
63
77
|
interface StepMeta {
|
|
64
78
|
index: number;
|
|
65
|
-
type: "fn" | "branch" | "loop" | "batch" | "parallel" | "anchor";
|
|
79
|
+
type: "fn" | "branch" | "loop" | "batch" | "parallel" | "anchor" | "dag";
|
|
66
80
|
label?: string;
|
|
67
81
|
}
|
|
68
82
|
/**
|
|
@@ -105,8 +119,18 @@ interface FlowHooks<S = any, P extends Record<string, unknown> = Record<string,
|
|
|
105
119
|
* `fnIndex` is the position within the fns array.
|
|
106
120
|
*/
|
|
107
121
|
wrapParallelFn?: (meta: StepMeta, fnIndex: number, next: () => Promise<void>, shared: S, params: P) => Promise<void>;
|
|
108
|
-
onError?: (meta: StepMeta, error: unknown, shared: S, params: P) => void
|
|
122
|
+
onError?: (meta: StepMeta, error: unknown, shared: S, params: P) => void | Promise<void>;
|
|
109
123
|
afterFlow?: (shared: S, params: P) => void | Promise<void>;
|
|
124
|
+
/**
|
|
125
|
+
* Fires after each loop iteration completes. `iteration` is zero-based.
|
|
126
|
+
* Only fires for `.loop()` steps, not `.batch()` or `.parallel()`.
|
|
127
|
+
*/
|
|
128
|
+
onLoopIteration?: (meta: StepMeta, iteration: number, shared: S, params: P) => void | Promise<void>;
|
|
129
|
+
/**
|
|
130
|
+
* Fires when a goto jump resolves to an anchor (i.e. a step returned `"#anchorName"`).
|
|
131
|
+
* `anchorName` is the anchor label without the `#` prefix.
|
|
132
|
+
*/
|
|
133
|
+
onAnchorHit?: (anchorName: string, shared: S, params: P) => void | Promise<void>;
|
|
110
134
|
}
|
|
111
135
|
/**
|
|
112
136
|
* A plugin is an object whose keys become methods on a `FlowBuilder.extend()` subclass prototype.
|
|
@@ -120,6 +144,17 @@ interface FlowHooks<S = any, P extends Record<string, unknown> = Record<string,
|
|
|
120
144
|
* ```
|
|
121
145
|
*/
|
|
122
146
|
type FlowneerPlugin = Record<string, (this: FlowBuilder<any, any>, ...args: any[]) => any>;
|
|
147
|
+
type ResolvedHooks<S, P extends Record<string, unknown>> = {
|
|
148
|
+
beforeFlow: NonNullable<FlowHooks<S, P>["beforeFlow"]>[];
|
|
149
|
+
beforeStep: NonNullable<FlowHooks<S, P>["beforeStep"]>[];
|
|
150
|
+
wrapStep: NonNullable<FlowHooks<S, P>["wrapStep"]>[];
|
|
151
|
+
afterStep: NonNullable<FlowHooks<S, P>["afterStep"]>[];
|
|
152
|
+
wrapParallelFn: NonNullable<FlowHooks<S, P>["wrapParallelFn"]>[];
|
|
153
|
+
onError: NonNullable<FlowHooks<S, P>["onError"]>[];
|
|
154
|
+
afterFlow: NonNullable<FlowHooks<S, P>["afterFlow"]>[];
|
|
155
|
+
onLoopIteration: NonNullable<FlowHooks<S, P>["onLoopIteration"]>[];
|
|
156
|
+
onAnchorHit: NonNullable<FlowHooks<S, P>["onAnchorHit"]>[];
|
|
157
|
+
};
|
|
123
158
|
|
|
124
159
|
interface FnStep<S, P extends Record<string, unknown>> {
|
|
125
160
|
type: "fn";
|
|
@@ -166,17 +201,36 @@ interface AnchorStep {
|
|
|
166
201
|
/** Maximum number of times a goto may jump to this anchor per run. */
|
|
167
202
|
maxVisits?: number;
|
|
168
203
|
}
|
|
169
|
-
|
|
204
|
+
/**
|
|
205
|
+
* A compiled DAG step. Produced by the graph plugin's `.compile()` and
|
|
206
|
+
* executed by the registered "dag" step handler, which traverses nodes
|
|
207
|
+
* in topological order and fires per-node lifecycle hooks natively.
|
|
208
|
+
*/
|
|
209
|
+
interface DagStep<S, P extends Record<string, unknown>> {
|
|
210
|
+
type: "dag";
|
|
211
|
+
nodes: Map<string, {
|
|
212
|
+
name: string;
|
|
213
|
+
fn: NodeFn<S, P>;
|
|
214
|
+
options?: NodeOptions<S, P>;
|
|
215
|
+
}>;
|
|
216
|
+
/** Topologically sorted node names. */
|
|
217
|
+
order: string[];
|
|
218
|
+
/** Conditional edges that point backwards (cycles). Always have a condition. */
|
|
219
|
+
backEdges: Array<{
|
|
220
|
+
from: string;
|
|
221
|
+
to: string;
|
|
222
|
+
condition: (shared: S, params: P) => boolean | Promise<boolean>;
|
|
223
|
+
}>;
|
|
224
|
+
/** Conditional edges that skip forward (not back-edges). Always have a condition. */
|
|
225
|
+
conditionalForward: Array<{
|
|
226
|
+
from: string;
|
|
227
|
+
to: string;
|
|
228
|
+
condition: (shared: S, params: P) => boolean | Promise<boolean>;
|
|
229
|
+
}>;
|
|
230
|
+
label?: string;
|
|
231
|
+
}
|
|
232
|
+
type Step<S, P extends Record<string, unknown>> = FnStep<S, P> | BranchStep<S, P> | LoopStep<S, P> | BatchStep<S, P> | ParallelStep<S, P> | AnchorStep | DagStep<S, P>;
|
|
170
233
|
|
|
171
|
-
type ResolvedHooks<S, P extends Record<string, unknown>> = {
|
|
172
|
-
beforeFlow: NonNullable<FlowHooks<S, P>["beforeFlow"]>[];
|
|
173
|
-
beforeStep: NonNullable<FlowHooks<S, P>["beforeStep"]>[];
|
|
174
|
-
wrapStep: NonNullable<FlowHooks<S, P>["wrapStep"]>[];
|
|
175
|
-
afterStep: NonNullable<FlowHooks<S, P>["afterStep"]>[];
|
|
176
|
-
wrapParallelFn: NonNullable<FlowHooks<S, P>["wrapParallelFn"]>[];
|
|
177
|
-
onError: NonNullable<FlowHooks<S, P>["onError"]>[];
|
|
178
|
-
afterFlow: NonNullable<FlowHooks<S, P>["afterFlow"]>[];
|
|
179
|
-
};
|
|
180
234
|
/**
|
|
181
235
|
* Runtime context passed to every registered step handler.
|
|
182
236
|
* Full access to the builder so handlers can run sub-flows etc.
|
|
@@ -212,17 +266,30 @@ declare class CoreFlowBuilder<S = any, P extends Record<string, unknown> = Recor
|
|
|
212
266
|
private _hooksList;
|
|
213
267
|
private _hooksCache;
|
|
214
268
|
private static _stepHandlers;
|
|
269
|
+
/**
|
|
270
|
+
* Step types registered with `{ transparent: true }` are invoked directly
|
|
271
|
+
* by `_execute` without wrapping in the outer beforeStep/wrapStep/afterStep
|
|
272
|
+
* lifecycle. Use this for step types that manage per-item lifecycle
|
|
273
|
+
* internally (e.g. a DAG handler that fires hooks once per graph node).
|
|
274
|
+
*/
|
|
275
|
+
private static _transparentSteps;
|
|
215
276
|
/**
|
|
216
277
|
* Register a handler for a custom step type.
|
|
217
278
|
* Called once at module load time from each step plugin.
|
|
218
279
|
*
|
|
280
|
+
* Pass `{ transparent: true }` when the handler manages its own per-item
|
|
281
|
+
* lifecycle hooks internally and should not be wrapped by the outer
|
|
282
|
+
* beforeStep / wrapStep / afterStep guards.
|
|
283
|
+
*
|
|
219
284
|
* @example
|
|
220
285
|
* CoreFlowBuilder.registerStepType("myStep", async (step, ctx) => {
|
|
221
286
|
* await doWork(ctx.shared);
|
|
222
287
|
* return undefined;
|
|
223
288
|
* });
|
|
224
289
|
*/
|
|
225
|
-
static registerStepType(type: string, handler: StepHandler
|
|
290
|
+
static registerStepType(type: string, handler: StepHandler, options?: {
|
|
291
|
+
transparent?: boolean;
|
|
292
|
+
}): void;
|
|
226
293
|
private _hooks;
|
|
227
294
|
/**
|
|
228
295
|
* Register lifecycle hooks (called by plugin methods, not by consumers).
|
|
@@ -354,4 +421,4 @@ declare class FlowBuilder<S = any, P extends Record<string, unknown> = Record<st
|
|
|
354
421
|
private _addFn;
|
|
355
422
|
}
|
|
356
423
|
|
|
357
|
-
export { type AnchorStep as A, type BatchStep as B, CoreFlowBuilder as C, FlowBuilder as F, type LoopStep as L, type NodeFn as N, type ParallelStep as P, type ResolvedHooks as R, type StepFilter as S, type Validator as V, type FlowneerPlugin as a, type NodeOptions as b, type FlowHooks as c, type StepMeta as d, type
|
|
424
|
+
export { type AnchorStep as A, type BatchStep as B, CoreFlowBuilder as C, type DagStep as D, FlowBuilder as F, type LoopStep as L, type NodeFn as N, type ParallelStep as P, type ResolvedHooks as R, type StepFilter as S, type Validator as V, type FlowneerPlugin as a, type NodeOptions as b, type FlowHooks as c, type StepMeta as d, type AugmentedState as e, type BranchStep as f, type FnStep as g, type NumberOrFn as h, type RunOptions as i, type Step as j, type StepContext as k, type StepHandler as l, type StreamEvent as m };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { F as FlowBuilder, c as FlowHooks, a as FlowneerPlugin, N as NodeFn, b as NodeOptions,
|
|
1
|
+
export { F as FlowBuilder, c as FlowHooks, a as FlowneerPlugin, N as NodeFn, b as NodeOptions, h as NumberOrFn, i as RunOptions, d as StepMeta, m as StreamEvent, V as Validator } from './FlowBuilder-8kwREeyD.js';
|
|
2
2
|
export { Fragment, fragment } from './src/index.js';
|
|
3
3
|
export { F as FlowError, I as InterruptError } from './errors-u-hq7p5N.js';
|
package/dist/index.js
CHANGED
|
@@ -97,6 +97,7 @@ function applyStepFilter(hooks, filter) {
|
|
|
97
97
|
wrap("onError");
|
|
98
98
|
wrap("wrapStep", (_m, next) => next());
|
|
99
99
|
wrap("wrapParallelFn", (_m, _fi, next) => next());
|
|
100
|
+
wrap("onLoopIteration");
|
|
100
101
|
return hooks;
|
|
101
102
|
}
|
|
102
103
|
function buildHookCache(list) {
|
|
@@ -108,7 +109,9 @@ function buildHookCache(list) {
|
|
|
108
109
|
afterStep: pick("afterStep"),
|
|
109
110
|
wrapParallelFn: pick("wrapParallelFn"),
|
|
110
111
|
onError: pick("onError"),
|
|
111
|
-
afterFlow: pick("afterFlow")
|
|
112
|
+
afterFlow: pick("afterFlow"),
|
|
113
|
+
onLoopIteration: pick("onLoopIteration"),
|
|
114
|
+
onAnchorHit: pick("onAnchorHit")
|
|
112
115
|
};
|
|
113
116
|
}
|
|
114
117
|
var CoreFlowBuilder = class _CoreFlowBuilder {
|
|
@@ -122,18 +125,30 @@ var CoreFlowBuilder = class _CoreFlowBuilder {
|
|
|
122
125
|
// Step-type registry (static — shared across all instances)
|
|
123
126
|
// -------------------------------------------------------------------------
|
|
124
127
|
static _stepHandlers = /* @__PURE__ */ new Map();
|
|
128
|
+
/**
|
|
129
|
+
* Step types registered with `{ transparent: true }` are invoked directly
|
|
130
|
+
* by `_execute` without wrapping in the outer beforeStep/wrapStep/afterStep
|
|
131
|
+
* lifecycle. Use this for step types that manage per-item lifecycle
|
|
132
|
+
* internally (e.g. a DAG handler that fires hooks once per graph node).
|
|
133
|
+
*/
|
|
134
|
+
static _transparentSteps = /* @__PURE__ */ new Set();
|
|
125
135
|
/**
|
|
126
136
|
* Register a handler for a custom step type.
|
|
127
137
|
* Called once at module load time from each step plugin.
|
|
128
138
|
*
|
|
139
|
+
* Pass `{ transparent: true }` when the handler manages its own per-item
|
|
140
|
+
* lifecycle hooks internally and should not be wrapped by the outer
|
|
141
|
+
* beforeStep / wrapStep / afterStep guards.
|
|
142
|
+
*
|
|
129
143
|
* @example
|
|
130
144
|
* CoreFlowBuilder.registerStepType("myStep", async (step, ctx) => {
|
|
131
145
|
* await doWork(ctx.shared);
|
|
132
146
|
* return undefined;
|
|
133
147
|
* });
|
|
134
148
|
*/
|
|
135
|
-
static registerStepType(type, handler) {
|
|
149
|
+
static registerStepType(type, handler, options) {
|
|
136
150
|
_CoreFlowBuilder._stepHandlers.set(type, handler);
|
|
151
|
+
if (options?.transparent) _CoreFlowBuilder._transparentSteps.add(type);
|
|
137
152
|
}
|
|
138
153
|
// -------------------------------------------------------------------------
|
|
139
154
|
// Hooks & plugins
|
|
@@ -263,6 +278,25 @@ var CoreFlowBuilder = class _CoreFlowBuilder {
|
|
|
263
278
|
signal?.throwIfAborted();
|
|
264
279
|
const step = this.steps[i];
|
|
265
280
|
if (step.type === "anchor") continue;
|
|
281
|
+
if (_CoreFlowBuilder._transparentSteps.has(step.type)) {
|
|
282
|
+
const handler = _CoreFlowBuilder._stepHandlers.get(step.type);
|
|
283
|
+
if (handler) {
|
|
284
|
+
const meta2 = {
|
|
285
|
+
index: i,
|
|
286
|
+
type: step.type,
|
|
287
|
+
label: step.label
|
|
288
|
+
};
|
|
289
|
+
await handler(step, {
|
|
290
|
+
shared,
|
|
291
|
+
params,
|
|
292
|
+
signal,
|
|
293
|
+
hooks,
|
|
294
|
+
meta: meta2,
|
|
295
|
+
builder: this
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
266
300
|
const stepLabel = step.label;
|
|
267
301
|
const meta = {
|
|
268
302
|
index: i,
|
|
@@ -294,10 +328,12 @@ var CoreFlowBuilder = class _CoreFlowBuilder {
|
|
|
294
328
|
);
|
|
295
329
|
}
|
|
296
330
|
i = target;
|
|
331
|
+
for (const h of hooks.onAnchorHit)
|
|
332
|
+
await h(gotoTarget, shared, params);
|
|
297
333
|
}
|
|
298
334
|
} catch (err) {
|
|
299
335
|
if (err instanceof InterruptError) throw err;
|
|
300
|
-
for (const h of hooks.onError) h(meta, err, shared, params);
|
|
336
|
+
for (const h of hooks.onError) await h(meta, err, shared, params);
|
|
301
337
|
if (err instanceof FlowError) throw err;
|
|
302
338
|
const labelPart = stepLabel ? `"${stepLabel}" ` : "";
|
|
303
339
|
const label = step.type === "fn" ? `${labelPart}step ${i}` : `${labelPart}${step.type} (step ${i})`;
|
|
@@ -385,12 +421,16 @@ var branchHandler = async (step, { shared, params }) => {
|
|
|
385
421
|
};
|
|
386
422
|
|
|
387
423
|
// src/core/steps/loop.ts
|
|
388
|
-
var loopHandler = async (step, { shared, params, signal, meta, builder }) => {
|
|
389
|
-
|
|
424
|
+
var loopHandler = async (step, { shared, params, signal, meta, hooks, builder }) => {
|
|
425
|
+
let iteration = 0;
|
|
426
|
+
while (await step.condition(shared, params)) {
|
|
390
427
|
await builder._runSub(
|
|
391
428
|
`loop (step ${meta.index})`,
|
|
392
429
|
() => step.body._execute(shared, params, signal)
|
|
393
430
|
);
|
|
431
|
+
for (const h of hooks.onLoopIteration)
|
|
432
|
+
await h(meta, iteration++, shared, params);
|
|
433
|
+
}
|
|
394
434
|
return void 0;
|
|
395
435
|
};
|
|
396
436
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { F as FlowBuilder, a as FlowneerPlugin } from '../../FlowBuilder-
|
|
1
|
+
import { F as FlowBuilder, a as FlowneerPlugin } from '../../FlowBuilder-8kwREeyD.js';
|
|
2
2
|
|
|
3
3
|
interface HumanNodeOptions<S = any, P extends Record<string, unknown> = Record<string, unknown>> {
|
|
4
4
|
/**
|
|
@@ -45,6 +45,12 @@ declare module "../../Flowneer" {
|
|
|
45
45
|
*/
|
|
46
46
|
humanNode(options?: HumanNodeOptions<S, P>): this;
|
|
47
47
|
}
|
|
48
|
+
interface AugmentedState {
|
|
49
|
+
/** Prompt/question for the human reviewer. Written by `.humanNode()` before interrupting. */
|
|
50
|
+
__humanPrompt?: string;
|
|
51
|
+
/** Human-provided response. Write this onto the saved shared state before calling `resumeFlow()`. */
|
|
52
|
+
__humanFeedback?: string;
|
|
53
|
+
}
|
|
48
54
|
}
|
|
49
55
|
declare const withHumanNode: FlowneerPlugin;
|
|
50
56
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as FlowneerPlugin, N as NodeFn, b as NodeOptions, S as StepFilter } from '../../FlowBuilder-
|
|
1
|
+
import { a as FlowneerPlugin, N as NodeFn, b as NodeOptions, S as StepFilter } from '../../FlowBuilder-8kwREeyD.js';
|
|
2
2
|
|
|
3
3
|
declare module "../../Flowneer" {
|
|
4
4
|
interface FlowBuilder<S, P> {
|
|
@@ -130,39 +130,118 @@ declare module "../../Flowneer" {
|
|
|
130
130
|
}
|
|
131
131
|
declare const withFlowAnalyzer: FlowneerPlugin;
|
|
132
132
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
133
|
+
/** Per-step performance snapshot recorded by `.withPerfAnalyzer()`. */
|
|
134
|
+
interface StepPerfStats {
|
|
135
|
+
/** Step index (0-based). */
|
|
136
|
+
index: number;
|
|
137
|
+
/** Step type: "fn" | "branch" | "loop" | "batch" | "parallel" | "dag". */
|
|
138
|
+
type: string;
|
|
139
|
+
/** Step label if set via `NodeOptions.label`. */
|
|
140
|
+
label?: string;
|
|
141
|
+
/** Wall-clock duration in ms (high-res via `performance.now()`). */
|
|
142
|
+
durationMs: number;
|
|
143
|
+
/** User-space CPU time consumed during this step (ms). 0 on non-Node runtimes. */
|
|
144
|
+
cpuUserMs: number;
|
|
145
|
+
/** Kernel CPU time consumed during this step (ms). 0 on non-Node runtimes. */
|
|
146
|
+
cpuSystemMs: number;
|
|
147
|
+
/** V8 heap used at step start (bytes). */
|
|
148
|
+
heapUsedBefore: number;
|
|
149
|
+
/** V8 heap used at step end (bytes). */
|
|
150
|
+
heapUsedAfter: number;
|
|
151
|
+
/**
|
|
152
|
+
* Net change in V8 heap usage (bytes, positive = allocated, negative = freed
|
|
153
|
+
* due to a GC cycle that completed during the step).
|
|
154
|
+
*/
|
|
155
|
+
heapDeltaBytes: number;
|
|
156
|
+
/** Net change in Resident Set Size (bytes). */
|
|
157
|
+
rssDeltaBytes: number;
|
|
158
|
+
/** Net change in external (C++ / Buffer) memory bound to V8 (bytes). */
|
|
159
|
+
externalDeltaBytes: number;
|
|
160
|
+
/**
|
|
161
|
+
* Number of GC events accumulated since last step end. Best-effort; see
|
|
162
|
+
* module note regarding async PerformanceObserver delivery.
|
|
163
|
+
*/
|
|
164
|
+
gcCount: number;
|
|
165
|
+
/** Total GC pause duration attributed to this step (ms). Best-effort. */
|
|
166
|
+
gcDurationMs: number;
|
|
167
|
+
/** `true` if the step threw an error (stats still recorded via finally). */
|
|
168
|
+
threw: boolean;
|
|
169
|
+
}
|
|
170
|
+
/** Flow-level performance summary written to `shared.__perfReport`. */
|
|
171
|
+
interface PerfReport {
|
|
172
|
+
/** Sum of all step `durationMs` (includes parallel overlap). */
|
|
173
|
+
totalDurationMs: number;
|
|
174
|
+
/** Sum of all step `cpuUserMs`. */
|
|
175
|
+
totalCpuUserMs: number;
|
|
176
|
+
/** Sum of all step `cpuSystemMs`. */
|
|
177
|
+
totalCpuSystemMs: number;
|
|
178
|
+
/** Sum of all GC pause durations across the flow (ms). */
|
|
179
|
+
totalGcDurationMs: number;
|
|
180
|
+
/** Total GC event count during the flow. */
|
|
181
|
+
totalGcCount: number;
|
|
182
|
+
/** Highest `heapUsedAfter` seen across all steps (bytes). */
|
|
183
|
+
peakHeapUsedBytes: number;
|
|
184
|
+
/** All per-step stats in execution order. */
|
|
185
|
+
steps: StepPerfStats[];
|
|
186
|
+
/** The step with the longest wall-clock duration, or `null` if no steps ran. */
|
|
187
|
+
slowest: StepPerfStats | null;
|
|
188
|
+
/** The step with the largest heap delta, or `null` if no steps ran. */
|
|
189
|
+
heaviest: StepPerfStats | null;
|
|
190
|
+
}
|
|
191
|
+
interface PerfAnalyzerOptions {
|
|
192
|
+
/**
|
|
193
|
+
* Track GC pause events via `PerformanceObserver`. Requires Node.js.
|
|
194
|
+
* @default true
|
|
195
|
+
*/
|
|
196
|
+
trackGc?: boolean;
|
|
197
|
+
/**
|
|
198
|
+
* Called with the final `PerfReport` in `afterFlow`.
|
|
199
|
+
* Use this to log, persist, or ship metrics — formatting is left to the caller.
|
|
200
|
+
*/
|
|
201
|
+
onReport?: (report: PerfReport) => void;
|
|
142
202
|
}
|
|
143
203
|
declare module "../../Flowneer" {
|
|
144
204
|
interface FlowBuilder<S, P> {
|
|
145
205
|
/**
|
|
146
|
-
*
|
|
147
|
-
*
|
|
206
|
+
* Profiles each step using Node.js built-in performance APIs — no external
|
|
207
|
+
* dependencies.
|
|
148
208
|
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
209
|
+
* Per step, records:
|
|
210
|
+
* - Wall-clock duration (`performance.now()`)
|
|
211
|
+
* - CPU user + system time (`process.cpuUsage()`)
|
|
212
|
+
* - Heap used delta (`process.memoryUsage().heapUsed`)
|
|
213
|
+
* - RSS and external memory delta
|
|
214
|
+
* - GC pause count + duration (`PerformanceObserver`, best-effort)
|
|
215
|
+
*
|
|
216
|
+
* Results are written to `shared.__perfStats` (array, in execution order)
|
|
217
|
+
* and `shared.__perfReport` (flow summary) when the flow completes.
|
|
151
218
|
*
|
|
152
219
|
* @example
|
|
153
|
-
*
|
|
154
|
-
*
|
|
220
|
+
* const flow = new AppFlow<State>()
|
|
221
|
+
* .withPerfAnalyzer({
|
|
222
|
+
* onReport: (r) => console.log(JSON.stringify(r, null, 2)),
|
|
223
|
+
* })
|
|
224
|
+
* .then(fetchData, { label: "fetch" })
|
|
225
|
+
* .then(callLlm, { label: "llm:generate" })
|
|
226
|
+
* .then(save, { label: "save" });
|
|
227
|
+
*
|
|
228
|
+
* await flow.run(shared);
|
|
229
|
+
* // shared.__perfReport.slowest → { label: "llm:generate", durationMs: ... }
|
|
230
|
+
* // shared.__perfStats[0] → { heapDeltaBytes: 2621440, cpuUserMs: 12 … }
|
|
155
231
|
*
|
|
156
232
|
* @example
|
|
157
|
-
* //
|
|
158
|
-
*
|
|
159
|
-
* .withDebugger(["llm:*"], { beforeStep: true, afterStep: true })
|
|
160
|
-
* .then(callLlm)
|
|
161
|
-
* .run(shared);
|
|
233
|
+
* // Profile only LLM steps
|
|
234
|
+
* flow.withPerfAnalyzer({}, ["llm:*"])
|
|
162
235
|
*/
|
|
163
|
-
|
|
236
|
+
withPerfAnalyzer(options?: PerfAnalyzerOptions, filter?: StepFilter): this;
|
|
237
|
+
}
|
|
238
|
+
interface AugmentedState {
|
|
239
|
+
/** Per-step perf stats written by `.withPerfAnalyzer()`. In execution order. */
|
|
240
|
+
__perfStats?: StepPerfStats[];
|
|
241
|
+
/** Flow-level perf summary written by `.withPerfAnalyzer()` on flow completion. */
|
|
242
|
+
__perfReport?: PerfReport;
|
|
164
243
|
}
|
|
165
244
|
}
|
|
166
|
-
declare const
|
|
245
|
+
declare const withPerfAnalyzer: FlowneerPlugin;
|
|
167
246
|
|
|
168
|
-
export { type
|
|
247
|
+
export { type PathMap, type PathNode, type PerfAnalyzerOptions, type PerfReport, type StepPerfStats, type TraceEvent, type TraceHandle, type TraceReport, withAtomicUpdates, withDryRun, withFlowAnalyzer, withMocks, withPerfAnalyzer, withStepLimit };
|
|
@@ -180,41 +180,131 @@ var withFlowAnalyzer = {
|
|
|
180
180
|
}
|
|
181
181
|
};
|
|
182
182
|
|
|
183
|
-
// plugins/dev/
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
183
|
+
// plugins/dev/withPerfAnalyzer.ts
|
|
184
|
+
import { performance, PerformanceObserver } from "perf_hooks";
|
|
185
|
+
function cpuUsage() {
|
|
186
|
+
return process.cpuUsage?.() ?? {
|
|
187
|
+
user: 0,
|
|
188
|
+
system: 0
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function cpuUsageDelta(start) {
|
|
192
|
+
return process.cpuUsage?.(start) ?? { user: 0, system: 0 };
|
|
193
|
+
}
|
|
194
|
+
function memUsage() {
|
|
195
|
+
return process.memoryUsage?.() ?? {
|
|
196
|
+
rss: 0,
|
|
197
|
+
heapTotal: 0,
|
|
198
|
+
heapUsed: 0,
|
|
199
|
+
external: 0,
|
|
200
|
+
arrayBuffers: 0
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
var withPerfAnalyzer = {
|
|
204
|
+
withPerfAnalyzer(options = {}, filter) {
|
|
205
|
+
const { trackGc = true, onReport } = options;
|
|
206
|
+
const gcLog = [];
|
|
207
|
+
let gcObserver = null;
|
|
208
|
+
if (trackGc) {
|
|
209
|
+
try {
|
|
210
|
+
gcObserver = new PerformanceObserver((list) => {
|
|
211
|
+
for (const entry of list.getEntries()) {
|
|
212
|
+
gcLog.push({
|
|
213
|
+
startTime: entry.startTime,
|
|
214
|
+
duration: entry.duration
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
gcObserver.observe({ entryTypes: ["gc"] });
|
|
219
|
+
} catch {
|
|
220
|
+
}
|
|
208
221
|
}
|
|
209
|
-
|
|
222
|
+
const snapshots = /* @__PURE__ */ new Map();
|
|
223
|
+
this._setHooks(
|
|
224
|
+
{
|
|
225
|
+
wrapStep: async (meta, next, shared) => {
|
|
226
|
+
const gcMsBefore = gcLog.reduce((a, e) => a + e.duration, 0);
|
|
227
|
+
const t0 = performance.now();
|
|
228
|
+
const cpu0 = cpuUsage();
|
|
229
|
+
const mem0 = memUsage();
|
|
230
|
+
snapshots.set(meta.index, {
|
|
231
|
+
t0,
|
|
232
|
+
cpu0,
|
|
233
|
+
mem0,
|
|
234
|
+
gcLenBefore: gcLog.length,
|
|
235
|
+
gcMsBefore
|
|
236
|
+
});
|
|
237
|
+
let threw = false;
|
|
238
|
+
try {
|
|
239
|
+
await next();
|
|
240
|
+
} catch (e) {
|
|
241
|
+
threw = true;
|
|
242
|
+
throw e;
|
|
243
|
+
} finally {
|
|
244
|
+
const durationMs = performance.now() - t0;
|
|
245
|
+
const cpuDelta = cpuUsageDelta(cpu0);
|
|
246
|
+
const mem1 = memUsage();
|
|
247
|
+
const snap = snapshots.get(meta.index);
|
|
248
|
+
snapshots.delete(meta.index);
|
|
249
|
+
const gcMsAfter = gcLog.reduce((a, e) => a + e.duration, 0);
|
|
250
|
+
const gcCountAfter = gcLog.length;
|
|
251
|
+
const gcLenBefore = snap?.gcLenBefore ?? gcLog.length;
|
|
252
|
+
const gcMsSnap = snap?.gcMsBefore ?? gcMsAfter;
|
|
253
|
+
const stat = {
|
|
254
|
+
index: meta.index,
|
|
255
|
+
type: meta.type,
|
|
256
|
+
label: meta.label,
|
|
257
|
+
durationMs,
|
|
258
|
+
cpuUserMs: cpuDelta.user / 1e3,
|
|
259
|
+
cpuSystemMs: cpuDelta.system / 1e3,
|
|
260
|
+
heapUsedBefore: mem0.heapUsed,
|
|
261
|
+
heapUsedAfter: mem1.heapUsed,
|
|
262
|
+
heapDeltaBytes: mem1.heapUsed - mem0.heapUsed,
|
|
263
|
+
rssDeltaBytes: mem1.rss - mem0.rss,
|
|
264
|
+
externalDeltaBytes: mem1.external - mem0.external,
|
|
265
|
+
gcCount: gcCountAfter - gcLenBefore,
|
|
266
|
+
gcDurationMs: gcMsAfter - gcMsSnap,
|
|
267
|
+
threw
|
|
268
|
+
};
|
|
269
|
+
if (!shared.__perfStats) shared.__perfStats = [];
|
|
270
|
+
shared.__perfStats.push(stat);
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
afterFlow: (shared) => {
|
|
274
|
+
gcObserver?.disconnect();
|
|
275
|
+
const stats = shared.__perfStats ?? [];
|
|
276
|
+
const totalGcDurationMs = gcLog.reduce((a, e) => a + e.duration, 0);
|
|
277
|
+
const totalGcCount = gcLog.length;
|
|
278
|
+
const report = {
|
|
279
|
+
totalDurationMs: stats.reduce((a, s) => a + s.durationMs, 0),
|
|
280
|
+
totalCpuUserMs: stats.reduce((a, s) => a + s.cpuUserMs, 0),
|
|
281
|
+
totalCpuSystemMs: stats.reduce((a, s) => a + s.cpuSystemMs, 0),
|
|
282
|
+
totalGcDurationMs,
|
|
283
|
+
totalGcCount,
|
|
284
|
+
peakHeapUsedBytes: stats.reduce(
|
|
285
|
+
(a, s) => Math.max(a, s.heapUsedAfter),
|
|
286
|
+
0
|
|
287
|
+
),
|
|
288
|
+
steps: stats,
|
|
289
|
+
slowest: stats.length > 0 ? stats.reduce((a, s) => s.durationMs > a.durationMs ? s : a) : null,
|
|
290
|
+
heaviest: stats.length > 0 ? stats.reduce(
|
|
291
|
+
(a, s) => s.heapDeltaBytes > a.heapDeltaBytes ? s : a
|
|
292
|
+
) : null
|
|
293
|
+
};
|
|
294
|
+
shared.__perfReport = report;
|
|
295
|
+
onReport?.(report);
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
filter
|
|
299
|
+
);
|
|
210
300
|
return this;
|
|
211
301
|
}
|
|
212
302
|
};
|
|
213
303
|
export {
|
|
214
304
|
withAtomicUpdates,
|
|
215
|
-
withDebugger,
|
|
216
305
|
withDryRun,
|
|
217
306
|
withFlowAnalyzer,
|
|
218
307
|
withMocks,
|
|
308
|
+
withPerfAnalyzer,
|
|
219
309
|
withStepLimit
|
|
220
310
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as FlowneerPlugin, N as NodeFn, b as NodeOptions } from '../../FlowBuilder-
|
|
1
|
+
import { a as FlowneerPlugin, N as NodeFn, b as NodeOptions } from '../../FlowBuilder-8kwREeyD.js';
|
|
2
2
|
|
|
3
3
|
/** A single node entry in the exported graph. */
|
|
4
4
|
interface GraphNodeExport {
|