@ai-sdk/workflow 1.0.66 → 1.0.67
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/CHANGELOG.md +6 -0
- package/dist/index.js +51 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/do-stream-step.ts +58 -9
- package/src/providers/mock-function-wrapper.ts +91 -4
- package/src/providers/mock.ts +2 -92
- package/src/stream-text-iterator.ts +34 -14
- package/src/workflow-agent.ts +19 -8
package/package.json
CHANGED
package/src/do-stream-step.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type {
|
|
|
2
2
|
LanguageModelV4CallOptions,
|
|
3
3
|
LanguageModelV4Prompt,
|
|
4
4
|
} from '@ai-sdk/provider';
|
|
5
|
+
import { isAbortError } from '@ai-sdk/provider-utils';
|
|
5
6
|
import {
|
|
6
7
|
experimental_streamLanguageModelCall as streamModelCall,
|
|
7
8
|
gateway,
|
|
@@ -48,6 +49,7 @@ export interface DoStreamStepOptions {
|
|
|
48
49
|
seed?: number;
|
|
49
50
|
maxRetries?: number;
|
|
50
51
|
abortSignal?: AbortSignal;
|
|
52
|
+
timeoutAt?: number;
|
|
51
53
|
headers?: Record<string, string | undefined>;
|
|
52
54
|
reasoning?: LanguageModelV4CallOptions['reasoning'];
|
|
53
55
|
providerOptions?: ProviderOptions;
|
|
@@ -99,20 +101,42 @@ export interface DoStreamStepRawResult {
|
|
|
99
101
|
warnings?: unknown[];
|
|
100
102
|
}
|
|
101
103
|
|
|
104
|
+
export type DoStreamStepResult =
|
|
105
|
+
| { aborted: true }
|
|
106
|
+
| {
|
|
107
|
+
aborted?: false;
|
|
108
|
+
toolCalls: ParsedToolCall[];
|
|
109
|
+
finish: StreamFinish | undefined;
|
|
110
|
+
raw: DoStreamStepRawResult;
|
|
111
|
+
providerExecutedToolResults: Map<string, ProviderExecutedToolResult>;
|
|
112
|
+
};
|
|
113
|
+
|
|
102
114
|
export async function doStreamStep(
|
|
103
115
|
conversationPrompt: LanguageModelV4Prompt,
|
|
104
116
|
modelInit: LanguageModel,
|
|
105
117
|
writable?: WritableStream<ModelCallStreamPart<ToolSet>>,
|
|
106
118
|
serializedTools?: Record<string, SerializableToolDef>,
|
|
107
119
|
options?: DoStreamStepOptions,
|
|
108
|
-
): Promise<{
|
|
109
|
-
toolCalls: ParsedToolCall[];
|
|
110
|
-
finish: StreamFinish | undefined;
|
|
111
|
-
raw: DoStreamStepRawResult;
|
|
112
|
-
providerExecutedToolResults: Map<string, ProviderExecutedToolResult>;
|
|
113
|
-
}> {
|
|
120
|
+
): Promise<DoStreamStepResult> {
|
|
114
121
|
'use step';
|
|
115
122
|
|
|
123
|
+
const timeout =
|
|
124
|
+
options?.timeoutAt == null ? undefined : options.timeoutAt - Date.now();
|
|
125
|
+
|
|
126
|
+
// AbortSignal.timeout(0) does not abort synchronously. Check the deadline
|
|
127
|
+
// explicitly so an expired call never reaches the model, including when a
|
|
128
|
+
// durable step is retried after the original timeout has elapsed.
|
|
129
|
+
if (options?.abortSignal?.aborted || (timeout != null && timeout <= 0)) {
|
|
130
|
+
return { aborted: true };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const abortSignal =
|
|
134
|
+
timeout == null
|
|
135
|
+
? options?.abortSignal
|
|
136
|
+
: options?.abortSignal == null
|
|
137
|
+
? AbortSignal.timeout(timeout)
|
|
138
|
+
: AbortSignal.any([options.abortSignal, AbortSignal.timeout(timeout)]);
|
|
139
|
+
|
|
116
140
|
// Resolve model inside step (must happen here for serialization boundary)
|
|
117
141
|
const model: LanguageModel =
|
|
118
142
|
typeof modelInit === 'string'
|
|
@@ -151,7 +175,7 @@ export async function doStreamStep(
|
|
|
151
175
|
// streamModelCall handles: prompt standardization, tool preparation,
|
|
152
176
|
// model.doStream(), retry logic, and stream part transformation
|
|
153
177
|
// (tool call parsing, finish reason mapping, file wrapping).
|
|
154
|
-
const
|
|
178
|
+
const modelStream = await streamModelCall({
|
|
155
179
|
model,
|
|
156
180
|
// streamModelCall expects Prompt (ModelMessage[]) but we pass the
|
|
157
181
|
// pre-converted LanguageModelV4Prompt. standardizePrompt inside
|
|
@@ -162,7 +186,7 @@ export async function doStreamStep(
|
|
|
162
186
|
toolChoice: options?.toolChoice,
|
|
163
187
|
includeRawChunks: options?.includeRawChunks,
|
|
164
188
|
providerOptions: options?.providerOptions,
|
|
165
|
-
abortSignal
|
|
189
|
+
abortSignal,
|
|
166
190
|
headers: options?.headers,
|
|
167
191
|
reasoning: options?.reasoning,
|
|
168
192
|
output,
|
|
@@ -175,7 +199,19 @@ export async function doStreamStep(
|
|
|
175
199
|
stopSequences: options?.stopSequences,
|
|
176
200
|
seed: options?.seed,
|
|
177
201
|
repairToolCall: options?.repairToolCall,
|
|
178
|
-
})
|
|
202
|
+
})
|
|
203
|
+
.then(result => result.stream)
|
|
204
|
+
.catch(error => {
|
|
205
|
+
if (abortSignal?.aborted && isAbortError(error)) {
|
|
206
|
+
return undefined;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
throw error;
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
if (modelStream == null) {
|
|
213
|
+
return { aborted: true };
|
|
214
|
+
}
|
|
179
215
|
|
|
180
216
|
// Consume the stream: capture data and write to writable in real-time
|
|
181
217
|
const toolCalls: ParsedToolCall[] = [];
|
|
@@ -270,10 +306,23 @@ export async function doStreamStep(
|
|
|
270
306
|
await writer.write(part);
|
|
271
307
|
}
|
|
272
308
|
}
|
|
309
|
+
} catch (error) {
|
|
310
|
+
if (abortSignal?.aborted && isAbortError(error)) {
|
|
311
|
+
return { aborted: true };
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
throw error;
|
|
273
315
|
} finally {
|
|
274
316
|
writer?.releaseLock();
|
|
275
317
|
}
|
|
276
318
|
|
|
319
|
+
if (
|
|
320
|
+
abortSignal?.aborted ||
|
|
321
|
+
(options?.timeoutAt != null && options.timeoutAt <= Date.now())
|
|
322
|
+
) {
|
|
323
|
+
return { aborted: true };
|
|
324
|
+
}
|
|
325
|
+
|
|
277
326
|
return {
|
|
278
327
|
toolCalls,
|
|
279
328
|
finish,
|
|
@@ -1,11 +1,98 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
WORKFLOW_DESERIALIZE,
|
|
3
|
+
WORKFLOW_SERIALIZE,
|
|
4
|
+
} from '@ai-sdk/provider-utils';
|
|
5
|
+
import { convertArrayToReadableStream, MockLanguageModelV4 } from 'ai/test';
|
|
6
|
+
import type { MockResponseDescriptor } from './mock.js';
|
|
7
|
+
|
|
8
|
+
type MockStreamOptions = Parameters<MockLanguageModelV4['doStream']>[0];
|
|
9
|
+
type MockStreamResult = Awaited<ReturnType<MockLanguageModelV4['doStream']>>;
|
|
10
|
+
type MockStreamPart = MockStreamResult extends {
|
|
11
|
+
stream: ReadableStream<infer PART>;
|
|
12
|
+
}
|
|
13
|
+
? PART
|
|
14
|
+
: never;
|
|
15
|
+
|
|
16
|
+
const usage = {
|
|
17
|
+
inputTokens: {
|
|
18
|
+
total: 5,
|
|
19
|
+
noCache: 5,
|
|
20
|
+
cacheRead: undefined,
|
|
21
|
+
cacheWrite: undefined,
|
|
22
|
+
},
|
|
23
|
+
outputTokens: { total: 10, text: 10, reasoning: undefined },
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
class SerializableMockLanguageModel extends MockLanguageModelV4 {
|
|
27
|
+
static [WORKFLOW_SERIALIZE](model: SerializableMockLanguageModel) {
|
|
28
|
+
return { responses: model.responses };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static [WORKFLOW_DESERIALIZE](options: {
|
|
32
|
+
responses: MockResponseDescriptor[];
|
|
33
|
+
}) {
|
|
34
|
+
return new SerializableMockLanguageModel(options.responses);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
constructor(private readonly responses: MockResponseDescriptor[]) {
|
|
38
|
+
super({
|
|
39
|
+
provider: 'workflow-test',
|
|
40
|
+
modelId: 'workflow-test-model',
|
|
41
|
+
doStream: async (options: MockStreamOptions) => {
|
|
42
|
+
const responseIndex = Math.min(
|
|
43
|
+
options.prompt.filter(message => message.role === 'assistant').length,
|
|
44
|
+
responses.length - 1,
|
|
45
|
+
);
|
|
46
|
+
const response = responses[responseIndex];
|
|
47
|
+
const prefix: MockStreamPart[] = [
|
|
48
|
+
{ type: 'stream-start', warnings: [] },
|
|
49
|
+
{
|
|
50
|
+
type: 'response-metadata',
|
|
51
|
+
id: 'r',
|
|
52
|
+
modelId: 'mock',
|
|
53
|
+
timestamp: new Date(),
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
const streamParts: MockStreamPart[] =
|
|
57
|
+
response.type === 'text'
|
|
58
|
+
? [
|
|
59
|
+
...prefix,
|
|
60
|
+
{ type: 'text-start', id: '1' },
|
|
61
|
+
{ type: 'text-delta', id: '1', delta: response.text },
|
|
62
|
+
{ type: 'text-end', id: '1' },
|
|
63
|
+
{
|
|
64
|
+
type: 'finish',
|
|
65
|
+
finishReason: { unified: 'stop', raw: 'stop' },
|
|
66
|
+
usage,
|
|
67
|
+
},
|
|
68
|
+
]
|
|
69
|
+
: [
|
|
70
|
+
...prefix,
|
|
71
|
+
{
|
|
72
|
+
type: 'tool-call',
|
|
73
|
+
toolCallId: `call-${responseIndex + 1}`,
|
|
74
|
+
toolName: response.toolName,
|
|
75
|
+
input: response.input,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
type: 'finish',
|
|
79
|
+
finishReason: { unified: 'tool-calls', raw: undefined },
|
|
80
|
+
usage,
|
|
81
|
+
},
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
return { stream: convertArrayToReadableStream(streamParts) };
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
2
89
|
|
|
3
90
|
// Workaround for SWC plugin bug (https://github.com/vercel/workflow/issues/1365):
|
|
4
91
|
// `new ClassName(...)` in a step closure doesn't get closure vars hoisted
|
|
5
92
|
// correctly. Wrapping the constructor call in a plain function (imported
|
|
6
93
|
// from a separate file) fixes it.
|
|
7
94
|
export function mockProvider(
|
|
8
|
-
|
|
9
|
-
) {
|
|
10
|
-
return new
|
|
95
|
+
responses: MockResponseDescriptor[],
|
|
96
|
+
): MockLanguageModelV4 {
|
|
97
|
+
return new SerializableMockLanguageModel(responses);
|
|
11
98
|
}
|
package/src/providers/mock.ts
CHANGED
|
@@ -8,36 +8,7 @@ export type MockResponseDescriptor =
|
|
|
8
8
|
* Mock model that returns a fixed text response.
|
|
9
9
|
*/
|
|
10
10
|
export function mockTextModel(text: string) {
|
|
11
|
-
return mockProvider({
|
|
12
|
-
doStream: async () => ({
|
|
13
|
-
stream: new ReadableStream({
|
|
14
|
-
start(c) {
|
|
15
|
-
for (const v of [
|
|
16
|
-
{ type: 'stream-start', warnings: [] },
|
|
17
|
-
{
|
|
18
|
-
type: 'response-metadata',
|
|
19
|
-
id: 'r',
|
|
20
|
-
modelId: 'mock',
|
|
21
|
-
timestamp: new Date(),
|
|
22
|
-
},
|
|
23
|
-
{ type: 'text-start', id: '1' },
|
|
24
|
-
{ type: 'text-delta', id: '1', delta: text },
|
|
25
|
-
{ type: 'text-end', id: '1' },
|
|
26
|
-
{
|
|
27
|
-
type: 'finish',
|
|
28
|
-
finishReason: { unified: 'stop', raw: 'stop' },
|
|
29
|
-
usage: {
|
|
30
|
-
inputTokens: { total: 5, noCache: 5 },
|
|
31
|
-
outputTokens: { total: 10, text: 10 },
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
] as any[])
|
|
35
|
-
c.enqueue(v);
|
|
36
|
-
c.close();
|
|
37
|
-
},
|
|
38
|
-
}),
|
|
39
|
-
}),
|
|
40
|
-
});
|
|
11
|
+
return mockProvider([{ type: 'text', text }]);
|
|
41
12
|
}
|
|
42
13
|
|
|
43
14
|
/**
|
|
@@ -45,66 +16,5 @@ export function mockTextModel(text: string) {
|
|
|
45
16
|
* Determines which response to return by counting assistant messages in the prompt.
|
|
46
17
|
*/
|
|
47
18
|
export function mockSequenceModel(responses: MockResponseDescriptor[]) {
|
|
48
|
-
return mockProvider(
|
|
49
|
-
doStream: async (options: any) => {
|
|
50
|
-
const responseIndex = Math.min(
|
|
51
|
-
options.prompt.filter((m: any) => m.role === 'assistant').length,
|
|
52
|
-
responses.length - 1,
|
|
53
|
-
);
|
|
54
|
-
const selectedResponse = responses[responseIndex];
|
|
55
|
-
const parts =
|
|
56
|
-
selectedResponse.type === 'text'
|
|
57
|
-
? [
|
|
58
|
-
{ type: 'stream-start', warnings: [] },
|
|
59
|
-
{
|
|
60
|
-
type: 'response-metadata',
|
|
61
|
-
id: 'r',
|
|
62
|
-
modelId: 'mock',
|
|
63
|
-
timestamp: new Date(),
|
|
64
|
-
},
|
|
65
|
-
{ type: 'text-start', id: '1' },
|
|
66
|
-
{ type: 'text-delta', id: '1', delta: selectedResponse.text },
|
|
67
|
-
{ type: 'text-end', id: '1' },
|
|
68
|
-
{
|
|
69
|
-
type: 'finish',
|
|
70
|
-
finishReason: { unified: 'stop', raw: 'stop' },
|
|
71
|
-
usage: {
|
|
72
|
-
inputTokens: { total: 5, noCache: 5 },
|
|
73
|
-
outputTokens: { total: 10, text: 10 },
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
]
|
|
77
|
-
: [
|
|
78
|
-
{ type: 'stream-start', warnings: [] },
|
|
79
|
-
{
|
|
80
|
-
type: 'response-metadata',
|
|
81
|
-
id: 'r',
|
|
82
|
-
modelId: 'mock',
|
|
83
|
-
timestamp: new Date(),
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
type: 'tool-call',
|
|
87
|
-
toolCallId: `call-${responseIndex + 1}`,
|
|
88
|
-
toolName: selectedResponse.toolName,
|
|
89
|
-
input: selectedResponse.input,
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
type: 'finish',
|
|
93
|
-
finishReason: { unified: 'tool-calls', raw: undefined },
|
|
94
|
-
usage: {
|
|
95
|
-
inputTokens: { total: 5, noCache: 5 },
|
|
96
|
-
outputTokens: { total: 10, text: 10 },
|
|
97
|
-
},
|
|
98
|
-
},
|
|
99
|
-
];
|
|
100
|
-
return {
|
|
101
|
-
stream: new ReadableStream({
|
|
102
|
-
start(c) {
|
|
103
|
-
for (const streamPart of parts as any[]) c.enqueue(streamPart);
|
|
104
|
-
c.close();
|
|
105
|
-
},
|
|
106
|
-
}),
|
|
107
|
-
};
|
|
108
|
-
},
|
|
109
|
-
});
|
|
19
|
+
return mockProvider(responses);
|
|
110
20
|
}
|
|
@@ -92,6 +92,11 @@ export interface StreamTextIteratorYieldValue {
|
|
|
92
92
|
experimental_sandbox?: SandboxSession;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
export interface StreamTextIteratorAbortedValue {
|
|
96
|
+
aborted: true;
|
|
97
|
+
messages: LanguageModelV4Prompt;
|
|
98
|
+
}
|
|
99
|
+
|
|
95
100
|
// This runs in the workflow context
|
|
96
101
|
export async function* streamTextIterator({
|
|
97
102
|
prompt,
|
|
@@ -112,6 +117,7 @@ export async function* streamTextIterator({
|
|
|
112
117
|
toolsContext,
|
|
113
118
|
telemetry,
|
|
114
119
|
includeRawChunks = false,
|
|
120
|
+
timeoutAt,
|
|
115
121
|
repairToolCall,
|
|
116
122
|
responseFormat,
|
|
117
123
|
experimental_sandbox: sandbox,
|
|
@@ -135,12 +141,13 @@ export async function* streamTextIterator({
|
|
|
135
141
|
toolsContext?: Record<string, Context | undefined>;
|
|
136
142
|
telemetry?: TelemetryOptions<Context, ToolSet>;
|
|
137
143
|
includeRawChunks?: boolean;
|
|
144
|
+
timeoutAt?: number;
|
|
138
145
|
repairToolCall?: ToolCallRepairFunction<ToolSet>;
|
|
139
146
|
responseFormat?: LanguageModelV4CallOptions['responseFormat'];
|
|
140
147
|
experimental_sandbox?: SandboxSession;
|
|
141
148
|
}): AsyncGenerator<
|
|
142
149
|
StreamTextIteratorYieldValue,
|
|
143
|
-
LanguageModelV4Prompt,
|
|
150
|
+
LanguageModelV4Prompt | StreamTextIteratorAbortedValue,
|
|
144
151
|
LanguageModelV4ToolResultPart[]
|
|
145
152
|
> {
|
|
146
153
|
let conversationPrompt = [...prompt]; // Create a mutable copy
|
|
@@ -158,6 +165,7 @@ export async function* streamTextIterator({
|
|
|
158
165
|
let stepNumber = 0;
|
|
159
166
|
let lastStep: StepResult<any, any> | undefined;
|
|
160
167
|
let lastStepWasToolCalls = false;
|
|
168
|
+
let wasAborted = false;
|
|
161
169
|
|
|
162
170
|
// TODO(#12164): replace this AI-core telemetry bridge with a
|
|
163
171
|
// WorkflowAgent-specific typed dispatcher. `streamTextIterator` widens
|
|
@@ -319,20 +327,28 @@ export async function* streamTextIterator({
|
|
|
319
327
|
headers: currentGenerationSettings.headers,
|
|
320
328
|
} as never);
|
|
321
329
|
|
|
330
|
+
const streamStepResult = await doStreamStep(
|
|
331
|
+
conversationPrompt,
|
|
332
|
+
currentModel,
|
|
333
|
+
writable,
|
|
334
|
+
serializedTools,
|
|
335
|
+
{
|
|
336
|
+
...currentGenerationSettings,
|
|
337
|
+
toolChoice: currentToolChoice,
|
|
338
|
+
includeRawChunks,
|
|
339
|
+
timeoutAt,
|
|
340
|
+
repairToolCall,
|
|
341
|
+
responseFormat,
|
|
342
|
+
},
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
if (streamStepResult.aborted) {
|
|
346
|
+
wasAborted = true;
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
|
|
322
350
|
const { toolCalls, finish, raw, providerExecutedToolResults } =
|
|
323
|
-
|
|
324
|
-
conversationPrompt,
|
|
325
|
-
currentModel,
|
|
326
|
-
writable,
|
|
327
|
-
serializedTools,
|
|
328
|
-
{
|
|
329
|
-
...currentGenerationSettings,
|
|
330
|
-
toolChoice: currentToolChoice,
|
|
331
|
-
includeRawChunks,
|
|
332
|
-
repairToolCall,
|
|
333
|
-
responseFormat,
|
|
334
|
-
},
|
|
335
|
-
);
|
|
351
|
+
streamStepResult;
|
|
336
352
|
// Reconstruct the full StepResult outside the step boundary so the
|
|
337
353
|
// durable event log doesn't carry StepResult's redundant copies (or the
|
|
338
354
|
// per-chunk snapshot the step used to return).
|
|
@@ -488,6 +504,10 @@ export async function* streamTextIterator({
|
|
|
488
504
|
};
|
|
489
505
|
}
|
|
490
506
|
|
|
507
|
+
if (wasAborted) {
|
|
508
|
+
return { aborted: true, messages: conversationPrompt };
|
|
509
|
+
}
|
|
510
|
+
|
|
491
511
|
return conversationPrompt;
|
|
492
512
|
}
|
|
493
513
|
|
package/src/workflow-agent.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
} from '@ai-sdk/provider';
|
|
8
8
|
import {
|
|
9
9
|
getErrorMessage,
|
|
10
|
+
isAbortError,
|
|
10
11
|
validateTypes,
|
|
11
12
|
withUserAgentSuffix,
|
|
12
13
|
type Context,
|
|
@@ -39,7 +40,6 @@ import {
|
|
|
39
40
|
createRestrictedTelemetryDispatcher,
|
|
40
41
|
collectToolApprovals,
|
|
41
42
|
convertToLanguageModelPrompt,
|
|
42
|
-
mergeAbortSignals,
|
|
43
43
|
mergeCallbacks,
|
|
44
44
|
standardizePrompt,
|
|
45
45
|
validateApprovedToolApprovals,
|
|
@@ -1788,10 +1788,10 @@ export class WorkflowAgent<
|
|
|
1788
1788
|
download,
|
|
1789
1789
|
});
|
|
1790
1790
|
|
|
1791
|
-
const effectiveAbortSignal =
|
|
1792
|
-
options.abortSignal ?? effectiveGenerationSettings.abortSignal
|
|
1793
|
-
|
|
1794
|
-
|
|
1791
|
+
const effectiveAbortSignal =
|
|
1792
|
+
options.abortSignal ?? effectiveGenerationSettings.abortSignal;
|
|
1793
|
+
const timeoutAt =
|
|
1794
|
+
options.timeout == null ? undefined : Date.now() + options.timeout;
|
|
1795
1795
|
|
|
1796
1796
|
// Merge generation settings: constructor defaults < prepareCall < stream options
|
|
1797
1797
|
const mergedGenerationSettings: GenerationSettings = {
|
|
@@ -2157,6 +2157,7 @@ export class WorkflowAgent<
|
|
|
2157
2157
|
toolsContext,
|
|
2158
2158
|
telemetry: effectiveTelemetry,
|
|
2159
2159
|
includeRawChunks: options.includeRawChunks ?? false,
|
|
2160
|
+
timeoutAt,
|
|
2160
2161
|
repairToolCall: (options.repairToolCall ??
|
|
2161
2162
|
options.experimental_repairToolCall ??
|
|
2162
2163
|
this.repairToolCall) as ToolCallRepairFunction<ToolSet> | undefined,
|
|
@@ -2519,14 +2520,24 @@ export class WorkflowAgent<
|
|
|
2519
2520
|
}
|
|
2520
2521
|
}
|
|
2521
2522
|
|
|
2522
|
-
// When the iterator completes normally, result.value contains the final
|
|
2523
|
+
// When the iterator completes normally, result.value contains the final
|
|
2524
|
+
// conversation prompt. Aborts inside the retryable model step are
|
|
2525
|
+
// returned as data so the workflow runtime does not retry them.
|
|
2523
2526
|
if (result.done) {
|
|
2524
|
-
|
|
2527
|
+
if (Array.isArray(result.value)) {
|
|
2528
|
+
finalMessages = result.value;
|
|
2529
|
+
} else {
|
|
2530
|
+
finalMessages = result.value.messages;
|
|
2531
|
+
wasAborted = true;
|
|
2532
|
+
if (options.onAbort) {
|
|
2533
|
+
await options.onAbort({ steps });
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2525
2536
|
}
|
|
2526
2537
|
} catch (error) {
|
|
2527
2538
|
encounteredError = error;
|
|
2528
2539
|
// Check if this is an abort error
|
|
2529
|
-
if (error
|
|
2540
|
+
if (isAbortError(error)) {
|
|
2530
2541
|
wasAborted = true;
|
|
2531
2542
|
if (options.onAbort) {
|
|
2532
2543
|
await options.onAbort({ steps });
|