@ai-sdk/google 4.0.53 → 4.0.55

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.53",
3
+ "version": "4.0.55",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -388,11 +388,16 @@ export class GoogleLanguageModel implements LanguageModelV4 {
388
388
  Object.fromEntries(
389
389
  providerOptionsNames.map(name => [name, payload]),
390
390
  ) as SharedV4ProviderMetadata;
391
- const candidate = response.candidates[0];
391
+ const candidate = response.candidates?.[0];
392
+ const promptBlockReason = response.promptFeedback?.blockReason;
393
+ const isPromptBlocked =
394
+ candidate?.finishReason == null && promptBlockReason != null;
395
+ const rawFinishReason =
396
+ candidate?.finishReason ?? promptBlockReason ?? undefined;
392
397
  const content: Array<LanguageModelV4Content> = [];
393
398
 
394
399
  // map ordered parts to content:
395
- const parts = candidate.content?.parts ?? [];
400
+ const parts = candidate?.content?.parts ?? [];
396
401
 
397
402
  const usageMetadata = response.usageMetadata;
398
403
 
@@ -517,7 +522,7 @@ export class GoogleLanguageModel implements LanguageModelV4 {
517
522
 
518
523
  const sources =
519
524
  extractSources({
520
- groundingMetadata: candidate.groundingMetadata,
525
+ groundingMetadata: candidate?.groundingMetadata,
521
526
  generateId: this.config.generateId,
522
527
  }) ?? [];
523
528
  for (const source of sources) {
@@ -527,24 +532,26 @@ export class GoogleLanguageModel implements LanguageModelV4 {
527
532
  return {
528
533
  content,
529
534
  finishReason: {
530
- unified: mapGoogleFinishReason({
531
- finishReason: candidate.finishReason,
532
- // Only count client-executed tool calls for finish reason determination.
533
- hasToolCalls: content.some(
534
- part => part.type === 'tool-call' && !part.providerExecuted,
535
- ),
536
- }),
537
- raw: candidate.finishReason ?? undefined,
535
+ unified: isPromptBlocked
536
+ ? 'content-filter'
537
+ : mapGoogleFinishReason({
538
+ finishReason: rawFinishReason,
539
+ // Only count client-executed tool calls for finish reason determination.
540
+ hasToolCalls: content.some(
541
+ part => part.type === 'tool-call' && !part.providerExecuted,
542
+ ),
543
+ }),
544
+ raw: rawFinishReason,
538
545
  },
539
546
  usage: convertGoogleUsage(usageMetadata),
540
547
  warnings,
541
548
  providerMetadata: wrapProviderMetadata({
542
549
  promptFeedback: response.promptFeedback ?? null,
543
- groundingMetadata: candidate.groundingMetadata ?? null,
544
- urlContextMetadata: candidate.urlContextMetadata ?? null,
545
- safetyRatings: candidate.safetyRatings ?? null,
550
+ groundingMetadata: candidate?.groundingMetadata ?? null,
551
+ urlContextMetadata: candidate?.urlContextMetadata ?? null,
552
+ safetyRatings: candidate?.safetyRatings ?? null,
546
553
  usageMetadata: usageMetadata ?? null,
547
- finishMessage: candidate.finishMessage ?? null,
554
+ finishMessage: candidate?.finishMessage ?? null,
548
555
  serviceTier: usageMetadata?.serviceTier ?? null,
549
556
  } satisfies GoogleProviderMetadata),
550
557
  response: {
@@ -735,6 +742,22 @@ export class GoogleLanguageModel implements LanguageModelV4 {
735
742
 
736
743
  // sometimes the API returns an empty candidates array
737
744
  if (candidate == null) {
745
+ const promptBlockReason = value.promptFeedback?.blockReason;
746
+ if (promptBlockReason != null) {
747
+ finishReason = {
748
+ unified: 'content-filter',
749
+ raw: promptBlockReason,
750
+ };
751
+ providerMetadata = wrapProviderMetadata({
752
+ promptFeedback: value.promptFeedback ?? null,
753
+ groundingMetadata: lastGroundingMetadata,
754
+ urlContextMetadata: lastUrlContextMetadata,
755
+ safetyRatings: null,
756
+ usageMetadata: usageMetadata ?? null,
757
+ finishMessage: null,
758
+ serviceTier: usage?.serviceTier ?? null,
759
+ } satisfies GoogleProviderMetadata);
760
+ }
738
761
  return;
739
762
  }
740
763
 
@@ -1110,13 +1133,21 @@ export class GoogleLanguageModel implements LanguageModelV4 {
1110
1133
  }
1111
1134
  }
1112
1135
 
1113
- if (candidate.finishReason != null) {
1136
+ const promptBlockReason = value.promptFeedback?.blockReason;
1137
+ const isPromptBlocked =
1138
+ candidate.finishReason == null && promptBlockReason != null;
1139
+ const rawFinishReason =
1140
+ candidate.finishReason ?? promptBlockReason ?? undefined;
1141
+
1142
+ if (rawFinishReason != null) {
1114
1143
  finishReason = {
1115
- unified: mapGoogleFinishReason({
1116
- finishReason: candidate.finishReason,
1117
- hasToolCalls,
1118
- }),
1119
- raw: candidate.finishReason,
1144
+ unified: isPromptBlocked
1145
+ ? 'content-filter'
1146
+ : mapGoogleFinishReason({
1147
+ finishReason: rawFinishReason,
1148
+ hasToolCalls,
1149
+ }),
1150
+ raw: rawFinishReason,
1120
1151
  };
1121
1152
 
1122
1153
  providerMetadata = wrapProviderMetadata({
@@ -1590,16 +1621,18 @@ export const responseSchema = lazySchema(() =>
1590
1621
  zodSchema(
1591
1622
  z.object({
1592
1623
  responseId: z.string().nullish(),
1593
- candidates: z.array(
1594
- z.object({
1595
- content: getContentSchema().nullish().or(z.object({}).strict()),
1596
- finishReason: z.string().nullish(),
1597
- finishMessage: z.string().nullish(),
1598
- safetyRatings: z.array(getSafetyRatingSchema()).nullish(),
1599
- groundingMetadata: getGroundingMetadataSchema().nullish(),
1600
- urlContextMetadata: getUrlContextMetadataSchema().nullish(),
1601
- }),
1602
- ),
1624
+ candidates: z
1625
+ .array(
1626
+ z.object({
1627
+ content: getContentSchema().nullish().or(z.object({}).strict()),
1628
+ finishReason: z.string().nullish(),
1629
+ finishMessage: z.string().nullish(),
1630
+ safetyRatings: z.array(getSafetyRatingSchema()).nullish(),
1631
+ groundingMetadata: getGroundingMetadataSchema().nullish(),
1632
+ urlContextMetadata: getUrlContextMetadataSchema().nullish(),
1633
+ }),
1634
+ )
1635
+ .nullish(),
1603
1636
  usageMetadata: usageSchema.nullish(),
1604
1637
  promptFeedback: z
1605
1638
  .object({
@@ -1611,16 +1644,20 @@ export const responseSchema = lazySchema(() =>
1611
1644
  ),
1612
1645
  );
1613
1646
 
1647
+ type CandidateSchema = NonNullable<
1648
+ InferSchema<typeof responseSchema>['candidates']
1649
+ >[number];
1650
+
1614
1651
  export type GroundingMetadataSchema = NonNullable<
1615
- InferSchema<typeof responseSchema>['candidates'][number]['groundingMetadata']
1652
+ CandidateSchema['groundingMetadata']
1616
1653
  >;
1617
1654
 
1618
1655
  export type UrlContextMetadataSchema = NonNullable<
1619
- InferSchema<typeof responseSchema>['candidates'][number]['urlContextMetadata']
1656
+ CandidateSchema['urlContextMetadata']
1620
1657
  >;
1621
1658
 
1622
1659
  export type SafetyRatingSchema = NonNullable<
1623
- InferSchema<typeof responseSchema>['candidates'][number]['safetyRatings']
1660
+ CandidateSchema['safetyRatings']
1624
1661
  >[number];
1625
1662
 
1626
1663
  export type PromptFeedbackSchema = NonNullable<
@@ -10,6 +10,7 @@ import type {
10
10
  Experimental_RealtimeFactoryV4GetTokenOptions as RealtimeFactoryV4GetTokenOptions,
11
11
  SpeechModelV4,
12
12
  Experimental_SpeechTranslationModelV4 as SpeechTranslationModelV4,
13
+ TranscriptionModelV4,
13
14
  } from '@ai-sdk/provider';
14
15
  import {
15
16
  generateId,
@@ -43,6 +44,8 @@ import {
43
44
  import type { GoogleInteractionsModelId } from './interactions/google-interactions-language-model-options';
44
45
  import type { GoogleInteractionsAgentName } from './interactions/google-interactions-agent';
45
46
  import { GoogleRealtimeModel } from './realtime/google-realtime-model';
47
+ import { GoogleTranscriptionModel } from './transcription/google-transcription-model';
48
+ import type { GoogleTranscriptionModelId } from './transcription/google-transcription-model-options';
46
49
  import { GoogleSpeechTranslationModel } from './speech-translation/google-speech-translation-model';
47
50
  import type { GoogleSpeechTranslationModelId } from './speech-translation/google-speech-translation-model-options';
48
51
 
@@ -120,6 +123,19 @@ export interface GoogleProvider extends ProviderV4 {
120
123
  */
121
124
  speechModel(modelId: GoogleSpeechModelId): SpeechModelV4;
122
125
 
126
+ /**
127
+ * Creates a model for transcription (speech-to-text). Unary models
128
+ * (e.g. `gemini-3.5-transcribe`) transcribe audio files; live models
129
+ * (e.g. `gemini-3.5-transcribe-live`) stream transcription over the
130
+ * Gemini Live API WebSocket via `experimental_streamTranscribe`.
131
+ */
132
+ transcription(modelId: GoogleTranscriptionModelId): TranscriptionModelV4;
133
+
134
+ /**
135
+ * Creates a model for transcription (speech-to-text).
136
+ */
137
+ transcriptionModel(modelId: GoogleTranscriptionModelId): TranscriptionModelV4;
138
+
123
139
  files(): FilesV4;
124
140
 
125
141
  /**
@@ -331,6 +347,15 @@ export function createGoogle(
331
347
  fetch: options.fetch,
332
348
  });
333
349
 
350
+ const createTranscriptionModel = (modelId: GoogleTranscriptionModelId) =>
351
+ new GoogleTranscriptionModel(modelId, {
352
+ provider: `${providerName}.transcription`,
353
+ baseURL,
354
+ headers: getHeaders,
355
+ fetch: options.fetch,
356
+ webSocket: options.webSocket,
357
+ });
358
+
334
359
  const experimentalRealtimeFactory = Object.assign(
335
360
  (modelId: string) => createRealtimeModel(modelId),
336
361
  {
@@ -393,6 +418,8 @@ export function createGoogle(
393
418
  provider.files = createFiles;
394
419
  provider.speech = createSpeechModel;
395
420
  provider.speechModel = createSpeechModel;
421
+ provider.transcription = createTranscriptionModel;
422
+ provider.transcriptionModel = createTranscriptionModel;
396
423
  provider.translation = createSpeechTranslationModel;
397
424
  provider.speechTranslationModel = createSpeechTranslationModel;
398
425
  provider.interactions = createInteractionsModel;
package/src/index.ts CHANGED
@@ -60,6 +60,11 @@ export type {
60
60
  GoogleRealtimeModelId as Experimental_GoogleRealtimeModelId,
61
61
  GoogleRealtimeModelOptions as Experimental_GoogleRealtimeModelOptions,
62
62
  } from './realtime/google-realtime-model-options';
63
+ export { GoogleTranscriptionModel } from './transcription/google-transcription-model';
64
+ export type {
65
+ GoogleTranscriptionModelId,
66
+ GoogleTranscriptionModelOptions,
67
+ } from './transcription/google-transcription-model-options';
63
68
  export {
64
69
  GoogleSpeechTranslationModel as Experimental_GoogleSpeechTranslationModel,
65
70
  /** @deprecated Use `Experimental_GoogleSpeechTranslationModel` instead. */
@@ -0,0 +1,50 @@
1
+ import { z } from 'zod/v4';
2
+
3
+ export type GoogleTranscriptionModelId =
4
+ | 'gemini-3.5-transcribe'
5
+ | 'gemini-3.5-transcribe-live'
6
+ | (string & {});
7
+
8
+ /**
9
+ * Speech recognition options shared by unary (`gemini-3.5-transcribe`) and
10
+ * live (`gemini-3.5-transcribe-live`) transcription. Maps onto Google's
11
+ * `AudioTranscriptionConfig`.
12
+ */
13
+ export const googleTranscriptionModelOptions = z.object({
14
+ /**
15
+ * BCP-47 language codes providing hints about the languages present in the
16
+ * audio. If omitted or empty, defaults to automatic language detection.
17
+ */
18
+ languageCodes: z.array(z.string()).optional(),
19
+
20
+ /**
21
+ * Custom vocabulary phrases, which bias the speech recognition model
22
+ * toward recognizing specific terms.
23
+ */
24
+ customVocabulary: z.array(z.string()).optional(),
25
+
26
+ /**
27
+ * Enables word-level timestamp generation.
28
+ */
29
+ wordTimestamp: z.boolean().optional(),
30
+
31
+ /**
32
+ * Enables speaker diarization.
33
+ */
34
+ diarization: z.boolean().optional(),
35
+
36
+ /**
37
+ * Transcription output formatting mode.
38
+ *
39
+ * - `VERBATIM` (default): exact literal transcript preserving filler
40
+ * words, repetitions, and false starts.
41
+ * - `SMART`: cleans up and structures the transcript in real time —
42
+ * disfluency removal, inline self-corrections, structured formatting
43
+ * (lists, numbers, dates, paragraph breaks), and grammar/casing polish.
44
+ */
45
+ mode: z.enum(['SMART', 'VERBATIM']).optional(),
46
+ });
47
+
48
+ export type GoogleTranscriptionModelOptions = z.infer<
49
+ typeof googleTranscriptionModelOptions
50
+ >;