@ai-sdk/google 4.0.102 → 4.0.104

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/google",
3
- "version": "4.0.102",
3
+ "version": "4.0.104",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -35,8 +35,8 @@
35
35
  }
36
36
  },
37
37
  "dependencies": {
38
- "@ai-sdk/provider": "4.0.31",
39
- "@ai-sdk/provider-utils": "5.0.65"
38
+ "@ai-sdk/provider": "4.0.33",
39
+ "@ai-sdk/provider-utils": "5.0.67"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@ai-sdk/test-server": "2.0.4",
@@ -1,5 +1,9 @@
1
1
  import type { ParseResult } from '@ai-sdk/provider-utils';
2
2
 
3
+ export type GoogleStreamPart<T> =
4
+ | ParseResult<T>
5
+ | { type: 'continuation-start' };
6
+
3
7
  /**
4
8
  * Concatenates the event streams of a long decode response that spans several
5
9
  * requests (Gemini 4 long decode).
@@ -7,7 +11,9 @@ import type { ParseResult } from '@ai-sdk/provider-utils';
7
11
  * Events of the current stream are passed through unchanged. When an event
8
12
  * signals that the per-request cap was reached while budget remains
9
13
  * (`getContinuationToken` returns a token), the next request is sent with that
10
- * token once the current stream ends, and its events are appended. If the
14
+ * token once the current stream ends, and its events are appended after a
15
+ * `continuation-start` marker. The marker orders per-response state changes
16
+ * with buffered events, including when the new stream is empty. If the
11
17
  * continuation request fails, the error is emitted as a failed parse result
12
18
  * and the stream ends.
13
19
  */
@@ -21,11 +27,11 @@ export function continueGoogleStream<T>({
21
27
  continueStream: (
22
28
  continuationToken: string,
23
29
  ) => Promise<ReadableStream<ParseResult<T>>>;
24
- }): ReadableStream<ParseResult<T>> {
30
+ }): ReadableStream<GoogleStreamPart<T>> {
25
31
  let reader = stream.getReader();
26
32
  let pendingContinuationToken: string | undefined;
27
33
 
28
- return new ReadableStream<ParseResult<T>>({
34
+ return new ReadableStream<GoogleStreamPart<T>>({
29
35
  async pull(controller) {
30
36
  while (true) {
31
37
  const { done, value } = await reader.read();
@@ -51,6 +57,8 @@ export function continueGoogleStream<T>({
51
57
 
52
58
  try {
53
59
  reader = (await continueStream(continuationToken)).getReader();
60
+ controller.enqueue({ type: 'continuation-start' });
61
+ return;
54
62
  } catch (error) {
55
63
  // Surfaced as an `error` stream part, followed by the `finish` part
56
64
  // with the last continuation token so the caller can resume.
@@ -33,11 +33,13 @@ import {
33
33
  zodSchema,
34
34
  type FetchFunction,
35
35
  type InferSchema,
36
- type ParseResult,
37
36
  type Resolvable,
38
37
  } from '@ai-sdk/provider-utils';
39
38
  import { z } from 'zod/v4';
40
- import { continueGoogleStream } from './continue-google-stream';
39
+ import {
40
+ continueGoogleStream,
41
+ type GoogleStreamPart,
42
+ } from './continue-google-stream';
41
43
  import {
42
44
  addGoogleUsageMetadata,
43
45
  convertGoogleUsage,
@@ -926,8 +928,9 @@ export class GoogleLanguageModel implements LanguageModelV4 {
926
928
  },
927
929
  });
928
930
 
931
+ // A clean end without the provider's completion signal is interrupted.
929
932
  let finishReason: LanguageModelV4FinishReason = {
930
- unified: 'other',
933
+ unified: 'interrupted',
931
934
  raw: undefined,
932
935
  };
933
936
  let usage: GoogleUsageMetadata | undefined = undefined;
@@ -1005,7 +1008,7 @@ export class GoogleLanguageModel implements LanguageModelV4 {
1005
1008
  const result = {
1006
1009
  stream: response.pipeThrough(
1007
1010
  new TransformStream<
1008
- ParseResult<ChunkSchema>,
1011
+ GoogleStreamPart<ChunkSchema>,
1009
1012
  LanguageModelV4StreamPart
1010
1013
  >({
1011
1014
  start(controller) {
@@ -1013,11 +1016,21 @@ export class GoogleLanguageModel implements LanguageModelV4 {
1013
1016
  },
1014
1017
 
1015
1018
  transform(chunk, controller) {
1019
+ if ('type' in chunk) {
1020
+ // Reset in stream order, after the prior response's buffered
1021
+ // CONTINUATION chunk, rather than when the request starts.
1022
+ finishReason = { unified: 'interrupted', raw: undefined };
1023
+ return;
1024
+ }
1025
+
1016
1026
  if (options.includeRawChunks) {
1017
1027
  controller.enqueue({ type: 'raw', rawValue: chunk.rawValue });
1018
1028
  }
1019
1029
 
1020
1030
  if (!chunk.success) {
1031
+ if (finishReason.unified === 'interrupted') {
1032
+ finishReason = { unified: 'error', raw: undefined };
1033
+ }
1021
1034
  controller.enqueue({ type: 'error', error: chunk.error });
1022
1035
  return;
1023
1036
  }
@@ -152,6 +152,7 @@ export function buildGoogleInteractionsStreamTransform({
152
152
  let serviceTier: string | undefined = headerServiceTier;
153
153
  let finishStatus: GoogleInteractionsStatus | string | undefined;
154
154
  let hasFunctionCall = false;
155
+ let hasCompletionEvent = false;
155
156
 
156
157
  /*
157
158
  * Per-index open step slots. The Interactions API frames concurrent steps
@@ -838,6 +839,7 @@ export function buildGoogleInteractionsStreamTransform({
838
839
  }
839
840
 
840
841
  case 'interaction.completed': {
842
+ hasCompletionEvent = true;
841
843
  const event = value as Extract<
842
844
  GoogleInteractionsEvent,
843
845
  { event_type: 'interaction.completed' }
@@ -893,12 +895,17 @@ export function buildGoogleInteractionsStreamTransform({
893
895
  },
894
896
 
895
897
  flush(controller) {
898
+ const isInterrupted =
899
+ !hasCompletionEvent &&
900
+ (finishStatus == null || finishStatus === 'in_progress');
896
901
  const finishReason: LanguageModelV4FinishReason = {
897
- unified: mapGoogleInteractionsFinishReason({
898
- status: finishStatus,
899
- hasFunctionCall,
900
- }),
901
- raw: finishStatus,
902
+ unified: isInterrupted
903
+ ? 'interrupted'
904
+ : mapGoogleInteractionsFinishReason({
905
+ status: finishStatus,
906
+ hasFunctionCall,
907
+ }),
908
+ raw: isInterrupted ? undefined : finishStatus,
902
909
  };
903
910
 
904
911
  const outputTokensByModality =