pipeai 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,593 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ Agent: () => Agent,
24
+ Workflow: () => Workflow,
25
+ WorkflowBranchError: () => WorkflowBranchError,
26
+ WorkflowLoopError: () => WorkflowLoopError,
27
+ defineTool: () => defineTool
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+
31
+ // src/agent.ts
32
+ var import_ai2 = require("ai");
33
+
34
+ // src/tool-provider.ts
35
+ var import_ai = require("ai");
36
+ var TOOL_PROVIDER_BRAND = /* @__PURE__ */ Symbol.for("agent-workflow.ToolProvider");
37
+ var ToolProvider = class {
38
+ [TOOL_PROVIDER_BRAND] = true;
39
+ config;
40
+ constructor(config) {
41
+ this.config = config;
42
+ }
43
+ createTool(context) {
44
+ const { execute, input: inputSchema, ...toolDef } = this.config;
45
+ return (0, import_ai.tool)({
46
+ ...toolDef,
47
+ parameters: inputSchema,
48
+ execute: (input, options) => execute(input, context, options)
49
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
+ });
51
+ }
52
+ };
53
+ function defineTool() {
54
+ return (config) => new ToolProvider(config);
55
+ }
56
+ function isToolProvider(obj) {
57
+ return typeof obj === "object" && obj !== null && TOOL_PROVIDER_BRAND in obj;
58
+ }
59
+
60
+ // src/utils.ts
61
+ function resolveValue(value, ctx, input) {
62
+ if (typeof value === "function") {
63
+ return value(ctx, input);
64
+ }
65
+ return value;
66
+ }
67
+ async function extractOutput(result, hasStructuredOutput) {
68
+ if (hasStructuredOutput) {
69
+ const output = await result.output;
70
+ if (output !== void 0) return output;
71
+ }
72
+ return await result.text;
73
+ }
74
+
75
+ // src/agent.ts
76
+ var Agent = class {
77
+ id;
78
+ description;
79
+ hasOutput;
80
+ config;
81
+ _hasDynamicConfig;
82
+ _resolvedStaticTools = null;
83
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
84
+ _passthrough;
85
+ _onStepFinish;
86
+ _onFinish;
87
+ constructor(config) {
88
+ this.id = config.id;
89
+ this.description = config.description ?? "";
90
+ this.hasOutput = config.output !== void 0;
91
+ this.config = config;
92
+ this._hasDynamicConfig = [
93
+ config.model,
94
+ config.system,
95
+ config.prompt,
96
+ config.messages,
97
+ config.tools,
98
+ config.activeTools,
99
+ config.toolChoice,
100
+ config.stopWhen
101
+ ].some((v) => typeof v === "function");
102
+ if (!this._hasDynamicConfig) {
103
+ const rawTools = config.tools ?? {};
104
+ const hasProvider = Object.values(rawTools).some((v) => isToolProvider(v));
105
+ if (!hasProvider) {
106
+ this._resolvedStaticTools = rawTools;
107
+ }
108
+ }
109
+ const {
110
+ id: _id,
111
+ description: _desc,
112
+ input: _inputSchema,
113
+ output: _output,
114
+ model: _m,
115
+ system: _s,
116
+ prompt: _p,
117
+ messages: _msg,
118
+ tools: _t,
119
+ activeTools: _at,
120
+ toolChoice: _tc,
121
+ stopWhen: _sw,
122
+ onStepFinish,
123
+ onFinish,
124
+ onError: _onError,
125
+ ...passthrough
126
+ } = config;
127
+ this._passthrough = passthrough;
128
+ this._onStepFinish = onStepFinish;
129
+ this._onFinish = onFinish;
130
+ }
131
+ async generate(ctx, ...args) {
132
+ const input = args[0];
133
+ const resolved = await this.resolveConfig(ctx, input);
134
+ const options = this.buildCallOptions(resolved, ctx, input);
135
+ try {
136
+ return await (0, import_ai2.generateText)(options);
137
+ } catch (error) {
138
+ if (this.config.onError) {
139
+ await this.config.onError({ error, ctx, input });
140
+ }
141
+ throw error;
142
+ }
143
+ }
144
+ async stream(ctx, ...args) {
145
+ const input = args[0];
146
+ const resolved = await this.resolveConfig(ctx, input);
147
+ const options = this.buildCallOptions(resolved, ctx, input);
148
+ return (0, import_ai2.streamText)({
149
+ ...options,
150
+ onError: this.config.onError ? ({ error }) => this.config.onError({ error, ctx, input }) : void 0
151
+ });
152
+ }
153
+ asTool(ctx, options) {
154
+ return this.createToolInstance(ctx, options);
155
+ }
156
+ asToolProvider(options) {
157
+ if (!this.config.input) {
158
+ throw new Error(`Agent "${this.id}": asToolProvider() requires an input schema`);
159
+ }
160
+ return {
161
+ [TOOL_PROVIDER_BRAND]: true,
162
+ createTool: (ctx) => this.createToolInstance(ctx, options)
163
+ };
164
+ }
165
+ createToolInstance(ctx, options) {
166
+ if (!this.config.input) {
167
+ throw new Error(`Agent "${this.id}": asTool() requires an input schema`);
168
+ }
169
+ return (0, import_ai2.tool)({
170
+ description: this.description,
171
+ parameters: this.config.input,
172
+ execute: async (toolInput) => {
173
+ const result = await this.generate(ctx, toolInput);
174
+ if (options?.mapOutput) return options.mapOutput(result);
175
+ return extractOutput(result, this.hasOutput);
176
+ }
177
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
178
+ });
179
+ }
180
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
181
+ buildCallOptions(resolved, ctx, input) {
182
+ return {
183
+ ...this._passthrough,
184
+ model: resolved.model,
185
+ tools: resolved.tools,
186
+ activeTools: resolved.activeTools,
187
+ toolChoice: resolved.toolChoice,
188
+ stopWhen: resolved.stopWhen,
189
+ ...resolved.messages ? { messages: resolved.messages } : { prompt: resolved.prompt ?? "" },
190
+ ...resolved.system ? { system: resolved.system } : {},
191
+ ...this.config.output ? { output: this.config.output } : {},
192
+ onStepFinish: this._onStepFinish ? (event) => this._onStepFinish({ result: event, ctx, input }) : void 0,
193
+ onFinish: this._onFinish ? (event) => this._onFinish({ result: event, ctx, input }) : void 0
194
+ };
195
+ }
196
+ resolveConfig(ctx, input) {
197
+ if (!this._hasDynamicConfig) {
198
+ return {
199
+ model: this.config.model,
200
+ prompt: this.config.prompt,
201
+ system: this.config.system,
202
+ messages: this.config.messages,
203
+ tools: this._resolvedStaticTools ?? this.resolveTools(
204
+ this.config.tools ?? {},
205
+ ctx
206
+ ),
207
+ activeTools: this.config.activeTools,
208
+ toolChoice: this.config.toolChoice,
209
+ stopWhen: this.config.stopWhen
210
+ };
211
+ }
212
+ return this.resolveConfigAsync(ctx, input);
213
+ }
214
+ async resolveConfigAsync(ctx, input) {
215
+ const [model, prompt, system, messages, rawTools, activeTools, toolChoice, stopWhen] = await Promise.all([
216
+ resolveValue(this.config.model, ctx, input),
217
+ resolveValue(this.config.prompt, ctx, input),
218
+ resolveValue(this.config.system, ctx, input),
219
+ resolveValue(this.config.messages, ctx, input),
220
+ resolveValue(this.config.tools, ctx, input),
221
+ resolveValue(this.config.activeTools, ctx, input),
222
+ resolveValue(this.config.toolChoice, ctx, input),
223
+ resolveValue(this.config.stopWhen, ctx, input)
224
+ ]);
225
+ const tools = this.resolveTools(rawTools ?? {}, ctx);
226
+ return { model, prompt, system, messages, tools, activeTools, toolChoice, stopWhen };
227
+ }
228
+ resolveTools(tools, ctx) {
229
+ const entries = Object.entries(tools);
230
+ if (entries.length === 0) return tools;
231
+ let hasProvider = false;
232
+ const resolved = {};
233
+ for (const [key, toolOrProvider] of entries) {
234
+ if (isToolProvider(toolOrProvider)) {
235
+ hasProvider = true;
236
+ resolved[key] = toolOrProvider.createTool(ctx);
237
+ } else {
238
+ resolved[key] = toolOrProvider;
239
+ }
240
+ }
241
+ return hasProvider ? resolved : tools;
242
+ }
243
+ };
244
+
245
+ // src/workflow.ts
246
+ var import_ai3 = require("ai");
247
+ var WorkflowBranchError = class extends Error {
248
+ constructor(branchType, message) {
249
+ super(message);
250
+ this.branchType = branchType;
251
+ this.name = "WorkflowBranchError";
252
+ }
253
+ };
254
+ var WorkflowLoopError = class extends Error {
255
+ constructor(iterations, maxIterations) {
256
+ super(`Loop exceeded maximum iterations (${maxIterations})`);
257
+ this.iterations = iterations;
258
+ this.maxIterations = maxIterations;
259
+ this.name = "WorkflowLoopError";
260
+ }
261
+ };
262
+ var SealedWorkflow = class {
263
+ id;
264
+ steps;
265
+ constructor(steps, id) {
266
+ this.steps = steps;
267
+ this.id = id;
268
+ }
269
+ // ── Execution ─────────────────────────────────────────────────
270
+ async generate(ctx, ...args) {
271
+ const input = args[0];
272
+ const state = {
273
+ ctx,
274
+ output: input,
275
+ mode: "generate"
276
+ };
277
+ await this.execute(state);
278
+ return {
279
+ output: state.output
280
+ };
281
+ }
282
+ stream(ctx, ...args) {
283
+ const input = args[0];
284
+ const options = args[1];
285
+ let resolveOutput;
286
+ let rejectOutput;
287
+ const outputPromise = new Promise((res, rej) => {
288
+ resolveOutput = res;
289
+ rejectOutput = rej;
290
+ });
291
+ outputPromise.catch(() => {
292
+ });
293
+ const stream = (0, import_ai3.createUIMessageStream)({
294
+ execute: async ({ writer }) => {
295
+ const state = {
296
+ ctx,
297
+ output: input,
298
+ mode: "stream",
299
+ writer
300
+ };
301
+ try {
302
+ await this.execute(state);
303
+ resolveOutput(state.output);
304
+ } catch (error) {
305
+ rejectOutput(error);
306
+ throw error;
307
+ }
308
+ },
309
+ ...options?.onError ? { onError: options.onError } : {},
310
+ ...options?.onFinish ? { onFinish: options.onFinish } : {}
311
+ });
312
+ return {
313
+ stream,
314
+ output: outputPromise
315
+ };
316
+ }
317
+ // ── Internal: execute pipeline ────────────────────────────────
318
+ async execute(state) {
319
+ if (this.steps.length === 0) {
320
+ throw new Error("Workflow has no steps. Add at least one step before calling generate() or stream().");
321
+ }
322
+ let pendingError = null;
323
+ for (const node of this.steps) {
324
+ if (node.type === "finally") {
325
+ await node.execute(state);
326
+ continue;
327
+ }
328
+ if (node.type === "catch") {
329
+ if (!pendingError) continue;
330
+ try {
331
+ state.output = await node.catchFn({
332
+ error: pendingError.error,
333
+ ctx: state.ctx,
334
+ lastOutput: state.output,
335
+ stepId: pendingError.stepId
336
+ });
337
+ pendingError = null;
338
+ } catch (catchError) {
339
+ pendingError = { error: catchError, stepId: node.id };
340
+ }
341
+ continue;
342
+ }
343
+ if (pendingError) continue;
344
+ try {
345
+ await node.execute(state);
346
+ } catch (error) {
347
+ pendingError = { error, stepId: node.id };
348
+ }
349
+ }
350
+ if (pendingError) throw pendingError.error;
351
+ }
352
+ // ── Internal: execute a nested workflow within a step/loop ─────
353
+ // Defined on SealedWorkflow (not Workflow) because TypeScript's protected
354
+ // access rules only allow calling workflow.execute() from the same class.
355
+ async executeNestedWorkflow(state, workflow) {
356
+ await workflow.execute(state);
357
+ }
358
+ // ── Internal: execute an agent within a step/branch ───────────
359
+ // In stream mode, output extraction awaits the full stream before returning.
360
+ // Streaming benefits the client (incremental output), not pipeline throughput —
361
+ // each step still runs sequentially.
362
+ async executeAgent(state, agent, ctx, options) {
363
+ const input = state.output;
364
+ const hasStructuredOutput = agent.hasOutput;
365
+ if (state.mode === "stream" && state.writer) {
366
+ const result = await agent.stream(ctx, state.output);
367
+ if (options?.handleStream) {
368
+ await options.handleStream({ result, writer: state.writer, ctx });
369
+ } else {
370
+ state.writer.merge(result.toUIMessageStream());
371
+ }
372
+ if (options?.onStreamResult) {
373
+ await options.onStreamResult({ result, ctx, input });
374
+ }
375
+ if (options?.mapStreamResult) {
376
+ state.output = await options.mapStreamResult({ result, ctx, input });
377
+ } else {
378
+ state.output = await extractOutput(result, hasStructuredOutput);
379
+ }
380
+ } else {
381
+ const result = await agent.generate(ctx, state.output);
382
+ if (options?.onGenerateResult) {
383
+ await options.onGenerateResult({ result, ctx, input });
384
+ }
385
+ if (options?.mapGenerateResult) {
386
+ state.output = await options.mapGenerateResult({ result, ctx, input });
387
+ } else {
388
+ state.output = await extractOutput(result, hasStructuredOutput);
389
+ }
390
+ }
391
+ }
392
+ };
393
+ var Workflow = class _Workflow extends SealedWorkflow {
394
+ constructor(steps = [], id) {
395
+ super(steps, id);
396
+ }
397
+ static create(options) {
398
+ return new _Workflow([], options?.id);
399
+ }
400
+ static from(agent, options) {
401
+ return new _Workflow([]).step(agent, options);
402
+ }
403
+ // ── step: implementation ──────────────────────────────────────
404
+ step(target, optionsOrFn) {
405
+ if (target instanceof SealedWorkflow) {
406
+ const workflow = target;
407
+ const node2 = {
408
+ type: "step",
409
+ id: workflow.id ?? "nested-workflow",
410
+ execute: async (state) => {
411
+ await this.executeNestedWorkflow(state, workflow);
412
+ }
413
+ };
414
+ return new _Workflow([...this.steps, node2], this.id);
415
+ }
416
+ if (typeof target === "string") {
417
+ if (typeof optionsOrFn !== "function") {
418
+ throw new Error(`Workflow step("${target}"): second argument must be a function`);
419
+ }
420
+ const fn = optionsOrFn;
421
+ const node2 = {
422
+ type: "step",
423
+ id: target,
424
+ execute: async (state) => {
425
+ state.output = await fn({
426
+ ctx: state.ctx,
427
+ input: state.output
428
+ });
429
+ }
430
+ };
431
+ return new _Workflow([...this.steps, node2], this.id);
432
+ }
433
+ const agent = target;
434
+ const options = optionsOrFn;
435
+ const node = {
436
+ type: "step",
437
+ id: agent.id,
438
+ execute: async (state) => {
439
+ const ctx = state.ctx;
440
+ await this.executeAgent(state, agent, ctx, options);
441
+ }
442
+ };
443
+ return new _Workflow([...this.steps, node], this.id);
444
+ }
445
+ // ── branch: implementation ────────────────────────────────────
446
+ branch(casesOrConfig) {
447
+ if (Array.isArray(casesOrConfig)) {
448
+ return this.branchPredicate(casesOrConfig);
449
+ }
450
+ return this.branchSelect(casesOrConfig);
451
+ }
452
+ branchPredicate(cases) {
453
+ const node = {
454
+ type: "step",
455
+ id: "branch:predicate",
456
+ execute: async (state) => {
457
+ const ctx = state.ctx;
458
+ const input = state.output;
459
+ for (const branchCase of cases) {
460
+ if (branchCase.when) {
461
+ const match = await branchCase.when({ ctx, input });
462
+ if (!match) continue;
463
+ }
464
+ await this.executeAgent(state, branchCase.agent, ctx, branchCase);
465
+ return;
466
+ }
467
+ throw new WorkflowBranchError("predicate", `No branch matched and no default branch (a case without \`when\`) was provided. Input: ${JSON.stringify(input)}`);
468
+ }
469
+ };
470
+ return new _Workflow([...this.steps, node], this.id);
471
+ }
472
+ branchSelect(config) {
473
+ const node = {
474
+ type: "step",
475
+ id: "branch:select",
476
+ execute: async (state) => {
477
+ const ctx = state.ctx;
478
+ const input = state.output;
479
+ const key = await config.select({ ctx, input });
480
+ let agent = config.agents[key];
481
+ if (!agent) {
482
+ if (config.fallback) {
483
+ agent = config.fallback;
484
+ } else {
485
+ throw new WorkflowBranchError("select", `No agent found for key "${key}" and no fallback provided. Available keys: ${Object.keys(config.agents).join(", ")}`);
486
+ }
487
+ }
488
+ await this.executeAgent(state, agent, ctx, config);
489
+ }
490
+ };
491
+ return new _Workflow([...this.steps, node], this.id);
492
+ }
493
+ // ── foreach: array iteration ─────────────────────────────────
494
+ foreach(target, options) {
495
+ const concurrency = options?.concurrency ?? 1;
496
+ const isWorkflow = target instanceof SealedWorkflow;
497
+ const id = isWorkflow ? target.id ?? "foreach" : `foreach:${target.id}`;
498
+ const node = {
499
+ type: "step",
500
+ id,
501
+ execute: async (state) => {
502
+ const items = state.output;
503
+ if (!Array.isArray(items)) {
504
+ throw new Error(`foreach "${id}": expected array input, got ${typeof items}`);
505
+ }
506
+ const ctx = state.ctx;
507
+ const results = new Array(items.length);
508
+ const executeItem = async (item, index) => {
509
+ const itemState = { ctx: state.ctx, output: item, mode: "generate" };
510
+ if (isWorkflow) {
511
+ await this.executeNestedWorkflow(itemState, target);
512
+ } else {
513
+ await this.executeAgent(itemState, target, ctx);
514
+ }
515
+ results[index] = itemState.output;
516
+ };
517
+ if (concurrency <= 1) {
518
+ for (let i = 0; i < items.length; i++) {
519
+ await executeItem(items[i], i);
520
+ }
521
+ } else {
522
+ for (let i = 0; i < items.length; i += concurrency) {
523
+ const batch = items.slice(i, i + concurrency);
524
+ await Promise.all(batch.map((item, j) => executeItem(item, i + j)));
525
+ }
526
+ }
527
+ state.output = results;
528
+ }
529
+ };
530
+ return new _Workflow([...this.steps, node], this.id);
531
+ }
532
+ // ── repeat: conditional loop ─────────────────────────────────
533
+ repeat(target, options) {
534
+ const maxIterations = options.maxIterations ?? 10;
535
+ const isWorkflow = target instanceof SealedWorkflow;
536
+ const id = isWorkflow ? target.id ?? "repeat" : `repeat:${target.id}`;
537
+ const predicate = options.until ?? (async (p) => !await options.while(p));
538
+ const node = {
539
+ type: "step",
540
+ id,
541
+ execute: async (state) => {
542
+ const ctx = state.ctx;
543
+ for (let i = 1; i <= maxIterations; i++) {
544
+ if (isWorkflow) {
545
+ await this.executeNestedWorkflow(state, target);
546
+ } else {
547
+ await this.executeAgent(state, target, ctx);
548
+ }
549
+ const done = await predicate({
550
+ output: state.output,
551
+ ctx,
552
+ iterations: i
553
+ });
554
+ if (done) return;
555
+ }
556
+ throw new WorkflowLoopError(maxIterations, maxIterations);
557
+ }
558
+ };
559
+ return new _Workflow([...this.steps, node], this.id);
560
+ }
561
+ // ── catch ─────────────────────────────────────────────────────
562
+ catch(id, fn) {
563
+ if (!this.steps.some((s) => s.type === "step")) {
564
+ throw new Error(`Workflow: catch("${id}") requires at least one preceding step.`);
565
+ }
566
+ const node = {
567
+ type: "catch",
568
+ id,
569
+ catchFn: fn
570
+ };
571
+ return new _Workflow([...this.steps, node], this.id);
572
+ }
573
+ // ── finally (terminal — returns sealed workflow) ──────────────
574
+ finally(id, fn) {
575
+ const node = {
576
+ type: "finally",
577
+ id,
578
+ execute: async (state) => {
579
+ await fn({ ctx: state.ctx });
580
+ }
581
+ };
582
+ return new SealedWorkflow([...this.steps, node], this.id);
583
+ }
584
+ };
585
+ // Annotate the CommonJS export names for ESM import in node:
586
+ 0 && (module.exports = {
587
+ Agent,
588
+ Workflow,
589
+ WorkflowBranchError,
590
+ WorkflowLoopError,
591
+ defineTool
592
+ });
593
+ //# sourceMappingURL=index.cjs.map