ai 5.0.239 → 5.0.241

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,20 @@
1
1
  # ai
2
2
 
3
+ ## 5.0.241
4
+
5
+ ### Patch Changes
6
+
7
+ - 9d0b84c: fix: reject `streamObject` result promises and report failed completion when the provider stream errors
8
+ - 05df123: Filter preliminary tool outputs when `ignoreIncompleteToolCalls` is enabled.
9
+ - e532644: Prevent automatic tool execution when a model call ends with an unsafe finish reason.
10
+
11
+ ## 5.0.240
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated dependencies [bef36a6]
16
+ - @ai-sdk/gateway@2.0.137
17
+
3
18
  ## 5.0.239
4
19
 
5
20
  ### Patch Changes
package/dist/index.js CHANGED
@@ -816,7 +816,7 @@ function detectMediaType({
816
816
  var import_provider_utils2 = require("@ai-sdk/provider-utils");
817
817
 
818
818
  // src/version.ts
819
- var VERSION = true ? "5.0.239" : "0.0.0-test";
819
+ var VERSION = true ? "5.0.241" : "0.0.0-test";
820
820
 
821
821
  // src/util/download/download.ts
822
822
  var download = async ({
@@ -2012,6 +2012,11 @@ var DefaultGeneratedFileWithType = class extends DefaultGeneratedFile {
2012
2012
  }
2013
2013
  };
2014
2014
 
2015
+ // src/generate-text/is-tool-execution-allowed-finish-reason.ts
2016
+ function isToolExecutionAllowedFinishReason(finishReason) {
2017
+ return finishReason === "stop" || finishReason === "tool-calls";
2018
+ }
2019
+
2015
2020
  // src/generate-text/parse-tool-call.ts
2016
2021
  var import_provider_utils9 = require("@ai-sdk/provider-utils");
2017
2022
  async function parseToolCall({
@@ -2573,7 +2578,9 @@ async function generateText({
2573
2578
  clientToolCalls = stepToolCalls.filter(
2574
2579
  (toolCall) => !toolCall.providerExecuted
2575
2580
  );
2576
- if (stepToolSet != null) {
2581
+ if (stepToolSet != null && isToolExecutionAllowedFinishReason(
2582
+ currentModelResponse.finishReason
2583
+ )) {
2577
2584
  clientToolOutputs.push(
2578
2585
  ...await executeTools({
2579
2586
  toolCalls: clientToolCalls.filter(
@@ -4340,32 +4347,33 @@ function createResolvablePromise() {
4340
4347
 
4341
4348
  // src/util/create-stitchable-stream.ts
4342
4349
  function createStitchableStream() {
4343
- let innerStreamReaders = [];
4350
+ let innerStreams = [];
4344
4351
  let controller = null;
4345
4352
  let isClosed = false;
4346
4353
  let waitForNewStream = createResolvablePromise();
4347
4354
  const terminate = () => {
4348
4355
  isClosed = true;
4349
4356
  waitForNewStream.resolve();
4350
- innerStreamReaders.forEach((reader) => reader.cancel());
4351
- innerStreamReaders = [];
4357
+ innerStreams.forEach(({ reader }) => reader.cancel());
4358
+ innerStreams = [];
4352
4359
  controller == null ? void 0 : controller.close();
4353
4360
  };
4354
4361
  const processPull = async () => {
4355
- if (isClosed && innerStreamReaders.length === 0) {
4362
+ var _a16, _b;
4363
+ if (isClosed && innerStreams.length === 0) {
4356
4364
  controller == null ? void 0 : controller.close();
4357
4365
  return;
4358
4366
  }
4359
- if (innerStreamReaders.length === 0) {
4367
+ if (innerStreams.length === 0) {
4360
4368
  waitForNewStream = createResolvablePromise();
4361
4369
  await waitForNewStream.promise;
4362
4370
  return processPull();
4363
4371
  }
4364
4372
  try {
4365
- const { value, done } = await innerStreamReaders[0].read();
4373
+ const { value, done } = await innerStreams[0].reader.read();
4366
4374
  if (done) {
4367
- innerStreamReaders.shift();
4368
- if (innerStreamReaders.length > 0) {
4375
+ innerStreams.shift();
4376
+ if (innerStreams.length > 0) {
4369
4377
  await processPull();
4370
4378
  } else if (isClosed) {
4371
4379
  controller == null ? void 0 : controller.close();
@@ -4374,8 +4382,9 @@ function createStitchableStream() {
4374
4382
  controller == null ? void 0 : controller.enqueue(value);
4375
4383
  }
4376
4384
  } catch (error) {
4385
+ (_b = (_a16 = innerStreams[0]).onError) == null ? void 0 : _b.call(_a16, error);
4377
4386
  controller == null ? void 0 : controller.error(error);
4378
- innerStreamReaders.shift();
4387
+ innerStreams.shift();
4379
4388
  terminate();
4380
4389
  }
4381
4390
  };
@@ -4386,18 +4395,21 @@ function createStitchableStream() {
4386
4395
  },
4387
4396
  pull: processPull,
4388
4397
  async cancel() {
4389
- for (const reader of innerStreamReaders) {
4398
+ for (const { reader } of innerStreams) {
4390
4399
  await reader.cancel();
4391
4400
  }
4392
- innerStreamReaders = [];
4401
+ innerStreams = [];
4393
4402
  isClosed = true;
4394
4403
  }
4395
4404
  }),
4396
- addStream: (innerStream) => {
4405
+ addStream: (innerStream, callbacks) => {
4397
4406
  if (isClosed) {
4398
4407
  throw new Error("Cannot add inner stream: outer stream is closed");
4399
4408
  }
4400
- innerStreamReaders.push(innerStream.getReader());
4409
+ innerStreams.push({
4410
+ reader: innerStream.getReader(),
4411
+ ...callbacks
4412
+ });
4401
4413
  waitForNewStream.resolve();
4402
4414
  },
4403
4415
  /**
@@ -4407,7 +4419,7 @@ function createStitchableStream() {
4407
4419
  close: () => {
4408
4420
  isClosed = true;
4409
4421
  waitForNewStream.resolve();
4410
- if (innerStreamReaders.length === 0) {
4422
+ if (innerStreams.length === 0) {
4411
4423
  controller == null ? void 0 : controller.close();
4412
4424
  }
4413
4425
  },
@@ -4469,6 +4481,7 @@ function runToolsTransformation({
4469
4481
  }
4470
4482
  }
4471
4483
  const outstandingToolResults = /* @__PURE__ */ new Set();
4484
+ const pendingToolCalls = [];
4472
4485
  const toolInputs = /* @__PURE__ */ new Map();
4473
4486
  let canClose = false;
4474
4487
  let finishChunk = void 0;
@@ -4480,6 +4493,82 @@ function runToolsTransformation({
4480
4493
  closeToolResultsStream();
4481
4494
  }
4482
4495
  }
4496
+ function executePendingToolCall(toolCall) {
4497
+ const tool2 = tools[toolCall.toolName];
4498
+ const toolExecutionId = (0, import_provider_utils14.generateId)();
4499
+ outstandingToolResults.add(toolExecutionId);
4500
+ recordSpan({
4501
+ name: "ai.toolCall",
4502
+ attributes: selectTelemetryAttributes({
4503
+ telemetry,
4504
+ attributes: {
4505
+ ...assembleOperationName({
4506
+ operationId: "ai.toolCall",
4507
+ telemetry
4508
+ }),
4509
+ "ai.toolCall.name": toolCall.toolName,
4510
+ "ai.toolCall.id": toolCall.toolCallId,
4511
+ "ai.toolCall.args": {
4512
+ output: () => JSON.stringify(toolCall.input)
4513
+ }
4514
+ }
4515
+ }),
4516
+ tracer,
4517
+ fn: async (span) => {
4518
+ let output;
4519
+ try {
4520
+ const stream = (0, import_provider_utils14.executeTool)({
4521
+ execute: tool2.execute.bind(tool2),
4522
+ input: toolCall.input,
4523
+ options: {
4524
+ toolCallId: toolCall.toolCallId,
4525
+ messages,
4526
+ abortSignal,
4527
+ experimental_context
4528
+ }
4529
+ });
4530
+ for await (const part of stream) {
4531
+ enqueueToolResult({
4532
+ ...toolCall,
4533
+ type: "tool-result",
4534
+ output: part.output,
4535
+ ...part.type === "preliminary" && {
4536
+ preliminary: true
4537
+ }
4538
+ });
4539
+ if (part.type === "final") {
4540
+ output = part.output;
4541
+ }
4542
+ }
4543
+ } catch (error) {
4544
+ recordErrorOnSpan(span, error);
4545
+ enqueueToolResult({
4546
+ ...toolCall,
4547
+ type: "tool-error",
4548
+ error
4549
+ });
4550
+ outstandingToolResults.delete(toolExecutionId);
4551
+ attemptClose();
4552
+ return;
4553
+ }
4554
+ outstandingToolResults.delete(toolExecutionId);
4555
+ attemptClose();
4556
+ try {
4557
+ span.setAttributes(
4558
+ selectTelemetryAttributes({
4559
+ telemetry,
4560
+ attributes: {
4561
+ "ai.toolCall.result": {
4562
+ output: () => JSON.stringify(output)
4563
+ }
4564
+ }
4565
+ })
4566
+ );
4567
+ } catch (ignored) {
4568
+ }
4569
+ }
4570
+ });
4571
+ }
4483
4572
  const forwardStream = new TransformStream({
4484
4573
  async transform(chunk, controller) {
4485
4574
  const chunkType = chunk.type;
@@ -4518,6 +4607,13 @@ function runToolsTransformation({
4518
4607
  usage: chunk.usage,
4519
4608
  providerMetadata: chunk.providerMetadata
4520
4609
  };
4610
+ if (isToolExecutionAllowedFinishReason(chunk.finishReason)) {
4611
+ for (const toolCall of pendingToolCalls.splice(0)) {
4612
+ executePendingToolCall(toolCall);
4613
+ }
4614
+ } else {
4615
+ pendingToolCalls.length = 0;
4616
+ }
4521
4617
  break;
4522
4618
  }
4523
4619
  case "tool-call": {
@@ -4553,79 +4649,7 @@ function runToolsTransformation({
4553
4649
  });
4554
4650
  }
4555
4651
  if (tool2.execute != null && toolCall.providerExecuted !== true) {
4556
- const toolExecutionId = (0, import_provider_utils14.generateId)();
4557
- outstandingToolResults.add(toolExecutionId);
4558
- recordSpan({
4559
- name: "ai.toolCall",
4560
- attributes: selectTelemetryAttributes({
4561
- telemetry,
4562
- attributes: {
4563
- ...assembleOperationName({
4564
- operationId: "ai.toolCall",
4565
- telemetry
4566
- }),
4567
- "ai.toolCall.name": toolCall.toolName,
4568
- "ai.toolCall.id": toolCall.toolCallId,
4569
- "ai.toolCall.args": {
4570
- output: () => JSON.stringify(toolCall.input)
4571
- }
4572
- }
4573
- }),
4574
- tracer,
4575
- fn: async (span) => {
4576
- let output;
4577
- try {
4578
- const stream = (0, import_provider_utils14.executeTool)({
4579
- execute: tool2.execute.bind(tool2),
4580
- input: toolCall.input,
4581
- options: {
4582
- toolCallId: toolCall.toolCallId,
4583
- messages,
4584
- abortSignal,
4585
- experimental_context
4586
- }
4587
- });
4588
- for await (const part of stream) {
4589
- enqueueToolResult({
4590
- ...toolCall,
4591
- type: "tool-result",
4592
- output: part.output,
4593
- ...part.type === "preliminary" && {
4594
- preliminary: true
4595
- }
4596
- });
4597
- if (part.type === "final") {
4598
- output = part.output;
4599
- }
4600
- }
4601
- } catch (error) {
4602
- recordErrorOnSpan(span, error);
4603
- enqueueToolResult({
4604
- ...toolCall,
4605
- type: "tool-error",
4606
- error
4607
- });
4608
- outstandingToolResults.delete(toolExecutionId);
4609
- attemptClose();
4610
- return;
4611
- }
4612
- outstandingToolResults.delete(toolExecutionId);
4613
- attemptClose();
4614
- try {
4615
- span.setAttributes(
4616
- selectTelemetryAttributes({
4617
- telemetry,
4618
- attributes: {
4619
- "ai.toolCall.result": {
4620
- output: () => JSON.stringify(output)
4621
- }
4622
- }
4623
- })
4624
- );
4625
- } catch (ignored) {
4626
- }
4627
- }
4628
- });
4652
+ pendingToolCalls.push({ ...toolCall });
4629
4653
  }
4630
4654
  } catch (error) {
4631
4655
  enqueueToolResult({ type: "error", error });
@@ -6050,7 +6074,7 @@ function convertToModelMessages(messages, options) {
6050
6074
  messages = messages.map((message) => ({
6051
6075
  ...message,
6052
6076
  parts: message.parts.filter(
6053
- (part) => !isToolOrDynamicToolUIPart(part) || part.state !== "input-streaming" && part.state !== "input-available"
6077
+ (part) => !isToolOrDynamicToolUIPart(part) || part.state !== "input-streaming" && part.state !== "input-available" && (part.state !== "output-available" || part.preliminary !== true)
6054
6078
  )
6055
6079
  }));
6056
6080
  }
@@ -7619,6 +7643,12 @@ function simulateReadableStream({
7619
7643
 
7620
7644
  // src/generate-object/stream-object.ts
7621
7645
  var originalGenerateId4 = (0, import_provider_utils23.createIdGenerator)({ prefix: "aiobj", size: 24 });
7646
+ async function markPromiseAsHandled(promise) {
7647
+ try {
7648
+ await promise;
7649
+ } catch (e) {
7650
+ }
7651
+ }
7622
7652
  function streamObject(options) {
7623
7653
  const {
7624
7654
  model,
@@ -7855,6 +7885,7 @@ var DefaultStreamObjectResult = class {
7855
7885
  let providerMetadata;
7856
7886
  let object2;
7857
7887
  let error;
7888
+ let terminalError;
7858
7889
  let accumulatedText = "";
7859
7890
  let textDelta = "";
7860
7891
  let fullResponse = {
@@ -7925,11 +7956,22 @@ var DefaultStreamObjectResult = class {
7925
7956
  };
7926
7957
  break;
7927
7958
  }
7959
+ case "error": {
7960
+ if (terminalError === void 0) {
7961
+ const wrappedError = wrapGatewayError(chunk.error);
7962
+ terminalError = { error: wrappedError };
7963
+ error = wrappedError;
7964
+ finishReason = "error";
7965
+ self.rejectResultPromises(wrappedError);
7966
+ }
7967
+ controller.enqueue(chunk);
7968
+ break;
7969
+ }
7928
7970
  case "finish": {
7929
7971
  if (textDelta !== "") {
7930
7972
  controller.enqueue({ type: "text-delta", textDelta });
7931
7973
  }
7932
- finishReason = chunk.finishReason;
7974
+ finishReason = terminalError === void 0 ? chunk.finishReason : "error";
7933
7975
  usage = chunk.usage;
7934
7976
  providerMetadata = chunk.providerMetadata;
7935
7977
  controller.enqueue({
@@ -7938,6 +7980,9 @@ var DefaultStreamObjectResult = class {
7938
7980
  response: fullResponse
7939
7981
  });
7940
7982
  logWarnings(warnings != null ? warnings : []);
7983
+ if (terminalError !== void 0) {
7984
+ break;
7985
+ }
7941
7986
  self._usage.resolve(usage);
7942
7987
  self._providerMetadata.resolve(providerMetadata);
7943
7988
  self._warnings.resolve(warnings);
@@ -8040,9 +8085,16 @@ var DefaultStreamObjectResult = class {
8040
8085
  }
8041
8086
  })
8042
8087
  );
8043
- stitchableStream.addStream(transformedStream);
8088
+ stitchableStream.addStream(transformedStream, {
8089
+ onError(error2) {
8090
+ const wrappedError = wrapGatewayError(error2);
8091
+ self.rejectResultPromises(wrappedError);
8092
+ void onError({ error: wrappedError });
8093
+ }
8094
+ });
8044
8095
  }
8045
8096
  }).catch((error) => {
8097
+ self.rejectResultPromises(error);
8046
8098
  stitchableStream.addStream(
8047
8099
  new ReadableStream({
8048
8100
  start(controller) {
@@ -8056,6 +8108,24 @@ var DefaultStreamObjectResult = class {
8056
8108
  });
8057
8109
  this.outputStrategy = outputStrategy;
8058
8110
  }
8111
+ rejectResultPromises(error) {
8112
+ this.rejectResultPromise({ delayedPromise: this._object, error });
8113
+ this.rejectResultPromise({ delayedPromise: this._usage, error });
8114
+ this.rejectResultPromise({ delayedPromise: this._providerMetadata, error });
8115
+ this.rejectResultPromise({ delayedPromise: this._warnings, error });
8116
+ this.rejectResultPromise({ delayedPromise: this._request, error });
8117
+ this.rejectResultPromise({ delayedPromise: this._response, error });
8118
+ this.rejectResultPromise({ delayedPromise: this._finishReason, error });
8119
+ }
8120
+ rejectResultPromise({
8121
+ delayedPromise,
8122
+ error
8123
+ }) {
8124
+ if (delayedPromise.isPending()) {
8125
+ delayedPromise.reject(error);
8126
+ markPromiseAsHandled(delayedPromise.promise);
8127
+ }
8128
+ }
8059
8129
  get object() {
8060
8130
  return this._object.promise;
8061
8131
  }