@ai-sdk/google 4.0.111 → 4.0.113

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.
@@ -1358,6 +1358,8 @@ When the serialized batch creation body is under 20 MB, the provider sends the r
1358
1358
 
1359
1359
  ## Realtime Models
1360
1360
 
1361
+ For a trusted [server-owned connection](/docs/ai-sdk-core/realtime#server-owned-connections), `getServerWebSocketConfig()` uses the unconstrained Gemini Live endpoint with resolved API-key headers. Use `createServerEventParser()` once per socket. Completion-frame usage is attached to `response-done`; independently delivered usage uses `custom` events with `rawType: 'usageMetadata'` and typed `usage`, without inferring a response association. Gross totals never substitute for missing modality counts.
1362
+
1361
1363
  <Note type="warning">Realtime is an experimental feature.</Note>
1362
1364
 
1363
1365
  You can create models that call the [Gemini Live API](https://ai.google.dev/gemini-api/docs/live)
@@ -2514,6 +2516,11 @@ the provider passes those bytes through unchanged. Older models return raw PCM,
2514
2516
  which the provider wraps in a WAV container. Write `result.audio.uint8Array`
2515
2517
  directly to disk without adding another WAV header.
2516
2518
 
2519
+ Set `language` to a supported BCP-47 language code, such as `en-US` or `de-DE`.
2520
+ The provider forwards explicit codes unchanged as `speechConfig.languageCode`
2521
+ for both single- and multi-speaker requests. Omit `language` or set it to `'auto'`
2522
+ to let Gemini detect the language from the input text.
2523
+
2517
2524
  Gemini 3.8 supports the following `outputFormat` values:
2518
2525
 
2519
2526
  | Format | Values | Result |
@@ -2614,9 +2621,8 @@ Older models continue to use labelled transcripts, such as
2614
2621
  `multiSpeakerVoiceConfig` and without `turns`.
2615
2622
 
2616
2623
  <Note>
2617
- Gemini TTS models do not support the `speed` or `language` options; passing
2618
- them adds a warning to `result.warnings`. Language is detected automatically
2619
- from the input text.
2624
+ Gemini TTS models do not support the `speed` option; passing it adds a warning
2625
+ to `result.warnings`.
2620
2626
  </Note>
2621
2627
 
2622
2628
  ### Model Capabilities
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/google",
3
- "version": "4.0.111",
3
+ "version": "4.0.113",
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.35",
39
- "@ai-sdk/provider-utils": "5.0.74"
38
+ "@ai-sdk/provider": "4.0.36",
39
+ "@ai-sdk/provider-utils": "5.0.75"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@ai-sdk/test-server": "2.0.4",
@@ -466,7 +466,7 @@ export function convertToGoogleMessages(
466
466
 
467
467
  switch (part.type) {
468
468
  case 'text': {
469
- return part.text.length === 0
469
+ return part.text.length === 0 && thoughtSignature == null
470
470
  ? undefined
471
471
  : {
472
472
  text: part.text,
@@ -475,7 +475,7 @@ export function convertToGoogleMessages(
475
475
  }
476
476
 
477
477
  case 'reasoning': {
478
- return part.text.length === 0
478
+ return part.text.length === 0 && thoughtSignature == null
479
479
  ? undefined
480
480
  : {
481
481
  text: part.text,
@@ -978,6 +978,19 @@ export class GoogleLanguageModel implements LanguageModelV4 {
978
978
  return;
979
979
  }
980
980
 
981
+ // Finalized partial-argument calls also separate surrounding blocks.
982
+ if (currentTextBlockId !== null) {
983
+ controller.enqueue({ type: 'text-end', id: currentTextBlockId });
984
+ currentTextBlockId = null;
985
+ }
986
+ if (currentReasoningBlockId !== null) {
987
+ controller.enqueue({
988
+ type: 'reasoning-end',
989
+ id: currentReasoningBlockId,
990
+ });
991
+ currentReasoningBlockId = null;
992
+ }
993
+
981
994
  const { finalJSON, closingDelta } = active.accumulator.finalize();
982
995
 
983
996
  if (closingDelta.length > 0) {
@@ -1182,16 +1195,40 @@ export class GoogleLanguageModel implements LanguageModelV4 {
1182
1195
  : undefined;
1183
1196
 
1184
1197
  if (part.text.length === 0) {
1185
- if (
1186
- thoughtSignatureMetadata != null &&
1187
- currentTextBlockId !== null
1188
- ) {
1189
- controller.enqueue({
1190
- type: 'text-delta',
1191
- id: currentTextBlockId,
1192
- delta: '',
1193
- providerMetadata: thoughtSignatureMetadata,
1194
- });
1198
+ if (thoughtSignatureMetadata != null) {
1199
+ if (currentTextBlockId !== null) {
1200
+ controller.enqueue({
1201
+ type: 'text-delta',
1202
+ id: currentTextBlockId,
1203
+ delta: '',
1204
+ providerMetadata: thoughtSignatureMetadata,
1205
+ });
1206
+ } else {
1207
+ if (currentReasoningBlockId !== null) {
1208
+ controller.enqueue({
1209
+ type: 'reasoning-end',
1210
+ id: currentReasoningBlockId,
1211
+ });
1212
+ currentReasoningBlockId = null;
1213
+ }
1214
+
1215
+ // Keep the signature in position without merging later content.
1216
+ const id = String(blockCounter++);
1217
+ const type =
1218
+ part.thought === true ? 'reasoning' : 'text';
1219
+ controller.enqueue({
1220
+ type: `${type}-start`,
1221
+ id,
1222
+ providerMetadata: thoughtSignatureMetadata,
1223
+ });
1224
+ controller.enqueue({
1225
+ type: `${type}-delta`,
1226
+ id,
1227
+ delta: '',
1228
+ providerMetadata: thoughtSignatureMetadata,
1229
+ });
1230
+ controller.enqueue({ type: `${type}-end`, id });
1231
+ }
1195
1232
  }
1196
1233
  } else if (part.thought === true) {
1197
1234
  // End any active text block before starting reasoning
@@ -1316,67 +1353,108 @@ export class GoogleLanguageModel implements LanguageModelV4 {
1316
1353
  providerMetadata: serverMeta,
1317
1354
  });
1318
1355
  lastServerToolCallId = undefined;
1319
- }
1320
- }
1356
+ } else if ('functionCall' in part) {
1357
+ const providerMeta = part.thoughtSignature
1358
+ ? wrapProviderMetadata({
1359
+ thoughtSignature: part.thoughtSignature,
1360
+ })
1361
+ : undefined;
1321
1362
 
1322
- // Handle streaming and complete function calls
1323
- for (const part of parts) {
1324
- if (!('functionCall' in part)) continue;
1325
-
1326
- const providerMeta = part.thoughtSignature
1327
- ? wrapProviderMetadata({
1328
- thoughtSignature: part.thoughtSignature,
1329
- })
1330
- : undefined;
1331
-
1332
- const isStreamingChunk =
1333
- part.functionCall.partialArgs != null ||
1334
- (part.functionCall.name != null &&
1335
- part.functionCall.willContinue === true);
1336
- const isTerminalChunk =
1337
- part.functionCall.name == null &&
1338
- part.functionCall.args == null &&
1339
- part.functionCall.partialArgs == null &&
1340
- part.functionCall.willContinue == null;
1341
- const isCompleteCall =
1342
- part.functionCall.name != null &&
1343
- part.functionCall.args != null &&
1344
- part.functionCall.partialArgs == null;
1345
- // Single-chunk no-args call: `{ name: 'X' }` with no `args`,
1346
- // `partialArgs`, or `willContinue`. Carries `thoughtSignature`.
1347
- const isNoArgsCompleteCall =
1348
- part.functionCall.name != null &&
1349
- part.functionCall.args == null &&
1350
- part.functionCall.partialArgs == null &&
1351
- part.functionCall.willContinue !== true;
1352
-
1353
- if (isStreamingChunk) {
1354
- if (part.functionCall.name != null) {
1355
- const toolCallId = part.functionCall.id || generateId();
1356
- const accumulator = new GoogleJSONAccumulator();
1357
- activeStreamingToolCalls.push({
1358
- toolCallId,
1359
- toolName: part.functionCall.name,
1360
- accumulator,
1361
- providerMetadata: providerMeta,
1362
- });
1363
+ const isStreamingChunk =
1364
+ part.functionCall.partialArgs != null ||
1365
+ (part.functionCall.name != null &&
1366
+ part.functionCall.willContinue === true);
1367
+ const isTerminalChunk =
1368
+ part.functionCall.name == null &&
1369
+ part.functionCall.args == null &&
1370
+ part.functionCall.partialArgs == null &&
1371
+ part.functionCall.willContinue == null;
1372
+ const isCompleteCall =
1373
+ part.functionCall.name != null &&
1374
+ part.functionCall.args != null &&
1375
+ part.functionCall.partialArgs == null;
1376
+ // Single-chunk no-args call: `{ name: 'X' }` with no `args`,
1377
+ // `partialArgs`, or `willContinue`. Carries `thoughtSignature`.
1378
+ const isNoArgsCompleteCall =
1379
+ part.functionCall.name != null &&
1380
+ part.functionCall.args == null &&
1381
+ part.functionCall.partialArgs == null &&
1382
+ part.functionCall.willContinue !== true;
1383
+
1384
+ if (
1385
+ !isStreamingChunk &&
1386
+ (isCompleteCall || isNoArgsCompleteCall)
1387
+ ) {
1388
+ if (currentTextBlockId !== null) {
1389
+ controller.enqueue({
1390
+ type: 'text-end',
1391
+ id: currentTextBlockId,
1392
+ });
1393
+ currentTextBlockId = null;
1394
+ }
1395
+ if (currentReasoningBlockId !== null) {
1396
+ controller.enqueue({
1397
+ type: 'reasoning-end',
1398
+ id: currentReasoningBlockId,
1399
+ });
1400
+ currentReasoningBlockId = null;
1401
+ }
1402
+ }
1363
1403
 
1364
- controller.enqueue({
1365
- type: 'tool-input-start',
1366
- id: toolCallId,
1367
- toolName: part.functionCall.name,
1368
- providerMetadata: providerMeta,
1369
- });
1404
+ if (isStreamingChunk) {
1405
+ if (part.functionCall.name != null) {
1406
+ const toolCallId = part.functionCall.id || generateId();
1407
+ const accumulator = new GoogleJSONAccumulator();
1408
+ activeStreamingToolCalls.push({
1409
+ toolCallId,
1410
+ toolName: part.functionCall.name,
1411
+ accumulator,
1412
+ providerMetadata: providerMeta,
1413
+ });
1414
+
1415
+ controller.enqueue({
1416
+ type: 'tool-input-start',
1417
+ id: toolCallId,
1418
+ toolName: part.functionCall.name,
1419
+ providerMetadata: providerMeta,
1420
+ });
1370
1421
 
1371
- if (part.functionCall.partialArgs != null) {
1422
+ if (part.functionCall.partialArgs != null) {
1423
+ const partialArgs = part.functionCall
1424
+ .partialArgs as PartialArg[];
1425
+ const { textDelta } =
1426
+ accumulator.processPartialArgs(partialArgs);
1427
+ if (textDelta.length > 0) {
1428
+ controller.enqueue({
1429
+ type: 'tool-input-delta',
1430
+ id: toolCallId,
1431
+ delta: textDelta,
1432
+ providerMetadata: providerMeta,
1433
+ });
1434
+ }
1435
+ if (
1436
+ part.functionCall.willContinue !== true &&
1437
+ partialArgs.every(arg => arg.willContinue !== true)
1438
+ ) {
1439
+ finishActiveStreamingToolCall(controller);
1440
+ }
1441
+ }
1442
+ } else if (
1443
+ part.functionCall.partialArgs != null &&
1444
+ activeStreamingToolCalls.length > 0
1445
+ ) {
1446
+ const active =
1447
+ activeStreamingToolCalls[
1448
+ activeStreamingToolCalls.length - 1
1449
+ ];
1372
1450
  const partialArgs = part.functionCall
1373
1451
  .partialArgs as PartialArg[];
1374
1452
  const { textDelta } =
1375
- accumulator.processPartialArgs(partialArgs);
1453
+ active.accumulator.processPartialArgs(partialArgs);
1376
1454
  if (textDelta.length > 0) {
1377
1455
  controller.enqueue({
1378
1456
  type: 'tool-input-delta',
1379
- id: toolCallId,
1457
+ id: active.toolCallId,
1380
1458
  delta: textDelta,
1381
1459
  providerMetadata: providerMeta,
1382
1460
  });
@@ -1389,100 +1467,74 @@ export class GoogleLanguageModel implements LanguageModelV4 {
1389
1467
  }
1390
1468
  }
1391
1469
  } else if (
1392
- part.functionCall.partialArgs != null &&
1470
+ isTerminalChunk &&
1393
1471
  activeStreamingToolCalls.length > 0
1394
1472
  ) {
1395
- const active =
1396
- activeStreamingToolCalls[
1397
- activeStreamingToolCalls.length - 1
1398
- ];
1399
- const partialArgs = part.functionCall
1400
- .partialArgs as PartialArg[];
1401
- const { textDelta } =
1402
- active.accumulator.processPartialArgs(partialArgs);
1403
- if (textDelta.length > 0) {
1404
- controller.enqueue({
1405
- type: 'tool-input-delta',
1406
- id: active.toolCallId,
1407
- delta: textDelta,
1408
- providerMetadata: providerMeta,
1409
- });
1410
- }
1411
- if (
1412
- part.functionCall.willContinue !== true &&
1413
- partialArgs.every(arg => arg.willContinue !== true)
1414
- ) {
1415
- finishActiveStreamingToolCall(controller);
1416
- }
1417
- }
1418
- } else if (
1419
- isTerminalChunk &&
1420
- activeStreamingToolCalls.length > 0
1421
- ) {
1422
- finishActiveStreamingToolCall(controller);
1423
- } else if (isCompleteCall) {
1424
- const toolCallId = part.functionCall.id || generateId();
1425
- const toolName = part.functionCall.name!;
1426
- const args =
1427
- typeof part.functionCall.args === 'string'
1428
- ? part.functionCall.args
1429
- : JSON.stringify(part.functionCall.args ?? {});
1473
+ finishActiveStreamingToolCall(controller);
1474
+ } else if (isCompleteCall) {
1475
+ const toolCallId = part.functionCall.id || generateId();
1476
+ const toolName = part.functionCall.name!;
1477
+ const args =
1478
+ typeof part.functionCall.args === 'string'
1479
+ ? part.functionCall.args
1480
+ : JSON.stringify(part.functionCall.args ?? {});
1430
1481
 
1431
- controller.enqueue({
1432
- type: 'tool-input-start',
1433
- id: toolCallId,
1434
- toolName,
1435
- providerMetadata: providerMeta,
1436
- });
1482
+ controller.enqueue({
1483
+ type: 'tool-input-start',
1484
+ id: toolCallId,
1485
+ toolName,
1486
+ providerMetadata: providerMeta,
1487
+ });
1437
1488
 
1438
- controller.enqueue({
1439
- type: 'tool-input-delta',
1440
- id: toolCallId,
1441
- delta: args,
1442
- providerMetadata: providerMeta,
1443
- });
1489
+ controller.enqueue({
1490
+ type: 'tool-input-delta',
1491
+ id: toolCallId,
1492
+ delta: args,
1493
+ providerMetadata: providerMeta,
1494
+ });
1444
1495
 
1445
- controller.enqueue({
1446
- type: 'tool-input-end',
1447
- id: toolCallId,
1448
- providerMetadata: providerMeta,
1449
- });
1496
+ controller.enqueue({
1497
+ type: 'tool-input-end',
1498
+ id: toolCallId,
1499
+ providerMetadata: providerMeta,
1500
+ });
1450
1501
 
1451
- controller.enqueue({
1452
- type: 'tool-call',
1453
- toolCallId,
1454
- toolName,
1455
- input: args,
1456
- providerMetadata: providerMeta,
1457
- });
1502
+ controller.enqueue({
1503
+ type: 'tool-call',
1504
+ toolCallId,
1505
+ toolName,
1506
+ input: args,
1507
+ providerMetadata: providerMeta,
1508
+ });
1458
1509
 
1459
- hasToolCalls = true;
1460
- } else if (isNoArgsCompleteCall) {
1461
- const toolCallId = part.functionCall.id || generateId();
1462
- const toolName = part.functionCall.name!;
1510
+ hasToolCalls = true;
1511
+ } else if (isNoArgsCompleteCall) {
1512
+ const toolCallId = part.functionCall.id || generateId();
1513
+ const toolName = part.functionCall.name!;
1463
1514
 
1464
- controller.enqueue({
1465
- type: 'tool-input-start',
1466
- id: toolCallId,
1467
- toolName,
1468
- providerMetadata: providerMeta,
1469
- });
1515
+ controller.enqueue({
1516
+ type: 'tool-input-start',
1517
+ id: toolCallId,
1518
+ toolName,
1519
+ providerMetadata: providerMeta,
1520
+ });
1470
1521
 
1471
- controller.enqueue({
1472
- type: 'tool-input-end',
1473
- id: toolCallId,
1474
- providerMetadata: providerMeta,
1475
- });
1522
+ controller.enqueue({
1523
+ type: 'tool-input-end',
1524
+ id: toolCallId,
1525
+ providerMetadata: providerMeta,
1526
+ });
1476
1527
 
1477
- controller.enqueue({
1478
- type: 'tool-call',
1479
- toolCallId,
1480
- toolName,
1481
- input: '{}',
1482
- providerMetadata: providerMeta,
1483
- });
1528
+ controller.enqueue({
1529
+ type: 'tool-call',
1530
+ toolCallId,
1531
+ toolName,
1532
+ input: '{}',
1533
+ providerMetadata: providerMeta,
1534
+ });
1484
1535
 
1485
- hasToolCalls = true;
1536
+ hasToolCalls = true;
1537
+ }
1486
1538
  }
1487
1539
  }
1488
1540
  }
@@ -128,9 +128,16 @@ export class GoogleSpeechModel implements SpeechModelV4 {
128
128
 
129
129
  // Multi-speaker (provider option) takes precedence over the single voice.
130
130
  const multiSpeakerVoiceConfig = googleOptions?.multiSpeakerVoiceConfig;
131
+ const languageCode = language === 'auto' ? undefined : language;
131
132
  const speechConfig = multiSpeakerVoiceConfig
132
- ? { multiSpeakerVoiceConfig }
133
- : { voiceConfig: { prebuiltVoiceConfig: { voiceName: voice } } };
133
+ ? {
134
+ multiSpeakerVoiceConfig,
135
+ ...(languageCode != null && { languageCode }),
136
+ }
137
+ : {
138
+ voiceConfig: { prebuiltVoiceConfig: { voiceName: voice } },
139
+ ...(languageCode != null && { languageCode }),
140
+ };
134
141
 
135
142
  // Older models expect directions in the prompt. Prepending them to a
136
143
  // labelled multi-speaker transcript would break speaker parsing.
@@ -217,16 +224,6 @@ export class GoogleSpeechModel implements SpeechModelV4 {
217
224
  });
218
225
  }
219
226
 
220
- if (language != null) {
221
- warnings.push({
222
- type: 'unsupported',
223
- feature: 'language',
224
- details:
225
- 'Google Gemini TTS models do not support the `language` option. ' +
226
- 'Language is detected automatically from the input text.',
227
- });
228
- }
229
-
230
227
  const formats: Record<string, string> = usesStructuredSpeech
231
228
  ? {
232
229
  wav: 'AUDIO_WAV',
@@ -232,7 +232,7 @@ export class GoogleVideoModel implements VideoModelV4 {
232
232
  parameters.durationSeconds = options.duration;
233
233
  }
234
234
 
235
- if (options.seed) {
235
+ if (options.seed != null) {
236
236
  parameters.seed = options.seed;
237
237
  }
238
238
 
@@ -9,3 +9,8 @@ export {
9
9
  } from '../interactions/google-interactions-language-model';
10
10
  export type { GoogleInteractionsModelId } from '../interactions/google-interactions-language-model-options';
11
11
  export type { GoogleInteractionsAgentName } from '../interactions/google-interactions-agent';
12
+
13
+ export {
14
+ getGoogleLanguageModelEntry,
15
+ googleProviderOptions,
16
+ } from '../google-model-catalog';