@ai-sdk/workflow 1.0.4 → 1.0.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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @ai-sdk/workflow
2
2
 
3
+ ## 1.0.6
4
+
5
+ ### Patch Changes
6
+
7
+ - 4a9f4d5: `WorkflowChatTransport` now repairs UI message stream part framing, so duplicated or interleaved durable stream writes no longer crash the AI SDK consumer with `Received text-delta for missing text part`.
8
+ - 4016539: Forward `reasoning` generation settings from `WorkflowAgent` to model calls.
9
+ - Updated dependencies [989402d]
10
+ - ai@7.0.6
11
+
12
+ ## 1.0.5
13
+
14
+ ### Patch Changes
15
+
16
+ - Updated dependencies [a2750db]
17
+ - ai@7.0.5
18
+
3
19
  ## 1.0.4
4
20
 
5
21
  ### Patch Changes
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { LanguageModelV4, SharedV4ProviderOptions, LanguageModelV4CallOptions, LanguageModelV4Prompt, LanguageModelV4StreamPart } from '@ai-sdk/provider';
1
+ import { LanguageModelV4, LanguageModelV4CallOptions, SharedV4ProviderOptions, LanguageModelV4Prompt, LanguageModelV4StreamPart } from '@ai-sdk/provider';
2
2
  import { Context, HasRequiredKey, InferToolSetContext } from '@ai-sdk/provider-utils';
3
3
  import { ToolSet, LanguageModel, Instructions, ToolChoice, TelemetryOptions as TelemetryOptions$1, StopCondition, ActiveTools, LanguageModelResponseMetadata, LanguageModelUsage, FinishReason, ToolCallRepairFunction, Experimental_SandboxSession, StepResult, GenerateTextOnStepEndCallback, ModelMessage, Experimental_LanguageModelStreamPart, UIMessage, UIMessageChunk, ChatTransport, PrepareSendMessagesRequest, PrepareReconnectToStreamRequest, ChatRequestOptions } from 'ai';
4
4
  export { Experimental_LanguageModelStreamPart as ModelCallStreamPart, Output, ToolCallRepairFunction } from 'ai';
