@ai-sdk/google 4.0.68 → 4.0.69

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.68",
3
+ "version": "4.0.69",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -0,0 +1,124 @@
1
+ import type {
2
+ LanguageModelV4Prompt,
3
+ LanguageModelV4ToolResultOutput,
4
+ } from '@ai-sdk/provider';
5
+ import {
6
+ detectMediaType,
7
+ downloadBlob,
8
+ isFullMediaType,
9
+ } from '@ai-sdk/provider-utils';
10
+
11
+ /**
12
+ * Vertex function responses only accept inline file data. Download remote tool
13
+ * result files before converting the prompt to the Google request format.
14
+ */
15
+ export async function downloadToolResultFiles(
16
+ prompt: LanguageModelV4Prompt,
17
+ {
18
+ abortSignal,
19
+ maxBytes,
20
+ }: {
21
+ abortSignal: AbortSignal | undefined;
22
+ maxBytes: number;
23
+ },
24
+ ): Promise<LanguageModelV4Prompt> {
25
+ const result: LanguageModelV4Prompt = [];
26
+
27
+ for (const message of prompt) {
28
+ if (message.role === 'assistant') {
29
+ const content: typeof message.content = [];
30
+
31
+ for (const part of message.content) {
32
+ content.push(
33
+ part.type === 'tool-result'
34
+ ? {
35
+ ...part,
36
+ output: await downloadToolResultOutput(part.output, {
37
+ abortSignal,
38
+ maxBytes,
39
+ }),
40
+ }
41
+ : part,
42
+ );
43
+ }
44
+
45
+ result.push({ ...message, content });
46
+ continue;
47
+ }
48
+
49
+ if (message.role === 'tool') {
50
+ const content: typeof message.content = [];
51
+
52
+ for (const part of message.content) {
53
+ if (part.type !== 'tool-result') {
54
+ content.push(part);
55
+ continue;
56
+ }
57
+
58
+ content.push({
59
+ ...part,
60
+ output: await downloadToolResultOutput(part.output, {
61
+ abortSignal,
62
+ maxBytes,
63
+ }),
64
+ });
65
+ }
66
+
67
+ result.push({ ...message, content });
68
+ continue;
69
+ }
70
+
71
+ result.push(message);
72
+ }
73
+
74
+ return result;
75
+ }
76
+
77
+ async function downloadToolResultOutput(
78
+ output: LanguageModelV4ToolResultOutput,
79
+ {
80
+ abortSignal,
81
+ maxBytes,
82
+ }: {
83
+ abortSignal: AbortSignal | undefined;
84
+ maxBytes: number;
85
+ },
86
+ ): Promise<LanguageModelV4ToolResultOutput> {
87
+ if (output.type !== 'content') {
88
+ return output;
89
+ }
90
+
91
+ const value: typeof output.value = [];
92
+
93
+ for (const part of output.value) {
94
+ if (part.type !== 'file' || part.data.type !== 'url') {
95
+ value.push(part);
96
+ continue;
97
+ }
98
+
99
+ const blob = await downloadBlob(part.data.url.toString(), {
100
+ abortSignal,
101
+ maxBytes,
102
+ });
103
+ const data = new Uint8Array(await blob.arrayBuffer());
104
+ const detectedMediaType = detectMediaType({
105
+ data,
106
+ topLevelType: 'image',
107
+ });
108
+
109
+ value.push({
110
+ ...part,
111
+ data: { type: 'data' as const, data },
112
+ mediaType:
113
+ detectedMediaType ??
114
+ (blob.type && !isFullMediaType(part.mediaType)
115
+ ? blob.type
116
+ : part.mediaType),
117
+ });
118
+ }
119
+
120
+ return {
121
+ ...output,
122
+ value,
123
+ };
124
+ }
@@ -40,6 +40,7 @@ import {
40
40
  } from './convert-google-usage';
41
41
  import { convertJSONSchemaToOpenAPISchema } from './convert-json-schema-to-openapi-schema';
42
42
  import { convertToGoogleMessages } from './convert-to-google-messages';
43
+ import { downloadToolResultFiles } from './download-tool-result-files';
43
44
  import { getModelPath } from './get-model-path';
44
45
  import { googleFailedResponseHandler } from './google-error';
45
46
  import {
@@ -76,6 +77,13 @@ export type GoogleLanguageModelConfig = {
76
77
  * The supported URLs for the model.
77
78
  */
78
79
  supportedUrls?: () => LanguageModelV4['supportedUrls'];
80
+
81
+ /**
82
+ * Settings for downloading remote files in tool results before conversion.
83
+ */
84
+ downloadToolResultFiles?: {
85
+ maxBytes: number;
86
+ };
79
87
  };
80
88
 
81
89
  export class GoogleLanguageModel implements LanguageModelV4 {
@@ -132,6 +140,7 @@ export class GoogleLanguageModel implements LanguageModelV4 {
132
140
  toolChoice,
133
141
  reasoning,
134
142
  providerOptions,
143
+ abortSignal,
135
144
  },
136
145
  isStreaming = false,
137
146
  }: {
@@ -287,14 +296,24 @@ export class GoogleLanguageModel implements LanguageModelV4 {
287
296
 
288
297
  const { usesGemini3Features } = getGoogleModelCapabilities(modelId);
289
298
 
290
- const { contents, systemInstruction } = convertToGoogleMessages(prompt, {
291
- isGemmaModel,
292
- isGemini3Model: usesGemini3Features,
293
- onWarning: warning => warnings.push(warning),
294
- providerOptionsNames,
295
- supportsFunctionResponseParts: usesGemini3Features,
296
- includeFunctionCallIds: !isVertexProvider,
297
- });
299
+ const promptWithDownloadedToolResultFiles = config.downloadToolResultFiles
300
+ ? await downloadToolResultFiles(prompt, {
301
+ abortSignal,
302
+ maxBytes: config.downloadToolResultFiles.maxBytes,
303
+ })
304
+ : prompt;
305
+
306
+ const { contents, systemInstruction } = convertToGoogleMessages(
307
+ promptWithDownloadedToolResultFiles,
308
+ {
309
+ isGemmaModel,
310
+ isGemini3Model: usesGemini3Features,
311
+ onWarning: warning => warnings.push(warning),
312
+ providerOptionsNames,
313
+ supportsFunctionResponseParts: usesGemini3Features,
314
+ includeFunctionCallIds: !isVertexProvider,
315
+ },
316
+ );
298
317
 
299
318
  const {
300
319
  tools: googleTools,