ai 7.0.152 → 7.0.154

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.
Files changed (44) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/index.d.ts +224 -9
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +440 -120
  5. package/dist/index.js.map +1 -1
  6. package/dist/internal/index.d.ts +52 -4
  7. package/dist/internal/index.d.ts.map +1 -1
  8. package/dist/internal/index.js +26 -20
  9. package/dist/internal/index.js.map +1 -1
  10. package/docs/02-getting-started/02-nextjs-app-router.mdx +2 -2
  11. package/docs/02-getting-started/08-angular.mdx +327 -0
  12. package/docs/02-getting-started/{03-nextjs-pages-router.mdx → 10-nextjs-pages-router.mdx} +12 -20
  13. package/docs/02-getting-started/index.mdx +2 -0
  14. package/docs/03-agents/01-overview.mdx +1 -0
  15. package/docs/03-agents/04-loop-control.mdx +117 -0
  16. package/docs/03-agents/06-tool-approvals.mdx +1 -0
  17. package/docs/03-agents/09-chat-platforms.mdx +164 -0
  18. package/docs/03-agents/index.mdx +6 -0
  19. package/docs/03-ai-sdk-core/60-telemetry.mdx +16 -3
  20. package/docs/03-ai-sdk-core/65-lifecycle-callbacks.mdx +10 -0
  21. package/docs/04-ai-sdk-ui/21-transport.mdx +86 -0
  22. package/docs/07-reference/01-ai-sdk-core/01-generate-text.mdx +46 -0
  23. package/docs/07-reference/01-ai-sdk-core/02-stream-text.mdx +46 -0
  24. package/docs/07-reference/01-ai-sdk-core/16-tool-loop-agent.mdx +35 -0
  25. package/package.json +12 -12
  26. package/src/agent/create-agent-ui-stream-response.ts +3 -0
  27. package/src/agent/create-agent-ui-stream.ts +16 -0
  28. package/src/agent/pipe-agent-ui-stream-to-response.ts +3 -0
  29. package/src/agent/tool-loop-agent-settings.ts +37 -1
  30. package/src/agent/tool-loop-agent.ts +43 -7
  31. package/src/generate-object/generate-object.ts +1 -1
  32. package/src/generate-text/generate-text-events.ts +37 -0
  33. package/src/generate-text/generate-text.ts +528 -374
  34. package/src/generate-text/index.ts +8 -0
  35. package/src/generate-text/prepare-step.ts +12 -0
  36. package/src/generate-text/restricted-telemetry-dispatcher.ts +31 -18
  37. package/src/generate-text/steering.ts +293 -0
  38. package/src/generate-text/step-result.ts +18 -0
  39. package/src/generate-text/stream-text.ts +664 -473
  40. package/src/middleware/extract-json-middleware.ts +8 -2
  41. package/src/middleware/extract-reasoning-middleware.ts +42 -30
  42. package/src/telemetry/create-telemetry-dispatcher.ts +3 -0
  43. package/src/telemetry/telemetry.ts +15 -1
  44. package/src/util/is-caused-by-abort.ts +20 -0
@@ -21,6 +21,7 @@ import {
21
21
  type ToolApprovalResponse,
22
22
  type ToolContent,
23
23
  type ToolSet,
24
+ type UserModelMessage,
24
25
  } from '@ai-sdk/provider-utils';
25
26
  import type { ServerResponse } from 'node:http';
26
27
  import { NoOutputGeneratedError, ToolChoiceViolationError } from '../error';
@@ -105,8 +106,10 @@ import type {
105
106
  GenerateTextEndEvent,
106
107
  GenerateTextOnStartCallback,
107
108
  GenerateTextOnStepEndCallback,
109
+ GenerateTextOnStepErrorCallback,
108
110
  GenerateTextOnStepFinishCallback,
109
111
  GenerateTextOnStepStartCallback,
112
+ GenerateTextStepStartEvent,
110
113
  } from './generate-text-events';
111
114
  import { invokeToolCallbacksFromStream } from './invoke-tool-callbacks-from-stream';
