@ai-sdk/google 4.0.50 → 4.0.53

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.
@@ -34,6 +34,17 @@ interface GoogleFilesConfig {
34
34
  fetch?: FetchFunction;
35
35
  }
36
36
 
37
+ function encodePathSegment(value: string): string {
38
+ const encodedValue = encodeURIComponent(value);
39
+
40
+ // URL parsing normalizes both literal and percent-encoded dot segments.
41
+ return encodedValue === '.'
42
+ ? '%252E'
43
+ : encodedValue === '..'
44
+ ? '%252E%252E'
45
+ : encodedValue;
46
+ }
47
+
37
48
  export class GoogleFiles implements FilesV4 {
38
49
  readonly specificationVersion = 'v4';
39
50
 
@@ -106,7 +117,7 @@ export class GoogleFiles implements FilesV4 {
106
117
  'X-Goog-Upload-Offset': '0',
107
118
  'X-Goog-Upload-Command': 'upload, finalize',
108
119
  },
109
- body: fileBytes,
120
+ body: ensureArrayBufferBacked(fileBytes),
110
121
  });
111
122
 
112
123
  if (!uploadResponse.ok) {
@@ -137,8 +148,14 @@ export class GoogleFiles implements FilesV4 {
137
148
 
138
149
  await delay(pollIntervalMs);
139
150
 
151
+ const fileNameMatch = /^files\/([^/]+)$/.exec(file.name);
152
+ const filePath =
153
+ fileNameMatch != null
154
+ ? `files/${encodePathSegment(fileNameMatch[1])}`
155
+ : encodePathSegment(file.name);
156
+
140
157
  const { value: fileStatus } = await getFromApi({
141
- url: `${this.config.baseURL}/${file.name}`,
158
+ url: `${this.config.baseURL}/${filePath}`,
142
159
  validateUrl: false,
143
160
  headers: combineHeaders(resolvedHeaders),
144
161
  successfulResponseHandler: createJsonResponseHandler(
@@ -182,6 +199,14 @@ export class GoogleFiles implements FilesV4 {
182
199
  }
183
200
  }
184
201
 
202
+ function ensureArrayBufferBacked(data: Uint8Array): Uint8Array<ArrayBuffer> {
203
+ if (data.buffer instanceof ArrayBuffer) {
204
+ return data as Uint8Array<ArrayBuffer>;
205
+ }
206
+
207
+ return new Uint8Array(data);
208
+ }
209
+
185
210
  type GoogleFileResource = {
186
211
  name: string;
187
212
  displayName?: string | null;
@@ -62,7 +62,7 @@ const configurableSafetySettingCategories = [
62
62
  'HARM_CATEGORY_SEXUALLY_EXPLICIT',
63
63
  ] as const;
64
64
 
65
- type GoogleConfig = {
65
+ export type GoogleLanguageModelConfig = {
66
66
  provider: string;
67
67
  baseURL: string;
68
68
  headers?: Resolvable<Record<string, string | undefined>>;
@@ -80,7 +80,7 @@ export class GoogleLanguageModel implements LanguageModelV4 {
80
80
 
81
81
  readonly modelId: GoogleModelId;
82
82
 
83
- private readonly config: GoogleConfig;
83
+ private readonly config: GoogleLanguageModelConfig;
84
84
  private readonly generateId: () => string;
85
85
 
86
86
  static [WORKFLOW_SERIALIZE](model: GoogleLanguageModel) {
@@ -92,12 +92,12 @@ export class GoogleLanguageModel implements LanguageModelV4 {
92
92
 
93
93
  static [WORKFLOW_DESERIALIZE](options: {
94
94
  modelId: string;
95
- config: GoogleConfig;
95
+ config: GoogleLanguageModelConfig;
96
96
  }) {
97
97
  return new GoogleLanguageModel(options.modelId, options.config);
98
98
  }
99
99
 
100
- constructor(modelId: GoogleModelId, config: GoogleConfig) {
100
+ constructor(modelId: GoogleModelId, config: GoogleLanguageModelConfig) {
101
101
  this.modelId = modelId;
102
102
  this.config = config;
103
103
  this.generateId = config.generateId ?? generateId;
@@ -111,7 +111,7 @@ export class GoogleLanguageModel implements LanguageModelV4 {
111
111
  return this.config.supportedUrls?.() ?? {};
112
112
  }
113
113
 
114
- private async getArgs(
114
+ protected async getArgs(
115
115
  {
116
116
  prompt,
117
117
  maxOutputTokens,
@@ -375,38 +375,19 @@ export class GoogleLanguageModel implements LanguageModelV4 {
375
375
  };
376
376
  }
377
377
 
378
- async doGenerate(
379
- options: LanguageModelV4CallOptions,
380
- ): Promise<LanguageModelV4GenerateResult> {
381
- const { args, warnings, providerOptionsNames, extraHeaders } =
382
- await this.getArgs(options);
378
+ protected convertGenerateContentResponse({
379
+ response,
380
+ warnings,
381
+ providerOptionsNames,
382
+ }: {
383
+ response: InferSchema<typeof responseSchema>;
384
+ warnings: SharedV4Warning[];
385
+ providerOptionsNames: readonly string[];
386
+ }): LanguageModelV4GenerateResult {
383
387
  const wrapProviderMetadata = (payload: Record<string, unknown>) =>
384
388
  Object.fromEntries(
385
389
  providerOptionsNames.map(name => [name, payload]),
386
390
  ) as SharedV4ProviderMetadata;
387
-
388
- const mergedHeaders = combineHeaders(
389
- this.config.headers ? await resolve(this.config.headers) : undefined,
390
- options.headers,
391
- extraHeaders,
392
- );
393
-
394
- const {
395
- responseHeaders,
396
- value: response,
397
- rawValue: rawResponse,
398
- } = await postJsonToApi({
399
- url: `${this.config.baseURL}/${getModelPath(
400
- this.modelId,
401
- )}:generateContent`,
402
- headers: mergedHeaders,
403
- body: args,
404
- failedResponseHandler: googleFailedResponseHandler,
405
- successfulResponseHandler: createJsonResponseHandler(responseSchema),
406
- abortSignal: options.abortSignal,
407
- fetch: this.config.fetch,
408
- });
409
-
410
391
  const candidate = response.candidates[0];
411
392
  const content: Array<LanguageModelV4Content> = [];
412
393
 
@@ -566,10 +547,52 @@ export class GoogleLanguageModel implements LanguageModelV4 {
566
547
  finishMessage: candidate.finishMessage ?? null,
567
548
  serviceTier: usageMetadata?.serviceTier ?? null,
568
549
  } satisfies GoogleProviderMetadata),
569
- request: { body: args },
570
550
  response: {
571
551
  // TODO timestamp, model id
572
552
  id: response.responseId ?? undefined,
553
+ },
554
+ };
555
+ }
556
+
557
+ async doGenerate(
558
+ options: LanguageModelV4CallOptions,
559
+ ): Promise<LanguageModelV4GenerateResult> {
560
+ const { args, warnings, providerOptionsNames, extraHeaders } =
561
+ await this.getArgs(options);
562
+
563
+ const mergedHeaders = combineHeaders(
564
+ this.config.headers ? await resolve(this.config.headers) : undefined,
565
+ options.headers,
566
+ extraHeaders,
567
+ );
568
+
569
+ const {
570
+ responseHeaders,
571
+ value: response,
572
+ rawValue: rawResponse,
573
+ } = await postJsonToApi({
574
+ url: `${this.config.baseURL}/${getModelPath(
575
+ this.modelId,
576
+ )}:generateContent`,
577
+ headers: mergedHeaders,
578
+ body: args,
579
+ failedResponseHandler: googleFailedResponseHandler,
580
+ successfulResponseHandler: createJsonResponseHandler(responseSchema),
581
+ abortSignal: options.abortSignal,
582
+ fetch: this.config.fetch,
583
+ });
584
+
585
+ const result = this.convertGenerateContentResponse({
586
+ response,
587
+ warnings,
588
+ providerOptionsNames,
589
+ });
590
+
591
+ return {
592
+ ...result,
593
+ request: { body: args },
594
+ response: {
595
+ ...result.response,
573
596
  headers: responseHeaders,
574
597
  body: rawResponse,
575
598
  },
@@ -1563,7 +1586,7 @@ export const getUrlContextMetadataSchema = () =>
1563
1586
  .nullish(),
1564
1587
  });
1565
1588
 
1566
- const responseSchema = lazySchema(() =>
1589
+ export const responseSchema = lazySchema(() =>
1567
1590
  zodSchema(
1568
1591
  z.object({
1569
1592
  responseId: z.string().nullish(),
@@ -3,10 +3,25 @@ import {
3
3
  type LanguageModelV4CallOptions,
4
4
  type SharedV4Warning,
5
5
  } from '@ai-sdk/provider';
6
- import { convertJSONSchemaToOpenAPISchema } from './convert-json-schema-to-openapi-schema';
6
+ import {
7
+ convertJSONSchemaToOpenAPISchema,
8
+ isRecursiveJSONSchemaReferenceError,
9
+ } from './convert-json-schema-to-openapi-schema';
7
10
  import type { GoogleModelId } from './google-language-model-options';
8
11
  import { getGoogleModelCapabilities } from './google-model-capabilities';
9
12
 
13
+ type FunctionTool = Extract<
14
+ NonNullable<LanguageModelV4CallOptions['tools']>[number],
15
+ { type: 'function' }
16
+ >;
17
+
18
+ type GoogleFunctionDeclaration = {
19
+ name: string;
20
+ description: string;
21
+ parameters?: unknown;
22
+ parametersJsonSchema?: unknown;
23
+ };
24
+
10
25
  export function prepareTools({
11
26
  tools,
12
27
  toolChoice,
@@ -21,11 +36,7 @@ export function prepareTools({
21
36
  tools:
22
37
  | Array<
23
38
  | {
24
- functionDeclarations: Array<{
25
- name: string;
26
- description: string;
27
- parameters: unknown;
28
- }>;
39
+ functionDeclarations: GoogleFunctionDeclaration[];
29
40
  }
30
41
  | Record<string, any>
31
42
  >
@@ -172,18 +183,10 @@ export function prepareTools({
172
183
  });
173
184
 
174
185
  if (hasFunctionTools && usesGemini3Features && googleTools.length > 0) {
175
- const functionDeclarations: Array<{
176
- name: string;
177
- description: string;
178
- parameters: unknown;
179
- }> = [];
186
+ const functionDeclarations: GoogleFunctionDeclaration[] = [];
180
187
  for (const tool of tools) {
181
188
  if (tool.type === 'function') {
182
- functionDeclarations.push({
183
- name: tool.name,
184
- description: tool.description ?? '',
185
- parameters: convertJSONSchemaToOpenAPISchema(tool.inputSchema),
186
- });
189
+ functionDeclarations.push(prepareFunctionDeclaration(tool));
187
190
  }
188
191
  }
189
192
 
@@ -238,11 +241,7 @@ export function prepareTools({
238
241
  for (const tool of tools) {
239
242
  switch (tool.type) {
240
243
  case 'function':
241
- functionDeclarations.push({
242
- name: tool.name,
243
- description: tool.description ?? '',
244
- parameters: convertJSONSchemaToOpenAPISchema(tool.inputSchema),
245
- });
244
+ functionDeclarations.push(prepareFunctionDeclaration(tool));
246
245
  if (tool.strict === true) {
247
246
  hasStrictTools = true;
248
247
  }
@@ -314,3 +313,28 @@ export function prepareTools({
314
313
  }
315
314
  }
316
315
  }
316
+
317
+ function prepareFunctionDeclaration(
318
+ tool: FunctionTool,
319
+ ): GoogleFunctionDeclaration {
320
+ const declaration = {
321
+ name: tool.name,
322
+ description: tool.description ?? '',
323
+ };
324
+
325
+ try {
326
+ return {
327
+ ...declaration,
328
+ parameters: convertJSONSchemaToOpenAPISchema(tool.inputSchema),
329
+ };
330
+ } catch (error) {
331
+ if (!isRecursiveJSONSchemaReferenceError(error)) {
332
+ throw error;
333
+ }
334
+
335
+ return {
336
+ ...declaration,
337
+ parametersJsonSchema: tool.inputSchema,
338
+ };
339
+ }
340
+ }
@@ -1,5 +1,6 @@
1
1
  import type {
2
2
  EmbeddingModelV4,
3
+ Experimental_BatchLanguageModelV4 as BatchLanguageModelV4,
3
4
  Experimental_VideoModelV4,
4
5
  FilesV4,
5
6
  ImageModelV4,
@@ -21,7 +22,7 @@ import {
21
22
  import { VERSION } from './version';
22
23
  import { GoogleEmbeddingModel } from './google-embedding-model';
23
24
  import type { GoogleEmbeddingModelId } from './google-embedding-model-options';
24
- import { GoogleLanguageModel } from './google-language-model';
25
+ import { GoogleBatchLanguageModel } from './google-batch';
25
26
  import type { GoogleModelId } from './google-language-model-options';
26
27
  import { googleTools } from './google-tools';
27
28
 
@@ -46,11 +47,11 @@ import { GoogleSpeechTranslationModel } from './speech-translation/google-speech
46
47
  import type { GoogleSpeechTranslationModelId } from './speech-translation/google-speech-translation-model-options';
47
48
 
48
49
  export interface GoogleProvider extends ProviderV4 {
49
- (modelId: GoogleModelId): LanguageModelV4;
50
+ (modelId: GoogleModelId): BatchLanguageModelV4;
50
51
 
51
- languageModel(modelId: GoogleModelId): LanguageModelV4;
52
+ languageModel(modelId: GoogleModelId): BatchLanguageModelV4;
52
53
 
53
- chat(modelId: GoogleModelId): LanguageModelV4;
54
+ chat(modelId: GoogleModelId): BatchLanguageModelV4;
54
55
 
55
56
  /**
56
57
  * Creates a model for image generation.
@@ -63,7 +64,7 @@ export interface GoogleProvider extends ProviderV4 {
63
64
  /**
64
65
  * @deprecated Use `chat()` instead.
65
66
  */
66
- generativeAI(modelId: GoogleModelId): LanguageModelV4;
67
+ generativeAI(modelId: GoogleModelId): BatchLanguageModelV4;
67
68
 
68
69
  /**
69
70
  * Creates a model for text embeddings.
@@ -240,7 +241,7 @@ export function createGoogle(
240
241
  );
241
242
 
242
243
  const createChatModel = (modelId: GoogleModelId) =>
243
- new GoogleLanguageModel(modelId, {
244
+ new GoogleBatchLanguageModel(modelId, {
244
245
  provider: providerName,
245
246
  baseURL,
246
247
  headers: getHeaders,
@@ -6,7 +6,10 @@ import type {
6
6
  SharedV4ProviderMetadata,
7
7
  SharedV4Warning,
8
8
  } from '@ai-sdk/provider';
9
- import type { ParseResult } from '@ai-sdk/provider-utils';
9
+ import {
10
+ createProviderStreamError,
11
+ type ParseResult,
12
+ } from '@ai-sdk/provider-utils';
10
13
  import type {
11
14
  GoogleInteractionsEvent,
12
15
  GoogleInteractionsUsage,
@@ -821,10 +824,15 @@ export function buildGoogleInteractionsStreamTransform({
821
824
  { event_type: 'error' }
822
825
  >;
823
826
  finishStatus = 'failed';
824
- const errorPayload = event.error ?? {
825
- message: 'Unknown interaction error',
826
- };
827
- controller.enqueue({ type: 'error', error: errorPayload });
827
+ controller.enqueue({
828
+ type: 'error',
829
+ error: createProviderStreamError({
830
+ message: event.error?.message ?? 'Unknown interaction error',
831
+ type: event.event_type,
832
+ code: event.error?.code ?? undefined,
833
+ data: event,
834
+ }),
835
+ });
828
836
  break;
829
837
  }
830
838