@ai-sdk/google 4.0.108 → 4.0.109

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.
@@ -0,0 +1,319 @@
1
+ import type {
2
+ ModelCapabilities,
3
+ ModelCapability,
4
+ ModelEntry,
5
+ ProviderOptionDefinition,
6
+ ProviderOptionDefinitions,
7
+ } from '@ai-sdk/provider';
8
+ import type { GoogleLanguageModelOptions } from './google-language-model-options';
9
+
10
+ const gemini1ModelPattern = /(^|[/.])gemini-1(?:[.-]|$)/i;
11
+ const gemini2ModelPattern = /(^|[/.])gemini-2(?:[.-]|$)/i;
12
+ const gemini25ModelPattern = /(^|[/.])gemini-2\.5(?:[.-]|$)/i;
13
+ const geminiModelPattern = /(^|[/.])gemini-/i;
14
+
15
+ type GoogleLanguageProviderOption = keyof GoogleLanguageModelOptions;
16
+
17
+ /** Options accepted under providerOptions.google (or googleVertex on Vertex). */
18
+ export const googleProviderOptions = {
19
+ language: {
20
+ responseModalities: { description: 'Response modalities to generate.' },
21
+ thinkingConfig: {
22
+ description: 'Thinking budget, level, and thought output.',
23
+ },
24
+ cachedContent: { description: 'Cached content to use as request context.' },
25
+ structuredOutputs: {
26
+ description: 'Whether to send the JSON response schema.',
27
+ },
28
+ safetySettings: { description: 'Safety settings for the request.' },
29
+ threshold: {
30
+ description: 'Default threshold for configurable safety categories.',
31
+ },
32
+ audioTimestamp: {
33
+ description: 'Enable timestamp understanding for audio.',
34
+ },
35
+ labels: { description: 'Billing labels. Vertex AI only.' },
36
+ mediaResolution: {
37
+ description: 'Resolution used to process media inputs.',
38
+ },
39
+ imageConfig: { description: 'Image generation configuration.' },
40
+ retrievalConfig: { description: 'Retrieval configuration for tools.' },
41
+ streamFunctionCallArguments: {
42
+ description: 'Stream function arguments. Vertex AI only.',
43
+ },
44
+ serviceTier: { description: 'Service tier. Gemini API only.' },
45
+ continuationToken: {
46
+ description: 'Resume a response using its continuation token.',
47
+ },
48
+ sharedRequestType: { description: 'Shared PayGo tier. Vertex AI only.' },
49
+ requestType: { description: 'Request routing type. Vertex AI only.' },
50
+ } satisfies Record<GoogleLanguageProviderOption, ProviderOptionDefinition>,
51
+ } as const satisfies ProviderOptionDefinitions;
52
+
53
+ type GoogleLanguageCapabilities = NonNullable<ModelCapabilities['language']> & {
54
+ tools: NonNullable<ModelCapabilities['language']>['tools'] & {
55
+ /** Existing request gate for Gemini 2 provider-defined tools. */
56
+ gemini2: boolean;
57
+ googleSearch: ModelCapability;
58
+ urlContext: ModelCapability;
59
+ fileSearch: boolean;
60
+ mixed: boolean;
61
+ functionResponseParts: boolean;
62
+ };
63
+ reasoning: NonNullable<ModelCapabilities['language']>['reasoning'] & {
64
+ minBudget: number;
65
+ maxBudget: number;
66
+ };
67
+ sampling: NonNullable<ModelCapabilities['language']>['sampling'] & {
68
+ topK: ModelCapability;
69
+ penalties: ModelCapability;
70
+ };
71
+ structuredOutput: {
72
+ withFunctionTools: boolean;
73
+ };
74
+ };
75
+
76
+ type GoogleLanguageRequestConfig = {
77
+ systemInstruction: 'system' | 'user';
78
+ thinkingFormat: 'level' | 'budget';
79
+ reasoningMaxOutputTokens: number;
80
+ jsonResponseTool: boolean;
81
+ };
82
+
83
+ export type GoogleLanguageModelEntry = ModelEntry<{
84
+ language: GoogleLanguageRequestConfig;
85
+ }> & {
86
+ capabilities: { language: GoogleLanguageCapabilities };
87
+ providerOptions: {
88
+ language: Record<GoogleLanguageProviderOption, ModelCapability>;
89
+ };
90
+ requestConfig: { language: GoogleLanguageRequestConfig };
91
+ };
92
+
93
+ type DocumentedCapabilities = {
94
+ image: boolean;
95
+ objectGeneration: boolean;
96
+ tools: boolean;
97
+ streaming: boolean;
98
+ googleSearch: boolean;
99
+ urlContext: boolean;
100
+ };
101
+
102
+ // Preserve the existing documentation's model set and per-model exceptions.
103
+ const standard = {
104
+ image: true,
105
+ objectGeneration: true,
106
+ tools: true,
107
+ streaming: true,
108
+ googleSearch: true,
109
+ urlContext: true,
110
+ } as const;
111
+
112
+ const documentedModels: Record<string, DocumentedCapabilities> = {
113
+ 'gemini-nano-banana-2.1': {
114
+ image: true,
115
+ objectGeneration: false,
116
+ tools: false,
117
+ streaming: false,
118
+ googleSearch: true,
119
+ urlContext: false,
120
+ },
121
+ 'gemini-4-argon': standard,
122
+ 'gemini-3.8-flash': standard,
123
+ 'gemini-3.7-flash': standard,
124
+ 'gemini-3.6-flash': standard,
125
+ 'gemini-3.5-flash': standard,
126
+ 'gemini-3.5-flash-lite': standard,
127
+ 'gemini-3.1-pro-preview': standard,
128
+ 'gemini-3.1-flash-image-preview': standard,
129
+ 'gemini-3.1-flash-lite-preview': standard,
130
+ 'gemini-3-pro-preview': standard,
131
+ 'gemini-3-pro-image-preview': standard,
132
+ 'gemini-3-flash-preview': standard,
133
+ 'gemini-2.5-pro': standard,
134
+ 'gemini-2.5-flash': standard,
135
+ 'gemini-2.5-flash-lite': standard,
136
+ 'gemini-2.5-flash-lite-preview-06-17': standard,
137
+ 'gemini-2.0-flash': standard,
138
+ };
139
+
140
+ export const googleModelCatalog: Readonly<
141
+ Record<string, GoogleLanguageModelEntry>
142
+ > = Object.fromEntries(
143
+ Object.entries(documentedModels).map(([modelId, documented]) => [
144
+ modelId,
145
+ createGoogleLanguageModelEntry(modelId, documented),
146
+ ]),
147
+ );
148
+
149
+ export function getGoogleLanguageModelEntry(
150
+ modelId: string,
151
+ ): GoogleLanguageModelEntry {
152
+ return Object.prototype.hasOwnProperty.call(googleModelCatalog, modelId)
153
+ ? googleModelCatalog[modelId]
154
+ : createGoogleLanguageModelEntry(modelId);
155
+ }
156
+
157
+ function createGoogleLanguageModelEntry(
158
+ modelId: string,
159
+ documented?: DocumentedCapabilities,
160
+ ): GoogleLanguageModelEntry {
161
+ const runtime = resolveRuntimeDefinition(modelId);
162
+ const declared = documented != null;
163
+ const id = modelId.toLowerCase();
164
+ // Preserve the original case-sensitive exception in the thinking resolver.
165
+ const thinkingFormat =
166
+ runtime.usesGemini3Features && !modelId.includes('gemini-3-pro-image')
167
+ ? 'level'
168
+ : 'budget';
169
+ const minLevel = getMinimumThinkingLevelForGemini3Model(modelId);
170
+ const language: GoogleLanguageCapabilities = {
171
+ inputs: { image: documented?.image ?? 'unknown', audio: 'unknown' },
172
+ objectGeneration: documented?.objectGeneration ?? 'unknown',
173
+ tools: {
174
+ usage: documented?.tools ?? 'unknown',
175
+ streaming: documented?.streaming ?? 'unknown',
176
+ gemini2: runtime.supportsGemini2Tools,
177
+ googleSearch: documented?.googleSearch ?? 'unknown',
178
+ urlContext: documented?.urlContext ?? 'unknown',
179
+ fileSearch: runtime.supportsFileSearch,
180
+ mixed: runtime.usesGemini3Features,
181
+ functionResponseParts: runtime.usesGemini3Features,
182
+ },
183
+ reasoning: {
184
+ support: 'unknown',
185
+ efforts:
186
+ thinkingFormat === 'level'
187
+ ? [
188
+ ...(minLevel === 'minimal' ? ['minimal'] : []),
189
+ 'low',
190
+ 'medium',
191
+ 'high',
192
+ ]
193
+ : undefined,
194
+ minBudget: id.includes('2.5-pro') ? 128 : 0,
195
+ maxBudget:
196
+ id.includes('2.5-pro') || id.includes('gemini-3-pro-image')
197
+ ? 32768
198
+ : 24576,
199
+ },
200
+ sampling: {
201
+ temperature: 'unknown',
202
+ topP: 'unknown',
203
+ topK: 'unknown',
204
+ logprobs: false,
205
+ penalties: gemini25ModelPattern.test(modelId)
206
+ ? { condition: 'Only supported on the Vertex AI API.' }
207
+ : true,
208
+ },
209
+ structuredOutput: {
210
+ withFunctionTools: runtime.usesGemini3Features,
211
+ },
212
+ };
213
+
214
+ return {
215
+ capabilities: { language },
216
+ providerOptions: {
217
+ language: declareProviderOptions(declared ? true : 'unknown', {
218
+ labels: { condition: 'Only supported on the Vertex AI API.' },
219
+ sharedRequestType: {
220
+ condition: 'Only supported on the Vertex AI API.',
221
+ },
222
+ requestType: { condition: 'Only supported on the Vertex AI API.' },
223
+ serviceTier: { condition: 'Only supported on the Gemini API.' },
224
+ streamFunctionCallArguments: {
225
+ condition:
226
+ 'Only supported for streaming requests on the Vertex AI API with Gemini 3 or newer.',
227
+ },
228
+ }),
229
+ },
230
+ requestConfig: {
231
+ language: {
232
+ systemInstruction: id.startsWith('gemma-') ? 'user' : 'system',
233
+ thinkingFormat,
234
+ reasoningMaxOutputTokens: 65536,
235
+ jsonResponseTool: /(^|\/)gemini-/i.test(modelId),
236
+ },
237
+ },
238
+ };
239
+ }
240
+
241
+ function declareProviderOptions(
242
+ support: ModelCapability,
243
+ exceptions: Partial<Record<GoogleLanguageProviderOption, ModelCapability>>,
244
+ ): Record<GoogleLanguageProviderOption, ModelCapability> {
245
+ return Object.fromEntries(
246
+ (
247
+ Object.keys(
248
+ googleProviderOptions.language,
249
+ ) as GoogleLanguageProviderOption[]
250
+ ).map(option => [option, exceptions[option] ?? support]),
251
+ ) as Record<GoogleLanguageProviderOption, ModelCapability>;
252
+ }
253
+
254
+ function isKnownPreGemini2Model(modelId: string): boolean {
255
+ return (
256
+ gemini1ModelPattern.test(modelId) ||
257
+ /(^|[/.])gemini-pro(?:-vision)?$/i.test(modelId) ||
258
+ /(^|[/.])gemini-robotics-er-1\.5(?:[.-]|$)/i.test(modelId)
259
+ );
260
+ }
261
+
262
+ /**
263
+ * Classifies Gemini capabilities by excluding known older generations.
264
+ *
265
+ * Google model IDs are open-ended, so unrecognized Gemini IDs and aliases
266
+ * intentionally inherit the newest supported behavior. Add exceptions here
267
+ * only when a model is known to require a legacy request shape.
268
+ */
269
+ function resolveRuntimeDefinition(modelId: string) {
270
+ const isGeminiModel = geminiModelPattern.test(modelId);
271
+ const isGemini2Model = gemini2ModelPattern.test(modelId);
272
+ const isKnownPreGemini2 = isKnownPreGemini2Model(modelId);
273
+ const isKnownOlderModel = isKnownPreGemini2 || isGemini2Model;
274
+ const usesGemini3Features = isGeminiModel && !isKnownOlderModel;
275
+
276
+ return {
277
+ supportsGemini2Tools:
278
+ (isGeminiModel && !isKnownPreGemini2) ||
279
+ modelId.toLowerCase().includes('nano-banana'),
280
+ supportsFileSearch:
281
+ gemini25ModelPattern.test(modelId) || usesGemini3Features,
282
+ usesGemini3Features,
283
+ };
284
+ }
285
+
286
+ function getMinimumThinkingLevelForGemini3Model(
287
+ modelId: string,
288
+ ): 'minimal' | 'low' {
289
+ const modelName = /(?:^|[/.])(gemini-[^/]*)$/i
290
+ .exec(modelId)?.[1]
291
+ .toLowerCase();
292
+
293
+ if (modelName === 'gemini-flash-latest') {
294
+ return 'low';
295
+ }
296
+
297
+ const proVersionMatch = /^gemini-(\d+)(?:\.\d+)?-pro(?:$|-)/.exec(
298
+ modelName ?? '',
299
+ );
300
+
301
+ if (Number(proVersionMatch?.[1]) >= 3) {
302
+ return 'low';
303
+ }
304
+
305
+ const versionMatch = /^gemini-(\d+)\.(\d+)-flash(?:$|-(?!lite(?:-|$)))/.exec(
306
+ modelName ?? '',
307
+ );
308
+
309
+ if (versionMatch == null) {
310
+ return 'minimal';
311
+ }
312
+
313
+ const majorVersion = Number(versionMatch[1]);
314
+ const minorVersion = Number(versionMatch[2]);
315
+
316
+ return majorVersion > 3 || (majorVersion === 3 && minorVersion >= 7)
317
+ ? 'low'
318
+ : 'minimal';
319
+ }
@@ -14,6 +14,8 @@ import type {
14
14
  TranscriptionModelV4,
15
15
  } from '@ai-sdk/provider';