112
115
  import type {
@@ -133,6 +136,12 @@ import {
133
136
  type StepResult,
134
137
  type StepResultPerformance,
135
138
  } from './step-result';
139
+ import {
140
+ startSteeringRun,
141
+ type Steering,
142
+ type SteeringNotDeliveredReason,
143
+ type SteeringRun,
144
+ } from './steering';
136
145
  import {
137
146
  isStepCount,
138
147
  isStopConditionMet,
@@ -348,6 +357,7 @@ type StreamTextOptions<
348
357
  * @param headers - Additional HTTP headers to be sent with the request. Only applicable for HTTP-based providers.
349
358
  *
350
359
  * @param experimental_sandbox - The sandbox environment that is passed through to tool execution.
360
+ * @param experimental_steering - A steering object that adds user messages to the generation at the next step boundary.
351
361
  * @param runtimeContext - User-defined runtime context that flows through the entire generation lifecycle.
352
362
  * @param experimental_refineToolInput - Optional mapping of tool names to functions that refine parsed tool inputs before tools are executed and before outputs, callbacks, and telemetry are recorded.
353
363
  *
@@ -366,6 +376,7 @@ type StreamTextOptions<
366
376
  * @param onToolExecutionEnd - Callback invoked after each tool execution completes.
367
377
  * @param experimental_onToolCallFinish - Deprecated alias for `onToolExecutionEnd`.
368
378
  * @param onStepEnd - Callback that is called when each step (LLM call) ends, including intermediate steps.
379
+ * @param onStepError - Callback that is called when a step that started fails before it produces a step result, i.e. when the provider call throws or rejects, or when the model stream ends without output. A provider `error` chunk still ends the step with `onStepEnd`, and an abort or timeout ends it with `onAbort`.
369
380
  * @param onStepFinish - Deprecated alias for `onStepEnd`.
370
381
  * @param onEnd - Callback that is called when all steps are finished and the response is complete.
371
382
  * @param onFinish - Deprecated alias for `onEnd`.
@@ -380,7 +391,7 @@ export function streamText<
380
391
  >(
381
392
  options: StreamTextOptions<TOOLS, RUNTIME_CONTEXT, OUTPUT>,
382
393
  ): StreamTextResult<TOOLS, RUNTIME_CONTEXT, OUTPUT> {
383
- return streamTextInternal<TOOLS, RUNTIME_CONTEXT, OUTPUT>(options);
394
+ return startStreamText<TOOLS, RUNTIME_CONTEXT, OUTPUT>(options);
384
395
  }
385
396
 
386
397
  /**
@@ -417,7 +428,7 @@ export function streamTextLowMemory<
417
428
  (telemetry?.isEnabled !== false && integrations.length > 0);
418
429
 
419
430
  return createSingleConsumerStreamTextResult(
420
- streamTextInternal<TOOLS, RUNTIME_CONTEXT, OUTPUT>({
431
+ startStreamText<TOOLS, RUNTIME_CONTEXT, OUTPUT>({
421
432
  ...options,
422
433
  _internal: {
423
434
  ...options._internal,
@@ -427,440 +438,489 @@ export function streamTextLowMemory<
427
438
  );
428
439
  }
429
440
 
441
+ function startStreamText<
442
+ TOOLS extends ToolSet,
443
+ RUNTIME_CONTEXT extends Context,
444
+ OUTPUT extends Output,
445
+ >(
446
+ options: StreamTextOptions<TOOLS, RUNTIME_CONTEXT, OUTPUT>,
447
+ ): StreamTextResult<TOOLS, RUNTIME_CONTEXT, OUTPUT> {
448
+ const steeringRun = startSteeringRun(options.experimental_steering);
449
+
450
+ try {
451
+ return streamTextInternal<TOOLS, RUNTIME_CONTEXT, OUTPUT>(
452
+ options,
453
+ steeringRun,
454
+ );
455
+ } catch (error) {
456
+ // the call can fail before the stream starts, e.g. for invalid settings:
457
+ steeringRun?.end('error');
458
+ throw error;
459
+ }
460
+ }
461
+
430
462
  function streamTextInternal<
431
463
  TOOLS extends ToolSet,
432
464
  RUNTIME_CONTEXT extends Context = Context,
433
465
  OUTPUT extends Output = Output<string, string, never>,
434
- >({
435
- model,
436
- tools,
437
- toolChoice,
438
- instructions,
439
- system,
440
- prompt,
441
- messages,
442
- allowSystemInMessages,
443
- maxRetries,
444
- streamRetries,
445
- abortSignal,
446
- timeout,
447
- headers,
448
- stopWhen = isStepCount(1),
449
- experimental_sandbox: sandbox,
450
- output,
451
- toolApproval,
452
- experimental_toolCallers,
453
- experimental_toolApprovalSecret,
454
- experimental_telemetry,
455
- telemetry = experimental_telemetry,
456
- prepareStep,
457
- providerOptions,
458
- activeTools,
459
- toolOrder,
460
- toolCallConcurrency,
461
- experimental_repairToolCall,
462
- repairToolCall = experimental_repairToolCall,
463
- experimental_refineToolInput: refineToolInput,
464
- experimental_transform: transform,
465
- experimental_download: download,
466
- includeRawChunks,
467
- onChunk,
468
- onError: onErrorArg,
469
- onFinish,
470
- onEnd = onFinish,
471
- onAbort,
472
- onStepEnd,
473
- onStepFinish,
474
- onStart,
475
- experimental_onStart,
476
- onStepStart,
477
- experimental_onStepStart,
478
- onLanguageModelCallStart,
479
- experimental_onLanguageModelCallStart,
480
- onLanguageModelCallEnd,
481
- experimental_onLanguageModelCallEnd,
482
- onToolExecutionStart,
483
- onToolExecutionEnd,
484
- experimental_onToolCallStart,
485
- experimental_onToolCallFinish,
486
- runtimeContext = {} as RUNTIME_CONTEXT,
487
- toolsContext = {} as InferToolSetContext<TOOLS>,
488
- experimental_include,
489
- include = experimental_include,
490
- _internal: {
491
- now = originalNow,
492
- generateId = originalGenerateId,
493
- generateCallId = originalGenerateCallId,
494
- retention = { replay: true, collectContent: true },
495
- } = {},
496
- ...settings
497
- }: LanguageModelCallOptions &
498
- RequestOptions<TOOLS> &
499
- Prompt &
500
- ToolsContextParameter<TOOLS> & {
501
- /**
502
- * The language model to use.
503
- */
504
- model: LanguageModel;
505
-
506
- /**
507
- * The tool choice strategy. Default: 'auto'.
508
- */
509
- toolChoice?: ToolChoice<TOOLS>;
510
-
511
- /**
512
- * Condition for stopping the generation when there are tool results in the last step.
513
- * When the condition is an array, any of the conditions can be met to stop the generation.
514
- *
515
- * @default isStepCount(1)
516
- */
517
- stopWhen?: Arrayable<StopCondition<NoInfer<TOOLS>, RUNTIME_CONTEXT>>;
518
-
519
- /**
520
- * Optional telemetry configuration.
521
- */
522
- telemetry?: TelemetryOptions<RUNTIME_CONTEXT, NoInfer<TOOLS>>;
523
-
524
- /**
525
- * Optional telemetry configuration.
526
- *
527
- * @deprecated Use `telemetry` instead. This alias will be removed in a future major release.
528
- */
529
- experimental_telemetry?: TelemetryOptions<RUNTIME_CONTEXT, NoInfer<TOOLS>>;
530
-
531
- /**
532
- * Additional provider-specific options. They are passed through
533
- * to the provider from the AI SDK and enable provider-specific
534
- * functionality that can be fully encapsulated in the provider.
535
- */
536
- providerOptions?: ProviderOptions;
537
-
538
- /**
539
- * The sandbox environment that is passed through to tool execution.
540
- */
541
- experimental_sandbox?: SandboxSession;
542
-
543
- /**
544
- * Runtime context. Treat runtime context as immutable.
545
- * If you need to mutate runtime context, update it in `prepareStep`.
546
- */
547
- runtimeContext?: RUNTIME_CONTEXT;
548
-
549
- /**
550
- * Limits the tools that are available for the model to call without
551
- * changing the tool call and result types in the result.
552
- */
553
- activeTools?: ActiveTools<NoInfer<TOOLS>>;
554
-
555
- /**
556
- * Controls the order in which tools are sent to the provider.
557
- *
558
- * The list can be partial. Tools not listed in `toolOrder` are sent after
559
- * the listed tools, sorted alphabetically. This can improve provider-side
560
- * caching by keeping tool definitions in a stable order.
561
- */
562
- toolOrder?: ToolOrder<NoInfer<TOOLS>>;
563
-
564
- /**
565
- * Maximum number of tool calls that may execute concurrently within a step.
566
- *
567
- * Set to `1` to execute tool calls sequentially in the order they were
568
- * generated. By default, all tool calls execute concurrently.
569
- */
570
- toolCallConcurrency?: number;
571
-
572
- /**
573
- * Optional specification for parsing structured outputs from the LLM response.
574
- */
575
- output?: OUTPUT;
576
-
577
- /**
578
- * Optional tool approval configuration.
579
- *
580
- * This configuration takes precedence over tool-defined approval settings.
581
- */
582
- toolApproval?: ToolApprovalConfiguration<TOOLS, RUNTIME_CONTEXT>;
583
-
584
- /**
585
- * Configures which caller tools may invoke each tool.
586
- */
587
- experimental_toolCallers?: Experimental_ToolCallers<NoInfer<TOOLS>>;
588
-
589
- /**
590
- * Secret for HMAC-signing tool approval requests. When set, the server
591
- * signs each approval request at issuance and verifies the signature when
592
- * the approval is replayed, preventing client-forged approvals.
593
- */
594
- experimental_toolApprovalSecret?: string | Uint8Array;
595
-
596
- /**
597
- * Optional function that you can use to provide different settings for a step.
598
- *
599
- * @param options - The options for the step.
600
- * @param options.steps - The steps that have been executed so far.
601
- * @param options.stepNumber - The number of the step that is being executed.
602
- * @param options.model - The model that is being used.
603
- *
604
- * @returns An object that contains the settings for the step.
605
- * If you return undefined (or for undefined settings), the settings from the outer level will be used.
606
- */
607
- prepareStep?: PrepareStepFunction<NoInfer<TOOLS>, RUNTIME_CONTEXT>;
608
-
609
- /**
610
- * A function that attempts to repair a tool call that failed to parse.
611
- */
612
- repairToolCall?: ToolCallRepairFunction<TOOLS>;
613
-
614
- /**
615
- * A function that attempts to repair a tool call that failed to parse.
616
- *
617
- * @deprecated Use `repairToolCall` instead.
618
- */
619
- experimental_repairToolCall?: ToolCallRepairFunction<TOOLS>;
620
-
621
- /**
622
- * Optional mapping of tool names to functions that refine parsed tool inputs.
623
- *
624
- * The refined input must have the same type shape as the tool input. Refined
625
- * inputs are used for tool execution, stream parts, callbacks, and telemetry.
626
- */
627
- experimental_refineToolInput?: ToolInputRefinement<NoInfer<TOOLS>>;
628
-
629
- /**
630
- * Optional stream transformations.
631
- * They are applied in the order they are provided.
632
- * The stream transformations must maintain the stream structure for streamText to work correctly.
633
- */
634
- experimental_transform?: Arrayable<StreamTextTransform<TOOLS>>;
635
-
636
- /**
637
- * Custom download function to use for URLs.
638
- *
639
- * By default, files are downloaded if the model does not support the URL for the given media type.
640
- */
641
- experimental_download?: DownloadFunction | undefined;
642
-
643
- /**
644
- * Whether to include raw chunks from the provider in the stream.
645
- * When enabled, you will receive raw chunks with type 'raw' that contain the unprocessed data from the provider.
646
- * This allows access to cutting-edge provider features not yet wrapped by the AI SDK.
647
- * Defaults to false.
648
- *
649
- * @deprecated Use `include.rawChunks` instead.
650
- */
651
- includeRawChunks?: boolean;
652
-
653
- /**
654
- * Callback that is called for each chunk of the stream.
655
- * The stream processing will pause until the callback promise is resolved.
656
- */
657
- onChunk?: StreamTextOnChunkCallback<TOOLS>;
658
-
659
- /**
660
- * Callback that is invoked when an error occurs during streaming.
661
- * You can use it to log errors.
662
- * Return `{ retry: true }` to retry the current model step after a provider
663
- * error is received from the response stream when `streamRetries` is
664
- * explicitly configured.
665
- * The stream processing will pause until the callback promise is resolved.
666
- */
667
- onError?:
668
- | StreamTextOnErrorCallback
669
- | StreamTextOnErrorRetryCallback
670
- | StreamTextOnErrorHandler;
671
-
672
- /**
673
- * Maximum number of automatic retries for provider errors received after
674
- * response streaming has started. Each retry reruns only the current model
675
- * step. Completed earlier steps and their tool results are preserved.
676
- *
677
- * Partial output from a failed attempt that was already emitted cannot be
678
- * retracted and remains in consumer-facing streams. It is excluded from
679
- * the recovered step result, structured output parsing, response messages,
680
- * and subsequent model steps.
681
- *
682
- * Set to `0` to disable automatic retries while allowing `onError` to
683
- * request one retry. When automatic retries are configured, `onError` can
684
- * request at most one additional retry after they are exhausted. Omit this
685
- * option to disable all stream retry behavior and preserve incremental tool
686
- * streaming for existing `onError` observers.
687
- *
688
- * @default 0 (stream retry behavior disabled when omitted)
689
- */
690
- streamRetries?: number;
691
-
692
- /**
693
- * Callback that is called when the LLM response and all request tool executions
694
- * (for tools that have an `execute` function) are finished.
695
- *
696
- * The usage is the combined usage of all steps.
697
- */
698
- onEnd?: StreamTextOnEndCallback<
699
- NoInfer<TOOLS>,
700
- NoInfer<RUNTIME_CONTEXT>,
701
- NoInfer<OUTPUT>
702
- >;
703
-
704
- /**
705
- * Callback that is called when the LLM response and all request tool executions
706
- * (for tools that have an `execute` function) are finished.
707
- *
708
- * The usage is the combined usage of all steps.
709
- *
710
- * @deprecated Use `onEnd` instead.
711
- */
712
- onFinish?: StreamTextOnEndCallback<
713
- NoInfer<TOOLS>,
714
- NoInfer<RUNTIME_CONTEXT>,
715
- NoInfer<OUTPUT>
716
- >;
717
-
718
- onAbort?: StreamTextOnAbortCallback<
719
- NoInfer<TOOLS>,
720
- NoInfer<RUNTIME_CONTEXT>
721
- >;
722
-
723
- /**
724
- * Callback that is called when each step (LLM call) ends, including intermediate steps.
725
- */
726
- onStepEnd?: GenerateTextOnStepEndCallback<
727
- NoInfer<TOOLS>,
728
- NoInfer<RUNTIME_CONTEXT>
729
- >;
730
-
731
- /**
732
- * Callback that is called when each step (LLM call) ends, including intermediate steps.
733
- *
734
- * @deprecated Use `onStepEnd` instead.
735
- */
736
- onStepFinish?: GenerateTextOnStepFinishCallback<
737
- NoInfer<TOOLS>,
738
- NoInfer<RUNTIME_CONTEXT>
739
- >;
740
-
741
- /**
742
- * Callback that is called when the streamText operation begins,
743
- * before any LLM calls are made.
744
- */
745
- onStart?: GenerateTextOnStartCallback<
746
- NoInfer<TOOLS>,
747
- NoInfer<RUNTIME_CONTEXT>,
748
- NoInfer<OUTPUT>
749
- >;
750
-
751
- /**
752
- * Callback that is called when the streamText operation begins,
753
- * before any LLM calls are made.
754
- *
755
- * @deprecated Use `onStart` instead.
756
- */
757
- experimental_onStart?: GenerateTextOnStartCallback<
758
- NoInfer<TOOLS>,
759
- NoInfer<RUNTIME_CONTEXT>,
760
- NoInfer<OUTPUT>
761
- >;
762
-
763
- /**
764
- * Callback that is called when a step (LLM call) begins,
765
- * before the provider is called.
766
- */
767
- onStepStart?: GenerateTextOnStepStartCallback<
768
- NoInfer<TOOLS>,
769
- NoInfer<RUNTIME_CONTEXT>,
770
- NoInfer<OUTPUT>
771
- >;
772
-
773
- /**
774
- * Callback that is called when a step (LLM call) begins,
775
- * before the provider is called.
776
- *
777
- * @deprecated Use `onStepStart` instead.
778
- */
779
- experimental_onStepStart?: GenerateTextOnStepStartCallback<
780
- NoInfer<TOOLS>,
781
- NoInfer<RUNTIME_CONTEXT>,
782
- NoInfer<OUTPUT>
783
- >;
784
-
785
- /**
786
- * Callback that is called immediately before the provider model call begins.
787
- */
788
- onLanguageModelCallStart?: OnLanguageModelCallStartCallback;
789
-
790
- /**
791
- * Callback that is called immediately before the provider model call begins.
792
- *
793
- * @deprecated Use `onLanguageModelCallStart` instead.
794
- */
795
- experimental_onLanguageModelCallStart?: OnLanguageModelCallStartCallback;
796
-
797
- /**
798
- * Callback that is called after the model response has been normalized and parsed,
799
- * but before any client-side tool execution begins.
800
- */
801
- onLanguageModelCallEnd?: OnLanguageModelCallEndCallback<NoInfer<TOOLS>>;
802
-
803
- /**
804
- * Callback that is called after the model response has been normalized and parsed,
805
- * but before any client-side tool execution begins.
806
- *
807
- * @deprecated Use `onLanguageModelCallEnd` instead.
808
- */
809
- experimental_onLanguageModelCallEnd?: OnLanguageModelCallEndCallback<
810
- NoInfer<TOOLS>
811
- >;
812
-
813
- /**
814
- * Callback that is called right before a tool's execute function runs.
815
- */
816
- onToolExecutionStart?: OnToolExecutionStartCallback<NoInfer<TOOLS>>;
817
-
818
- /**
819
- * Callback that is called right before a tool's execute function runs.
820
- *
821
- * @deprecated Use `onToolExecutionStart` instead.
822
- */
823
- experimental_onToolCallStart?: OnToolExecutionStartCallback<NoInfer<TOOLS>>;
824
-
825
- /**
826
- * Callback that is called right after a tool's execute function completes (or errors).
827
- */
828
- onToolExecutionEnd?: OnToolExecutionEndCallback<NoInfer<TOOLS>>;
829
-
830
- /**
831
- * Callback that is called right after a tool's execute function completes (or errors).
832
- *
833
- * @deprecated Use `onToolExecutionEnd` instead.
834
- */
835
- experimental_onToolCallFinish?: OnToolExecutionEndCallback<NoInfer<TOOLS>>;
836
-
837
- /**
838
- * Settings for controlling what data is included in step results.
839
- * Disabling inclusion can help reduce memory usage when processing
840
- * large payloads like images.
841
- *
842
- * By default, request bodies and request messages are excluded.
843
- */
844
- include?: StreamTextInclude;
845
-
846
- /**
847
- * Settings for controlling what data is included in step results.
848
- *
849
- * @deprecated Use `include` instead.
850
- */
851
- experimental_include?: StreamTextInclude;
852
-
853
- /**
854
- * Internal. For test use only. May change without notice.
855
- */
856
- _internal?: {
857
- now?: () => number;
858
- generateId?: IdGenerator;
859
- generateCallId?: IdGenerator;
860
- /** Internal policy used by the streaming-only facade. */
861
- retention?: StreamTextRetention;
862
- };
863
- }): StreamTextResult<TOOLS, RUNTIME_CONTEXT, OUTPUT> {
466
+ >(
467
+ {
468
+ model,
469
+ tools,
470
+ toolChoice,
471
+ instructions,
472
+ system,
473
+ prompt,
474
+ messages,
475
+ allowSystemInMessages,
476
+ maxRetries,
477
+ streamRetries,
478
+ abortSignal,
479
+ timeout,
480
+ headers,
481
+ stopWhen = isStepCount(1),
482
+ experimental_sandbox: sandbox,
483
+ output,
484
+ toolApproval,
485
+ experimental_toolCallers,
486
+ experimental_toolApprovalSecret,
487
+ experimental_telemetry,
488
+ telemetry = experimental_telemetry,
489
+ prepareStep,
490
+ providerOptions,
491
+ activeTools,
492
+ toolOrder,
493
+ toolCallConcurrency,
494
+ experimental_repairToolCall,
495
+ repairToolCall = experimental_repairToolCall,
496
+ experimental_refineToolInput: refineToolInput,
497
+ experimental_transform: transform,
498
+ experimental_download: download,
499
+ includeRawChunks,
500
+ onChunk,
501
+ onError: onErrorArg,
502
+ onFinish,
503
+ onEnd = onFinish,
504
+ onAbort,
505
+ onStepEnd,
506
+ onStepError,
507
+ onStepFinish,
508
+ onStart,
509
+ experimental_onStart,
510
+ onStepStart,
511
+ experimental_onStepStart,
512
+ onLanguageModelCallStart,
513
+ experimental_onLanguageModelCallStart,
514
+ onLanguageModelCallEnd,
515
+ experimental_onLanguageModelCallEnd,
516
+ onToolExecutionStart,
517
+ onToolExecutionEnd,
518
+ experimental_onToolCallStart,
519
+ experimental_onToolCallFinish,
520
+ runtimeContext = {} as RUNTIME_CONTEXT,
521
+ toolsContext = {} as InferToolSetContext<TOOLS>,
522
+ experimental_include,
523
+ include = experimental_include,
524
+ _internal: {
525
+ now = originalNow,
526
+ generateId = originalGenerateId,
527
+ generateCallId = originalGenerateCallId,
528
+ retention = { replay: true, collectContent: true },
529
+ } = {},
530
+ ...settings
531
+ }: LanguageModelCallOptions &
532
+ RequestOptions<TOOLS> &
533
+ Prompt &
534
+ ToolsContextParameter<TOOLS> & {
535
+ /**
536
+ * The language model to use.
537
+ */
538
+ model: LanguageModel;
539
+
540
+ /**
541
+ * The tool choice strategy. Default: 'auto'.
542
+ */
543
+ toolChoice?: ToolChoice<TOOLS>;
544
+
545
+ /**
546
+ * Condition for stopping the generation when there are tool results in the last step.
547
+ * When the condition is an array, any of the conditions can be met to stop the generation.
548
+ *
549
+ * @default isStepCount(1)
550
+ */
551
+ stopWhen?: Arrayable<StopCondition<NoInfer<TOOLS>, RUNTIME_CONTEXT>>;
552
+
553
+ /**
554
+ * Optional telemetry configuration.
555
+ */
556
+ telemetry?: TelemetryOptions<RUNTIME_CONTEXT, NoInfer<TOOLS>>;
557
+
558
+ /**
559
+ * Optional telemetry configuration.
560
+ *
561
+ * @deprecated Use `telemetry` instead. This alias will be removed in a future major release.
562
+ */
563
+ experimental_telemetry?: TelemetryOptions<
564
+ RUNTIME_CONTEXT,
565
+ NoInfer<TOOLS>
566
+ >;
567
+
568
+ /**
569
+ * Additional provider-specific options. They are passed through
570
+ * to the provider from the AI SDK and enable provider-specific
571
+ * functionality that can be fully encapsulated in the provider.
572
+ */
573
+ providerOptions?: ProviderOptions;
574
+
575
+ /**
576
+ * The sandbox environment that is passed through to tool execution.
577
+ */
578
+ experimental_sandbox?: SandboxSession;
579
+
580
+ /**
581
+ * A steering object created with `experimental_createSteering()`.
582
+ * Messages sent with it are added to the generation at the next step
583
+ * boundary.
584
+ */
585
+ experimental_steering?: Steering;
586
+
587
+ /**
588
+ * Runtime context. Treat runtime context as immutable.
589
+ * If you need to mutate runtime context, update it in `prepareStep`.
590
+ */
591
+ runtimeContext?: RUNTIME_CONTEXT;
592
+
593
+ /**
594
+ * Limits the tools that are available for the model to call without
595
+ * changing the tool call and result types in the result.
596
+ */
597
+ activeTools?: ActiveTools<NoInfer<TOOLS>>;
598
+
599
+ /**
600
+ * Controls the order in which tools are sent to the provider.
601
+ *
602
+ * The list can be partial. Tools not listed in `toolOrder` are sent after
603
+ * the listed tools, sorted alphabetically. This can improve provider-side
604
+ * caching by keeping tool definitions in a stable order.
605
+ */
606
+ toolOrder?: ToolOrder<NoInfer<TOOLS>>;
607
+
608
+ /**
609
+ * Maximum number of tool calls that may execute concurrently within a step.
610
+ *
611
+ * Set to `1` to execute tool calls sequentially in the order they were
612
+ * generated. By default, all tool calls execute concurrently.
613
+ */
614
+ toolCallConcurrency?: number;
615
+
616
+ /**
617
+ * Optional specification for parsing structured outputs from the LLM response.
618
+ */
619
+ output?: OUTPUT;
620
+
621
+ /**
622
+ * Optional tool approval configuration.
623
+ *
624
+ * This configuration takes precedence over tool-defined approval settings.
625
+ */
626
+ toolApproval?: ToolApprovalConfiguration<TOOLS, RUNTIME_CONTEXT>;
627
+
628
+ /**
629
+ * Configures which caller tools may invoke each tool.
630
+ */
631
+ experimental_toolCallers?: Experimental_ToolCallers<NoInfer<TOOLS>>;
632
+
633
+ /**
634
+ * Secret for HMAC-signing tool approval requests. When set, the server
635
+ * signs each approval request at issuance and verifies the signature when
636
+ * the approval is replayed, preventing client-forged approvals.
637
+ */
638
+ experimental_toolApprovalSecret?: string | Uint8Array;
639
+
640
+ /**
641
+ * Optional function that you can use to provide different settings for a step.
642
+ *
643
+ * @param options - The options for the step.
644
+ * @param options.steps - The steps that have been executed so far.
645
+ * @param options.stepNumber - The number of the step that is being executed.
646
+ * @param options.model - The model that is being used.
647
+ *
648
+ * @returns An object that contains the settings for the step.
649
+ * If you return undefined (or for undefined settings), the settings from the outer level will be used.
650
+ */
651
+ prepareStep?: PrepareStepFunction<NoInfer<TOOLS>, RUNTIME_CONTEXT>;
652
+
653
+ /**
654
+ * A function that attempts to repair a tool call that failed to parse.
655
+ */
656
+ repairToolCall?: ToolCallRepairFunction<TOOLS>;
657
+
658
+ /**
659
+ * A function that attempts to repair a tool call that failed to parse.
660
+ *
661
+ * @deprecated Use `repairToolCall` instead.
662
+ */
663
+ experimental_repairToolCall?: ToolCallRepairFunction<TOOLS>;
664
+
665
+ /**
666
+ * Optional mapping of tool names to functions that refine parsed tool inputs.
667
+ *
668
+ * The refined input must have the same type shape as the tool input. Refined
669
+ * inputs are used for tool execution, stream parts, callbacks, and telemetry.
670
+ */
671
+ experimental_refineToolInput?: ToolInputRefinement<NoInfer<TOOLS>>;
672
+
673
+ /**
674
+ * Optional stream transformations.
675
+ * They are applied in the order they are provided.
676
+ * The stream transformations must maintain the stream structure for streamText to work correctly.
677
+ */
678
+ experimental_transform?: Arrayable<StreamTextTransform<TOOLS>>;
679
+
680
+ /**
681
+ * Custom download function to use for URLs.
682
+ *
683
+ * By default, files are downloaded if the model does not support the URL for the given media type.
684
+ */
685
+ experimental_download?: DownloadFunction | undefined;
686
+
687
+ /**
688
+ * Whether to include raw chunks from the provider in the stream.
689
+ * When enabled, you will receive raw chunks with type 'raw' that contain the unprocessed data from the provider.
690
+ * This allows access to cutting-edge provider features not yet wrapped by the AI SDK.
691
+ * Defaults to false.
692
+ *
693
+ * @deprecated Use `include.rawChunks` instead.
694
+ */
695
+ includeRawChunks?: boolean;
696
+
697
+ /**
698
+ * Callback that is called for each chunk of the stream.
699
+ * The stream processing will pause until the callback promise is resolved.
700
+ */
701
+ onChunk?: StreamTextOnChunkCallback<TOOLS>;
702
+
703
+ /**
704
+ * Callback that is invoked when an error occurs during streaming.
705
+ * You can use it to log errors.
706
+ * Return `{ retry: true }` to retry the current model step after a provider
707
+ * error is received from the response stream when `streamRetries` is
708
+ * explicitly configured.
709
+ * The stream processing will pause until the callback promise is resolved.
710
+ */
711
+ onError?:
712
+ | StreamTextOnErrorCallback
713
+ | StreamTextOnErrorRetryCallback
714
+ | StreamTextOnErrorHandler;
715
+
716
+ /**
717
+ * Maximum number of automatic retries for provider errors received after
718
+ * response streaming has started. Each retry reruns only the current model
719
+ * step. Completed earlier steps and their tool results are preserved.
720
+ *
721
+ * Partial output from a failed attempt that was already emitted cannot be
722
+ * retracted and remains in consumer-facing streams. It is excluded from
723
+ * the recovered step result, structured output parsing, response messages,
724
+ * and subsequent model steps.
725
+ *
726
+ * Set to `0` to disable automatic retries while allowing `onError` to
727
+ * request one retry. When automatic retries are configured, `onError` can
728
+ * request at most one additional retry after they are exhausted. Omit this
729
+ * option to disable all stream retry behavior and preserve incremental tool
730
+ * streaming for existing `onError` observers.
731
+ *
732
+ * @default 0 (stream retry behavior disabled when omitted)
733
+ */
734
+ streamRetries?: number;
735
+
736
+ /**
737
+ * Callback that is called when the LLM response and all request tool executions
738
+ * (for tools that have an `execute` function) are finished.
739
+ *
740
+ * The usage is the combined usage of all steps.
741
+ */
742
+ onEnd?: StreamTextOnEndCallback<
743
+ NoInfer<TOOLS>,
744
+ NoInfer<RUNTIME_CONTEXT>,
745
+ NoInfer<OUTPUT>
746
+ >;
747
+
748
+ /**
749
+ * Callback that is called when the LLM response and all request tool executions
750
+ * (for tools that have an `execute` function) are finished.
751
+ *
752
+ * The usage is the combined usage of all steps.
753
+ *
754
+ * @deprecated Use `onEnd` instead.
755
+ */
756
+ onFinish?: StreamTextOnEndCallback<
757
+ NoInfer<TOOLS>,
758
+ NoInfer<RUNTIME_CONTEXT>,
759
+ NoInfer<OUTPUT>
760
+ >;
761
+
762
+ onAbort?: StreamTextOnAbortCallback<
763
+ NoInfer<TOOLS>,
764
+ NoInfer<RUNTIME_CONTEXT>
765
+ >;
766
+
767
+ /**
768
+ * Callback that is called when each step (LLM call) ends, including intermediate steps.
769
+ */
770
+ onStepEnd?: GenerateTextOnStepEndCallback<
771
+ NoInfer<TOOLS>,
772
+ NoInfer<RUNTIME_CONTEXT>
773
+ >;
774
+
775
+ /**
776
+ * Callback that is called when a step that started fails before it
777
+ * produces a step result. `onStepEnd` is not called for such a step.
778
+ */
779
+ onStepError?: GenerateTextOnStepErrorCallback<
780
+ NoInfer<TOOLS>,
781
+ NoInfer<RUNTIME_CONTEXT>,
782
+ NoInfer<OUTPUT>
783
+ >;
784
+
785
+ /**
786
+ * Callback that is called when each step (LLM call) ends, including intermediate steps.
787
+ *
788
+ * @deprecated Use `onStepEnd` instead.
789
+ */
790
+ onStepFinish?: GenerateTextOnStepFinishCallback<
791
+ NoInfer<TOOLS>,
792
+ NoInfer<RUNTIME_CONTEXT>
793
+ >;
794
+
795
+ /**
796
+ * Callback that is called when the streamText operation begins,
797
+ * before any LLM calls are made.
798
+ */
799
+ onStart?: GenerateTextOnStartCallback<
800
+ NoInfer<TOOLS>,
801
+ NoInfer<RUNTIME_CONTEXT>,
802
+ NoInfer<OUTPUT>
803
+ >;
804
+
805
+ /**
806
+ * Callback that is called when the streamText operation begins,
807
+ * before any LLM calls are made.
808
+ *
809
+ * @deprecated Use `onStart` instead.
810
+ */
811
+ experimental_onStart?: GenerateTextOnStartCallback<
812
+ NoInfer<TOOLS>,
813
+ NoInfer<RUNTIME_CONTEXT>,
814
+ NoInfer<OUTPUT>
815
+ >;
816
+
817
+ /**
818
+ * Callback that is called when a step (LLM call) begins,
819
+ * before the provider is called.
820
+ */
821
+ onStepStart?: GenerateTextOnStepStartCallback<
822
+ NoInfer<TOOLS>,
823
+ NoInfer<RUNTIME_CONTEXT>,
824
+ NoInfer<OUTPUT>
825
+ >;
826
+
827
+ /**
828
+ * Callback that is called when a step (LLM call) begins,
829
+ * before the provider is called.
830
+ *
831
+ * @deprecated Use `onStepStart` instead.
832
+ */
833
+ experimental_onStepStart?: GenerateTextOnStepStartCallback<
834
+ NoInfer<TOOLS>,
835
+ NoInfer<RUNTIME_CONTEXT>,
836
+ NoInfer<OUTPUT>
837
+ >;
838
+
839
+ /**
840
+ * Callback that is called immediately before the provider model call begins.
841
+ */
842
+ onLanguageModelCallStart?: OnLanguageModelCallStartCallback;
843
+
844
+ /**
845
+ * Callback that is called immediately before the provider model call begins.
846
+ *
847
+ * @deprecated Use `onLanguageModelCallStart` instead.
848
+ */
849
+ experimental_onLanguageModelCallStart?: OnLanguageModelCallStartCallback;
850
+
851
+ /**
852
+ * Callback that is called after the model response has been normalized and parsed,
853
+ * but before any client-side tool execution begins.
854
+ */
855
+ onLanguageModelCallEnd?: OnLanguageModelCallEndCallback<NoInfer<TOOLS>>;
856
+
857
+ /**
858
+ * Callback that is called after the model response has been normalized and parsed,
859
+ * but before any client-side tool execution begins.
860
+ *
861
+ * @deprecated Use `onLanguageModelCallEnd` instead.
862
+ */
863
+ experimental_onLanguageModelCallEnd?: OnLanguageModelCallEndCallback<
864
+ NoInfer<TOOLS>
865
+ >;
866
+
867
+ /**
868
+ * Callback that is called right before a tool's execute function runs.
869
+ */
870
+ onToolExecutionStart?: OnToolExecutionStartCallback<NoInfer<TOOLS>>;
871
+
872
+ /**
873
+ * Callback that is called right before a tool's execute function runs.
874
+ *
875
+ * @deprecated Use `onToolExecutionStart` instead.
876
+ */
877
+ experimental_onToolCallStart?: OnToolExecutionStartCallback<
878
+ NoInfer<TOOLS>
879
+ >;
880
+
881
+ /**
882
+ * Callback that is called right after a tool's execute function completes (or errors).
883
+ */
884
+ onToolExecutionEnd?: OnToolExecutionEndCallback<NoInfer<TOOLS>>;
885
+
886
+ /**
887
+ * Callback that is called right after a tool's execute function completes (or errors).
888
+ *
889
+ * @deprecated Use `onToolExecutionEnd` instead.
890
+ */
891
+ experimental_onToolCallFinish?: OnToolExecutionEndCallback<
892
+ NoInfer<TOOLS>
893
+ >;
894
+
895
+ /**
896
+ * Settings for controlling what data is included in step results.
897
+ * Disabling inclusion can help reduce memory usage when processing
898
+ * large payloads like images.
899
+ *
900
+ * By default, request bodies and request messages are excluded.
901
+ */
902
+ include?: StreamTextInclude;
903
+
904
+ /**
905
+ * Settings for controlling what data is included in step results.
906
+ *
907
+ * @deprecated Use `include` instead.
908
+ */
909
+ experimental_include?: StreamTextInclude;
910
+
911
+ /**
912
+ * Internal. For test use only. May change without notice.
913
+ */
914
+ _internal?: {
915
+ now?: () => number;
916
+ generateId?: IdGenerator;
917
+ generateCallId?: IdGenerator;
918
+ /** Internal policy used by the streaming-only facade. */
919
+ retention?: StreamTextRetention;
920
+ };
921
+ },
922
+ steeringRun: SteeringRun | undefined,
923
+ ): StreamTextResult<TOOLS, RUNTIME_CONTEXT, OUTPUT> {
864
924
  const resolvedToolCallConcurrency =
865
925
  prepareToolCallConcurrency(toolCallConcurrency);
866
926
  const totalTimeoutMs = getTotalTimeoutMs(timeout);
@@ -916,6 +976,7 @@ function streamTextInternal<
916
976
  messages,
917
977
  allowSystemInMessages,
918
978
  experimental_sandbox: sandbox,
979
+ steeringRun,
919
980
  tools,
920
981
  toolsContext,
921
982
  runtimeContext,
@@ -940,6 +1001,7 @@ function streamTextInternal<
940
1001
  onEnd,
941
1002
  onAbort,
942
1003
  onStepFinish: resolvedOnStepEnd,
1004
+ onStepError,
943
1005
  onStart: resolvedOnStart,
944
1006
  onStepStart: resolvedOnStepStart,
945
1007
  onLanguageModelCallStart: resolvedOnLanguageModelCallStart,
@@ -1316,6 +1378,7 @@ class DefaultStreamTextResult<
1316
1378
  messages,
1317
1379
  allowSystemInMessages,
1318
1380
  experimental_sandbox: sandbox,
1381
+ steeringRun,
1319
1382
  tools,
1320
1383
  toolChoice,
1321
1384
  transforms,
@@ -1341,6 +1404,7 @@ class DefaultStreamTextResult<
1341
1404
  onEnd,
1342
1405
  onAbort,
1343
1406
  onStepFinish,
1407
+ onStepError,
1344
1408
  onStart,
1345
1409
  onStepStart,
1346
1410
  onLanguageModelCallStart,
@@ -1374,6 +1438,7 @@ class DefaultStreamTextResult<
1374
1438
  messages: Prompt['messages'];
1375
1439
  allowSystemInMessages: Prompt['allowSystemInMessages'];
1376
1440
  experimental_sandbox: SandboxSession | undefined;
1441
+ steeringRun: SteeringRun | undefined;
1377
1442
  tools: TOOLS | undefined;
1378
1443
  toolChoice: ToolChoice<TOOLS> | undefined;
1379
1444
  transforms: Array<StreamTextTransform<TOOLS>>;
@@ -1420,6 +1485,13 @@ class DefaultStreamTextResult<
1420
1485
  NoInfer<TOOLS>,
1421
1486
  NoInfer<RUNTIME_CONTEXT>
1422
1487
  >;
1488
+ onStepError:
1489
+ | undefined
1490
+ | GenerateTextOnStepErrorCallback<
1491
+ NoInfer<TOOLS>,
1492
+ NoInfer<RUNTIME_CONTEXT>,
1493
+ NoInfer<OUTPUT>
1494
+ >;
1423
1495
  onStart:
1424
1496
  | undefined
1425
1497
  | GenerateTextOnStartCallback<
@@ -1479,6 +1551,7 @@ class DefaultStreamTextResult<
1479
1551
  const initialResponseMessages: Array<ResponseMessage> = [];
1480
1552
  let stepMessagesForNextStep: Array<ModelMessage> | undefined;
1481
1553
  let currentStepMessages: Array<ModelMessage> = [];
1554
+ let currentStepSteeringMessages: Array<UserModelMessage> = [];
1482
1555
  let isAborted = false;
1483
1556
  let currentStepModel = model;
1484
1557
 
@@ -1537,6 +1610,30 @@ class DefaultStreamTextResult<
1537
1610
  let recordedNoOutputError: NoOutputGeneratedError | undefined;
1538
1611
  const errorsHandledForStreamRetry = new Set<unknown>();
1539
1612
 
1613
+ // the step that has started but not ended and its last error that was not
1614
+ // handled by a stream retry, reported to onStepError when the stream ends
1615
+ // before the step produces a result:
1616
+ let openStep:
1617
+ | {
1618
+ event: GenerateTextStepStartEvent<TOOLS, RUNTIME_CONTEXT, OUTPUT>;
1619
+ error?: unknown;
1620
+ }
1621
+ | undefined;
1622
+
1623
+ const notifyStepError = async (error: unknown) => {
1624
+ if (openStep == null || abortSignal?.aborted) {
1625
+ return;
1626
+ }
1627
+
1628
+ const { event } = openStep;
1629
+ openStep = undefined;
1630
+
1631
+ await notify({
1632
+ event: { ...event, error },
1633
+ callbacks: [onStepError, telemetryDispatcher.onStepError],
1634
+ });
1635
+ };
1636
+
1540
1637
  const eventProcessor = new TransformStream<
1541
1638
  InternalEnrichedStreamPart<TOOLS, InferPartialOutput<OUTPUT>>,
1542
1639
  EnrichedStreamPart<TOOLS, InferPartialOutput<OUTPUT>>
@@ -1576,6 +1673,9 @@ class DefaultStreamTextResult<
1576
1673
  if (callbacksHandledForStreamRetry) {
1577
1674
  errorsHandledForStreamRetry.delete(part.error);
1578
1675
  } else {
1676
+ if (openStep != null) {
1677
+ openStep.error = error;
1678
+ }
1579
1679
  await notify({
1580
1680
  event: { error },
1581
1681
  callbacks: async event => {
@@ -1757,8 +1857,11 @@ class DefaultStreamTextResult<
1757
1857
  messages: cloneModelMessages(stepResponseMessages),
1758
1858
  },
1759
1859
  providerMetadata: part.providerMetadata,
1860
+ steeringMessages: currentStepSteeringMessages,
1760
1861
  });
1761
1862
 
1863
+ openStep = undefined;
1864
+
1762
1865
  await notify({
1763
1866
  event: currentStepResult,
1764
1867
  callbacks: [onStepFinish, telemetryDispatcher.onStepEnd],
@@ -1789,16 +1892,36 @@ class DefaultStreamTextResult<
1789
1892
  },
1790
1893
 
1791
1894
  async flush(controller) {
1895
+ // steering messages that are still waiting cannot be delivered anymore:
1896
+ steeringRun?.end(
1897
+ recordedSteps.length === 0 || recordedNoOutputError != null
1898
+ ? 'error'
1899
+ : 'finished',
1900
+ );
1901
+
1792
1902
  try {
1903
+ const noOutputError = new NoOutputGeneratedError({
1904
+ message: 'No output generated. Check the stream for errors.',
1905
+ });
1906
+
1907
+ // a step that started without producing a result has failed:
1908
+ await notifyStepError(openStep?.error ?? noOutputError);
1909
+
1793
1910
  // reject when no output was generated or an incomplete model stream
1794
1911
  // ended a continuation step:
1795
1912
  if (recordedSteps.length === 0 || recordedNoOutputError != null) {
1796
1913
  const error = abortSignal?.aborted
1797
1914
  ? abortSignal.reason
1798
- : (recordedNoOutputError ??
1799
- new NoOutputGeneratedError({
1800
- message: 'No output generated. Check the stream for errors.',
1801
- }));
1915
+ : (recordedNoOutputError ?? noOutputError);
1916
+
1917
+ // an incomplete model stream is not reported to telemetry by the
1918
+ // stream itself, so report it here to close open spans:
1919
+ if (recordedNoOutputError != null && !abortSignal?.aborted) {
1920
+ await telemetryDispatcher.onError?.({
1921
+ callId,
1922
+ error: recordedNoOutputError,
1923
+ });
1924
+ }
1802
1925
 
1803
1926
  self.rejectResultPromises(error);
1804
1927
 
@@ -1954,6 +2077,7 @@ class DefaultStreamTextResult<
1954
2077
  async function abort() {
1955
2078
  isAborted = true;
1956
2079
  removeAbortListener();
2080
+ steeringRun?.end('aborted');
1957
2081
 
1958
2082
  await notify({
1959
2083
  event: {
@@ -1997,6 +2121,9 @@ class DefaultStreamTextResult<
1997
2121
  if (isAbortError(error) && abortSignal?.aborted) {
1998
2122
  await abort();
1999
2123
  } else {
2124
+ steeringRun?.end('error');
2125
+ // an erroring stream skips the event processor flush:
2126
+ await notifyStepError(wrapGatewayError(error));
2000
2127
  await telemetryDispatcher.onError?.({ callId, error });
2001
2128
  controller.error(error);
2002
2129
  }
@@ -2005,6 +2132,7 @@ class DefaultStreamTextResult<
2005
2132
 
2006
2133
  cancel(reason) {
2007
2134
  removeAbortListener();
2135
+ steeringRun?.end('aborted');
2008
2136
  return reader.cancel(reason);
2009
2137
  },
2010
2138
  });
@@ -2430,9 +2558,14 @@ class DefaultStreamTextResult<
2430
2558
  ...initialResponseMessages,
2431
2559
  ...responseMessagesFromPreviousSteps,
2432
2560
  ];
2433
- const stepInputMessages = stepMessagesForNextStep ?? [
2434
- ...initialMessages,
2435
- ...initialResponseMessages,
2561
+ const steeringMessages = steeringRun?.takeMessages() ?? [];
2562
+
2563
+ const stepInputMessages = [
2564
+ ...(stepMessagesForNextStep ?? [
2565
+ ...initialMessages,
2566
+ ...initialResponseMessages,
2567
+ ]),
2568
+ ...steeringMessages,
2436
2569
  ];
2437
2570
 
2438
2571
  const prepareStepResult = await prepareStep?.({
@@ -2444,6 +2577,9 @@ class DefaultStreamTextResult<
2444
2577
  messages: stepInputMessages,
2445
2578
  initialMessages,
2446
2579
  responseMessages: accumulatedResponseMessages,
2580
+ ...(steeringMessages.length > 0
2581
+ ? { experimental_steeringMessages: steeringMessages }
2582
+ : {}),
2447
2583
  toolsContext,
2448
2584
  runtimeContext,
2449
2585
  experimental_sandbox: sandbox,
@@ -2503,6 +2639,14 @@ class DefaultStreamTextResult<
2503
2639
  toolCallerMessages,
2504
2640
  });
2505
2641
  currentStepMessages = stepMessages;
2642
+
2643
+ // steering messages are delivered when they are in the messages that
2644
+ // are sent to the model after prepareStep:
2645
+ currentStepSteeringMessages =
2646
+ steeringRun?.resolveTakenMessages({
2647
+ stepNumber: currentStep,
2648
+ sentMessages: stepMessages,
2649
+ }) ?? [];
2506
2650
  const stepInstructions =
2507
2651
  prepareStepResult?.instructions ??
2508
2652
  prepareStepResult?.system ??
@@ -2566,30 +2710,33 @@ class DefaultStreamTextResult<
2566
2710
  }
2567
2711
  hasNotifiedStepStart = true;
2568
2712
 
2713
+ const stepStartEvent = {
2714
+ callId,
2715
+ provider: stepModel.provider,
2716
+ modelId: stepModel.modelId,
2717
+ stepNumber: recordedSteps.length,
2718
+ instructions: stepInstructions,
2719
+ messages: stepMessages,
2720
+ tools,
2721
+ toolChoice: prepareStepResult?.toolChoice ?? toolChoice,
2722
+ activeTools:
2723
+ prepareStepResult?.activeTools ?? activeTools,
2724
+ toolOrder: stepToolOrder,
2725
+ steps: [...recordedSteps],
2726
+ providerOptions: stepProviderOptions,
2727
+ runtimeContext,
2728
+ toolsContext,
2729
+ output,
2730
+ promptMessages,
2731
+ stepTools,
2732
+ stepToolChoice,
2733
+ };
2569
2734
  await notify({
2570
- event: {
2571
- callId,
2572
- provider: stepModel.provider,
2573
- modelId: stepModel.modelId,
2574
- stepNumber: recordedSteps.length,
2575
- instructions: stepInstructions,
2576
- messages: stepMessages,
2577
- tools,
2578
- toolChoice: prepareStepResult?.toolChoice ?? toolChoice,
2579
- activeTools:
2580
- prepareStepResult?.activeTools ?? activeTools,
2581
- toolOrder: stepToolOrder,
2582
- steps: [...recordedSteps],
2583
- providerOptions: stepProviderOptions,
2584
- runtimeContext,
2585
- toolsContext,
2586
- output,
2587
- promptMessages,
2588
- stepTools,
2589
- stepToolChoice,
2590
- },
2735
+ event: stepStartEvent,
2591
2736
  callbacks: [onStepStart, telemetryDispatcher.onStepStart],
2592
2737
  });
2738
+
2739
+ openStep = { event: stepStartEvent };
2593
2740
  },
2594
2741
  _internal: {
2595
2742
  now,
@@ -3216,21 +3363,58 @@ class DefaultStreamTextResult<
3216
3363
  // Clear this step's timeouts before the next step is started.
3217
3364
  cleanupStepTimeouts();
3218
3365
 
3219
- if (
3220
- stepFinishReason !== 'interrupted' &&
3221
- // Continue only after all client tool calls have been executed or denied,
3222
- // and if there are client results or pending deferred provider results.
3223
- clientToolCalls.length ===
3366
+ // why waiting steering messages are not delivered when
3367
+ // the stream ends:
3368
+ let steeringEndReason: SteeringNotDeliveredReason =
3369
+ 'finished';
3370
+
3371
+ const shouldStartNextStep = async () => {
3372
+ if (stepFinishReason === 'interrupted') {
3373
+ return false;
3374
+ }
3375
+
3376
+ // Continue only after all client tool calls have been executed or denied:
3377
+ if (
3378
+ clientToolCalls.length !==
3224
3379
  clientToolOutputs.length +
3225
- deniedToolApprovalResponses.length &&
3226
- (clientToolCalls.length > 0 ||
3227
- pendingDeferredToolCalls.size > 0) &&
3380
+ deniedToolApprovalResponses.length
3381
+ ) {
3382
+ steeringEndReason = 'waiting-for-tool-results';
3383
+ return false;
3384
+ }
3385
+
3386
+ // Continue if there are client results, pending deferred
3387
+ // provider results, or steering messages:
3388
+ if (
3389
+ clientToolCalls.length === 0 &&
3390
+ pendingDeferredToolCalls.size === 0
3391
+ ) {
3392
+ if (!steeringRun?.hasPendingMessages()) {
3393
+ return false;
3394
+ }
3395
+
3396
+ // steering messages do not continue a failed step:
3397
+ if (stepFinishReason === 'error') {
3398
+ steeringEndReason = 'error';
3399
+ return false;
3400
+ }
3401
+ }
3402
+
3228
3403
  // continue until a stop condition is met:
3229
- !(await isStopConditionMet({
3230
- stopConditions,
3231
- steps: recordedSteps,
3232
- }))
3233
- ) {
3404
+ if (
3405
+ await isStopConditionMet({
3406
+ stopConditions,
3407
+ steps: recordedSteps,
3408
+ })
3409
+ ) {
3410
+ steeringEndReason = 'stop-condition';
3411
+ return false;
3412
+ }
3413
+
3414
+ return true;
3415
+ };
3416
+
3417
+ if (await shouldStartNextStep()) {
3234
3418
  try {
3235
3419
  await runInStreamTextTracingChannelContext(() =>
3236
3420
  streamStep({
@@ -3239,6 +3423,8 @@ class DefaultStreamTextResult<
3239
3423
  }),
3240
3424
  );
3241
3425
  } catch (error) {
3426
+ steeringRun?.end('error');
3427
+
3242
3428
  enqueueStepPart(controller, {
3243
3429
  type: 'error',
3244
3430
  error,
@@ -3247,6 +3433,8 @@ class DefaultStreamTextResult<
3247
3433
  self.closeStream();
3248
3434
  }
3249
3435
  } else {
3436
+ steeringRun?.end(steeringEndReason);
3437
+
3250
3438
  enqueueStepPart(controller, {
3251
3439
  type: 'finish',
3252
3440
  finishReason: stepFinishReason,
@@ -3280,6 +3468,9 @@ class DefaultStreamTextResult<
3280
3468
  }),
3281
3469
  );
3282
3470
  })().catch(async error => {
3471
+ steeringRun?.end(abortSignal?.aborted ? 'aborted' : 'error');
3472
+ // report the failed step before the call error:
3473
+ await notifyStepError(wrapGatewayError(error));
3283
3474
  await telemetryDispatcher.onError?.({ callId, error });
3284
3475
  self._initialResponseMessages.reject(error);
3285
3476
  markPromiseAsHandled(self._initialResponseMessages.promise);