@@ -151,6 +151,11 @@ interface GenerationSettings {
151
151
  * Only applicable for HTTP-based providers.
152
152
  */
153
153
  headers?: Record<string, string | undefined>;
154
+ /**
155
+ * Reasoning effort level for the model. Controls how much reasoning
156
+ * the model performs before generating a response.
157
+ */
158
+ reasoning?: LanguageModelV4CallOptions['reasoning'];
154
159
  /**
155
160
  * Additional provider-specific options. They are passed through
156
161
  * to the provider from the AI SDK and enable provider-specific
@@ -1106,4 +1111,56 @@ declare class WorkflowChatTransport<UI_MESSAGE extends UIMessage> implements Cha
1106
1111
  private onFinish;
1107
1112
  }
1108
1113
 
1109
- export { type CompatibleLanguageModel, type DownloadFunction, type GenerationSettings, type InferWorkflowAgentTools, type InferWorkflowAgentUIMessage, type OutputSpecification, type PrepareCallCallback, type PrepareCallOptions, type PrepareCallResult, type PrepareStepCallback, type PrepareStepInfo, type PrepareStepResult, type ProviderOptions, type ReconnectToStreamOptions, type SendMessagesOptions, type StreamTextTransform, type TelemetryOptions, WorkflowAgent, type WorkflowAgentOnAbortCallback, type WorkflowAgentOnEndCallback, type WorkflowAgentOnErrorCallback, type WorkflowAgentOnFinishCallback, type WorkflowAgentOnStartCallback, type WorkflowAgentOnStepEndCallback, type WorkflowAgentOnStepFinishCallback, type WorkflowAgentOnStepStartCallback, type WorkflowAgentOnToolExecutionEndCallback, type WorkflowAgentOnToolExecutionStartCallback, type WorkflowAgentOptions, type WorkflowAgentStreamOptions, type WorkflowAgentStreamResult, WorkflowChatTransport, type WorkflowChatTransportOptions, createModelCallToUIChunkTransform, toUIMessageChunk };
1114
+ /**
1115
+ * Normalizes the part framing of a UI message stream so it is always
1116
+ * well-formed for the AI SDK's stream consumer (`processUIMessageStream`,
1117
+ * which backs `useChat`/`readUIMessageStream`).
1118
+ *
1119
+ * ## Why this exists
1120
+ *
1121
+ * The consumer maintains a map of "active" text/reasoning parts keyed by id.
1122
+ * A `text-delta`/`text-end` for an id that has no open part is a fatal error
1123
+ * (`Received text-delta for missing text part with ID "0" ...`) that kills the
1124
+ * whole turn. Two properties of the durable streaming model make that error
1125
+ * reachable:
1126
+ *
1127
+ * - A workflow run owns a single shared stream, and the consumer resets its
1128
+ * active-part maps on every `finish-step`. Multi-step turns reuse the same
1129
+ * part id (commonly `"0"`) in each step, so a single dropped or duplicated
1130
+ * `*-start` across a step boundary orphans the rest of that step's content.
1131
+ * - The same stream is read across reconnects, and a stream-producing step can
1132
+ * run more than once (retry/redelivery, or the concurrent-worker duplication
1133
+ * tracked in vercel/workflow#2331 and #2039). Either can interleave or
1134
+ * duplicate chunks on the shared stream — e.g. a `finish-step` landing in the
1135
+ * middle of another execution's text part.
1136
+ *
1137
+ * Since the content is still flowing and only the framing is damaged, repairing
1138
+ * the framing here degrades the worst case to "text begins slightly into the
1139
+ * step" or "a duplicated tail is dropped" instead of a dead turn.
1140
+ *
1141
+ * ## What it does
1142
+ *
1143
+ * Mirrors the consumer's part-lifetime state machine, per part type, per step:
1144
+ * - resets tracking on `finish-step` (exactly where the consumer resets);
1145
+ * - synthesizes a missing `*-start` when an orphaned `*-delta`/`*-end` arrives;
1146
+ * - drops a re-delivered `*-start`/`*-delta`/`*-end` for a part already
1147
+ * open or ended in the current step (reconnect/replay overlap).
1148
+ *
1149
+ * A well-formed stream passes through unchanged.
1150
+ *
1151
+ * ## Scope: text and reasoning only
1152
+ *
1153
+ * `tool-input-delta` raises the same class of fatal error (`Received
1154
+ * tool-input-delta for missing tool call ...`), but tool parts are deliberately
1155
+ * left untouched: the consumer does not reset its tool-call map on `finish-step`
1156
+ * and tool-call ids are unique, so the step-boundary id-reuse orphaning that
1157
+ * makes text/reasoning fragile does not apply to them. If a future duplication
1158
+ * mode is found to orphan tool-input parts, extend the same machine to that
1159
+ * family rather than special-casing it.
1160
+ *
1161
+ * @param source the raw UI message chunk stream to normalize.
1162
+ * @yields the framing-corrected UI message chunks.
1163
+ */
1164
+ declare function normalizeUIMessageStreamParts(source: AsyncIterable<UIMessageChunk>): AsyncGenerator<UIMessageChunk>;
1165
+
1166
+ export { type CompatibleLanguageModel, type DownloadFunction, type GenerationSettings, type InferWorkflowAgentTools, type InferWorkflowAgentUIMessage, type OutputSpecification, type PrepareCallCallback, type PrepareCallOptions, type PrepareCallResult, type PrepareStepCallback, type PrepareStepInfo, type PrepareStepResult, type ProviderOptions, type ReconnectToStreamOptions, type SendMessagesOptions, type StreamTextTransform, type TelemetryOptions, WorkflowAgent, type WorkflowAgentOnAbortCallback, type WorkflowAgentOnEndCallback, type WorkflowAgentOnErrorCallback, type WorkflowAgentOnFinishCallback, type WorkflowAgentOnStartCallback, type WorkflowAgentOnStepEndCallback, type WorkflowAgentOnStepFinishCallback, type WorkflowAgentOnStepStartCallback, type WorkflowAgentOnToolExecutionEndCallback, type WorkflowAgentOnToolExecutionStartCallback, type WorkflowAgentOptions, type WorkflowAgentStreamOptions, type WorkflowAgentStreamResult, WorkflowChatTransport, type WorkflowChatTransportOptions, createModelCallToUIChunkTransform, normalizeUIMessageStreamParts, toUIMessageChunk };
package/dist/index.mjs CHANGED
@@ -160,6 +160,7 @@ async function doStreamStep(conversationPrompt, modelInit, writable, serializedT
160
160
  providerOptions: options == null ? void 0 : options.providerOptions,
161
161
  abortSignal: options == null ? void 0 : options.abortSignal,
162
162
  headers: options == null ? void 0 : options.headers,
163
+ reasoning: options == null ? void 0 : options.reasoning,
163
164
  maxOutputTokens: options == null ? void 0 : options.maxOutputTokens,
164
165
  temperature: options == null ? void 0 : options.temperature,
165
166
  topP: options == null ? void 0 : options.topP,
@@ -492,6 +493,12 @@ async function* streamTextIterator({
492
493
  headers: prepareResult.headers
493
494
  };
494
495
  }
496
+ if ((prepareResult == null ? void 0 : prepareResult.reasoning) !== void 0) {
497
+ currentGenerationSettings = {
498
+ ...currentGenerationSettings,
499
+ reasoning: prepareResult.reasoning
500
+ };
501
+ }
495
502
  if ((prepareResult == null ? void 0 : prepareResult.providerOptions) !== void 0) {
496
503
  currentGenerationSettings = {
497
504
  ...currentGenerationSettings,
@@ -551,6 +558,7 @@ async function* streamTextIterator({
551
558
  frequencyPenalty: currentGenerationSettings.frequencyPenalty,
552
559
  stopSequences: currentGenerationSettings.stopSequences,
553
560
  seed: currentGenerationSettings.seed,
561
+ reasoning: currentGenerationSettings.reasoning,
554
562
  providerOptions: currentGenerationSettings.providerOptions,
555
563
  headers: currentGenerationSettings.headers
556
564
  }));
@@ -745,6 +753,7 @@ var WorkflowAgent = class {
745
753
  maxRetries: options.maxRetries,
746
754
  abortSignal: options.abortSignal,
747
755
  headers: options.headers,
756
+ reasoning: options.reasoning,
748
757
  providerOptions: options.providerOptions
749
758
  };
750
759
  }
@@ -809,6 +818,8 @@ var WorkflowAgent = class {
809
818
  effectiveGenerationSettings.seed = prepared.seed;
810
819
  if (prepared.headers !== void 0)
811
820
  effectiveGenerationSettings.headers = prepared.headers;
821
+ if (prepared.reasoning !== void 0)
822
+ effectiveGenerationSettings.reasoning = prepared.reasoning;
812
823
  if (prepared.providerOptions !== void 0)
813
824
  effectiveGenerationSettings.providerOptions = prepared.providerOptions;
814
825
  }
@@ -1110,6 +1121,7 @@ var WorkflowAgent = class {
1110
1121
  abortSignal: effectiveAbortSignal
1111
1122
  },
1112
1123
  ...options.headers !== void 0 && { headers: options.headers },
1124
+ ...options.reasoning !== void 0 && { reasoning: options.reasoning },
1113
1125
  ...options.providerOptions !== void 0 && {
1114
1126
  providerOptions: options.providerOptions
1115
1127
  }
@@ -1183,6 +1195,7 @@ var WorkflowAgent = class {
1183
1195
  maxRetries: (_t = mergedGenerationSettings.maxRetries) != null ? _t : 2,
1184
1196
  timeout: void 0,
1185
1197
  headers: mergedGenerationSettings.headers,
1198
+ reasoning: mergedGenerationSettings.reasoning,
1186
1199
  providerOptions: mergedGenerationSettings.providerOptions,
1187
1200
  output: (_u = options.output) != null ? _u : this.output,
1188
1201
  runtimeContext,
@@ -2159,6 +2172,83 @@ import {
2159
2172
  getErrorMessage as getErrorMessage2
2160
2173
  } from "@ai-sdk/provider-utils";
2161
2174
  import { createAsyncIterableStream } from "ai/internal";
2175
+
2176
+ // src/normalize-ui-message-stream.ts
2177
+ var newPartFrameState = () => ({
2178
+ open: /* @__PURE__ */ new Set(),
2179
+ ended: /* @__PURE__ */ new Set()
2180
+ });
2181
+ function* repairPart(kind, id, chunk, state, startType) {
2182
+ if (kind === "start") {
2183
+ if (state.open.has(id) || state.ended.has(id)) {
2184
+ return;
2185
+ }
2186
+ state.open.add(id);
2187
+ yield chunk;
2188
+ return;
2189
+ }
2190
+ if (state.ended.has(id)) {
2191
+ return;
2192
+ }
2193
+ if (!state.open.has(id)) {
2194
+ state.open.add(id);
2195
+ yield { type: startType, id };
2196
+ }
2197
+ if (kind === "end") {
2198
+ state.open.delete(id);
2199
+ state.ended.add(id);
2200
+ }
2201
+ yield chunk;
2202
+ }
2203
+ async function* normalizeUIMessageStreamParts(source) {
2204
+ const text = newPartFrameState();
2205
+ const reasoning = newPartFrameState();
2206
+ for await (const chunk of source) {
2207
+ switch (chunk.type) {
2208
+ case "finish-step":
2209
+ text.open.clear();
2210
+ text.ended.clear();
2211
+ reasoning.open.clear();
2212
+ reasoning.ended.clear();
2213
+ yield chunk;
2214
+ break;
2215
+ case "text-start":
2216
+ yield* repairPart("start", chunk.id, chunk, text, "text-start");
2217
+ break;
2218
+ case "text-delta":
2219
+ yield* repairPart("delta", chunk.id, chunk, text, "text-start");
2220
+ break;
2221
+ case "text-end":
2222
+ yield* repairPart("end", chunk.id, chunk, text, "text-start");
2223
+ break;
2224
+ case "reasoning-start":
2225
+ yield* repairPart(
2226
+ "start",
2227
+ chunk.id,
2228
+ chunk,
2229
+ reasoning,
2230
+ "reasoning-start"
2231
+ );
2232
+ break;
2233
+ case "reasoning-delta":
2234
+ yield* repairPart(
2235
+ "delta",
2236
+ chunk.id,
2237
+ chunk,
2238
+ reasoning,
2239
+ "reasoning-start"
2240
+ );
2241
+ break;
2242
+ case "reasoning-end":
2243
+ yield* repairPart("end", chunk.id, chunk, reasoning, "reasoning-start");
2244
+ break;
2245
+ default:
2246
+ yield chunk;
2247
+ }
2248
+ }
2249
+ }
2250
+
2251
+ // src/workflow-chat-transport.ts
2162
2252
  var WorkflowChatTransport = class {
2163
2253
  /**
2164
2254
  * Creates a new WorkflowChatTransport instance.
@@ -2203,7 +2293,7 @@ var WorkflowChatTransport = class {
2203
2293
  */
2204
2294
  async sendMessages(options) {
2205
2295
  return convertAsyncIteratorToReadableStream(
2206
- this.sendMessagesIterator(options)
2296
+ normalizeUIMessageStreamParts(this.sendMessagesIterator(options))
2207
2297
  );
2208
2298
  }
2209
2299
  async *sendMessagesIterator(options) {
@@ -2281,7 +2371,9 @@ var WorkflowChatTransport = class {
2281
2371
  * @throws Error if the reconnection request fails or returns a non-OK status
2282
2372
  */
2283
2373
  async reconnectToStream(options) {
2284
- const reconnectIterator = this.reconnectToStreamIterator(options);
2374
+ const reconnectIterator = normalizeUIMessageStreamParts(
2375
+ this.reconnectToStreamIterator(options)
2376
+ );
2285
2377
  return convertAsyncIteratorToReadableStream(reconnectIterator);
2286
2378
  }
2287
2379
  async *reconnectToStreamIterator(options, workflowRunId, initialChunkIndex = 0) {
@@ -2374,6 +2466,7 @@ export {
2374
2466
  WorkflowAgent,
2375
2467
  WorkflowChatTransport,
2376
2468
  createModelCallToUIChunkTransform,
2469
+ normalizeUIMessageStreamParts,
2377
2470
  toUIMessageChunk
2378
2471
  };
2379
2472
  //# sourceMappingURL=index.mjs.map