@ai-sdk/google 3.0.140 → 3.0.142
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 +18 -0
- package/dist/index.js +130 -38
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +126 -31
- package/dist/index.mjs.map +1 -1
- package/dist/internal/index.js +122 -30
- package/dist/internal/index.js.map +1 -1
- package/dist/internal/index.mjs +125 -30
- package/dist/internal/index.mjs.map +1 -1
- package/docs/15-google-generative-ai.mdx +14 -10
- package/package.json +2 -2
- package/src/convert-to-google-generative-ai-messages.ts +54 -5
- package/src/google-error.ts +46 -6
- package/src/google-generative-ai-language-model.ts +43 -16
- package/src/google-prepare-tools.ts +7 -0
- package/src/interactions/prepare-google-interactions-tools.ts +7 -4
|
@@ -447,12 +447,12 @@ Things to keep in mind:
|
|
|
447
447
|
|
|
448
448
|
#### Long decode with the Interactions API
|
|
449
449
|
|
|
450
|
-
With the [Interactions API](#interactions-api),
|
|
451
|
-
`background: true
|
|
452
|
-
continuation is needed, and `usage` covers all requests. Set an
|
|
453
|
-
`maxOutputTokens` above 65,536
|
|
454
|
-
|
|
455
|
-
|
|
450
|
+
With the [Interactions API](#interactions-api), the AI SDK currently requires
|
|
451
|
+
`background: true` for long decode. The server runs every request itself, so no
|
|
452
|
+
client-side continuation is needed, and `usage` covers all requests. Set an
|
|
453
|
+
explicit `maxOutputTokens` above 65,536 and keep `store` enabled (the default).
|
|
454
|
+
The provider polls the interaction (or streams it, reconnecting when the
|
|
455
|
+
connection drops) until it finishes.
|
|
456
456
|
|
|
457
457
|
```ts
|
|
458
458
|
import { google } from '@ai-sdk/google';
|
|
@@ -471,10 +471,14 @@ const result = await generateText({
|
|
|
471
471
|
});
|
|
472
472
|
```
|
|
473
473
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
474
|
+
Foreground client-side continuation is not yet supported by the AI SDK.
|
|
475
|
+
If you do not set `maxOutputTokens`, non-streaming calls (`generateText`)
|
|
476
|
+
default to 32,768 tokens and streaming calls (`streamText`) to 65,536 tokens.
|
|
477
|
+
An interaction that reaches its limit finishes with `length`. Long decode is
|
|
478
|
+
not available with the Vertex AI Interactions API.
|
|
479
|
+
|
|
480
|
+
The token budget includes both thinking and output tokens. To retain the previous
|
|
481
|
+
64K budget for a non-streaming call, set `maxOutputTokens: 65_536` explicitly.
|
|
478
482
|
|
|
479
483
|
### File Inputs
|
|
480
484
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/google",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.142",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@ai-sdk/provider": "3.0.19",
|
|
40
|
-
"@ai-sdk/provider-utils": "4.0.
|
|
40
|
+
"@ai-sdk/provider-utils": "4.0.64"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "20.17.24",
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type JSONValue,
|
|
2
3
|
UnsupportedFunctionalityError,
|
|
3
4
|
type LanguageModelV3Prompt,
|
|
4
5
|
type SharedV3Warning,
|
|
5
6
|
} from '@ai-sdk/provider';
|
|
6
7
|
import {
|
|
8
|
+
stringifyWithBigIntAndCycles,
|
|
7
9
|
convertToBase64,
|
|
8
10
|
isUrlSupported,
|
|
9
11
|
secureJsonParse,
|
|
@@ -105,6 +107,42 @@ function convertUrlToolResultPart(
|
|
|
105
107
|
: undefined;
|
|
106
108
|
}
|
|
107
109
|
|
|
110
|
+
function serializeFunctionResponseContent(
|
|
111
|
+
value: JSONValue,
|
|
112
|
+
): JSONValue | string {
|
|
113
|
+
// Keep Google's structured response for ordinary JSON, but normalize values
|
|
114
|
+
// that would otherwise fail when the final request body is serialized.
|
|
115
|
+
return containsNonJsonValue(value)
|
|
116
|
+
? secureJsonParse(stringifyWithBigIntAndCycles(value))
|
|
117
|
+
: value;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function containsNonJsonValue(
|
|
121
|
+
value: unknown,
|
|
122
|
+
ancestors = new Set<object>(),
|
|
123
|
+
): boolean {
|
|
124
|
+
if (typeof value === 'bigint') {
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
if (typeof value !== 'object' || value === null) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
if (typeof (value as { toJSON?: unknown }).toJSON === 'function') {
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
if (ancestors.has(value)) {
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
ancestors.add(value);
|
|
137
|
+
try {
|
|
138
|
+
return Object.values(value).some(item =>
|
|
139
|
+
containsNonJsonValue(item, ancestors),
|
|
140
|
+
);
|
|
141
|
+
} finally {
|
|
142
|
+
ancestors.delete(value);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
108
146
|
/*
|
|
109
147
|
* Appends tool result content parts to the message using the functionResponse
|
|
110
148
|
* format with support for multimodal parts (e.g. inline images/files alongside
|
|
@@ -151,12 +189,12 @@ function appendToolResultParts(
|
|
|
151
189
|
if (functionResponsePart != null) {
|
|
152
190
|
functionResponseParts.push(functionResponsePart);
|
|
153
191
|
} else {
|
|
154
|
-
responseTextParts.push(
|
|
192
|
+
responseTextParts.push(stringifyWithBigIntAndCycles(contentPart));
|
|
155
193
|
}
|
|
156
194
|
break;
|
|
157
195
|
}
|
|
158
196
|
default: {
|
|
159
|
-
responseTextParts.push(
|
|
197
|
+
responseTextParts.push(stringifyWithBigIntAndCycles(contentPart));
|
|
160
198
|
break;
|
|
161
199
|
}
|
|
162
200
|
}
|
|
@@ -234,7 +272,7 @@ function appendLegacyToolResultParts(
|
|
|
234
272
|
break;
|
|
235
273
|
}
|
|
236
274
|
default:
|
|
237
|
-
parts.push({ text:
|
|
275
|
+
parts.push({ text: stringifyWithBigIntAndCycles(contentPart) });
|
|
238
276
|
break;
|
|
239
277
|
}
|
|
240
278
|
}
|
|
@@ -462,7 +500,7 @@ export function convertToGoogleGenerativeAIMessages(
|
|
|
462
500
|
? { id: part.toolCallId }
|
|
463
501
|
: {}),
|
|
464
502
|
name: part.toolName,
|
|
465
|
-
args: part.input,
|
|
503
|
+
args: parseToolCallInput(part.input),
|
|
466
504
|
},
|
|
467
505
|
thoughtSignature: effectiveThoughtSignature,
|
|
468
506
|
};
|
|
@@ -579,7 +617,7 @@ export function convertToGoogleGenerativeAIMessages(
|
|
|
579
617
|
content:
|
|
580
618
|
output.type === 'execution-denied'
|
|
581
619
|
? (output.reason ?? 'Tool call execution denied.')
|
|
582
|
-
: output.value,
|
|
620
|
+
: serializeFunctionResponseContent(output.value),
|
|
583
621
|
},
|
|
584
622
|
},
|
|
585
623
|
});
|
|
@@ -633,3 +671,14 @@ export function convertToGoogleGenerativeAIMessages(
|
|
|
633
671
|
contents,
|
|
634
672
|
};
|
|
635
673
|
}
|
|
674
|
+
|
|
675
|
+
function parseToolCallInput(input: unknown): unknown {
|
|
676
|
+
if (typeof input !== 'string') {
|
|
677
|
+
return input;
|
|
678
|
+
}
|
|
679
|
+
try {
|
|
680
|
+
return secureJsonParse(input);
|
|
681
|
+
} catch {
|
|
682
|
+
return input;
|
|
683
|
+
}
|
|
684
|
+
}
|
package/src/google-error.ts
CHANGED
|
@@ -6,15 +6,18 @@ import {
|
|
|
6
6
|
} from '@ai-sdk/provider-utils';
|
|
7
7
|
import { z } from 'zod/v4';
|
|
8
8
|
|
|
9
|
+
export const getGoogleErrorSchema = () =>
|
|
10
|
+
z.object({
|
|
11
|
+
code: z.number().nullable(),
|
|
12
|
+
message: z.string(),
|
|
13
|
+
status: z.string(),
|
|
14
|
+
details: z.array(z.unknown()).nullish(),
|
|
15
|
+
});
|
|
16
|
+
|
|
9
17
|
const googleErrorDataSchema = lazySchema(() =>
|
|
10
18
|
zodSchema(
|
|
11
19
|
z.object({
|
|
12
|
-
error:
|
|
13
|
-
code: z.number().nullable(),
|
|
14
|
-
message: z.string(),
|
|
15
|
-
status: z.string(),
|
|
16
|
-
details: z.array(z.unknown()).nullish(),
|
|
17
|
-
}),
|
|
20
|
+
error: getGoogleErrorSchema(),
|
|
18
21
|
}),
|
|
19
22
|
),
|
|
20
23
|
);
|
|
@@ -25,3 +28,40 @@ export const googleFailedResponseHandler = createJsonErrorResponseHandler({
|
|
|
25
28
|
errorSchema: googleErrorDataSchema,
|
|
26
29
|
errorToMessage: data => data.error.message,
|
|
27
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
|
+
}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
import {
|
|
2
|
+
APICallError,
|
|
3
|
+
type LanguageModelV3,
|
|
4
|
+
type LanguageModelV3CallOptions,
|
|
5
|
+
type LanguageModelV3Content,
|
|
6
|
+
type LanguageModelV3FinishReason,
|
|
7
|
+
type LanguageModelV3GenerateResult,
|
|
8
|
+
type LanguageModelV3Source,
|
|
9
|
+
type LanguageModelV3StreamPart,
|
|
10
|
+
type LanguageModelV3StreamResult,
|
|
11
|
+
type JSONObject,
|
|
12
|
+
type SharedV3ProviderMetadata,
|
|
13
|
+
type SharedV3Warning,
|
|
13
14
|
} from '@ai-sdk/provider';
|
|
14
15
|
import {
|
|
15
16
|
combineHeaders,
|
|
@@ -37,7 +38,11 @@ import { convertJSONSchemaToOpenAPISchema } from './convert-json-schema-to-opena
|
|
|
37
38
|
import { convertToGoogleGenerativeAIMessages } from './convert-to-google-generative-ai-messages';
|
|
38
39
|
import { downloadToolResultFiles } from './download-tool-result-files';
|
|
39
40
|
import { getModelPath } from './get-model-path';
|
|
40
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
getGoogleErrorSchema,
|
|
43
|
+
googleFailedResponseHandler,
|
|
44
|
+
mapGoogleErrorStatusToHttpStatusCode,
|
|
45
|
+
} from './google-error';
|
|
41
46
|
import {
|
|
42
47
|
googleLanguageModelOptions,
|
|
43
48
|
type GoogleGenerativeAIModelId,
|
|
@@ -684,11 +689,12 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
684
689
|
extraHeaders,
|
|
685
690
|
);
|
|
686
691
|
|
|
692
|
+
const url = `${this.config.baseURL}/${getModelPath(
|
|
693
|
+
this.modelId,
|
|
694
|
+
)}:streamGenerateContent?alt=sse`;
|
|
687
695
|
const streamGenerateContent = (body: typeof args) =>
|
|
688
696
|
postJsonToApi({
|
|
689
|
-
url
|
|
690
|
-
this.modelId,
|
|
691
|
-
)}:streamGenerateContent?alt=sse`,
|
|
697
|
+
url,
|
|
692
698
|
headers,
|
|
693
699
|
body,
|
|
694
700
|
failedResponseHandler: googleFailedResponseHandler,
|
|
@@ -829,6 +835,26 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
829
835
|
|
|
830
836
|
const value = chunk.value;
|
|
831
837
|
|
|
838
|
+
if (value.error != null) {
|
|
839
|
+
finishReason = { unified: 'error', raw: undefined };
|
|
840
|
+
const error = value.error;
|
|
841
|
+
controller.enqueue({
|
|
842
|
+
type: 'error',
|
|
843
|
+
error: new APICallError({
|
|
844
|
+
message: error.message,
|
|
845
|
+
url,
|
|
846
|
+
requestBodyValues: args,
|
|
847
|
+
statusCode:
|
|
848
|
+
error.code ??
|
|
849
|
+
mapGoogleErrorStatusToHttpStatusCode(error.status),
|
|
850
|
+
responseHeaders,
|
|
851
|
+
responseBody: JSON.stringify({ error }),
|
|
852
|
+
data: error,
|
|
853
|
+
}),
|
|
854
|
+
});
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
|
|
832
858
|
if (!hasEmittedResponseMetadata && value.responseId != null) {
|
|
833
859
|
hasEmittedResponseMetadata = true;
|
|
834
860
|
controller.enqueue({
|
|
@@ -1768,6 +1794,7 @@ export type UsageMetadataSchema = NonNullable<
|
|
|
1768
1794
|
const chunkSchema = lazySchema(() =>
|
|
1769
1795
|
zodSchema(
|
|
1770
1796
|
z.object({
|
|
1797
|
+
error: getGoogleErrorSchema().nullish(),
|
|
1771
1798
|
responseId: z.string().nullish(),
|
|
1772
1799
|
candidates: z
|
|
1773
1800
|
.array(
|
|
@@ -53,6 +53,13 @@ export function prepareTools({
|
|
|
53
53
|
};
|
|
54
54
|
toolWarnings: SharedV3Warning[];
|
|
55
55
|
} {
|
|
56
|
+
// NONE only disables function calling in the Google API. Provider tools
|
|
57
|
+
// must be omitted so they cannot execute server-side. Keep function
|
|
58
|
+
// declarations for conversation history containing function calls.
|
|
59
|
+
if (toolChoice?.type === 'none') {
|
|
60
|
+
tools = tools?.filter(tool => tool.type === 'function');
|
|
61
|
+
}
|
|
62
|
+
|
|
56
63
|
// when the tools array is empty, change it to undefined to prevent errors:
|
|
57
64
|
tools = tools?.length ? tools : undefined;
|
|
58
65
|
|
|
@@ -39,7 +39,7 @@ export type PrepareGoogleInteractionsToolsResult = {
|
|
|
39
39
|
* `toolChoice` shapes:
|
|
40
40
|
* - `'auto'` -> `'auto'`
|
|
41
41
|
* - `'required'` -> `'any'`
|
|
42
|
-
* - `'none'` -> `
|
|
42
|
+
* - `'none'` -> omit all tools and `tool_choice`
|
|
43
43
|
* - `{ type: 'tool', toolName }` -> `{ allowed_tools: { mode: 'validated', tools: [name] } }`
|
|
44
44
|
* (Interactions `AllowedTools.tools` is an `Array<string>` of function
|
|
45
45
|
* names, not tool descriptors -- see `src/interactions/resources/interactions.ts`
|
|
@@ -54,6 +54,12 @@ export function prepareGoogleInteractionsTools({
|
|
|
54
54
|
}): PrepareGoogleInteractionsToolsResult {
|
|
55
55
|
const toolWarnings: Array<SharedV3Warning> = [];
|
|
56
56
|
|
|
57
|
+
// The API's tool_choice only controls function tools. Omit all tools to
|
|
58
|
+
// disable provider-executed tools as well.
|
|
59
|
+
if (toolChoice?.type === 'none') {
|
|
60
|
+
return { tools: undefined, toolChoice: undefined, toolWarnings };
|
|
61
|
+
}
|
|
62
|
+
|
|
57
63
|
const normalized = tools?.length ? tools : undefined;
|
|
58
64
|
|
|
59
65
|
if (normalized == null) {
|
|
@@ -223,9 +229,6 @@ export function prepareGoogleInteractionsTools({
|
|
|
223
229
|
case 'required':
|
|
224
230
|
mappedToolChoice = 'any';
|
|
225
231
|
break;
|
|
226
|
-
case 'none':
|
|
227
|
-
mappedToolChoice = 'none';
|
|
228
|
-
break;
|
|
229
232
|
case 'tool':
|
|
230
233
|
mappedToolChoice = {
|
|
231
234
|
allowed_tools: {
|