pipeai 0.1.0 → 0.2.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/README.md +223 -24
- package/dist/index.cjs +219 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +77 -16
- package/dist/index.d.ts +77 -16
- package/dist/index.js +218 -37
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import { Tool,
|
|
1
|
+
import { Tool, ToolExecutionOptions, UIMessageStreamWriter, FlexibleSchema, streamText, generateText, Output, LanguageModel, ModelMessage, ToolChoice, ToolSet, StopCondition, OnStepFinishEvent, OnFinishEvent, GenerateTextResult as GenerateTextResult$1, StreamTextResult as StreamTextResult$1 } from 'ai';
|
|
2
2
|
import { ZodType } from 'zod';
|
|
3
3
|
|
|
4
4
|
declare const TOOL_PROVIDER_BRAND: unique symbol;
|
|
5
|
+
type ToolExecuteOptions = ToolExecutionOptions & {
|
|
6
|
+
writer?: UIMessageStreamWriter;
|
|
7
|
+
};
|
|
5
8
|
type ToolProviderConfig<TContext, TInput, TOutput> = {
|
|
6
9
|
description?: string;
|
|
7
10
|
input: FlexibleSchema<TInput>;
|
|
8
11
|
output?: FlexibleSchema<unknown>;
|
|
9
12
|
providerOptions?: unknown;
|
|
10
|
-
execute: (input: TInput, ctx: Readonly<TContext>, options
|
|
13
|
+
execute: (input: TInput, ctx: Readonly<TContext>, options: ToolExecuteOptions) => Promise<TOutput>;
|
|
11
14
|
};
|
|
12
15
|
interface IToolProvider<TContext> {
|
|
13
16
|
readonly [TOOL_PROVIDER_BRAND]: true;
|
|
@@ -56,16 +59,19 @@ interface AgentConfig<TContext, TInput = void, TOutput = void> extends AIPassthr
|
|
|
56
59
|
result: OnStepFinishEvent;
|
|
57
60
|
ctx: Readonly<TContext>;
|
|
58
61
|
input: TInput;
|
|
62
|
+
writer?: UIMessageStreamWriter;
|
|
59
63
|
}) => MaybePromise<void>;
|
|
60
64
|
onFinish?: (params: {
|
|
61
65
|
result: OnFinishEvent;
|
|
62
66
|
ctx: Readonly<TContext>;
|
|
63
67
|
input: TInput;
|
|
68
|
+
writer?: UIMessageStreamWriter;
|
|
64
69
|
}) => MaybePromise<void>;
|
|
65
70
|
onError?: (params: {
|
|
66
71
|
error: unknown;
|
|
67
72
|
ctx: Readonly<TContext>;
|
|
68
73
|
input: TInput;
|
|
74
|
+
writer?: UIMessageStreamWriter;
|
|
69
75
|
}) => MaybePromise<void>;
|
|
70
76
|
}
|
|
71
77
|
declare class Agent<TContext, TInput = void, TOutput = void> {
|
|
@@ -103,6 +109,17 @@ declare class WorkflowLoopError extends Error {
|
|
|
103
109
|
readonly maxIterations: number;
|
|
104
110
|
constructor(iterations: number, maxIterations: number);
|
|
105
111
|
}
|
|
112
|
+
interface WorkflowSnapshot {
|
|
113
|
+
readonly version: 1;
|
|
114
|
+
readonly resumeFromIndex: number;
|
|
115
|
+
readonly output: unknown;
|
|
116
|
+
readonly gateId: string;
|
|
117
|
+
readonly gatePayload: unknown;
|
|
118
|
+
}
|
|
119
|
+
declare class WorkflowSuspended extends Error {
|
|
120
|
+
readonly snapshot: WorkflowSnapshot;
|
|
121
|
+
constructor(snapshot: WorkflowSnapshot);
|
|
122
|
+
}
|
|
106
123
|
interface AgentStepHooks<TContext, TOutput, TNextOutput> {
|
|
107
124
|
mapGenerateResult?: (params: {
|
|
108
125
|
result: GenerateTextResult;
|
|
@@ -172,6 +189,9 @@ type RepeatOptions<TContext, TOutput> = {
|
|
|
172
189
|
maxIterations?: number;
|
|
173
190
|
};
|
|
174
191
|
type ElementOf<T> = T extends readonly (infer E)[] ? E : never;
|
|
192
|
+
interface SchemaWithParse<T = unknown> {
|
|
193
|
+
parse(data: unknown): T;
|
|
194
|
+
}
|
|
175
195
|
type StepNode = {
|
|
176
196
|
readonly type: "step";
|
|
177
197
|
readonly id: string;
|
|
@@ -189,6 +209,16 @@ type StepNode = {
|
|
|
189
209
|
readonly type: "finally";
|
|
190
210
|
readonly id: string;
|
|
191
211
|
readonly execute: (state: RuntimeState) => MaybePromise<void>;
|
|
212
|
+
} | {
|
|
213
|
+
readonly type: "gate";
|
|
214
|
+
readonly id: string;
|
|
215
|
+
readonly payload: (state: RuntimeState) => MaybePromise<unknown>;
|
|
216
|
+
readonly schema?: SchemaWithParse;
|
|
217
|
+
readonly condition?: (state: RuntimeState) => MaybePromise<boolean>;
|
|
218
|
+
readonly merge?: (params: {
|
|
219
|
+
priorOutput: unknown;
|
|
220
|
+
response: unknown;
|
|
221
|
+
}) => MaybePromise<unknown>;
|
|
192
222
|
};
|
|
193
223
|
interface RuntimeState {
|
|
194
224
|
ctx: unknown;
|
|
@@ -196,45 +226,76 @@ interface RuntimeState {
|
|
|
196
226
|
mode: "generate" | "stream";
|
|
197
227
|
writer?: UIMessageStreamWriter;
|
|
198
228
|
}
|
|
199
|
-
declare class SealedWorkflow<TContext, TInput = void, TOutput = void> {
|
|
229
|
+
declare class SealedWorkflow<TContext, TInput = void, TOutput = void, TGates extends Record<string, unknown> = {}> {
|
|
200
230
|
readonly id?: string;
|
|
201
231
|
protected readonly steps: ReadonlyArray<StepNode>;
|
|
202
232
|
protected constructor(steps: ReadonlyArray<StepNode>, id?: string);
|
|
203
233
|
generate(ctx: TContext, ...args: TInput extends void ? [input?: TInput] : [input: TInput]): Promise<WorkflowResult<TOutput>>;
|
|
204
234
|
stream(ctx: TContext, ...args: TInput extends void ? [input?: TInput, options?: WorkflowStreamOptions] : [input: TInput, options?: WorkflowStreamOptions]): WorkflowStreamResult<TOutput>;
|
|
205
|
-
protected execute(state: RuntimeState): Promise<void>;
|
|
206
|
-
protected executeNestedWorkflow(state: RuntimeState, workflow: SealedWorkflow<TContext, unknown, unknown>): Promise<void>;
|
|
235
|
+
protected execute(state: RuntimeState, startIndex?: number): Promise<void>;
|
|
236
|
+
protected executeNestedWorkflow(state: RuntimeState, workflow: SealedWorkflow<TContext, unknown, unknown, any>): Promise<void>;
|
|
207
237
|
protected executeAgent<TAgentInput, TNextOutput>(state: RuntimeState, agent: Agent<TContext, any, TNextOutput>, ctx: TContext, options?: AgentStepHooks<TContext, any, TNextOutput>): Promise<void>;
|
|
238
|
+
loadState<K extends string & keyof TGates>(gateId: K, snapshot: WorkflowSnapshot): ResumedWorkflow<TContext, TGates[K], TOutput>;
|
|
239
|
+
private findGateIndex;
|
|
240
|
+
}
|
|
241
|
+
declare class ResumedWorkflow<TContext, TResponse = unknown, TOutput = void> extends SealedWorkflow<TContext, TResponse, TOutput> {
|
|
242
|
+
private readonly startIndex;
|
|
243
|
+
private readonly schema?;
|
|
244
|
+
private readonly mergeFn?;
|
|
245
|
+
private readonly priorOutput;
|
|
246
|
+
/** @internal */
|
|
247
|
+
constructor(steps: ReadonlyArray<StepNode>, startIndex: number, schema?: SchemaWithParse<TResponse>, mergeFn?: (params: {
|
|
248
|
+
priorOutput: unknown;
|
|
249
|
+
response: unknown;
|
|
250
|
+
}) => MaybePromise<unknown>, priorOutput?: unknown);
|
|
251
|
+
private validateResponse;
|
|
252
|
+
generate(ctx: TContext, ...args: TResponse extends void ? [response?: TResponse] : [response: TResponse]): Promise<WorkflowResult<TOutput>>;
|
|
253
|
+
stream(ctx: TContext, ...args: TResponse extends void ? [response?: TResponse, options?: WorkflowStreamOptions] : [response: TResponse, options?: WorkflowStreamOptions]): WorkflowStreamResult<TOutput>;
|
|
208
254
|
}
|
|
209
|
-
declare class Workflow<TContext, TInput = void, TOutput = void> extends SealedWorkflow<TContext, TInput, TOutput> {
|
|
255
|
+
declare class Workflow<TContext, TInput = void, TOutput = void, TGates extends Record<string, unknown> = {}> extends SealedWorkflow<TContext, TInput, TOutput, TGates> {
|
|
210
256
|
private constructor();
|
|
211
257
|
static create<TContext, TInput = void>(options?: {
|
|
212
258
|
id?: string;
|
|
213
259
|
}): Workflow<TContext, TInput, TInput>;
|
|
214
260
|
static from<TContext, TInput, TOutput>(agent: Agent<TContext, TInput, TOutput>, options?: StepOptions<TContext, TInput, TOutput>): Workflow<TContext, TInput, TOutput>;
|
|
215
|
-
step<TNextOutput>(agent: Agent<TContext, TOutput, TNextOutput>, options?: StepOptions<TContext, TOutput, TNextOutput>): Workflow<TContext, TInput, TNextOutput>;
|
|
216
|
-
step<TNextOutput>(workflow: SealedWorkflow<TContext, TOutput, TNextOutput>): Workflow<TContext, TInput, TNextOutput>;
|
|
261
|
+
step<TNextOutput>(agent: Agent<TContext, TOutput, TNextOutput>, options?: StepOptions<TContext, TOutput, TNextOutput>): Workflow<TContext, TInput, TNextOutput, TGates>;
|
|
262
|
+
step<TNextOutput>(workflow: SealedWorkflow<TContext, TOutput, TNextOutput>): Workflow<TContext, TInput, TNextOutput, TGates>;
|
|
217
263
|
step<TNextOutput>(id: string, fn: (params: {
|
|
218
264
|
ctx: Readonly<TContext>;
|
|
219
265
|
input: TOutput;
|
|
220
|
-
}) => MaybePromise<TNextOutput>): Workflow<TContext, TInput, TNextOutput>;
|
|
221
|
-
|
|
222
|
-
|
|
266
|
+
}) => MaybePromise<TNextOutput>): Workflow<TContext, TInput, TNextOutput, TGates>;
|
|
267
|
+
gate<TResponse = TOutput, Id extends string = string>(id: Id & (Id extends keyof TGates ? never : Id), options?: {
|
|
268
|
+
payload?: (params: {
|
|
269
|
+
ctx: Readonly<TContext>;
|
|
270
|
+
input: TOutput;
|
|
271
|
+
}) => MaybePromise<unknown>;
|
|
272
|
+
schema?: SchemaWithParse<TResponse>;
|
|
273
|
+
condition?: (params: {
|
|
274
|
+
ctx: Readonly<TContext>;
|
|
275
|
+
input: TOutput;
|
|
276
|
+
}) => MaybePromise<boolean>;
|
|
277
|
+
merge?: (params: {
|
|
278
|
+
priorOutput: TOutput;
|
|
279
|
+
response: TResponse;
|
|
280
|
+
}) => MaybePromise<TResponse>;
|
|
281
|
+
}): Workflow<TContext, TInput, TResponse, TGates & Record<Id, TResponse>>;
|
|
282
|
+
branch<TNextOutput>(cases: BranchCase<TContext, TOutput, TNextOutput>[]): Workflow<TContext, TInput, TNextOutput, TGates>;
|
|
283
|
+
branch<TKeys extends string, TNextOutput>(config: BranchSelect<TContext, TOutput, TKeys, TNextOutput>): Workflow<TContext, TInput, TNextOutput, TGates>;
|
|
223
284
|
private branchPredicate;
|
|
224
285
|
private branchSelect;
|
|
225
286
|
foreach<TNextOutput>(target: Agent<TContext, ElementOf<TOutput>, TNextOutput> | SealedWorkflow<TContext, ElementOf<TOutput>, TNextOutput>, options?: {
|
|
226
287
|
concurrency?: number;
|
|
227
|
-
}): Workflow<TContext, TInput, TNextOutput[]>;
|
|
228
|
-
repeat(target: Agent<TContext, TOutput, TOutput> | SealedWorkflow<TContext, TOutput, TOutput>, options: RepeatOptions<TContext, TOutput>): Workflow<TContext, TInput, TOutput>;
|
|
288
|
+
}): Workflow<TContext, TInput, TNextOutput[], TGates>;
|
|
289
|
+
repeat(target: Agent<TContext, TOutput, TOutput> | SealedWorkflow<TContext, TOutput, TOutput>, options: RepeatOptions<TContext, TOutput>): Workflow<TContext, TInput, TOutput, TGates>;
|
|
229
290
|
catch(id: string, fn: (params: {
|
|
230
291
|
error: unknown;
|
|
231
292
|
ctx: Readonly<TContext>;
|
|
232
293
|
lastOutput: TOutput;
|
|
233
294
|
stepId: string;
|
|
234
|
-
}) => MaybePromise<TOutput>): Workflow<TContext, TInput, TOutput>;
|
|
295
|
+
}) => MaybePromise<TOutput>): Workflow<TContext, TInput, TOutput, TGates>;
|
|
235
296
|
finally(id: string, fn: (params: {
|
|
236
297
|
ctx: Readonly<TContext>;
|
|
237
|
-
}) => MaybePromise<void>): SealedWorkflow<TContext, TInput, TOutput>;
|
|
298
|
+
}) => MaybePromise<void>): SealedWorkflow<TContext, TInput, TOutput, TGates>;
|
|
238
299
|
}
|
|
239
300
|
|
|
240
|
-
export { Agent, type AgentConfig, type AgentStepHooks, type BranchCase, type BranchSelect, type GenerateTextResult, type IToolProvider, type MaybePromise, type RepeatOptions, type Resolvable, SealedWorkflow, type StepOptions, type StreamTextResult, type ToolProviderConfig, Workflow, WorkflowBranchError, WorkflowLoopError, type WorkflowResult, type WorkflowStreamOptions, type WorkflowStreamResult, defineTool };
|
|
301
|
+
export { Agent, type AgentConfig, type AgentStepHooks, type BranchCase, type BranchSelect, type GenerateTextResult, type IToolProvider, type MaybePromise, type RepeatOptions, type Resolvable, ResumedWorkflow, SealedWorkflow, type StepOptions, type StreamTextResult, type ToolExecuteOptions, type ToolProviderConfig, Workflow, WorkflowBranchError, WorkflowLoopError, type WorkflowResult, type WorkflowSnapshot, type WorkflowStreamOptions, type WorkflowStreamResult, WorkflowSuspended, defineTool };
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,31 @@ import {
|
|
|
7
7
|
|
|
8
8
|
// src/tool-provider.ts
|
|
9
9
|
import { tool } from "ai";
|
|
10
|
+
|
|
11
|
+
// src/utils.ts
|
|
12
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
13
|
+
var writerStorage = new AsyncLocalStorage();
|
|
14
|
+
function runWithWriter(writer, fn) {
|
|
15
|
+
return writerStorage.run(writer, fn);
|
|
16
|
+
}
|
|
17
|
+
function getActiveWriter() {
|
|
18
|
+
return writerStorage.getStore();
|
|
19
|
+
}
|
|
20
|
+
function resolveValue(value, ctx, input) {
|
|
21
|
+
if (typeof value === "function") {
|
|
22
|
+
return value(ctx, input);
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
async function extractOutput(result, hasStructuredOutput) {
|
|
27
|
+
if (hasStructuredOutput) {
|
|
28
|
+
const output = await result.output;
|
|
29
|
+
if (output !== void 0) return output;
|
|
30
|
+
}
|
|
31
|
+
return await result.text;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// src/tool-provider.ts
|
|
10
35
|
var TOOL_PROVIDER_BRAND = /* @__PURE__ */ Symbol.for("agent-workflow.ToolProvider");
|
|
11
36
|
var ToolProvider = class {
|
|
12
37
|
[TOOL_PROVIDER_BRAND] = true;
|
|
@@ -19,7 +44,7 @@ var ToolProvider = class {
|
|
|
19
44
|
return tool({
|
|
20
45
|
...toolDef,
|
|
21
46
|
parameters: inputSchema,
|
|
22
|
-
execute: (input, options) => execute(input, context, options)
|
|
47
|
+
execute: (input, options) => execute(input, context, { ...options, writer: getActiveWriter() })
|
|
23
48
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
24
49
|
});
|
|
25
50
|
}
|
|
@@ -31,21 +56,6 @@ function isToolProvider(obj) {
|
|
|
31
56
|
return typeof obj === "object" && obj !== null && TOOL_PROVIDER_BRAND in obj;
|
|
32
57
|
}
|
|
33
58
|
|
|
34
|
-
// src/utils.ts
|
|
35
|
-
function resolveValue(value, ctx, input) {
|
|
36
|
-
if (typeof value === "function") {
|
|
37
|
-
return value(ctx, input);
|
|
38
|
-
}
|
|
39
|
-
return value;
|
|
40
|
-
}
|
|
41
|
-
async function extractOutput(result, hasStructuredOutput) {
|
|
42
|
-
if (hasStructuredOutput) {
|
|
43
|
-
const output = await result.output;
|
|
44
|
-
if (output !== void 0) return output;
|
|
45
|
-
}
|
|
46
|
-
return await result.text;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
59
|
// src/agent.ts
|
|
50
60
|
var Agent = class {
|
|
51
61
|
id;
|
|
@@ -110,7 +120,7 @@ var Agent = class {
|
|
|
110
120
|
return await generateText(options);
|
|
111
121
|
} catch (error) {
|
|
112
122
|
if (this.config.onError) {
|
|
113
|
-
await this.config.onError({ error, ctx, input });
|
|
123
|
+
await this.config.onError({ error, ctx, input, writer: getActiveWriter() });
|
|
114
124
|
}
|
|
115
125
|
throw error;
|
|
116
126
|
}
|
|
@@ -121,7 +131,7 @@ var Agent = class {
|
|
|
121
131
|
const options = this.buildCallOptions(resolved, ctx, input);
|
|
122
132
|
return streamText({
|
|
123
133
|
...options,
|
|
124
|
-
onError: this.config.onError ? ({ error }) => this.config.onError({ error, ctx, input }) : void 0
|
|
134
|
+
onError: this.config.onError ? ({ error }) => this.config.onError({ error, ctx, input, writer: getActiveWriter() }) : void 0
|
|
125
135
|
});
|
|
126
136
|
}
|
|
127
137
|
asTool(ctx, options) {
|
|
@@ -144,6 +154,13 @@ var Agent = class {
|
|
|
144
154
|
description: this.description,
|
|
145
155
|
parameters: this.config.input,
|
|
146
156
|
execute: async (toolInput) => {
|
|
157
|
+
const writer = getActiveWriter();
|
|
158
|
+
if (writer) {
|
|
159
|
+
const result2 = await this.stream(ctx, toolInput);
|
|
160
|
+
writer.merge(result2.toUIMessageStream());
|
|
161
|
+
if (options?.mapOutput) return options.mapOutput(result2);
|
|
162
|
+
return extractOutput(result2, this.hasOutput);
|
|
163
|
+
}
|
|
147
164
|
const result = await this.generate(ctx, toolInput);
|
|
148
165
|
if (options?.mapOutput) return options.mapOutput(result);
|
|
149
166
|
return extractOutput(result, this.hasOutput);
|
|
@@ -163,8 +180,8 @@ var Agent = class {
|
|
|
163
180
|
...resolved.messages ? { messages: resolved.messages } : { prompt: resolved.prompt ?? "" },
|
|
164
181
|
...resolved.system ? { system: resolved.system } : {},
|
|
165
182
|
...this.config.output ? { output: this.config.output } : {},
|
|
166
|
-
onStepFinish: this._onStepFinish ? (event) => this._onStepFinish({ result: event, ctx, input }) : void 0,
|
|
167
|
-
onFinish: this._onFinish ? (event) => this._onFinish({ result: event, ctx, input }) : void 0
|
|
183
|
+
onStepFinish: this._onStepFinish ? (event) => this._onStepFinish({ result: event, ctx, input, writer: getActiveWriter() }) : void 0,
|
|
184
|
+
onFinish: this._onFinish ? (event) => this._onFinish({ result: event, ctx, input, writer: getActiveWriter() }) : void 0
|
|
168
185
|
};
|
|
169
186
|
}
|
|
170
187
|
resolveConfig(ctx, input) {
|
|
@@ -235,6 +252,14 @@ var WorkflowLoopError = class extends Error {
|
|
|
235
252
|
this.name = "WorkflowLoopError";
|
|
236
253
|
}
|
|
237
254
|
};
|
|
255
|
+
var WorkflowSuspended = class extends Error {
|
|
256
|
+
snapshot;
|
|
257
|
+
constructor(snapshot) {
|
|
258
|
+
super(`Workflow suspended at gate "${snapshot.gateId}"`);
|
|
259
|
+
this.name = "WorkflowSuspended";
|
|
260
|
+
this.snapshot = snapshot;
|
|
261
|
+
}
|
|
262
|
+
};
|
|
238
263
|
var SealedWorkflow = class {
|
|
239
264
|
id;
|
|
240
265
|
steps;
|
|
@@ -291,12 +316,13 @@ var SealedWorkflow = class {
|
|
|
291
316
|
};
|
|
292
317
|
}
|
|
293
318
|
// ── Internal: execute pipeline ────────────────────────────────
|
|
294
|
-
async execute(state) {
|
|
319
|
+
async execute(state, startIndex = 0) {
|
|
295
320
|
if (this.steps.length === 0) {
|
|
296
321
|
throw new Error("Workflow has no steps. Add at least one step before calling generate() or stream().");
|
|
297
322
|
}
|
|
298
323
|
let pendingError = null;
|
|
299
|
-
for (
|
|
324
|
+
for (let i = startIndex; i < this.steps.length; i++) {
|
|
325
|
+
const node = this.steps[i];
|
|
300
326
|
if (node.type === "finally") {
|
|
301
327
|
await node.execute(state);
|
|
302
328
|
continue;
|
|
@@ -316,10 +342,26 @@ var SealedWorkflow = class {
|
|
|
316
342
|
}
|
|
317
343
|
continue;
|
|
318
344
|
}
|
|
345
|
+
if (node.type === "gate") {
|
|
346
|
+
if (pendingError) continue;
|
|
347
|
+
if (node.condition) {
|
|
348
|
+
const shouldSuspend = await node.condition(state);
|
|
349
|
+
if (!shouldSuspend) continue;
|
|
350
|
+
}
|
|
351
|
+
const gatePayload = await node.payload(state);
|
|
352
|
+
throw new WorkflowSuspended({
|
|
353
|
+
version: 1,
|
|
354
|
+
resumeFromIndex: i,
|
|
355
|
+
output: state.output,
|
|
356
|
+
gateId: node.id,
|
|
357
|
+
gatePayload
|
|
358
|
+
});
|
|
359
|
+
}
|
|
319
360
|
if (pendingError) continue;
|
|
320
361
|
try {
|
|
321
362
|
await node.execute(state);
|
|
322
363
|
} catch (error) {
|
|
364
|
+
if (error instanceof WorkflowSuspended) throw error;
|
|
323
365
|
pendingError = { error, stepId: node.id };
|
|
324
366
|
}
|
|
325
367
|
}
|
|
@@ -329,7 +371,16 @@ var SealedWorkflow = class {
|
|
|
329
371
|
// Defined on SealedWorkflow (not Workflow) because TypeScript's protected
|
|
330
372
|
// access rules only allow calling workflow.execute() from the same class.
|
|
331
373
|
async executeNestedWorkflow(state, workflow) {
|
|
332
|
-
|
|
374
|
+
try {
|
|
375
|
+
await workflow.execute(state);
|
|
376
|
+
} catch (error) {
|
|
377
|
+
if (error instanceof WorkflowSuspended) {
|
|
378
|
+
throw new Error(
|
|
379
|
+
`Gates inside nested workflows are not yet supported. Gate "${error.snapshot.gateId}" was hit inside nested workflow "${workflow.id ?? "(anonymous)"}". Consider using a conditional gate with \`condition\` to skip when criteria are met, or restructure the workflow to use gates at the top level only.`
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
throw error;
|
|
383
|
+
}
|
|
333
384
|
}
|
|
334
385
|
// ── Internal: execute an agent within a step/branch ───────────
|
|
335
386
|
// In stream mode, output extraction awaits the full stream before returning.
|
|
@@ -339,20 +390,23 @@ var SealedWorkflow = class {
|
|
|
339
390
|
const input = state.output;
|
|
340
391
|
const hasStructuredOutput = agent.hasOutput;
|
|
341
392
|
if (state.mode === "stream" && state.writer) {
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
await
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
393
|
+
const writer = state.writer;
|
|
394
|
+
await runWithWriter(writer, async () => {
|
|
395
|
+
const result = await agent.stream(ctx, state.output);
|
|
396
|
+
if (options?.handleStream) {
|
|
397
|
+
await options.handleStream({ result, writer, ctx });
|
|
398
|
+
} else {
|
|
399
|
+
writer.merge(result.toUIMessageStream());
|
|
400
|
+
}
|
|
401
|
+
if (options?.onStreamResult) {
|
|
402
|
+
await options.onStreamResult({ result, ctx, input });
|
|
403
|
+
}
|
|
404
|
+
if (options?.mapStreamResult) {
|
|
405
|
+
state.output = await options.mapStreamResult({ result, ctx, input });
|
|
406
|
+
} else {
|
|
407
|
+
state.output = await extractOutput(result, hasStructuredOutput);
|
|
408
|
+
}
|
|
409
|
+
});
|
|
356
410
|
} else {
|
|
357
411
|
const result = await agent.generate(ctx, state.output);
|
|
358
412
|
if (options?.onGenerateResult) {
|
|
@@ -365,6 +419,106 @@ var SealedWorkflow = class {
|
|
|
365
419
|
}
|
|
366
420
|
}
|
|
367
421
|
}
|
|
422
|
+
// ── Gate: load persisted state for resumption ──────────────────
|
|
423
|
+
loadState(gateId, snapshot) {
|
|
424
|
+
if (snapshot.gateId !== gateId) {
|
|
425
|
+
throw new Error(
|
|
426
|
+
`loadState: gate ID mismatch \u2014 expected "${gateId}" but snapshot has "${snapshot.gateId}".`
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
const gateIndex = this.findGateIndex(snapshot);
|
|
430
|
+
const gateNode = this.steps[gateIndex];
|
|
431
|
+
return new ResumedWorkflow(
|
|
432
|
+
this.steps,
|
|
433
|
+
gateIndex + 1,
|
|
434
|
+
gateNode.schema,
|
|
435
|
+
gateNode.merge,
|
|
436
|
+
snapshot.output
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
findGateIndex(snapshot) {
|
|
440
|
+
if (snapshot.version !== 1) {
|
|
441
|
+
throw new Error(`Unsupported snapshot version: ${snapshot.version}`);
|
|
442
|
+
}
|
|
443
|
+
const hint = snapshot.resumeFromIndex;
|
|
444
|
+
if (hint >= 0 && hint < this.steps.length) {
|
|
445
|
+
const node = this.steps[hint];
|
|
446
|
+
if (node.type === "gate" && node.id === snapshot.gateId) {
|
|
447
|
+
return hint;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
for (let i = 0; i < this.steps.length; i++) {
|
|
451
|
+
const node = this.steps[i];
|
|
452
|
+
if (node.type === "gate" && node.id === snapshot.gateId) {
|
|
453
|
+
return i;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
throw new Error(
|
|
457
|
+
`Gate "${snapshot.gateId}" not found in workflow. The workflow definition may have changed since the snapshot was created.`
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
var ResumedWorkflow = class extends SealedWorkflow {
|
|
462
|
+
startIndex;
|
|
463
|
+
schema;
|
|
464
|
+
mergeFn;
|
|
465
|
+
priorOutput;
|
|
466
|
+
/** @internal */
|
|
467
|
+
constructor(steps, startIndex, schema, mergeFn, priorOutput) {
|
|
468
|
+
super(steps);
|
|
469
|
+
this.startIndex = startIndex;
|
|
470
|
+
this.schema = schema;
|
|
471
|
+
this.mergeFn = mergeFn;
|
|
472
|
+
this.priorOutput = priorOutput;
|
|
473
|
+
}
|
|
474
|
+
validateResponse(response) {
|
|
475
|
+
if (this.schema) {
|
|
476
|
+
return this.schema.parse(response);
|
|
477
|
+
}
|
|
478
|
+
return response;
|
|
479
|
+
}
|
|
480
|
+
async generate(ctx, ...args) {
|
|
481
|
+
const response = this.validateResponse(args[0]);
|
|
482
|
+
const output = this.mergeFn ? await this.mergeFn({ priorOutput: this.priorOutput, response }) : response;
|
|
483
|
+
const state = { ctx, output, mode: "generate" };
|
|
484
|
+
await this.execute(state, this.startIndex);
|
|
485
|
+
return { output: state.output };
|
|
486
|
+
}
|
|
487
|
+
stream(ctx, ...args) {
|
|
488
|
+
const response = this.validateResponse(args[0]);
|
|
489
|
+
const options = args[1];
|
|
490
|
+
let resolveOutput;
|
|
491
|
+
let rejectOutput;
|
|
492
|
+
const outputPromise = new Promise((res, rej) => {
|
|
493
|
+
resolveOutput = res;
|
|
494
|
+
rejectOutput = rej;
|
|
495
|
+
});
|
|
496
|
+
outputPromise.catch(() => {
|
|
497
|
+
});
|
|
498
|
+
const mergeFn = this.mergeFn;
|
|
499
|
+
const priorOutput = this.priorOutput;
|
|
500
|
+
const stream = createUIMessageStream({
|
|
501
|
+
execute: async ({ writer }) => {
|
|
502
|
+
const output = mergeFn ? await mergeFn({ priorOutput, response }) : response;
|
|
503
|
+
const state = {
|
|
504
|
+
ctx,
|
|
505
|
+
output,
|
|
506
|
+
mode: "stream",
|
|
507
|
+
writer
|
|
508
|
+
};
|
|
509
|
+
try {
|
|
510
|
+
await this.execute(state, this.startIndex);
|
|
511
|
+
resolveOutput(state.output);
|
|
512
|
+
} catch (error) {
|
|
513
|
+
rejectOutput(error);
|
|
514
|
+
throw error;
|
|
515
|
+
}
|
|
516
|
+
},
|
|
517
|
+
...options?.onError ? { onError: options.onError } : {},
|
|
518
|
+
...options?.onFinish ? { onFinish: options.onFinish } : {}
|
|
519
|
+
});
|
|
520
|
+
return { stream, output: outputPromise };
|
|
521
|
+
}
|
|
368
522
|
};
|
|
369
523
|
var Workflow = class _Workflow extends SealedWorkflow {
|
|
370
524
|
constructor(steps = [], id) {
|
|
@@ -418,6 +572,32 @@ var Workflow = class _Workflow extends SealedWorkflow {
|
|
|
418
572
|
};
|
|
419
573
|
return new _Workflow([...this.steps, node], this.id);
|
|
420
574
|
}
|
|
575
|
+
// ── gate: human-in-the-loop suspension point ────────────────
|
|
576
|
+
gate(id, options) {
|
|
577
|
+
if (this.steps.some((s) => s.type === "gate" && s.id === id)) {
|
|
578
|
+
throw new Error(`Workflow: duplicate gate ID "${id}". Each gate must have a unique identifier.`);
|
|
579
|
+
}
|
|
580
|
+
const node = {
|
|
581
|
+
type: "gate",
|
|
582
|
+
id,
|
|
583
|
+
schema: options?.schema,
|
|
584
|
+
condition: options?.condition ? async (state) => options.condition({
|
|
585
|
+
ctx: state.ctx,
|
|
586
|
+
input: state.output
|
|
587
|
+
}) : void 0,
|
|
588
|
+
merge: options?.merge ? (params) => options.merge(params) : void 0,
|
|
589
|
+
payload: async (state) => {
|
|
590
|
+
if (options?.payload) {
|
|
591
|
+
return options.payload({
|
|
592
|
+
ctx: state.ctx,
|
|
593
|
+
input: state.output
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
return state.output;
|
|
597
|
+
}
|
|
598
|
+
};
|
|
599
|
+
return new _Workflow([...this.steps, node], this.id);
|
|
600
|
+
}
|
|
421
601
|
// ── branch: implementation ────────────────────────────────────
|
|
422
602
|
branch(casesOrConfig) {
|
|
423
603
|
if (Array.isArray(casesOrConfig)) {
|
|
@@ -563,6 +743,7 @@ export {
|
|
|
563
743
|
Workflow,
|
|
564
744
|
WorkflowBranchError,
|
|
565
745
|
WorkflowLoopError,
|
|
746
|
+
WorkflowSuspended,
|
|
566
747
|
defineTool
|
|
567
748
|
};
|
|
568
749
|
//# sourceMappingURL=index.js.map
|