16
16
  import {
17
+ resolveWith,
18
+ type Resolvable,
17
19
  generateId,
18
20
  loadApiKey,
19
21
  withoutTrailingSlash,
@@ -194,7 +196,7 @@ export interface GoogleProviderSettings {
194
196
  * API key that is being send using the `x-goog-api-key` header.
195
197
  * It defaults to the `GOOGLE_GENERATIVE_AI_API_KEY` environment variable.
196
198
  */
197
- apiKey?: string;
199
+ apiKey?: Resolvable<string>;
198
200
 
199
201
  /**
200
202
  * Custom headers to include in the requests.
@@ -267,16 +269,18 @@ export function createGoogle(
267
269
  const providerName = options.name ?? 'google.generative-ai';
268
270
 
269
271
  const getHeaders = () =>
270
- withUserAgentSuffix(
271
- {
272
- 'x-goog-api-key': loadApiKey({
273
- apiKey: options.apiKey,
274
- environmentVariableName: 'GOOGLE_GENERATIVE_AI_API_KEY',
275
- description: 'Google Generative AI',
276
- }),
277
- ...options.headers,
278
- },
279
- `ai-sdk-google/${VERSION}`,
272
+ resolveWith(options.apiKey, apiKey =>
273
+ withUserAgentSuffix(
274
+ {
275
+ 'x-goog-api-key': loadApiKey({
276
+ apiKey,
277
+ environmentVariableName: 'GOOGLE_GENERATIVE_AI_API_KEY',
278
+ description: 'Google Generative AI',
279
+ }),
280
+ ...options.headers,
281
+ },
282
+ `ai-sdk-google/${VERSION}`,
283
+ ),
280
284
  );
281
285
 
282
286
  const getSupportedUrls = (
@@ -1,4 +1,6 @@
1
1
  import {
2
+ resolve,
3
+ type Resolvable,
2
4
  combineHeaders,
3
5
  getRuntimeEnvironmentUserAgent,
4
6
  withUserAgentSuffix,
@@ -24,7 +26,7 @@ export async function cancelGoogleInteraction({
24
26
  }: {
25
27
  baseURL: string;
26
28
  interactionId: string | null | undefined;
27
- headers: Record<string, string | undefined>;
29
+ headers: Resolvable<Record<string, string | undefined>>;
28
30
  fetch?: FetchFunction;
29
31
  }): Promise<void> {
30
32
  if (interactionId == null || interactionId.length === 0) {
@@ -37,7 +39,10 @@ export async function cancelGoogleInteraction({
37
39
  const response = await fetch(url, {
38
40
  method: 'POST',
39
41
  headers: withUserAgentSuffix(
40
- combineHeaders({ 'Content-Type': 'application/json' }, headers),
42
+ combineHeaders(
43
+ { 'Content-Type': 'application/json' },
44
+ await resolve(headers),
45
+ ),
41
46
  getRuntimeEnvironmentUserAgent(),
42
47
  ),
43
48
  body: '{}',
@@ -487,14 +487,12 @@ export class GoogleInteractionsLanguageModel implements LanguageModelV4 {
487
487
 
488
488
  const url = `${this.config.baseURL}/interactions`;
489
489
 
490
- const mergedHeaders = combineHeaders(
491
- this.config.headers ? await resolve(this.config.headers) : undefined,
492
- options.headers,
493
- );
490
+ const mergedHeaders = async () =>
491
+ combineHeaders(await resolve(this.config.headers), options.headers);
494
492
 
495
493
  const postResult = await postJsonToApi({
496
494
  url,
497
- headers: mergedHeaders,
495
+ headers: await resolve(mergedHeaders),
498
496
  body: args,
499
497
  failedResponseHandler: googleFailedResponseHandler,
500
498
  successfulResponseHandler: createJsonResponseHandler(
@@ -620,10 +618,8 @@ export class GoogleInteractionsLanguageModel implements LanguageModelV4 {
620
618
 
621
619
  const url = `${this.config.baseURL}/interactions`;
622
620
 
623
- const mergedHeaders = combineHeaders(
624
- this.config.headers ? await resolve(this.config.headers) : undefined,
625
- options.headers,
626
- );
621
+ const mergedHeaders = async () =>
622
+ combineHeaders(await resolve(this.config.headers), options.headers);
627
623
 
628
624
  /*
629
625
  * `background: true` is incompatible with `stream: true` on POST. Drive
@@ -647,7 +643,7 @@ export class GoogleInteractionsLanguageModel implements LanguageModelV4 {
647
643
 
648
644
  const { responseHeaders, value: response } = await postJsonToApi({
649
645
  url,
650
- headers: mergedHeaders,
646
+ headers: await resolve(mergedHeaders),
651
647
  body,
652
648
  failedResponseHandler: googleFailedResponseHandler,
653
649
  successfulResponseHandler: createEventSourceResponseHandler(
@@ -709,13 +705,13 @@ export class GoogleInteractionsLanguageModel implements LanguageModelV4 {
709
705
  args: GoogleInteractionsRequestBody;
710
706
  warnings: Array<SharedV4Warning>;
711
707
  url: string;
712
- mergedHeaders: Record<string, string | undefined>;
708
+ mergedHeaders: Resolvable<Record<string, string | undefined>>;
713
709
  options: LanguageModelV4CallOptions;
714
710
  pollingTimeoutMs: number | undefined;
715
711
  }): Promise<LanguageModelV4StreamResult> {
716
712
  const postResult = await postJsonToApi({
717
713
  url,
718
- headers: mergedHeaders,
714
+ headers: await resolve(mergedHeaders),
719
715
  body: args,
720
716
  failedResponseHandler: googleFailedResponseHandler,
721
717
  successfulResponseHandler: createJsonResponseHandler(
@@ -1,4 +1,6 @@
1
1
  import {
2
+ resolve,
3
+ type Resolvable,
2
4
  createJsonResponseHandler,
3
5
  delay,
4
6
  getFromApi,
@@ -57,7 +59,7 @@ export async function pollGoogleInteractionUntilTerminal({
57
59
  }: {
58
60
  baseURL: string;
59
61
  interactionId: string | null | undefined;
60
- headers: Record<string, string | undefined>;
62
+ headers: Resolvable<Record<string, string | undefined>>;
61
63
  fetch?: FetchFunction;
62
64
  abortSignal?: AbortSignal;
63
65
  initialDelayMs?: number;
@@ -106,7 +108,7 @@ export async function pollGoogleInteractionUntilTerminal({
106
108
  } = await getFromApi({
107
109
  url,
108
110
  validateUrl: false,
109
- headers,
111
+ headers: await resolve(headers),
110
112
  failedResponseHandler: googleFailedResponseHandler,
111
113
  successfulResponseHandler: createJsonResponseHandler(
112
114
  googleInteractionsResponseSchema,
@@ -1,4 +1,6 @@
1
1
  import {
2
+ resolve,
3
+ type Resolvable,
2
4
  createEventSourceResponseHandler,
3
5
  delay,
4
6
  getFromApi,
@@ -43,7 +45,7 @@ export function streamGoogleInteractionEvents({
43
45
  }: {
44
46
  baseURL: string;
45
47
  interactionId: string;
46
- headers: Record<string, string | undefined>;
48
+ headers: Resolvable<Record<string, string | undefined>>;
47
49
  fetch?: FetchFunction;
48
50
  abortSignal?: AbortSignal;
49
51
  maxRetries?: number;
@@ -55,11 +57,6 @@ export function streamGoogleInteractionEvents({
55
57
  );
56
58
  }
57
59
 
58
- const eventSourceHeaders = {
59
- ...headers,
60
- accept: 'text/event-stream',
61
- };
62
-
63
60
  let lastEventId: string | undefined;
64
61
  let complete = false;
65
62
  let attempt = 0;
@@ -99,7 +96,7 @@ export function streamGoogleInteractionEvents({
99
96
  const { value: stream } = await getFromApi({
100
97
  url: buildUrl(),
101
98
  validateUrl: false,
102
- headers: eventSourceHeaders,
99
+ headers: { ...(await resolve(headers)), accept: 'text/event-stream' },
103
100
  failedResponseHandler: googleFailedResponseHandler,
104
101
  successfulResponseHandler: createEventSourceResponseHandler(
105
102
  googleInteractionsEventSchema,
@@ -6,7 +6,11 @@ import type {
6
6
  Experimental_RealtimeModelV4ServerEvent as RealtimeModelV4ServerEvent,
7
7
  Experimental_RealtimeModelV4SessionConfig as RealtimeModelV4SessionConfig,
8
8
  } from '@ai-sdk/provider';
9
- import type { FetchFunction } from '@ai-sdk/provider-utils';
9
+ import {
10
+ resolve,
11
+ type Resolvable,
12
+ type FetchFunction,
13
+ } from '@ai-sdk/provider-utils';
10
14
  import {
11
15
  getRealtimeBaseURL,
12
16
  getRealtimeWebSocketURL,
@@ -32,7 +36,7 @@ function getWebSocketURL(baseURL: string): string {
32
36
  export type GoogleRealtimeModelConfig = {
33
37
  provider: string;
34
38
  baseURL: string;
35
- headers: () => Record<string, string | undefined>;
39
+ headers: Resolvable<Record<string, string | undefined>>;
36
40
  fetch?: FetchFunction;
37
41
  };
38
42
 
@@ -54,7 +58,7 @@ export class GoogleRealtimeModel implements RealtimeModelV4 {
54
58
  options: RealtimeModelV4ClientSecretOptions,
55
59
  ): Promise<RealtimeModelV4ClientSecretResult> {
56
60
  const fetchFn = this.config.fetch ?? fetch;
57
- const headers = this.config.headers();
61
+ const headers = await resolve(this.config.headers);
58
62
  const apiKey = headers['x-goog-api-key'];
59
63
 
60
64
  if (!apiKey) {
@@ -7,6 +7,8 @@ import {
7
7
  type SharedV4Warning,
8
8
  } from '@ai-sdk/provider';
9
9
  import {
10
+ resolve,
11
+ type Resolvable,
10
12
  connectToWebSocket,
11
13
  combineHeaders,
12
14
  convertBase64ToUint8Array,
@@ -74,7 +76,7 @@ type GoogleLiveServerMessage = {
74
76
  export type GoogleSpeechTranslationModelConfig = {
75
77
  provider: string;
76
78
  baseURL: string;
77
- headers: () => Record<string, string | undefined>;
79
+ headers: Resolvable<Record<string, string | undefined>>;
78
80
  webSocket?: WebSocketConstructor;
79
81
  _internal?: {
80
82
  currentDate?: () => Date;
@@ -152,7 +154,10 @@ export class GoogleSpeechTranslationModel implements SpeechTranslationModelV4 {
152
154
  });
153
155
  }
154
156
 
155
- const headers = combineHeaders(this.config.headers(), options.headers);
157
+ const headers = combineHeaders(
158
+ await resolve(this.config.headers),
159
+ options.headers,
160
+ );
156
161
  // last case-variant wins: combineHeaders keeps case-distinct keys and
157
162
  // spreads per-call headers after configuration headers
158
163
  let apiKey: string | undefined;