@ai-sdk/google 4.0.109 → 4.0.110
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 +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +55 -10
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +54 -9
- package/dist/internal/index.js.map +1 -1
- package/docs/15-google.mdx +14 -10
- package/package.json +2 -2
- package/src/convert-to-google-messages.ts +61 -7
- package/src/google-error.ts +37 -0
- package/src/google-language-model.ts +10 -6
package/docs/15-google.mdx
CHANGED
|
@@ -486,12 +486,12 @@ Things to keep in mind:
|
|
|
486
486
|
|
|
487
487
|
#### Long decode with the Interactions API
|
|
488
488
|
|
|
489
|
-
With the [Interactions API](#interactions-api),
|
|
490
|
-
`background: true
|
|
491
|
-
continuation is needed, and `usage` covers all requests. Set an
|
|
492
|
-
`maxOutputTokens` above 65,536
|
|
493
|
-
|
|
494
|
-
|
|
489
|
+
With the [Interactions API](#interactions-api), the AI SDK currently requires
|
|
490
|
+
`background: true` for long decode. The server runs every request itself, so no
|
|
491
|
+
client-side continuation is needed, and `usage` covers all requests. Set an
|
|
492
|
+
explicit `maxOutputTokens` above 65,536 and keep `store` enabled (the default).
|
|
493
|
+
The provider polls the interaction (or streams it, reconnecting when the
|
|
494
|
+
connection drops) until it finishes.
|
|
495
495
|
|
|
496
496
|
```ts
|
|
497
497
|
import { google } from '@ai-sdk/google';
|
|
@@ -510,10 +510,14 @@ const result = await generateText({
|
|
|
510
510
|
});
|
|
511
511
|
```
|
|
512
512
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
513
|
+
Foreground client-side continuation is not yet supported by the AI SDK.
|
|
514
|
+
If you do not set `maxOutputTokens`, non-streaming calls (`generateText`)
|
|
515
|
+
default to 32,768 tokens and streaming calls (`streamText`) to 65,536 tokens.
|
|
516
|
+
An interaction that reaches its limit finishes with `length`. Long decode is
|
|
517
|
+
not available with the Vertex AI Interactions API.
|
|
518
|
+
|
|
519
|
+
The token budget includes both thinking and output tokens. To retain the previous
|
|
520
|
+
64K budget for a non-streaming call, set `maxOutputTokens: 65_536` explicitly.
|
|
517
521
|
|
|
518
522
|
### File Inputs
|
|
519
523
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/google",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.110",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@ai-sdk/provider": "4.0.35",
|
|
39
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
39
|
+
"@ai-sdk/provider-utils": "5.0.73"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@ai-sdk/test-server": "2.0.4",
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type SharedV4Warning,
|
|
7
7
|
} from '@ai-sdk/provider';
|
|
8
8
|
import {
|
|
9
|
+
stringifyWithBigIntAndCycles,
|
|
9
10
|
convertToBase64,
|
|
10
11
|
getTopLevelMediaType,
|
|
11
12
|
isFullMediaType,
|
|
@@ -67,18 +68,26 @@ function convertUrlToolResultPart(
|
|
|
67
68
|
};
|
|
68
69
|
}
|
|
69
70
|
|
|
70
|
-
function containsJSONSchemaReference(
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
function containsJSONSchemaReference(
|
|
72
|
+
value: JSONValue | undefined,
|
|
73
|
+
seen: WeakSet<object> = new WeakSet(),
|
|
74
|
+
): boolean {
|
|
75
|
+
if (value === null || typeof value !== 'object') {
|
|
76
|
+
return false;
|
|
73
77
|
}
|
|
74
78
|
|
|
75
|
-
if (
|
|
79
|
+
if (seen.has(value)) {
|
|
76
80
|
return false;
|
|
77
81
|
}
|
|
82
|
+
seen.add(value);
|
|
83
|
+
|
|
84
|
+
if (Array.isArray(value)) {
|
|
85
|
+
return value.some(item => containsJSONSchemaReference(item, seen));
|
|
86
|
+
}
|
|
78
87
|
|
|
79
88
|
return Object.entries(value).some(
|
|
80
89
|
([key, nestedValue]) =>
|
|
81
|
-
key === '$ref' || containsJSONSchemaReference(nestedValue),
|
|
90
|
+
key === '$ref' || containsJSONSchemaReference(nestedValue, seen),
|
|
82
91
|
);
|
|
83
92
|
}
|
|
84
93
|
|
|
@@ -88,7 +97,41 @@ function serializeFunctionResponseContent(
|
|
|
88
97
|
// Google reserves { $ref: displayName } in structured function responses for
|
|
89
98
|
// multimodal parts. This conflicts with JSON Schema $ref, so serialize the
|
|
90
99
|
// result to preserve it without triggering Google's reference handling.
|
|
91
|
-
|
|
100
|
+
if (containsJSONSchemaReference(value)) {
|
|
101
|
+
return stringifyWithBigIntAndCycles(value);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Keep Google's structured response for ordinary JSON, but normalize values
|
|
105
|
+
// that would otherwise fail when the final request body is serialized.
|
|
106
|
+
return containsNonJsonValue(value)
|
|
107
|
+
? secureJsonParse(stringifyWithBigIntAndCycles(value))
|
|
108
|
+
: value;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function containsNonJsonValue(
|
|
112
|
+
value: unknown,
|
|
113
|
+
ancestors = new Set<object>(),
|
|
114
|
+
): boolean {
|
|
115
|
+
if (typeof value === 'bigint') {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
if (typeof value !== 'object' || value === null) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
if (typeof (value as { toJSON?: unknown }).toJSON === 'function') {
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
if (ancestors.has(value)) {
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
ancestors.add(value);
|
|
128
|
+
try {
|
|
129
|
+
return Object.values(value).some(item =>
|
|
130
|
+
containsNonJsonValue(item, ancestors),
|
|
131
|
+
);
|
|
132
|
+
} finally {
|
|
133
|
+
ancestors.delete(value);
|
|
134
|
+
}
|
|
92
135
|
}
|
|
93
136
|
|
|
94
137
|
/*
|
|
@@ -586,7 +629,7 @@ export function convertToGoogleMessages(
|
|
|
586
629
|
? { id: part.toolCallId }
|
|
587
630
|
: {}),
|
|
588
631
|
name: part.toolName,
|
|
589
|
-
args: part.input,
|
|
632
|
+
args: parseToolCallInput(part.input),
|
|
590
633
|
},
|
|
591
634
|
thoughtSignature: effectiveThoughtSignature,
|
|
592
635
|
};
|
|
@@ -775,3 +818,14 @@ export function convertToGoogleMessages(
|
|
|
775
818
|
contents,
|
|
776
819
|
};
|
|
777
820
|
}
|
|
821
|
+
|
|
822
|
+
function parseToolCallInput(input: unknown): unknown {
|
|
823
|
+
if (typeof input !== 'string') {
|
|
824
|
+
return input;
|
|
825
|
+
}
|
|
826
|
+
try {
|
|
827
|
+
return secureJsonParse(input);
|
|
828
|
+
} catch {
|
|
829
|
+
return input;
|
|
830
|
+
}
|
|
831
|
+
}
|
package/src/google-error.ts
CHANGED
|
@@ -28,3 +28,40 @@ export const googleFailedResponseHandler = createJsonErrorResponseHandler({
|
|
|
28
28
|
errorSchema: googleErrorDataSchema,
|
|
29
29
|
errorToMessage: data => data.error.message,
|
|
30
30
|
});
|
|
31
|
+
|
|
32
|
+
// Google RPC status names can be sent without a numeric HTTP error code.
|
|
33
|
+
export function mapGoogleErrorStatusToHttpStatusCode(
|
|
34
|
+
status: string,
|
|
35
|
+
): number | undefined {
|
|
36
|
+
switch (status) {
|
|
37
|
+
case 'CANCELLED':
|
|
38
|
+
return 499;
|
|
39
|
+
case 'INVALID_ARGUMENT':
|
|
40
|
+
case 'FAILED_PRECONDITION':
|
|
41
|
+
case 'OUT_OF_RANGE':
|
|
42
|
+
return 400;
|
|
43
|
+
case 'DEADLINE_EXCEEDED':
|
|
44
|
+
return 504;
|
|
45
|
+
case 'NOT_FOUND':
|
|
46
|
+
return 404;
|
|
47
|
+
case 'ALREADY_EXISTS':
|
|
48
|
+
case 'ABORTED':
|
|
49
|
+
return 409;
|
|
50
|
+
case 'PERMISSION_DENIED':
|
|
51
|
+
return 403;
|
|
52
|
+
case 'UNAUTHENTICATED':
|
|
53
|
+
return 401;
|
|
54
|
+
case 'RESOURCE_EXHAUSTED':
|
|
55
|
+
return 429;
|
|
56
|
+
case 'UNIMPLEMENTED':
|
|
57
|
+
return 501;
|
|
58
|
+
case 'UNKNOWN':
|
|
59
|
+
case 'INTERNAL':
|
|
60
|
+
case 'DATA_LOSS':
|
|
61
|
+
return 500;
|
|
62
|
+
case 'UNAVAILABLE':
|
|
63
|
+
return 503;
|
|
64
|
+
default:
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -51,6 +51,7 @@ import { getModelPath } from './get-model-path';
|
|
|
51
51
|
import {
|
|
52
52
|
getGoogleErrorSchema,
|
|
53
53
|
googleFailedResponseHandler,
|
|
54
|
+
mapGoogleErrorStatusToHttpStatusCode,
|
|
54
55
|
} from './google-error';
|
|
55
56
|
import { sanitizeResponseJsonSchema } from './sanitize-response-json-schema';
|
|
56
57
|
import {
|
|
@@ -1040,19 +1041,22 @@ export class GoogleLanguageModel implements LanguageModelV4 {
|
|
|
1040
1041
|
if (value.error != null) {
|
|
1041
1042
|
finishReason = { unified: 'error', raw: undefined };
|
|
1042
1043
|
const error = value.error;
|
|
1044
|
+
const statusCode =
|
|
1045
|
+
error.code ??
|
|
1046
|
+
mapGoogleErrorStatusToHttpStatusCode(error.status);
|
|
1043
1047
|
controller.enqueue({
|
|
1044
1048
|
type: 'error',
|
|
1045
1049
|
error: createProviderStreamError({
|
|
1046
1050
|
message: error.message,
|
|
1047
1051
|
type: error.status,
|
|
1048
1052
|
code: error.code ?? undefined,
|
|
1049
|
-
statusCode
|
|
1053
|
+
statusCode,
|
|
1050
1054
|
isRetryable:
|
|
1051
|
-
|
|
1052
|
-
(
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1055
|
+
statusCode != null &&
|
|
1056
|
+
(statusCode === 408 ||
|
|
1057
|
+
statusCode === 409 ||
|
|
1058
|
+
statusCode === 429 ||
|
|
1059
|
+
statusCode >= 500),
|
|
1056
1060
|
data: error,
|
|
1057
1061
|
}),
|
|
1058
1062
|
});
|