flowneer 0.9.4 → 0.9.6
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-8kwREeyD.d.ts → FlowBuilder-CwBQDOEN.d.ts} +49 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +33 -6
- package/dist/plugins/agent/index.d.ts +1 -1
- package/dist/plugins/dev/index.d.ts +1 -1
- package/dist/plugins/eval/index.d.ts +1 -1
- package/dist/plugins/graph/index.d.ts +1 -1
- package/dist/plugins/graph/index.js +33 -6
- package/dist/plugins/index.d.ts +1 -1
- package/dist/plugins/index.js +33 -6
- package/dist/plugins/llm/index.d.ts +1 -1
- package/dist/plugins/memory/index.d.ts +1 -1
- package/dist/plugins/messaging/index.d.ts +1 -1
- package/dist/plugins/observability/index.d.ts +1 -1
- package/dist/plugins/output/index.d.ts +1 -1
- package/dist/plugins/persistence/index.d.ts +1 -1
- package/dist/plugins/resilience/index.d.ts +1 -1
- package/dist/plugins/telemetry/index.d.ts +1 -1
- package/dist/plugins/tools/index.d.ts +1 -1
- package/dist/presets/agent/index.d.ts +168 -2
- package/dist/presets/agent/index.js +150 -6
- package/dist/presets/config/index.d.ts +1 -1
- package/dist/presets/config/index.js +33 -6
- package/dist/presets/index.d.ts +2 -2
- package/dist/presets/index.js +150 -6
- package/dist/presets/pipeline/index.d.ts +1 -1
- package/dist/presets/pipeline/index.js +33 -6
- package/dist/presets/rag/index.d.ts +1 -1
- package/dist/presets/rag/index.js +33 -6
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +33 -6
- package/package.json +1 -1
|
@@ -86,6 +86,11 @@ interface StepMeta {
|
|
|
86
86
|
* Entries are exact matches unless they contain `*`, which acts as a
|
|
87
87
|
* wildcard matching any substring (glob-style).
|
|
88
88
|
* e.g. `["llm:*"]` matches `"llm:summarise"`, `"llm:embed"`, etc.
|
|
89
|
+
* - **Negation** — prefix an entry with `!` to exclude matching steps.
|
|
90
|
+
* Negation veto always wins over a positive match in the same array.
|
|
91
|
+
* A negation-only array matches all labelled steps that are not excluded.
|
|
92
|
+
* e.g. `["!human:*"]` fires on every step _except_ `human:*` steps.
|
|
93
|
+
* e.g. `["!human:*", "llm:*"]` fires on `llm:*` steps only, never `human:*`.
|
|
89
94
|
* - **Predicate** — full control; return `true` to match.
|
|
90
95
|
*
|
|
91
96
|
* Unmatched `wrapStep`/`wrapParallelFn` hooks still call `next()` automatically
|
|
@@ -98,6 +103,12 @@ interface StepMeta {
|
|
|
98
103
|
* // Wildcard — any step whose label starts with "llm:"
|
|
99
104
|
* flow.withRateLimit({ intervalMs: 1000 }, ["llm:*"]);
|
|
100
105
|
*
|
|
106
|
+
* // Negation-only — apply everywhere except human-in-loop steps
|
|
107
|
+
* flow.withRateLimit({ intervalMs: 1000 }, ["!human:*"]);
|
|
108
|
+
*
|
|
109
|
+
* // Mixed — apply to llm steps, but never human steps
|
|
110
|
+
* flow.withRateLimit({ intervalMs: 1000 }, ["!human:*", "llm:*"]);
|
|
111
|
+
*
|
|
101
112
|
* // Custom predicate
|
|
102
113
|
* flow.addHooks({ beforeStep: log }, (meta) => meta.type === "fn");
|
|
103
114
|
*/
|
|
@@ -106,6 +117,7 @@ type StepFilter = string[] | ((meta: StepMeta) => boolean);
|
|
|
106
117
|
interface FlowHooks<S = any, P extends Record<string, unknown> = Record<string, unknown>> {
|
|
107
118
|
/** Fires once before the first step runs. */
|
|
108
119
|
beforeFlow?: (shared: S, params: P) => void | Promise<void>;
|
|
120
|
+
/** Fires before each step body executes. Respects `StepFilter` when registered via `_setHooks`. */
|
|
109
121
|
beforeStep?: (meta: StepMeta, shared: S, params: P) => void | Promise<void>;
|
|
110
122
|
/**
|
|
111
123
|
* Wraps step execution — call `next()` to invoke the step body.
|
|
@@ -113,13 +125,21 @@ interface FlowHooks<S = any, P extends Record<string, unknown> = Record<string,
|
|
|
113
125
|
* Multiple `wrapStep` registrations are composed innermost-first.
|
|
114
126
|
*/
|
|
115
127
|
wrapStep?: (meta: StepMeta, next: () => Promise<void>, shared: S, params: P) => Promise<void>;
|
|
128
|
+
/** Fires after each step body completes successfully. Respects `StepFilter` when registered via `_setHooks`. */
|
|
116
129
|
afterStep?: (meta: StepMeta, shared: S, params: P) => void | Promise<void>;
|
|
117
130
|
/**
|
|
118
131
|
* Wraps individual functions within a `.parallel()` step.
|
|
119
132
|
* `fnIndex` is the position within the fns array.
|
|
120
133
|
*/
|
|
121
134
|
wrapParallelFn?: (meta: StepMeta, fnIndex: number, next: () => Promise<void>, shared: S, params: P) => Promise<void>;
|
|
135
|
+
/**
|
|
136
|
+
* Fires when a step throws, before the error is re-thrown.
|
|
137
|
+
* Use for logging or writing error details to `shared` — do not suppress the error here;
|
|
138
|
+
* use `wrapStep` with a try/catch for recovery instead.
|
|
139
|
+
* Respects `StepFilter` when registered via `_setHooks`.
|
|
140
|
+
*/
|
|
122
141
|
onError?: (meta: StepMeta, error: unknown, shared: S, params: P) => void | Promise<void>;
|
|
142
|
+
/** Fires once after the last step completes (or after a flow error). Not affected by `StepFilter`. */
|
|
123
143
|
afterFlow?: (shared: S, params: P) => void | Promise<void>;
|
|
124
144
|
/**
|
|
125
145
|
* Fires after each loop iteration completes. `iteration` is zero-based.
|
|
@@ -132,6 +152,14 @@ interface FlowHooks<S = any, P extends Record<string, unknown> = Record<string,
|
|
|
132
152
|
*/
|
|
133
153
|
onAnchorHit?: (anchorName: string, shared: S, params: P) => void | Promise<void>;
|
|
134
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* The `this` type inside every plugin method. Extends the public `FlowBuilder`
|
|
157
|
+
* with `_setHooks` so plugins can register lifecycle hooks without casting to
|
|
158
|
+
* `any`.
|
|
159
|
+
*/
|
|
160
|
+
type PluginContext = FlowBuilder<any, any> & {
|
|
161
|
+
_setHooks(hooks: Partial<FlowHooks<any, any>>, filter?: StepFilter): () => void;
|
|
162
|
+
};
|
|
135
163
|
/**
|
|
136
164
|
* A plugin is an object whose keys become methods on a `FlowBuilder.extend()` subclass prototype.
|
|
137
165
|
* Each method receives the builder as `this` and should return `this` for chaining.
|
|
@@ -143,7 +171,7 @@ interface FlowHooks<S = any, P extends Record<string, unknown> = Record<string,
|
|
|
143
171
|
* }
|
|
144
172
|
* ```
|
|
145
173
|
*/
|
|
146
|
-
type FlowneerPlugin = Record<string, (this:
|
|
174
|
+
type FlowneerPlugin = Record<string, (this: PluginContext, ...args: any[]) => any>;
|
|
147
175
|
type ResolvedHooks<S, P extends Record<string, unknown>> = {
|
|
148
176
|
beforeFlow: NonNullable<FlowHooks<S, P>["beforeFlow"]>[];
|
|
149
177
|
beforeStep: NonNullable<FlowHooks<S, P>["beforeStep"]>[];
|
|
@@ -291,6 +319,25 @@ declare class CoreFlowBuilder<S = any, P extends Record<string, unknown> = Recor
|
|
|
291
319
|
transparent?: boolean;
|
|
292
320
|
}): void;
|
|
293
321
|
private _hooks;
|
|
322
|
+
/**
|
|
323
|
+
* Register lifecycle hooks on this flow instance at runtime.
|
|
324
|
+
*
|
|
325
|
+
* Unlike plugin methods (which use `_setHooks` internally), `addHooks` is
|
|
326
|
+
* intended for consumers — app code, tests, or request-scoped instrumentation
|
|
327
|
+
* that needs to observe or modify a specific flow instance without defining a
|
|
328
|
+
* plugin.
|
|
329
|
+
*
|
|
330
|
+
* Returns a `dispose` function that removes the registered hooks when called.
|
|
331
|
+
*
|
|
332
|
+
* @example
|
|
333
|
+
* const dispose = flow.addHooks(
|
|
334
|
+
* { beforeStep: (meta) => console.log("->", meta.label) },
|
|
335
|
+
* ["llm:*"],
|
|
336
|
+
* );
|
|
337
|
+
* await flow.run(shared);
|
|
338
|
+
* dispose(); // removes the hooks
|
|
339
|
+
*/
|
|
340
|
+
addHooks(hooks: Partial<FlowHooks<S, P>>, filter?: StepFilter): () => void;
|
|
294
341
|
/**
|
|
295
342
|
* Register lifecycle hooks (called by plugin methods, not by consumers).
|
|
296
343
|
* Returns a dispose function that removes these hooks when called.
|
|
@@ -421,4 +468,4 @@ declare class FlowBuilder<S = any, P extends Record<string, unknown> = Record<st
|
|
|
421
468
|
private _addFn;
|
|
422
469
|
}
|
|
423
470
|
|
|
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
|
|
471
|
+
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 PluginContext as i, type RunOptions as j, type Step as k, type StepContext as l, type StepHandler as m, type StreamEvent as n };
|
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, h as NumberOrFn,
|
|
1
|
+
export { F as FlowBuilder, c as FlowHooks, a as FlowneerPlugin, N as NodeFn, b as NodeOptions, h as NumberOrFn, j as RunOptions, d as StepMeta, n as StreamEvent, V as Validator } from './FlowBuilder-CwBQDOEN.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
|
@@ -21,15 +21,21 @@ var InterruptError = class extends Error {
|
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
// src/core/utils.ts
|
|
24
|
+
function matchesPattern(pattern, label) {
|
|
25
|
+
return pattern.includes("*") ? new RegExp(
|
|
26
|
+
"^" + pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$"
|
|
27
|
+
).test(label) : pattern === label;
|
|
28
|
+
}
|
|
24
29
|
function matchesFilter(filter, meta) {
|
|
25
30
|
if (!Array.isArray(filter)) return filter(meta);
|
|
31
|
+
const negations = filter.filter((p) => p.startsWith("!"));
|
|
32
|
+
const positives = filter.filter((p) => !p.startsWith("!"));
|
|
26
33
|
const label = meta.label;
|
|
27
|
-
if (label
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
);
|
|
34
|
+
if (label !== void 0 && negations.some((p) => matchesPattern(p.slice(1), label)))
|
|
35
|
+
return false;
|
|
36
|
+
if (positives.length > 0)
|
|
37
|
+
return label !== void 0 && positives.some((p) => matchesPattern(p, label));
|
|
38
|
+
return true;
|
|
33
39
|
}
|
|
34
40
|
function resolveNumber(val, fallback, shared, params) {
|
|
35
41
|
if (val === void 0) return fallback;
|
|
@@ -156,6 +162,27 @@ var CoreFlowBuilder = class _CoreFlowBuilder {
|
|
|
156
162
|
_hooks() {
|
|
157
163
|
return this._hooksCache ??= buildHookCache(this._hooksList);
|
|
158
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Register lifecycle hooks on this flow instance at runtime.
|
|
167
|
+
*
|
|
168
|
+
* Unlike plugin methods (which use `_setHooks` internally), `addHooks` is
|
|
169
|
+
* intended for consumers — app code, tests, or request-scoped instrumentation
|
|
170
|
+
* that needs to observe or modify a specific flow instance without defining a
|
|
171
|
+
* plugin.
|
|
172
|
+
*
|
|
173
|
+
* Returns a `dispose` function that removes the registered hooks when called.
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* const dispose = flow.addHooks(
|
|
177
|
+
* { beforeStep: (meta) => console.log("->", meta.label) },
|
|
178
|
+
* ["llm:*"],
|
|
179
|
+
* );
|
|
180
|
+
* await flow.run(shared);
|
|
181
|
+
* dispose(); // removes the hooks
|
|
182
|
+
*/
|
|
183
|
+
addHooks(hooks, filter) {
|
|
184
|
+
return this._setHooks(hooks, filter);
|
|
185
|
+
}
|
|
159
186
|
/**
|
|
160
187
|
* Register lifecycle hooks (called by plugin methods, not by consumers).
|
|
161
188
|
* Returns a dispose function that removes these hooks when called.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { F as FlowBuilder, a as FlowneerPlugin } from '../../FlowBuilder-
|
|
1
|
+
import { F as FlowBuilder, a as FlowneerPlugin } from '../../FlowBuilder-CwBQDOEN.js';
|
|
2
2
|
|
|
3
3
|
interface HumanNodeOptions<S = any, P extends Record<string, unknown> = Record<string, unknown>> {
|
|
4
4
|
/**
|
|
@@ -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-CwBQDOEN.js';
|
|
2
2
|
|
|
3
3
|
declare module "../../Flowneer" {
|
|
4
4
|
interface FlowBuilder<S, P> {
|
|
@@ -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-CwBQDOEN.js';
|
|
2
2
|
|
|
3
3
|
/** A single node entry in the exported graph. */
|
|
4
4
|
interface GraphNodeExport {
|
|
@@ -21,15 +21,21 @@ var InterruptError = class extends Error {
|
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
// src/core/utils.ts
|
|
24
|
+
function matchesPattern(pattern, label) {
|
|
25
|
+
return pattern.includes("*") ? new RegExp(
|
|
26
|
+
"^" + pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$"
|
|
27
|
+
).test(label) : pattern === label;
|
|
28
|
+
}
|
|
24
29
|
function matchesFilter(filter, meta) {
|
|
25
30
|
if (!Array.isArray(filter)) return filter(meta);
|
|
31
|
+
const negations = filter.filter((p) => p.startsWith("!"));
|
|
32
|
+
const positives = filter.filter((p) => !p.startsWith("!"));
|
|
26
33
|
const label = meta.label;
|
|
27
|
-
if (label
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
);
|
|
34
|
+
if (label !== void 0 && negations.some((p) => matchesPattern(p.slice(1), label)))
|
|
35
|
+
return false;
|
|
36
|
+
if (positives.length > 0)
|
|
37
|
+
return label !== void 0 && positives.some((p) => matchesPattern(p, label));
|
|
38
|
+
return true;
|
|
33
39
|
}
|
|
34
40
|
function resolveNumber(val, fallback, shared, params) {
|
|
35
41
|
if (val === void 0) return fallback;
|
|
@@ -156,6 +162,27 @@ var CoreFlowBuilder = class _CoreFlowBuilder {
|
|
|
156
162
|
_hooks() {
|
|
157
163
|
return this._hooksCache ??= buildHookCache(this._hooksList);
|
|
158
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Register lifecycle hooks on this flow instance at runtime.
|
|
167
|
+
*
|
|
168
|
+
* Unlike plugin methods (which use `_setHooks` internally), `addHooks` is
|
|
169
|
+
* intended for consumers — app code, tests, or request-scoped instrumentation
|
|
170
|
+
* that needs to observe or modify a specific flow instance without defining a
|
|
171
|
+
* plugin.
|
|
172
|
+
*
|
|
173
|
+
* Returns a `dispose` function that removes the registered hooks when called.
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* const dispose = flow.addHooks(
|
|
177
|
+
* { beforeStep: (meta) => console.log("->", meta.label) },
|
|
178
|
+
* ["llm:*"],
|
|
179
|
+
* );
|
|
180
|
+
* await flow.run(shared);
|
|
181
|
+
* dispose(); // removes the hooks
|
|
182
|
+
*/
|
|
183
|
+
addHooks(hooks, filter) {
|
|
184
|
+
return this._setHooks(hooks, filter);
|
|
185
|
+
}
|
|
159
186
|
/**
|
|
160
187
|
* Register lifecycle hooks (called by plugin methods, not by consumers).
|
|
161
188
|
* Returns a dispose function that removes these hooks when called.
|
package/dist/plugins/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export { parseJsonOutput, parseListOutput, parseMarkdownTable, parseRegexOutput
|
|
|
11
11
|
export { Span, TelemetryDaemon, TelemetryExporter, TelemetryOptions, consoleExporter, otlpExporter, withTelemetry } from './telemetry/index.js';
|
|
12
12
|
export { EvalResult, EvalSummary, ScoreFn, answerRelevance, containsMatch, exactMatch, f1Score, retrievalPrecision, retrievalRecall, runEvalSuite } from './eval/index.js';
|
|
13
13
|
export { GraphEdge, GraphNode, withGraph } from './graph/index.js';
|
|
14
|
-
import { S as StepFilter, a as FlowneerPlugin, d as StepMeta, F as FlowBuilder } from '../FlowBuilder-
|
|
14
|
+
import { S as StepFilter, a as FlowneerPlugin, d as StepMeta, F as FlowBuilder } from '../FlowBuilder-CwBQDOEN.js';
|
|
15
15
|
import { F as FlowError } from '../errors-u-hq7p5N.js';
|
|
16
16
|
export { validate } from './config/index.js';
|
|
17
17
|
export { F as FlowConfig, a as FnRegistry, S as StepConfig, V as ValidationError, b as ValidationResult } from '../schema-CIqQAXqY.js';
|
package/dist/plugins/index.js
CHANGED
|
@@ -115,15 +115,21 @@ var InterruptError = class extends Error {
|
|
|
115
115
|
};
|
|
116
116
|
|
|
117
117
|
// src/core/utils.ts
|
|
118
|
+
function matchesPattern(pattern, label) {
|
|
119
|
+
return pattern.includes("*") ? new RegExp(
|
|
120
|
+
"^" + pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$"
|
|
121
|
+
).test(label) : pattern === label;
|
|
122
|
+
}
|
|
118
123
|
function matchesFilter(filter, meta) {
|
|
119
124
|
if (!Array.isArray(filter)) return filter(meta);
|
|
125
|
+
const negations = filter.filter((p) => p.startsWith("!"));
|
|
126
|
+
const positives = filter.filter((p) => !p.startsWith("!"));
|
|
120
127
|
const label = meta.label;
|
|
121
|
-
if (label
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
);
|
|
128
|
+
if (label !== void 0 && negations.some((p) => matchesPattern(p.slice(1), label)))
|
|
129
|
+
return false;
|
|
130
|
+
if (positives.length > 0)
|
|
131
|
+
return label !== void 0 && positives.some((p) => matchesPattern(p, label));
|
|
132
|
+
return true;
|
|
127
133
|
}
|
|
128
134
|
function resolveNumber(val, fallback, shared, params) {
|
|
129
135
|
if (val === void 0) return fallback;
|
|
@@ -250,6 +256,27 @@ var CoreFlowBuilder = class _CoreFlowBuilder {
|
|
|
250
256
|
_hooks() {
|
|
251
257
|
return this._hooksCache ??= buildHookCache(this._hooksList);
|
|
252
258
|
}
|
|
259
|
+
/**
|
|
260
|
+
* Register lifecycle hooks on this flow instance at runtime.
|
|
261
|
+
*
|
|
262
|
+
* Unlike plugin methods (which use `_setHooks` internally), `addHooks` is
|
|
263
|
+
* intended for consumers — app code, tests, or request-scoped instrumentation
|
|
264
|
+
* that needs to observe or modify a specific flow instance without defining a
|
|
265
|
+
* plugin.
|
|
266
|
+
*
|
|
267
|
+
* Returns a `dispose` function that removes the registered hooks when called.
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* const dispose = flow.addHooks(
|
|
271
|
+
* { beforeStep: (meta) => console.log("->", meta.label) },
|
|
272
|
+
* ["llm:*"],
|
|
273
|
+
* );
|
|
274
|
+
* await flow.run(shared);
|
|
275
|
+
* dispose(); // removes the hooks
|
|
276
|
+
*/
|
|
277
|
+
addHooks(hooks, filter) {
|
|
278
|
+
return this._setHooks(hooks, filter);
|
|
279
|
+
}
|
|
253
280
|
/**
|
|
254
281
|
* Register lifecycle hooks (called by plugin methods, not by consumers).
|
|
255
282
|
* Returns a dispose function that removes these hooks when called.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S as StepFilter, a as FlowneerPlugin, V as Validator } from '../../FlowBuilder-
|
|
1
|
+
import { S as StepFilter, a as FlowneerPlugin, V as Validator } from '../../FlowBuilder-CwBQDOEN.js';
|
|
2
2
|
|
|
3
3
|
declare module "../../Flowneer" {
|
|
4
4
|
interface FlowBuilder<S, P> {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as FlowneerPlugin } from '../../FlowBuilder-
|
|
1
|
+
import { a as FlowneerPlugin } from '../../FlowBuilder-CwBQDOEN.js';
|
|
2
2
|
|
|
3
3
|
/** Send a message to a named channel on `shared.__channels`. */
|
|
4
4
|
declare function sendTo<S extends Record<string, any>>(shared: S, channel: string, message: unknown): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S as StepFilter, a as FlowneerPlugin, d as StepMeta } from '../../FlowBuilder-
|
|
1
|
+
import { S as StepFilter, a as FlowneerPlugin, d as StepMeta } from '../../FlowBuilder-CwBQDOEN.js';
|
|
2
2
|
|
|
3
3
|
declare module "../../Flowneer" {
|
|
4
4
|
interface FlowBuilder<S, P> {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as FlowneerPlugin, N as NodeFn, F as FlowBuilder } from '../../FlowBuilder-
|
|
1
|
+
import { a as FlowneerPlugin, N as NodeFn, F as FlowBuilder } from '../../FlowBuilder-CwBQDOEN.js';
|
|
2
2
|
import { ToolCall, ToolResult, ToolParam, Tool, ToolRegistry } from '../../plugins/tools/index.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -290,6 +290,172 @@ declare function selfConsistency<S = any, P extends Record<string, unknown> = Re
|
|
|
290
290
|
*/
|
|
291
291
|
declare function critiqueAndRevise<S = any, P extends Record<string, unknown> = Record<string, unknown>>(generate: NodeFn<S, P>, critique: NodeFn<S, P>, revise: NodeFn<S, P>, rounds?: number): FlowBuilder<S, P>;
|
|
292
292
|
|
|
293
|
+
/**
|
|
294
|
+
* A single message in the swarm conversation history.
|
|
295
|
+
* Agents write to `shared.messages` using this shape.
|
|
296
|
+
*/
|
|
297
|
+
interface SwarmMessage {
|
|
298
|
+
role: "user" | "assistant";
|
|
299
|
+
content: string;
|
|
300
|
+
/** Name of the agent that produced this message */
|
|
301
|
+
agent?: string;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Fields that `swarm()` reads and writes on shared state.
|
|
305
|
+
* Extend this with your own application fields via intersection:
|
|
306
|
+
*
|
|
307
|
+
* ```typescript
|
|
308
|
+
* type MyState = SwarmState & { topic: string; result?: string };
|
|
309
|
+
* ```
|
|
310
|
+
*/
|
|
311
|
+
interface SwarmState {
|
|
312
|
+
/** Name of the agent currently handling the request.
|
|
313
|
+
* Defaults to `options.defaultAgent` on the first `.run()` call. */
|
|
314
|
+
currentAgent?: string;
|
|
315
|
+
/** Conversation history — manage this inside your agent fns. */
|
|
316
|
+
messages?: SwarmMessage[];
|
|
317
|
+
/** Number of handoffs that have occurred in the current `.run()` call. */
|
|
318
|
+
turnCount?: number;
|
|
319
|
+
/** @internal — loop exit sentinel; removed after each `.run()` */
|
|
320
|
+
__swarmDone?: boolean;
|
|
321
|
+
/** @internal — set by `handoffTo()`; consumed by the handoff checker */
|
|
322
|
+
__swarmHandoff?: {
|
|
323
|
+
target: string;
|
|
324
|
+
reason?: string;
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* A single agent in the swarm.
|
|
329
|
+
* Each agent is a `NodeFn` paired with a name and description.
|
|
330
|
+
*/
|
|
331
|
+
interface SwarmAgent<S = any, P extends Record<string, unknown> = Record<string, unknown>> {
|
|
332
|
+
/** Unique name used in `handoffTo()` calls and `SwarmState.currentAgent`. */
|
|
333
|
+
name: string;
|
|
334
|
+
/** Human-readable description (can be provided to an LLM for routing). */
|
|
335
|
+
description: string;
|
|
336
|
+
/** The agent's step function — same signature as any Flowneer `NodeFn`. */
|
|
337
|
+
fn: NodeFn<S, P>;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Options for `swarm()`.
|
|
341
|
+
*/
|
|
342
|
+
interface SwarmOptions<S = any> {
|
|
343
|
+
/**
|
|
344
|
+
* Name of the agent that handles the first turn.
|
|
345
|
+
* Must appear in the `agents` array.
|
|
346
|
+
*/
|
|
347
|
+
defaultAgent: string;
|
|
348
|
+
/**
|
|
349
|
+
* Maximum number of handoffs per `.run()` call before the flow stops.
|
|
350
|
+
* Counts hops (not total agents); the original agent's first run is free.
|
|
351
|
+
* Defaults to `5`.
|
|
352
|
+
*/
|
|
353
|
+
maxHandoffs?: number;
|
|
354
|
+
/**
|
|
355
|
+
* Called each time a handoff is accepted.
|
|
356
|
+
* `from` is the agent that handed off, `to` is the new agent.
|
|
357
|
+
*/
|
|
358
|
+
onHandoff?: (from: string, to: string, reason: string | undefined, shared: S) => void | Promise<void>;
|
|
359
|
+
/**
|
|
360
|
+
* Called when `maxHandoffs` is exceeded instead of completing the handoff.
|
|
361
|
+
* The turn ends after this callback returns.
|
|
362
|
+
*/
|
|
363
|
+
onMaxHandoffs?: (shared: S) => void | Promise<void>;
|
|
364
|
+
/**
|
|
365
|
+
* Optional LLM router that selects the starting agent on each `.run()` call.
|
|
366
|
+
* Runs once after state initialisation, before the handoff loop begins.
|
|
367
|
+
*/
|
|
368
|
+
router?: SwarmRouter<S>;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Context object passed to a {@link SwarmRouter} prompt function.
|
|
372
|
+
*/
|
|
373
|
+
interface RouterContext<S = any> {
|
|
374
|
+
/** Full conversation history at the time of routing. */
|
|
375
|
+
messages: SwarmMessage[];
|
|
376
|
+
/** All agents registered in the swarm. */
|
|
377
|
+
agents: SwarmAgent<S, any>[];
|
|
378
|
+
/** Name of the agent that will be used if the router returns an unknown name. */
|
|
379
|
+
currentAgent: string;
|
|
380
|
+
/** Live shared state — mutations here are visible to the dispatched agent. */
|
|
381
|
+
shared: S;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* An optional LLM-based router that selects the starting agent for each `.run()` call.
|
|
385
|
+
*
|
|
386
|
+
* @example
|
|
387
|
+
* const flow = swarm(agents, {
|
|
388
|
+
* defaultAgent: "triage",
|
|
389
|
+
* router: {
|
|
390
|
+
* call: (prompt) => openai.chat.completions.create({ ... }).then(r => r.choices[0].message.content!),
|
|
391
|
+
* },
|
|
392
|
+
* });
|
|
393
|
+
*/
|
|
394
|
+
interface SwarmRouter<S = any> {
|
|
395
|
+
/**
|
|
396
|
+
* Calls the LLM with the resolved prompt and returns the agent name to start with.
|
|
397
|
+
* The response is trimmed and matched case-insensitively against the agents array.
|
|
398
|
+
* An unrecognised response is silently ignored and `currentAgent` remains unchanged.
|
|
399
|
+
*/
|
|
400
|
+
call: (prompt: string) => Promise<string>;
|
|
401
|
+
/**
|
|
402
|
+
* Static prompt string or async function that returns the prompt.
|
|
403
|
+
* When omitted, a default prompt listing all agents and the latest user message is used.
|
|
404
|
+
*/
|
|
405
|
+
prompt?: string | ((context: RouterContext<S>) => string | Promise<string>);
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Formats a `SwarmMessage[]` into a plain-text string suitable for use in LLM
|
|
409
|
+
* prompts. Each line is `[agentName] role: content`; the `[agentName]` prefix
|
|
410
|
+
* is omitted when `message.agent` is undefined.
|
|
411
|
+
*
|
|
412
|
+
* @example
|
|
413
|
+
* const prompt = `Conversation so far:\n${historyText(shared.messages ?? [])}`;
|
|
414
|
+
*/
|
|
415
|
+
declare function historyText(messages: SwarmMessage[]): string;
|
|
416
|
+
/**
|
|
417
|
+
* Signal that control should pass to another agent in the swarm.
|
|
418
|
+
*
|
|
419
|
+
* Call this inside an agent's `fn` to hand off to `agentName`.
|
|
420
|
+
* If the target name is not found in the swarm the handoff is silently dropped
|
|
421
|
+
* and the current turn ends.
|
|
422
|
+
*
|
|
423
|
+
* @example
|
|
424
|
+
* const billingAgent: SwarmAgent<MyState> = {
|
|
425
|
+
* name: "billing",
|
|
426
|
+
* description: "Handles billing and payment queries",
|
|
427
|
+
* fn: async (shared) => {
|
|
428
|
+
* if (!isBillingQuery(shared.messages)) {
|
|
429
|
+
* handoffTo(shared, "support", "not a billing question");
|
|
430
|
+
* return;
|
|
431
|
+
* }
|
|
432
|
+
* shared.messages!.push({ role: "assistant", content: await billingLlm(shared) });
|
|
433
|
+
* },
|
|
434
|
+
* };
|
|
435
|
+
*/
|
|
436
|
+
declare function handoffTo(shared: SwarmState, agentName: string, reason?: string): void;
|
|
437
|
+
/**
|
|
438
|
+
* Creates a decentralized swarm of agents that hand off to each other
|
|
439
|
+
* dynamically at runtime.
|
|
440
|
+
*
|
|
441
|
+
* Each agent can call `handoffTo(shared, targetName, reason?)` inside its `fn`
|
|
442
|
+
* to yield control to another agent. The flow loops until either:
|
|
443
|
+
* - An agent finishes without calling `handoffTo`, or
|
|
444
|
+
* - `options.maxHandoffs` is exceeded (default 5) — `onMaxHandoffs` is called.
|
|
445
|
+
*
|
|
446
|
+
* `currentAgent` persists between `.run()` calls so the swarm remembers which
|
|
447
|
+
* agent was active. It is set to `defaultAgent` only on the first call.
|
|
448
|
+
*
|
|
449
|
+
* @example
|
|
450
|
+
* const flow = swarm(
|
|
451
|
+
* [triageAgent, billingAgent, supportAgent],
|
|
452
|
+
* { defaultAgent: "triage" },
|
|
453
|
+
* );
|
|
454
|
+
*
|
|
455
|
+
* await flow.run({ messages: [{ role: "user", content: "I need a refund" }] });
|
|
456
|
+
*/
|
|
457
|
+
declare function swarm<S extends SwarmState = SwarmState, P extends Record<string, unknown> = Record<string, unknown>>(agents: SwarmAgent<S, P>[], options: SwarmOptions<S>, FlowClass?: new () => FlowBuilder<S, P>): FlowBuilder<S, P>;
|
|
458
|
+
|
|
293
459
|
/**
|
|
294
460
|
* Minimal structural interface that matches a Zod ZodObject.
|
|
295
461
|
* We duck-type against `.shape` so this plugin has zero Zod dependency —
|
|
@@ -438,4 +604,4 @@ interface CreateAgentOptions {
|
|
|
438
604
|
*/
|
|
439
605
|
declare function createAgent(options: CreateAgentOptions): FlowBuilder<AgentState>;
|
|
440
606
|
|
|
441
|
-
export { type AgentState, type ChatMessage, type CreateAgentOptions, type EvaluatorOptimizerOptions, type EvaluatorOptimizerResult, type LlmAdapter, type LlmResponse, type LlmToolDef, type PlanAndExecuteOptions, type ReActLoopOptions, type ReflexionOptions, type ThinkResult, type ToolConfig, type ToolConfigParams, type ToolConfigSchema, type ZodLikeObject, createAgent, critiqueAndRevise, evaluatorOptimizer, hierarchicalCrew, planAndExecute, reflexionAgent, roundRobinDebate, selfConsistency, sequentialCrew, supervisorCrew, tool, withReActLoop };
|
|
607
|
+
export { type AgentState, type ChatMessage, type CreateAgentOptions, type EvaluatorOptimizerOptions, type EvaluatorOptimizerResult, type LlmAdapter, type LlmResponse, type LlmToolDef, type PlanAndExecuteOptions, type ReActLoopOptions, type ReflexionOptions, type RouterContext, type SwarmAgent, type SwarmMessage, type SwarmOptions, type SwarmRouter, type SwarmState, type ThinkResult, type ToolConfig, type ToolConfigParams, type ToolConfigSchema, type ZodLikeObject, createAgent, critiqueAndRevise, evaluatorOptimizer, handoffTo, hierarchicalCrew, historyText, planAndExecute, reflexionAgent, roundRobinDebate, selfConsistency, sequentialCrew, supervisorCrew, swarm, tool, withReActLoop };
|
|
@@ -71,15 +71,21 @@ var InterruptError = class extends Error {
|
|
|
71
71
|
};
|
|
72
72
|
|
|
73
73
|
// src/core/utils.ts
|
|
74
|
+
function matchesPattern(pattern, label) {
|
|
75
|
+
return pattern.includes("*") ? new RegExp(
|
|
76
|
+
"^" + pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$"
|
|
77
|
+
).test(label) : pattern === label;
|
|
78
|
+
}
|
|
74
79
|
function matchesFilter(filter, meta) {
|
|
75
80
|
if (!Array.isArray(filter)) return filter(meta);
|
|
81
|
+
const negations = filter.filter((p) => p.startsWith("!"));
|
|
82
|
+
const positives = filter.filter((p) => !p.startsWith("!"));
|
|
76
83
|
const label = meta.label;
|
|
77
|
-
if (label
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
);
|
|
84
|
+
if (label !== void 0 && negations.some((p) => matchesPattern(p.slice(1), label)))
|
|
85
|
+
return false;
|
|
86
|
+
if (positives.length > 0)
|
|
87
|
+
return label !== void 0 && positives.some((p) => matchesPattern(p, label));
|
|
88
|
+
return true;
|
|
83
89
|
}
|
|
84
90
|
function resolveNumber(val, fallback, shared, params) {
|
|
85
91
|
if (val === void 0) return fallback;
|
|
@@ -206,6 +212,27 @@ var CoreFlowBuilder = class _CoreFlowBuilder {
|
|
|
206
212
|
_hooks() {
|
|
207
213
|
return this._hooksCache ??= buildHookCache(this._hooksList);
|
|
208
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* Register lifecycle hooks on this flow instance at runtime.
|
|
217
|
+
*
|
|
218
|
+
* Unlike plugin methods (which use `_setHooks` internally), `addHooks` is
|
|
219
|
+
* intended for consumers — app code, tests, or request-scoped instrumentation
|
|
220
|
+
* that needs to observe or modify a specific flow instance without defining a
|
|
221
|
+
* plugin.
|
|
222
|
+
*
|
|
223
|
+
* Returns a `dispose` function that removes the registered hooks when called.
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* const dispose = flow.addHooks(
|
|
227
|
+
* { beforeStep: (meta) => console.log("->", meta.label) },
|
|
228
|
+
* ["llm:*"],
|
|
229
|
+
* );
|
|
230
|
+
* await flow.run(shared);
|
|
231
|
+
* dispose(); // removes the hooks
|
|
232
|
+
*/
|
|
233
|
+
addHooks(hooks, filter) {
|
|
234
|
+
return this._setHooks(hooks, filter);
|
|
235
|
+
}
|
|
209
236
|
/**
|
|
210
237
|
* Register lifecycle hooks (called by plugin methods, not by consumers).
|
|
211
238
|
* Returns a dispose function that removes these hooks when called.
|
|
@@ -816,6 +843,120 @@ function critiqueAndRevise(generate, critique, revise, rounds = 1) {
|
|
|
816
843
|
});
|
|
817
844
|
}
|
|
818
845
|
|
|
846
|
+
// presets/agent/swarm.ts
|
|
847
|
+
function historyText(messages) {
|
|
848
|
+
return messages.map((m) => `${m.agent ? `[${m.agent}] ` : ""}${m.role}: ${m.content}`).join("\n");
|
|
849
|
+
}
|
|
850
|
+
function buildDefaultRouterPrompt(ctx) {
|
|
851
|
+
const agentList = ctx.agents.map((a) => `- ${a.name}: ${a.description}`).join("\n");
|
|
852
|
+
const history = historyText(ctx.messages);
|
|
853
|
+
const latest = [...ctx.messages].reverse().find((m) => m.role === "user");
|
|
854
|
+
return [
|
|
855
|
+
"You are a routing assistant. Choose the best agent to handle the user's request.",
|
|
856
|
+
"",
|
|
857
|
+
"Available agents:",
|
|
858
|
+
agentList,
|
|
859
|
+
"",
|
|
860
|
+
...history ? ["Conversation history:", history, ""] : [],
|
|
861
|
+
...latest ? [`Latest user message: ${latest.content}`, ""] : [],
|
|
862
|
+
"Respond with only the exact agent name, nothing else."
|
|
863
|
+
].join("\n");
|
|
864
|
+
}
|
|
865
|
+
function handoffTo(shared, agentName, reason) {
|
|
866
|
+
shared.__swarmHandoff = { target: agentName, reason };
|
|
867
|
+
}
|
|
868
|
+
function swarm(agents, options, FlowClass = FlowBuilder) {
|
|
869
|
+
const agentMap = new Map(
|
|
870
|
+
agents.map((a) => [a.name, a])
|
|
871
|
+
);
|
|
872
|
+
if (!agentMap.has(options.defaultAgent)) {
|
|
873
|
+
throw new Error(
|
|
874
|
+
`swarm: defaultAgent "${options.defaultAgent}" not found in agents list. Available agents: ${agents.map((a) => a.name).join(", ")}`
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
const maxHandoffs = options.maxHandoffs ?? 5;
|
|
878
|
+
return new FlowClass().startWith(
|
|
879
|
+
(shared) => {
|
|
880
|
+
if (shared.currentAgent === void 0) {
|
|
881
|
+
shared.currentAgent = options.defaultAgent;
|
|
882
|
+
}
|
|
883
|
+
shared.turnCount = 0;
|
|
884
|
+
shared.__swarmDone = false;
|
|
885
|
+
delete shared.__swarmHandoff;
|
|
886
|
+
},
|
|
887
|
+
{ label: "swarm:init" }
|
|
888
|
+
).then(
|
|
889
|
+
async (shared) => {
|
|
890
|
+
if (!options.router) return;
|
|
891
|
+
const ctx = {
|
|
892
|
+
messages: shared.messages ?? [],
|
|
893
|
+
agents,
|
|
894
|
+
currentAgent: shared.currentAgent,
|
|
895
|
+
shared
|
|
896
|
+
};
|
|
897
|
+
const rawPrompt = typeof options.router.prompt === "function" ? await options.router.prompt(ctx) : options.router.prompt ?? buildDefaultRouterPrompt(ctx);
|
|
898
|
+
const response = await options.router.call(rawPrompt);
|
|
899
|
+
const raw = response.trim();
|
|
900
|
+
const match = agents.find(
|
|
901
|
+
(a) => a.name.toLowerCase() === raw.toLowerCase()
|
|
902
|
+
);
|
|
903
|
+
if (match) {
|
|
904
|
+
shared.currentAgent = match.name;
|
|
905
|
+
}
|
|
906
|
+
},
|
|
907
|
+
{ label: "swarm:router" }
|
|
908
|
+
).loop(
|
|
909
|
+
(shared) => !shared.__swarmDone,
|
|
910
|
+
(b) => {
|
|
911
|
+
b.startWith(
|
|
912
|
+
async (shared, params) => {
|
|
913
|
+
const agent = agentMap.get(shared.currentAgent);
|
|
914
|
+
if (!agent) {
|
|
915
|
+
shared.currentAgent = options.defaultAgent;
|
|
916
|
+
shared.__swarmDone = true;
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
delete shared.__swarmHandoff;
|
|
920
|
+
await agent.fn(shared, params);
|
|
921
|
+
},
|
|
922
|
+
{ label: "swarm:dispatch" }
|
|
923
|
+
).then(
|
|
924
|
+
async (shared) => {
|
|
925
|
+
const handoff = shared.__swarmHandoff;
|
|
926
|
+
if (!handoff) {
|
|
927
|
+
shared.__swarmDone = true;
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
if (!agentMap.has(handoff.target)) {
|
|
931
|
+
shared.__swarmDone = true;
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
if ((shared.turnCount ?? 0) >= maxHandoffs) {
|
|
935
|
+
await options.onMaxHandoffs?.(shared);
|
|
936
|
+
shared.__swarmDone = true;
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
await options.onHandoff?.(
|
|
940
|
+
shared.currentAgent,
|
|
941
|
+
handoff.target,
|
|
942
|
+
handoff.reason,
|
|
943
|
+
shared
|
|
944
|
+
);
|
|
945
|
+
shared.turnCount = (shared.turnCount ?? 0) + 1;
|
|
946
|
+
shared.currentAgent = handoff.target;
|
|
947
|
+
},
|
|
948
|
+
{ label: "swarm:handoff" }
|
|
949
|
+
);
|
|
950
|
+
},
|
|
951
|
+
{ label: "swarm:loop" }
|
|
952
|
+
).then(
|
|
953
|
+
(shared) => {
|
|
954
|
+
delete shared.__swarmDone;
|
|
955
|
+
},
|
|
956
|
+
{ label: "swarm:cleanup" }
|
|
957
|
+
);
|
|
958
|
+
}
|
|
959
|
+
|
|
819
960
|
// presets/agent/tool.ts
|
|
820
961
|
function zodTypeToParamType(typeName) {
|
|
821
962
|
switch (typeName) {
|
|
@@ -987,13 +1128,16 @@ export {
|
|
|
987
1128
|
createAgent,
|
|
988
1129
|
critiqueAndRevise,
|
|
989
1130
|
evaluatorOptimizer,
|
|
1131
|
+
handoffTo,
|
|
990
1132
|
hierarchicalCrew,
|
|
1133
|
+
historyText,
|
|
991
1134
|
planAndExecute,
|
|
992
1135
|
reflexionAgent,
|
|
993
1136
|
roundRobinDebate,
|
|
994
1137
|
selfConsistency,
|
|
995
1138
|
sequentialCrew,
|
|
996
1139
|
supervisorCrew,
|
|
1140
|
+
swarm,
|
|
997
1141
|
tool,
|
|
998
1142
|
withReActLoop
|
|
999
1143
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { S as StepConfig, a as FnRegistry, b as ValidationResult, F as FlowConfig } from '../../schema-CIqQAXqY.js';
|
|
2
|
-
import { F as FlowBuilder } from '../../FlowBuilder-
|
|
2
|
+
import { F as FlowBuilder } from '../../FlowBuilder-CwBQDOEN.js';
|
|
3
3
|
|
|
4
4
|
/** Recursive applicator passed to nested step builders (loop body, batch processor). */
|
|
5
5
|
type ApplyFn = (steps: StepConfig[], flow: FlowBuilder<any, any>, registry: FnRegistry) => void;
|
|
@@ -21,15 +21,21 @@ var InterruptError = class extends Error {
|
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
// src/core/utils.ts
|
|
24
|
+
function matchesPattern(pattern, label) {
|
|
25
|
+
return pattern.includes("*") ? new RegExp(
|
|
26
|
+
"^" + pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$"
|
|
27
|
+
).test(label) : pattern === label;
|
|
28
|
+
}
|
|
24
29
|
function matchesFilter(filter, meta) {
|
|
25
30
|
if (!Array.isArray(filter)) return filter(meta);
|
|
31
|
+
const negations = filter.filter((p) => p.startsWith("!"));
|
|
32
|
+
const positives = filter.filter((p) => !p.startsWith("!"));
|
|
26
33
|
const label = meta.label;
|
|
27
|
-
if (label
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
);
|
|
34
|
+
if (label !== void 0 && negations.some((p) => matchesPattern(p.slice(1), label)))
|
|
35
|
+
return false;
|
|
36
|
+
if (positives.length > 0)
|
|
37
|
+
return label !== void 0 && positives.some((p) => matchesPattern(p, label));
|
|
38
|
+
return true;
|
|
33
39
|
}
|
|
34
40
|
function resolveNumber(val, fallback, shared, params) {
|
|
35
41
|
if (val === void 0) return fallback;
|
|
@@ -156,6 +162,27 @@ var CoreFlowBuilder = class _CoreFlowBuilder {
|
|
|
156
162
|
_hooks() {
|
|
157
163
|
return this._hooksCache ??= buildHookCache(this._hooksList);
|
|
158
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Register lifecycle hooks on this flow instance at runtime.
|
|
167
|
+
*
|
|
168
|
+
* Unlike plugin methods (which use `_setHooks` internally), `addHooks` is
|
|
169
|
+
* intended for consumers — app code, tests, or request-scoped instrumentation
|
|
170
|
+
* that needs to observe or modify a specific flow instance without defining a
|
|
171
|
+
* plugin.
|
|
172
|
+
*
|
|
173
|
+
* Returns a `dispose` function that removes the registered hooks when called.
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* const dispose = flow.addHooks(
|
|
177
|
+
* { beforeStep: (meta) => console.log("->", meta.label) },
|
|
178
|
+
* ["llm:*"],
|
|
179
|
+
* );
|
|
180
|
+
* await flow.run(shared);
|
|
181
|
+
* dispose(); // removes the hooks
|
|
182
|
+
*/
|
|
183
|
+
addHooks(hooks, filter) {
|
|
184
|
+
return this._setHooks(hooks, filter);
|
|
185
|
+
}
|
|
159
186
|
/**
|
|
160
187
|
* Register lifecycle hooks (called by plugin methods, not by consumers).
|
|
161
188
|
* Returns a dispose function that removes these hooks when called.
|
package/dist/presets/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { AgentState, ChatMessage, CreateAgentOptions, EvaluatorOptimizerOptions, EvaluatorOptimizerResult, LlmAdapter, LlmResponse, LlmToolDef, PlanAndExecuteOptions, ReActLoopOptions, ReflexionOptions, ThinkResult, ToolConfig, ToolConfigParams, ToolConfigSchema, ZodLikeObject, createAgent, critiqueAndRevise, evaluatorOptimizer, hierarchicalCrew, planAndExecute, reflexionAgent, roundRobinDebate, selfConsistency, sequentialCrew, supervisorCrew, tool, withReActLoop } from './agent/index.js';
|
|
1
|
+
export { AgentState, ChatMessage, CreateAgentOptions, EvaluatorOptimizerOptions, EvaluatorOptimizerResult, LlmAdapter, LlmResponse, LlmToolDef, PlanAndExecuteOptions, ReActLoopOptions, ReflexionOptions, RouterContext, SwarmAgent, SwarmMessage, SwarmOptions, SwarmRouter, SwarmState, ThinkResult, ToolConfig, ToolConfigParams, ToolConfigSchema, ZodLikeObject, createAgent, critiqueAndRevise, evaluatorOptimizer, handoffTo, hierarchicalCrew, historyText, planAndExecute, reflexionAgent, roundRobinDebate, selfConsistency, sequentialCrew, supervisorCrew, swarm, tool, withReActLoop } from './agent/index.js';
|
|
2
2
|
export { ApplyFn, ConfigValidationError, CustomStepBuilder, JsonFlowBuilder, StepConfigBuilder } from './config/index.js';
|
|
3
3
|
export { IterativeRagOptions, RagOptions, iterativeRag, ragPipeline } from './rag/index.js';
|
|
4
4
|
export { ApprovalGateOptions, ClarifyLoopOptions, GenerateUntilValidOptions, MapReduceOptions, approvalGate, clarifyLoop, generateUntilValid, mapReduceLlm } from './pipeline/index.js';
|
|
5
|
-
import '../FlowBuilder-
|
|
5
|
+
import '../FlowBuilder-CwBQDOEN.js';
|
|
6
6
|
import '../plugins/tools/index.js';
|
|
7
7
|
import '../schema-CIqQAXqY.js';
|
package/dist/presets/index.js
CHANGED
|
@@ -71,15 +71,21 @@ var InterruptError = class extends Error {
|
|
|
71
71
|
};
|
|
72
72
|
|
|
73
73
|
// src/core/utils.ts
|
|
74
|
+
function matchesPattern(pattern, label) {
|
|
75
|
+
return pattern.includes("*") ? new RegExp(
|
|
76
|
+
"^" + pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$"
|
|
77
|
+
).test(label) : pattern === label;
|
|
78
|
+
}
|
|
74
79
|
function matchesFilter(filter, meta) {
|
|
75
80
|
if (!Array.isArray(filter)) return filter(meta);
|
|
81
|
+
const negations = filter.filter((p) => p.startsWith("!"));
|
|
82
|
+
const positives = filter.filter((p) => !p.startsWith("!"));
|
|
76
83
|
const label = meta.label;
|
|
77
|
-
if (label
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
);
|
|
84
|
+
if (label !== void 0 && negations.some((p) => matchesPattern(p.slice(1), label)))
|
|
85
|
+
return false;
|
|
86
|
+
if (positives.length > 0)
|
|
87
|
+
return label !== void 0 && positives.some((p) => matchesPattern(p, label));
|
|
88
|
+
return true;
|
|
83
89
|
}
|
|
84
90
|
function resolveNumber(val, fallback, shared, params) {
|
|
85
91
|
if (val === void 0) return fallback;
|
|
@@ -206,6 +212,27 @@ var CoreFlowBuilder = class _CoreFlowBuilder {
|
|
|
206
212
|
_hooks() {
|
|
207
213
|
return this._hooksCache ??= buildHookCache(this._hooksList);
|
|
208
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* Register lifecycle hooks on this flow instance at runtime.
|
|
217
|
+
*
|
|
218
|
+
* Unlike plugin methods (which use `_setHooks` internally), `addHooks` is
|
|
219
|
+
* intended for consumers — app code, tests, or request-scoped instrumentation
|
|
220
|
+
* that needs to observe or modify a specific flow instance without defining a
|
|
221
|
+
* plugin.
|
|
222
|
+
*
|
|
223
|
+
* Returns a `dispose` function that removes the registered hooks when called.
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* const dispose = flow.addHooks(
|
|
227
|
+
* { beforeStep: (meta) => console.log("->", meta.label) },
|
|
228
|
+
* ["llm:*"],
|
|
229
|
+
* );
|
|
230
|
+
* await flow.run(shared);
|
|
231
|
+
* dispose(); // removes the hooks
|
|
232
|
+
*/
|
|
233
|
+
addHooks(hooks, filter) {
|
|
234
|
+
return this._setHooks(hooks, filter);
|
|
235
|
+
}
|
|
209
236
|
/**
|
|
210
237
|
* Register lifecycle hooks (called by plugin methods, not by consumers).
|
|
211
238
|
* Returns a dispose function that removes these hooks when called.
|
|
@@ -816,6 +843,120 @@ function critiqueAndRevise(generate, critique, revise, rounds = 1) {
|
|
|
816
843
|
});
|
|
817
844
|
}
|
|
818
845
|
|
|
846
|
+
// presets/agent/swarm.ts
|
|
847
|
+
function historyText(messages) {
|
|
848
|
+
return messages.map((m) => `${m.agent ? `[${m.agent}] ` : ""}${m.role}: ${m.content}`).join("\n");
|
|
849
|
+
}
|
|
850
|
+
function buildDefaultRouterPrompt(ctx) {
|
|
851
|
+
const agentList = ctx.agents.map((a) => `- ${a.name}: ${a.description}`).join("\n");
|
|
852
|
+
const history = historyText(ctx.messages);
|
|
853
|
+
const latest = [...ctx.messages].reverse().find((m) => m.role === "user");
|
|
854
|
+
return [
|
|
855
|
+
"You are a routing assistant. Choose the best agent to handle the user's request.",
|
|
856
|
+
"",
|
|
857
|
+
"Available agents:",
|
|
858
|
+
agentList,
|
|
859
|
+
"",
|
|
860
|
+
...history ? ["Conversation history:", history, ""] : [],
|
|
861
|
+
...latest ? [`Latest user message: ${latest.content}`, ""] : [],
|
|
862
|
+
"Respond with only the exact agent name, nothing else."
|
|
863
|
+
].join("\n");
|
|
864
|
+
}
|
|
865
|
+
function handoffTo(shared, agentName, reason) {
|
|
866
|
+
shared.__swarmHandoff = { target: agentName, reason };
|
|
867
|
+
}
|
|
868
|
+
function swarm(agents, options, FlowClass = FlowBuilder) {
|
|
869
|
+
const agentMap = new Map(
|
|
870
|
+
agents.map((a) => [a.name, a])
|
|
871
|
+
);
|
|
872
|
+
if (!agentMap.has(options.defaultAgent)) {
|
|
873
|
+
throw new Error(
|
|
874
|
+
`swarm: defaultAgent "${options.defaultAgent}" not found in agents list. Available agents: ${agents.map((a) => a.name).join(", ")}`
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
const maxHandoffs = options.maxHandoffs ?? 5;
|
|
878
|
+
return new FlowClass().startWith(
|
|
879
|
+
(shared) => {
|
|
880
|
+
if (shared.currentAgent === void 0) {
|
|
881
|
+
shared.currentAgent = options.defaultAgent;
|
|
882
|
+
}
|
|
883
|
+
shared.turnCount = 0;
|
|
884
|
+
shared.__swarmDone = false;
|
|
885
|
+
delete shared.__swarmHandoff;
|
|
886
|
+
},
|
|
887
|
+
{ label: "swarm:init" }
|
|
888
|
+
).then(
|
|
889
|
+
async (shared) => {
|
|
890
|
+
if (!options.router) return;
|
|
891
|
+
const ctx = {
|
|
892
|
+
messages: shared.messages ?? [],
|
|
893
|
+
agents,
|
|
894
|
+
currentAgent: shared.currentAgent,
|
|
895
|
+
shared
|
|
896
|
+
};
|
|
897
|
+
const rawPrompt = typeof options.router.prompt === "function" ? await options.router.prompt(ctx) : options.router.prompt ?? buildDefaultRouterPrompt(ctx);
|
|
898
|
+
const response = await options.router.call(rawPrompt);
|
|
899
|
+
const raw = response.trim();
|
|
900
|
+
const match = agents.find(
|
|
901
|
+
(a) => a.name.toLowerCase() === raw.toLowerCase()
|
|
902
|
+
);
|
|
903
|
+
if (match) {
|
|
904
|
+
shared.currentAgent = match.name;
|
|
905
|
+
}
|
|
906
|
+
},
|
|
907
|
+
{ label: "swarm:router" }
|
|
908
|
+
).loop(
|
|
909
|
+
(shared) => !shared.__swarmDone,
|
|
910
|
+
(b) => {
|
|
911
|
+
b.startWith(
|
|
912
|
+
async (shared, params) => {
|
|
913
|
+
const agent = agentMap.get(shared.currentAgent);
|
|
914
|
+
if (!agent) {
|
|
915
|
+
shared.currentAgent = options.defaultAgent;
|
|
916
|
+
shared.__swarmDone = true;
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
delete shared.__swarmHandoff;
|
|
920
|
+
await agent.fn(shared, params);
|
|
921
|
+
},
|
|
922
|
+
{ label: "swarm:dispatch" }
|
|
923
|
+
).then(
|
|
924
|
+
async (shared) => {
|
|
925
|
+
const handoff = shared.__swarmHandoff;
|
|
926
|
+
if (!handoff) {
|
|
927
|
+
shared.__swarmDone = true;
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
if (!agentMap.has(handoff.target)) {
|
|
931
|
+
shared.__swarmDone = true;
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
if ((shared.turnCount ?? 0) >= maxHandoffs) {
|
|
935
|
+
await options.onMaxHandoffs?.(shared);
|
|
936
|
+
shared.__swarmDone = true;
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
await options.onHandoff?.(
|
|
940
|
+
shared.currentAgent,
|
|
941
|
+
handoff.target,
|
|
942
|
+
handoff.reason,
|
|
943
|
+
shared
|
|
944
|
+
);
|
|
945
|
+
shared.turnCount = (shared.turnCount ?? 0) + 1;
|
|
946
|
+
shared.currentAgent = handoff.target;
|
|
947
|
+
},
|
|
948
|
+
{ label: "swarm:handoff" }
|
|
949
|
+
);
|
|
950
|
+
},
|
|
951
|
+
{ label: "swarm:loop" }
|
|
952
|
+
).then(
|
|
953
|
+
(shared) => {
|
|
954
|
+
delete shared.__swarmDone;
|
|
955
|
+
},
|
|
956
|
+
{ label: "swarm:cleanup" }
|
|
957
|
+
);
|
|
958
|
+
}
|
|
959
|
+
|
|
819
960
|
// presets/agent/tool.ts
|
|
820
961
|
function zodTypeToParamType(typeName) {
|
|
821
962
|
switch (typeName) {
|
|
@@ -1391,7 +1532,9 @@ export {
|
|
|
1391
1532
|
critiqueAndRevise,
|
|
1392
1533
|
evaluatorOptimizer,
|
|
1393
1534
|
generateUntilValid,
|
|
1535
|
+
handoffTo,
|
|
1394
1536
|
hierarchicalCrew,
|
|
1537
|
+
historyText,
|
|
1395
1538
|
iterativeRag,
|
|
1396
1539
|
mapReduceLlm,
|
|
1397
1540
|
planAndExecute,
|
|
@@ -1401,6 +1544,7 @@ export {
|
|
|
1401
1544
|
selfConsistency,
|
|
1402
1545
|
sequentialCrew,
|
|
1403
1546
|
supervisorCrew,
|
|
1547
|
+
swarm,
|
|
1404
1548
|
tool,
|
|
1405
1549
|
withReActLoop
|
|
1406
1550
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { N as NodeFn, F as FlowBuilder } from '../../FlowBuilder-
|
|
1
|
+
import { N as NodeFn, F as FlowBuilder } from '../../FlowBuilder-CwBQDOEN.js';
|
|
2
2
|
|
|
3
3
|
interface GenerateUntilValidOptions<S = any, P extends Record<string, unknown> = Record<string, unknown>> {
|
|
4
4
|
/**
|
|
@@ -21,15 +21,21 @@ var InterruptError = class extends Error {
|
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
// src/core/utils.ts
|
|
24
|
+
function matchesPattern(pattern, label) {
|
|
25
|
+
return pattern.includes("*") ? new RegExp(
|
|
26
|
+
"^" + pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$"
|
|
27
|
+
).test(label) : pattern === label;
|
|
28
|
+
}
|
|
24
29
|
function matchesFilter(filter, meta) {
|
|
25
30
|
if (!Array.isArray(filter)) return filter(meta);
|
|
31
|
+
const negations = filter.filter((p) => p.startsWith("!"));
|
|
32
|
+
const positives = filter.filter((p) => !p.startsWith("!"));
|
|
26
33
|
const label = meta.label;
|
|
27
|
-
if (label
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
);
|
|
34
|
+
if (label !== void 0 && negations.some((p) => matchesPattern(p.slice(1), label)))
|
|
35
|
+
return false;
|
|
36
|
+
if (positives.length > 0)
|
|
37
|
+
return label !== void 0 && positives.some((p) => matchesPattern(p, label));
|
|
38
|
+
return true;
|
|
33
39
|
}
|
|
34
40
|
function resolveNumber(val, fallback, shared, params) {
|
|
35
41
|
if (val === void 0) return fallback;
|
|
@@ -156,6 +162,27 @@ var CoreFlowBuilder = class _CoreFlowBuilder {
|
|
|
156
162
|
_hooks() {
|
|
157
163
|
return this._hooksCache ??= buildHookCache(this._hooksList);
|
|
158
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Register lifecycle hooks on this flow instance at runtime.
|
|
167
|
+
*
|
|
168
|
+
* Unlike plugin methods (which use `_setHooks` internally), `addHooks` is
|
|
169
|
+
* intended for consumers — app code, tests, or request-scoped instrumentation
|
|
170
|
+
* that needs to observe or modify a specific flow instance without defining a
|
|
171
|
+
* plugin.
|
|
172
|
+
*
|
|
173
|
+
* Returns a `dispose` function that removes the registered hooks when called.
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* const dispose = flow.addHooks(
|
|
177
|
+
* { beforeStep: (meta) => console.log("->", meta.label) },
|
|
178
|
+
* ["llm:*"],
|
|
179
|
+
* );
|
|
180
|
+
* await flow.run(shared);
|
|
181
|
+
* dispose(); // removes the hooks
|
|
182
|
+
*/
|
|
183
|
+
addHooks(hooks, filter) {
|
|
184
|
+
return this._setHooks(hooks, filter);
|
|
185
|
+
}
|
|
159
186
|
/**
|
|
160
187
|
* Register lifecycle hooks (called by plugin methods, not by consumers).
|
|
161
188
|
* Returns a dispose function that removes these hooks when called.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { N as NodeFn, F as FlowBuilder } from '../../FlowBuilder-
|
|
1
|
+
import { N as NodeFn, F as FlowBuilder } from '../../FlowBuilder-CwBQDOEN.js';
|
|
2
2
|
|
|
3
3
|
interface RagOptions<S = any, P extends Record<string, unknown> = Record<string, unknown>> {
|
|
4
4
|
/** Retrieves relevant documents/chunks and writes them to shared state. */
|
|
@@ -21,15 +21,21 @@ var InterruptError = class extends Error {
|
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
// src/core/utils.ts
|
|
24
|
+
function matchesPattern(pattern, label) {
|
|
25
|
+
return pattern.includes("*") ? new RegExp(
|
|
26
|
+
"^" + pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$"
|
|
27
|
+
).test(label) : pattern === label;
|
|
28
|
+
}
|
|
24
29
|
function matchesFilter(filter, meta) {
|
|
25
30
|
if (!Array.isArray(filter)) return filter(meta);
|
|
31
|
+
const negations = filter.filter((p) => p.startsWith("!"));
|
|
32
|
+
const positives = filter.filter((p) => !p.startsWith("!"));
|
|
26
33
|
const label = meta.label;
|
|
27
|
-
if (label
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
);
|
|
34
|
+
if (label !== void 0 && negations.some((p) => matchesPattern(p.slice(1), label)))
|
|
35
|
+
return false;
|
|
36
|
+
if (positives.length > 0)
|
|
37
|
+
return label !== void 0 && positives.some((p) => matchesPattern(p, label));
|
|
38
|
+
return true;
|
|
33
39
|
}
|
|
34
40
|
function resolveNumber(val, fallback, shared, params) {
|
|
35
41
|
if (val === void 0) return fallback;
|
|
@@ -156,6 +162,27 @@ var CoreFlowBuilder = class _CoreFlowBuilder {
|
|
|
156
162
|
_hooks() {
|
|
157
163
|
return this._hooksCache ??= buildHookCache(this._hooksList);
|
|
158
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Register lifecycle hooks on this flow instance at runtime.
|
|
167
|
+
*
|
|
168
|
+
* Unlike plugin methods (which use `_setHooks` internally), `addHooks` is
|
|
169
|
+
* intended for consumers — app code, tests, or request-scoped instrumentation
|
|
170
|
+
* that needs to observe or modify a specific flow instance without defining a
|
|
171
|
+
* plugin.
|
|
172
|
+
*
|
|
173
|
+
* Returns a `dispose` function that removes the registered hooks when called.
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* const dispose = flow.addHooks(
|
|
177
|
+
* { beforeStep: (meta) => console.log("->", meta.label) },
|
|
178
|
+
* ["llm:*"],
|
|
179
|
+
* );
|
|
180
|
+
* await flow.run(shared);
|
|
181
|
+
* dispose(); // removes the hooks
|
|
182
|
+
*/
|
|
183
|
+
addHooks(hooks, filter) {
|
|
184
|
+
return this._setHooks(hooks, filter);
|
|
185
|
+
}
|
|
159
186
|
/**
|
|
160
187
|
* Register lifecycle hooks (called by plugin methods, not by consumers).
|
|
161
188
|
* Returns a dispose function that removes these hooks when called.
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { F as FlowBuilder } from '../FlowBuilder-
|
|
2
|
-
export { A as AnchorStep, e as AugmentedState, B as BatchStep, f as BranchStep, C as CoreFlowBuilder, D as DagStep, c as FlowHooks, a as FlowneerPlugin, g as FnStep, L as LoopStep, N as NodeFn, b as NodeOptions, h as NumberOrFn, P as ParallelStep, R as ResolvedHooks,
|
|
1
|
+
import { F as FlowBuilder } from '../FlowBuilder-CwBQDOEN.js';
|
|
2
|
+
export { A as AnchorStep, e as AugmentedState, B as BatchStep, f as BranchStep, C as CoreFlowBuilder, D as DagStep, c as FlowHooks, a as FlowneerPlugin, g as FnStep, L as LoopStep, N as NodeFn, b as NodeOptions, h as NumberOrFn, P as ParallelStep, i as PluginContext, R as ResolvedHooks, j as RunOptions, k as Step, l as StepContext, S as StepFilter, m as StepHandler, d as StepMeta, n as StreamEvent, V as Validator } from '../FlowBuilder-CwBQDOEN.js';
|
|
3
3
|
export { F as FlowError, I as InterruptError } from '../errors-u-hq7p5N.js';
|
|
4
4
|
|
|
5
5
|
/**
|
package/dist/src/index.js
CHANGED
|
@@ -21,15 +21,21 @@ var InterruptError = class extends Error {
|
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
// src/core/utils.ts
|
|
24
|
+
function matchesPattern(pattern, label) {
|
|
25
|
+
return pattern.includes("*") ? new RegExp(
|
|
26
|
+
"^" + pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$"
|
|
27
|
+
).test(label) : pattern === label;
|
|
28
|
+
}
|
|
24
29
|
function matchesFilter(filter, meta) {
|
|
25
30
|
if (!Array.isArray(filter)) return filter(meta);
|
|
31
|
+
const negations = filter.filter((p) => p.startsWith("!"));
|
|
32
|
+
const positives = filter.filter((p) => !p.startsWith("!"));
|
|
26
33
|
const label = meta.label;
|
|
27
|
-
if (label
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
);
|
|
34
|
+
if (label !== void 0 && negations.some((p) => matchesPattern(p.slice(1), label)))
|
|
35
|
+
return false;
|
|
36
|
+
if (positives.length > 0)
|
|
37
|
+
return label !== void 0 && positives.some((p) => matchesPattern(p, label));
|
|
38
|
+
return true;
|
|
33
39
|
}
|
|
34
40
|
function resolveNumber(val, fallback, shared, params) {
|
|
35
41
|
if (val === void 0) return fallback;
|
|
@@ -156,6 +162,27 @@ var CoreFlowBuilder = class _CoreFlowBuilder {
|
|
|
156
162
|
_hooks() {
|
|
157
163
|
return this._hooksCache ??= buildHookCache(this._hooksList);
|
|
158
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Register lifecycle hooks on this flow instance at runtime.
|
|
167
|
+
*
|
|
168
|
+
* Unlike plugin methods (which use `_setHooks` internally), `addHooks` is
|
|
169
|
+
* intended for consumers — app code, tests, or request-scoped instrumentation
|
|
170
|
+
* that needs to observe or modify a specific flow instance without defining a
|
|
171
|
+
* plugin.
|
|
172
|
+
*
|
|
173
|
+
* Returns a `dispose` function that removes the registered hooks when called.
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* const dispose = flow.addHooks(
|
|
177
|
+
* { beforeStep: (meta) => console.log("->", meta.label) },
|
|
178
|
+
* ["llm:*"],
|
|
179
|
+
* );
|
|
180
|
+
* await flow.run(shared);
|
|
181
|
+
* dispose(); // removes the hooks
|
|
182
|
+
*/
|
|
183
|
+
addHooks(hooks, filter) {
|
|
184
|
+
return this._setHooks(hooks, filter);
|
|
185
|
+
}
|
|
159
186
|
/**
|
|
160
187
|
* Register lifecycle hooks (called by plugin methods, not by consumers).
|
|
161
188
|
* Returns a dispose function that removes these hooks when called.
|