@ai-sdk/google
Version:
The **[Google Generative AI provider](https://ai-sdk.dev/providers/ai-sdk-providers/google-generative-ai)** for the [AI SDK](https://ai-sdk.dev/docs) contains language model support for the [Google Generative AI](https://ai.google/discover/generativeai/)
1,475 lines (1,460 loc) • 217 kB
JavaScript
// src/google-provider.ts
import {
generateId as generateId2,
loadApiKey,
withoutTrailingSlash,
withUserAgentSuffix as withUserAgentSuffix2
} from "@ai-sdk/provider-utils";
// src/version.ts
var VERSION = true ? "3.0.79" : "0.0.0-test";
// src/google-generative-ai-embedding-model.ts
import {
TooManyEmbeddingValuesForCallError
} from "@ai-sdk/provider";
import {
combineHeaders,
createJsonResponseHandler,
lazySchema as lazySchema3,
parseProviderOptions,
postJsonToApi,
resolve,
zodSchema as zodSchema3
} from "@ai-sdk/provider-utils";
import { z as z3 } from "zod/v4";
// src/google-error.ts
import {
createJsonErrorResponseHandler,
lazySchema,
zodSchema
} from "@ai-sdk/provider-utils";
import { z } from "zod/v4";
var googleErrorDataSchema = lazySchema(
() => zodSchema(
z.object({
error: z.object({
code: z.number().nullable(),
message: z.string(),
status: z.string()
})
})
)
);
var googleFailedResponseHandler = createJsonErrorResponseHandler({
errorSchema: googleErrorDataSchema,
errorToMessage: (data) => data.error.message
});
// src/google-generative-ai-embedding-options.ts
import {
lazySchema as lazySchema2,
zodSchema as zodSchema2
} from "@ai-sdk/provider-utils";
import { z as z2 } from "zod/v4";
var googleEmbeddingContentPartSchema = z2.union([
z2.object({ text: z2.string() }),
z2.object({
inlineData: z2.object({
mimeType: z2.string(),
data: z2.string()
})
}),
z2.object({
fileData: z2.object({
fileUri: z2.string(),
mimeType: z2.string()
})
})
]);
var googleEmbeddingModelOptions = lazySchema2(
() => zodSchema2(
z2.object({
/**
* Optional. Optional reduced dimension for the output embedding.
* If set, excessive values in the output embedding are truncated from the end.
*/
outputDimensionality: z2.number().optional(),
/**
* Optional. Specifies the task type for generating embeddings.
* Supported task types:
* - SEMANTIC_SIMILARITY: Optimized for text similarity.
* - CLASSIFICATION: Optimized for text classification.
* - CLUSTERING: Optimized for clustering texts based on similarity.
* - RETRIEVAL_DOCUMENT: Optimized for document retrieval.
* - RETRIEVAL_QUERY: Optimized for query-based retrieval.
* - QUESTION_ANSWERING: Optimized for answering questions.
* - FACT_VERIFICATION: Optimized for verifying factual information.
* - CODE_RETRIEVAL_QUERY: Optimized for retrieving code blocks based on natural language queries.
*/
taskType: z2.enum([
"SEMANTIC_SIMILARITY",
"CLASSIFICATION",
"CLUSTERING",
"RETRIEVAL_DOCUMENT",
"RETRIEVAL_QUERY",
"QUESTION_ANSWERING",
"FACT_VERIFICATION",
"CODE_RETRIEVAL_QUERY"
]).optional(),
/**
* Optional. Per-value multimodal content parts for embedding non-text
* content (images, video, PDF, audio). Each entry corresponds to the
* embedding value at the same index and its parts are merged with the
* text value in the request. Use `null` for entries that are text-only.
*
* The array length must match the number of values being embedded. In
* the case of a single embedding, the array length must be 1.
*/
content: z2.array(z2.array(googleEmbeddingContentPartSchema).min(1).nullable()).optional()
})
)
);
// src/google-generative-ai-embedding-model.ts
var GoogleGenerativeAIEmbeddingModel = class {
constructor(modelId, config) {
this.specificationVersion = "v3";
this.maxEmbeddingsPerCall = 2048;
this.supportsParallelCalls = true;
this.modelId = modelId;
this.config = config;
}
get provider() {
return this.config.provider;
}
async doEmbed({
values,
headers,
abortSignal,
providerOptions
}) {
const googleOptions = await parseProviderOptions({
provider: "google",
providerOptions,
schema: googleEmbeddingModelOptions
});
if (values.length > this.maxEmbeddingsPerCall) {
throw new TooManyEmbeddingValuesForCallError({
provider: this.provider,
modelId: this.modelId,
maxEmbeddingsPerCall: this.maxEmbeddingsPerCall,
values
});
}
const mergedHeaders = combineHeaders(
await resolve(this.config.headers),
headers
);
const multimodalContent = googleOptions == null ? void 0 : googleOptions.content;
if (multimodalContent != null && multimodalContent.length !== values.length) {
throw new Error(
`The number of multimodal content entries (${multimodalContent.length}) must match the number of values (${values.length}).`
);
}
if (values.length === 1) {
const valueParts = multimodalContent == null ? void 0 : multimodalContent[0];
const textPart = values[0] ? [{ text: values[0] }] : [];
const parts = valueParts != null ? [...textPart, ...valueParts] : [{ text: values[0] }];
const {
responseHeaders: responseHeaders2,
value: response2,
rawValue: rawValue2
} = await postJsonToApi({
url: `${this.config.baseURL}/models/${this.modelId}:embedContent`,
headers: mergedHeaders,
body: {
model: `models/${this.modelId}`,
content: {
parts
},
outputDimensionality: googleOptions == null ? void 0 : googleOptions.outputDimensionality,
taskType: googleOptions == null ? void 0 : googleOptions.taskType
},
failedResponseHandler: googleFailedResponseHandler,
successfulResponseHandler: createJsonResponseHandler(
googleGenerativeAISingleEmbeddingResponseSchema
),
abortSignal,
fetch: this.config.fetch
});
return {
warnings: [],
embeddings: [response2.embedding.values],
usage: void 0,
response: { headers: responseHeaders2, body: rawValue2 }
};
}
const {
responseHeaders,
value: response,
rawValue
} = await postJsonToApi({
url: `${this.config.baseURL}/models/${this.modelId}:batchEmbedContents`,
headers: mergedHeaders,
body: {
requests: values.map((value, index) => {
const valueParts = multimodalContent == null ? void 0 : multimodalContent[index];
const textPart = value ? [{ text: value }] : [];
return {
model: `models/${this.modelId}`,
content: {
role: "user",
parts: valueParts != null ? [...textPart, ...valueParts] : [{ text: value }]
},
outputDimensionality: googleOptions == null ? void 0 : googleOptions.outputDimensionality,
taskType: googleOptions == null ? void 0 : googleOptions.taskType
};
})
},
failedResponseHandler: googleFailedResponseHandler,
successfulResponseHandler: createJsonResponseHandler(
googleGenerativeAITextEmbeddingResponseSchema
),
abortSignal,
fetch: this.config.fetch
});
return {
warnings: [],
embeddings: response.embeddings.map((item) => item.values),
usage: void 0,
response: { headers: responseHeaders, body: rawValue }
};
}
};
var googleGenerativeAITextEmbeddingResponseSchema = lazySchema3(
() => zodSchema3(
z3.object({
embeddings: z3.array(z3.object({ values: z3.array(z3.number()) }))
})
)
);
var googleGenerativeAISingleEmbeddingResponseSchema = lazySchema3(
() => zodSchema3(
z3.object({
embedding: z3.object({ values: z3.array(z3.number()) })
})
)
);
// src/google-generative-ai-language-model.ts
import {
combineHeaders as combineHeaders2,
createEventSourceResponseHandler,
createJsonResponseHandler as createJsonResponseHandler2,
generateId,
lazySchema as lazySchema5,
parseProviderOptions as parseProviderOptions2,
postJsonToApi as postJsonToApi2,
resolve as resolve2,
zodSchema as zodSchema5
} from "@ai-sdk/provider-utils";
import { z as z5 } from "zod/v4";
// src/convert-google-generative-ai-usage.ts
function convertGoogleGenerativeAIUsage(usage) {
var _a, _b, _c, _d;
if (usage == null) {
return {
inputTokens: {
total: void 0,
noCache: void 0,
cacheRead: void 0,
cacheWrite: void 0
},
outputTokens: {
total: void 0,
text: void 0,
reasoning: void 0
},
raw: void 0
};
}
const promptTokens = (_a = usage.promptTokenCount) != null ? _a : 0;
const candidatesTokens = (_b = usage.candidatesTokenCount) != null ? _b : 0;
const cachedContentTokens = (_c = usage.cachedContentTokenCount) != null ? _c : 0;
const thoughtsTokens = (_d = usage.thoughtsTokenCount) != null ? _d : 0;
return {
inputTokens: {
total: promptTokens,
noCache: promptTokens - cachedContentTokens,
cacheRead: cachedContentTokens,
cacheWrite: void 0
},
outputTokens: {
total: candidatesTokens + thoughtsTokens,
text: candidatesTokens,
reasoning: thoughtsTokens
},
raw: usage
};
}
// src/convert-json-schema-to-openapi-schema.ts
function convertJSONSchemaToOpenAPISchema(jsonSchema, isRoot = true) {
if (jsonSchema == null) {
return void 0;
}
if (isEmptyObjectSchema(jsonSchema)) {
if (isRoot) {
return void 0;
}
if (typeof jsonSchema === "object" && jsonSchema.description) {
return { type: "object", description: jsonSchema.description };
}
return { type: "object" };
}
if (typeof jsonSchema === "boolean") {
return { type: "boolean", properties: {} };
}
const {
type,
description,
required,
properties,
items,
allOf,
anyOf,
oneOf,
format,
const: constValue,
minLength,
enum: enumValues
} = jsonSchema;
const result = {};
if (description) result.description = description;
if (required) result.required = required;
if (format) result.format = format;
if (constValue !== void 0) {
result.enum = [constValue];
}
if (type) {
if (Array.isArray(type)) {
const hasNull = type.includes("null");
const nonNullTypes = type.filter((t) => t !== "null");
if (nonNullTypes.length === 0) {
result.type = "null";
} else {
result.anyOf = nonNullTypes.map((t) => ({ type: t }));
if (hasNull) {
result.nullable = true;
}
}
} else {
result.type = type;
}
}
if (enumValues !== void 0) {
result.enum = enumValues;
}
if (properties != null) {
result.properties = Object.entries(properties).reduce(
(acc, [key, value]) => {
acc[key] = convertJSONSchemaToOpenAPISchema(value, false);
return acc;
},
{}
);
}
if (items) {
result.items = Array.isArray(items) ? items.map((item) => convertJSONSchemaToOpenAPISchema(item, false)) : convertJSONSchemaToOpenAPISchema(items, false);
}
if (allOf) {
result.allOf = allOf.map(
(item) => convertJSONSchemaToOpenAPISchema(item, false)
);
}
if (anyOf) {
if (anyOf.some(
(schema) => typeof schema === "object" && (schema == null ? void 0 : schema.type) === "null"
)) {
const nonNullSchemas = anyOf.filter(
(schema) => !(typeof schema === "object" && (schema == null ? void 0 : schema.type) === "null")
);
if (nonNullSchemas.length === 1) {
const converted = convertJSONSchemaToOpenAPISchema(
nonNullSchemas[0],
false
);
if (typeof converted === "object") {
result.nullable = true;
Object.assign(result, converted);
}
} else {
result.anyOf = nonNullSchemas.map(
(item) => convertJSONSchemaToOpenAPISchema(item, false)
);
result.nullable = true;
}
} else {
result.anyOf = anyOf.map(
(item) => convertJSONSchemaToOpenAPISchema(item, false)
);
}
}
if (oneOf) {
result.oneOf = oneOf.map(
(item) => convertJSONSchemaToOpenAPISchema(item, false)
);
}
if (minLength !== void 0) {
result.minLength = minLength;
}
return result;
}
function isEmptyObjectSchema(jsonSchema) {
return jsonSchema != null && typeof jsonSchema === "object" && jsonSchema.type === "object" && (jsonSchema.properties == null || Object.keys(jsonSchema.properties).length === 0) && !jsonSchema.additionalProperties;
}
// src/convert-to-google-generative-ai-messages.ts
import {
UnsupportedFunctionalityError
} from "@ai-sdk/provider";
import { convertToBase64 } from "@ai-sdk/provider-utils";
var dataUrlRegex = /^data:([^;,]+);base64,(.+)$/s;
function parseBase64DataUrl(value) {
const match = dataUrlRegex.exec(value);
if (match == null) {
return void 0;
}
return {
mediaType: match[1],
data: match[2]
};
}
function convertUrlToolResultPart(url) {
const parsedDataUrl = parseBase64DataUrl(url);
if (parsedDataUrl == null) {
return void 0;
}
return {
inlineData: {
mimeType: parsedDataUrl.mediaType,
data: parsedDataUrl.data
}
};
}
function appendToolResultParts(parts, toolName, outputValue, toolCallId) {
const functionResponseParts = [];
const responseTextParts = [];
for (const contentPart of outputValue) {
switch (contentPart.type) {
case "text": {
responseTextParts.push(contentPart.text);
break;
}
case "image-data":
case "file-data": {
functionResponseParts.push({
inlineData: {
mimeType: contentPart.mediaType,
data: contentPart.data
}
});
break;
}
case "image-url":
case "file-url": {
const functionResponsePart = convertUrlToolResultPart(
contentPart.url
);
if (functionResponsePart != null) {
functionResponseParts.push(functionResponsePart);
} else {
responseTextParts.push(JSON.stringify(contentPart));
}
break;
}
default: {
responseTextParts.push(JSON.stringify(contentPart));
break;
}
}
}
parts.push({
functionResponse: {
...toolCallId != null ? { id: toolCallId } : {},
name: toolName,
response: {
name: toolName,
content: responseTextParts.length > 0 ? responseTextParts.join("\n") : "Tool executed successfully."
},
...functionResponseParts.length > 0 ? { parts: functionResponseParts } : {}
}
});
}
function appendLegacyToolResultParts(parts, toolName, outputValue, toolCallId) {
for (const contentPart of outputValue) {
switch (contentPart.type) {
case "text":
parts.push({
functionResponse: {
...toolCallId != null ? { id: toolCallId } : {},
name: toolName,
response: {
name: toolName,
content: contentPart.text
}
}
});
break;
case "image-data":
parts.push(
{
inlineData: {
mimeType: String(contentPart.mediaType),
data: String(contentPart.data)
}
},
{
text: "Tool executed successfully and returned this image as a response"
}
);
break;
default:
parts.push({ text: JSON.stringify(contentPart) });
break;
}
}
}
function convertToGoogleGenerativeAIMessages(prompt, options) {
var _a, _b, _c, _d, _e, _f, _g, _h;
const systemInstructionParts = [];
const contents = [];
let systemMessagesAllowed = true;
const isGemmaModel = (_a = options == null ? void 0 : options.isGemmaModel) != null ? _a : false;
const providerOptionsName = (_b = options == null ? void 0 : options.providerOptionsName) != null ? _b : "google";
const supportsFunctionResponseParts = (_c = options == null ? void 0 : options.supportsFunctionResponseParts) != null ? _c : true;
for (const { role, content } of prompt) {
switch (role) {
case "system": {
if (!systemMessagesAllowed) {
throw new UnsupportedFunctionalityError({
functionality: "system messages are only supported at the beginning of the conversation"
});
}
systemInstructionParts.push({ text: content });
break;
}
case "user": {
systemMessagesAllowed = false;
const parts = [];
for (const part of content) {
switch (part.type) {
case "text": {
parts.push({ text: part.text });
break;
}
case "file": {
const mediaType = part.mediaType === "image/*" ? "image/jpeg" : part.mediaType;
parts.push(
part.data instanceof URL ? {
fileData: {
mimeType: mediaType,
fileUri: part.data.toString()
}
} : {
inlineData: {
mimeType: mediaType,
data: convertToBase64(part.data)
}
}
);
break;
}
}
}
contents.push({ role: "user", parts });
break;
}
case "assistant": {
systemMessagesAllowed = false;
contents.push({
role: "model",
parts: content.map((part) => {
var _a2, _b2, _c2, _d2;
const providerOpts = (_d2 = (_a2 = part.providerOptions) == null ? void 0 : _a2[providerOptionsName]) != null ? _d2 : providerOptionsName !== "google" ? (_b2 = part.providerOptions) == null ? void 0 : _b2.google : (_c2 = part.providerOptions) == null ? void 0 : _c2.vertex;
const thoughtSignature = (providerOpts == null ? void 0 : providerOpts.thoughtSignature) != null ? String(providerOpts.thoughtSignature) : void 0;
switch (part.type) {
case "text": {
return part.text.length === 0 ? void 0 : {
text: part.text,
thoughtSignature
};
}
case "reasoning": {
return part.text.length === 0 ? void 0 : {
text: part.text,
thought: true,
thoughtSignature
};
}
case "file": {
if (part.data instanceof URL) {
throw new UnsupportedFunctionalityError({
functionality: "File data URLs in assistant messages are not supported"
});
}
return {
inlineData: {
mimeType: part.mediaType,
data: convertToBase64(part.data)
},
...(providerOpts == null ? void 0 : providerOpts.thought) === true ? { thought: true } : {},
thoughtSignature
};
}
case "tool-call": {
const serverToolCallId = (providerOpts == null ? void 0 : providerOpts.serverToolCallId) != null ? String(providerOpts.serverToolCallId) : void 0;
const serverToolType = (providerOpts == null ? void 0 : providerOpts.serverToolType) != null ? String(providerOpts.serverToolType) : void 0;
if (serverToolCallId && serverToolType) {
return {
toolCall: {
toolType: serverToolType,
args: typeof part.input === "string" ? JSON.parse(part.input) : part.input,
id: serverToolCallId
},
thoughtSignature
};
}
return {
functionCall: {
...part.toolCallId != null ? { id: part.toolCallId } : {},
name: part.toolName,
args: part.input
},
thoughtSignature
};
}
case "tool-result": {
const serverToolCallId = (providerOpts == null ? void 0 : providerOpts.serverToolCallId) != null ? String(providerOpts.serverToolCallId) : void 0;
const serverToolType = (providerOpts == null ? void 0 : providerOpts.serverToolType) != null ? String(providerOpts.serverToolType) : void 0;
if (serverToolCallId && serverToolType) {
return {
toolResponse: {
toolType: serverToolType,
response: part.output.type === "json" ? part.output.value : {},
id: serverToolCallId
},
thoughtSignature
};
}
return void 0;
}
}
}).filter((part) => part !== void 0)
});
break;
}
case "tool": {
systemMessagesAllowed = false;
const parts = [];
for (const part of content) {
if (part.type === "tool-approval-response") {
continue;
}
const partProviderOpts = (_g = (_d = part.providerOptions) == null ? void 0 : _d[providerOptionsName]) != null ? _g : providerOptionsName !== "google" ? (_e = part.providerOptions) == null ? void 0 : _e.google : (_f = part.providerOptions) == null ? void 0 : _f.vertex;
const serverToolCallId = (partProviderOpts == null ? void 0 : partProviderOpts.serverToolCallId) != null ? String(partProviderOpts.serverToolCallId) : void 0;
const serverToolType = (partProviderOpts == null ? void 0 : partProviderOpts.serverToolType) != null ? String(partProviderOpts.serverToolType) : void 0;
if (serverToolCallId && serverToolType) {
const serverThoughtSignature = (partProviderOpts == null ? void 0 : partProviderOpts.thoughtSignature) != null ? String(partProviderOpts.thoughtSignature) : void 0;
if (contents.length > 0) {
const lastContent = contents[contents.length - 1];
if (lastContent.role === "model") {
lastContent.parts.push({
toolResponse: {
toolType: serverToolType,
response: part.output.type === "json" ? part.output.value : {},
id: serverToolCallId
},
thoughtSignature: serverThoughtSignature
});
continue;
}
}
}
const output = part.output;
if (output.type === "content") {
if (supportsFunctionResponseParts) {
appendToolResultParts(
parts,
part.toolName,
output.value,
part.toolCallId
);
} else {
appendLegacyToolResultParts(
parts,
part.toolName,
output.value,
part.toolCallId
);
}
} else {
parts.push({
functionResponse: {
...part.toolCallId != null ? { id: part.toolCallId } : {},
name: part.toolName,
response: {
name: part.toolName,
content: output.type === "execution-denied" ? (_h = output.reason) != null ? _h : "Tool execution denied." : output.value
}
}
});
}
}
contents.push({
role: "user",
parts
});
break;
}
}
}
if (isGemmaModel && systemInstructionParts.length > 0 && contents.length > 0 && contents[0].role === "user") {
const systemText = systemInstructionParts.map((part) => part.text).join("\n\n");
contents[0].parts.unshift({ text: systemText + "\n\n" });
}
return {
systemInstruction: systemInstructionParts.length > 0 && !isGemmaModel ? { parts: systemInstructionParts } : void 0,
contents
};
}
// src/get-model-path.ts
function getModelPath(modelId) {
return modelId.includes("/") ? modelId : `models/${modelId}`;
}
// src/google-generative-ai-options.ts
import {
lazySchema as lazySchema4,
zodSchema as zodSchema4
} from "@ai-sdk/provider-utils";
import { z as z4 } from "zod/v4";
var googleLanguageModelOptions = lazySchema4(
() => zodSchema4(
z4.object({
responseModalities: z4.array(z4.enum(["TEXT", "IMAGE"])).optional(),
thinkingConfig: z4.object({
thinkingBudget: z4.number().optional(),
includeThoughts: z4.boolean().optional(),
// https://ai.google.dev/gemini-api/docs/gemini-3?thinking=high#thinking_level
thinkingLevel: z4.enum(["minimal", "low", "medium", "high"]).optional()
}).optional(),
/**
* Optional.
* The name of the cached content used as context to serve the prediction.
* Format: cachedContents/{cachedContent}
*/
cachedContent: z4.string().optional(),
/**
* Optional. Enable structured output. Default is true.
*
* This is useful when the JSON Schema contains elements that are
* not supported by the OpenAPI schema version that
* Google Generative AI uses. You can use this to disable
* structured outputs if you need to.
*/
structuredOutputs: z4.boolean().optional(),
/**
* Optional. A list of unique safety settings for blocking unsafe content.
*/
safetySettings: z4.array(
z4.object({
category: z4.enum([
"HARM_CATEGORY_UNSPECIFIED",
"HARM_CATEGORY_HATE_SPEECH",
"HARM_CATEGORY_DANGEROUS_CONTENT",
"HARM_CATEGORY_HARASSMENT",
"HARM_CATEGORY_SEXUALLY_EXPLICIT",
"HARM_CATEGORY_CIVIC_INTEGRITY"
]),
threshold: z4.enum([
"HARM_BLOCK_THRESHOLD_UNSPECIFIED",
"BLOCK_LOW_AND_ABOVE",
"BLOCK_MEDIUM_AND_ABOVE",
"BLOCK_ONLY_HIGH",
"BLOCK_NONE",
"OFF"
])
})
).optional(),
threshold: z4.enum([
"HARM_BLOCK_THRESHOLD_UNSPECIFIED",
"BLOCK_LOW_AND_ABOVE",
"BLOCK_MEDIUM_AND_ABOVE",
"BLOCK_ONLY_HIGH",
"BLOCK_NONE",
"OFF"
]).optional(),
/**
* Optional. Enables timestamp understanding for audio-only files.
*
* https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/audio-understanding
*/
audioTimestamp: z4.boolean().optional(),
/**
* Optional. Defines labels used in billing reports. Available on Vertex AI only.
*
* https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/add-labels-to-api-calls
*/
labels: z4.record(z4.string(), z4.string()).optional(),
/**
* Optional. If specified, the media resolution specified will be used.
*
* https://ai.google.dev/api/generate-content#MediaResolution
*/
mediaResolution: z4.enum([
"MEDIA_RESOLUTION_UNSPECIFIED",
"MEDIA_RESOLUTION_LOW",
"MEDIA_RESOLUTION_MEDIUM",
"MEDIA_RESOLUTION_HIGH"
]).optional(),
/**
* Optional. Configures the image generation aspect ratio for Gemini models.
*
* https://ai.google.dev/gemini-api/docs/image-generation#aspect_ratios
*/
imageConfig: z4.object({
aspectRatio: z4.enum([
"1:1",
"2:3",
"3:2",
"3:4",
"4:3",
"4:5",
"5:4",
"9:16",
"16:9",
"21:9",
"1:8",
"8:1",
"1:4",
"4:1"
]).optional(),
imageSize: z4.enum(["1K", "2K", "4K", "512"]).optional()
}).optional(),
/**
* Optional. Configuration for grounding retrieval.
* Used to provide location context for Google Maps and Google Search grounding.
*
* https://cloud.google.com/vertex-ai/generative-ai/docs/grounding/grounding-with-google-maps
*/
retrievalConfig: z4.object({
latLng: z4.object({
latitude: z4.number(),
longitude: z4.number()
}).optional()
}).optional(),
/**
* Optional. When set to true, function call arguments will be streamed
* incrementally via partialArgs in streaming responses. Only supported
* on the Vertex AI API (not the Gemini API) and only for Gemini 3+
* models.
*
* @default false
*
* https://docs.cloud.google.com/vertex-ai/generative-ai/docs/multimodal/function-calling#streaming-fc
*/
streamFunctionCallArguments: z4.boolean().optional(),
/**
* Optional. The service tier to use for the request. Sent as the
* `serviceTier` body field. Gemini API only.
*/
serviceTier: z4.enum(["standard", "flex", "priority"]).optional(),
/**
* Optional. Vertex AI only. Sent as the
* `X-Vertex-AI-LLM-Shared-Request-Type` request header to select a
* shared (PayGo) tier. With Provisioned Throughput allocated and
* `requestType` unset, the request falls back to this tier only if
* PT capacity is exhausted.
*
* https://docs.cloud.google.com/vertex-ai/generative-ai/docs/priority-paygo
* https://docs.cloud.google.com/vertex-ai/generative-ai/docs/flex-paygo
*/
sharedRequestType: z4.enum(["priority", "flex", "standard"]).optional(),
/**
* Optional. Vertex AI only. Sent as the `X-Vertex-AI-LLM-Request-Type`
* request header. Set to `'shared'` together with `sharedRequestType`
* to bypass Provisioned Throughput entirely.
*
* https://docs.cloud.google.com/vertex-ai/generative-ai/docs/priority-paygo
*/
requestType: z4.enum(["shared"]).optional()
})
)
);
// src/google-prepare-tools.ts
import {
UnsupportedFunctionalityError as UnsupportedFunctionalityError2
} from "@ai-sdk/provider";
function prepareTools({
tools,
toolChoice,
modelId,
isVertexProvider = false
}) {
var _a, _b;
tools = (tools == null ? void 0 : tools.length) ? tools : void 0;
const toolWarnings = [];
const isLatest = [
"gemini-flash-latest",
"gemini-flash-lite-latest",
"gemini-pro-latest"
].some((id) => id === modelId);
const isGemini2orNewer = modelId.includes("gemini-2") || modelId.includes("gemini-3") || modelId.includes("nano-banana") || isLatest;
const isGemini3orNewer = modelId.includes("gemini-3");
const supportsFileSearch = modelId.includes("gemini-2.5") || modelId.includes("gemini-3");
if (tools == null) {
return { tools: void 0, toolConfig: void 0, toolWarnings };
}
const hasFunctionTools = tools.some((tool) => tool.type === "function");
const hasProviderTools = tools.some((tool) => tool.type === "provider");
if (hasFunctionTools && hasProviderTools && !isGemini3orNewer) {
toolWarnings.push({
type: "unsupported",
feature: `combination of function and provider-defined tools`
});
}
if (hasProviderTools) {
const googleTools2 = [];
const ProviderTools = tools.filter((tool) => tool.type === "provider");
ProviderTools.forEach((tool) => {
switch (tool.id) {
case "google.google_search":
if (isGemini2orNewer) {
googleTools2.push({ googleSearch: { ...tool.args } });
} else {
toolWarnings.push({
type: "unsupported",
feature: `provider-defined tool ${tool.id}`,
details: "Google Search requires Gemini 2.0 or newer."
});
}
break;
case "google.enterprise_web_search":
if (isGemini2orNewer) {
googleTools2.push({ enterpriseWebSearch: {} });
} else {
toolWarnings.push({
type: "unsupported",
feature: `provider-defined tool ${tool.id}`,
details: "Enterprise Web Search requires Gemini 2.0 or newer."
});
}
break;
case "google.url_context":
if (isGemini2orNewer) {
googleTools2.push({ urlContext: {} });
} else {
toolWarnings.push({
type: "unsupported",
feature: `provider-defined tool ${tool.id}`,
details: "The URL context tool is not supported with other Gemini models than Gemini 2."
});
}
break;
case "google.code_execution":
if (isGemini2orNewer) {
googleTools2.push({ codeExecution: {} });
} else {
toolWarnings.push({
type: "unsupported",
feature: `provider-defined tool ${tool.id}`,
details: "The code execution tool is not supported with other Gemini models than Gemini 2."
});
}
break;
case "google.file_search":
if (supportsFileSearch) {
googleTools2.push({ fileSearch: { ...tool.args } });
} else {
toolWarnings.push({
type: "unsupported",
feature: `provider-defined tool ${tool.id}`,
details: "The file search tool is only supported with Gemini 2.5 models and Gemini 3 models."
});
}
break;
case "google.vertex_rag_store":
if (isGemini2orNewer) {
googleTools2.push({
retrieval: {
vertex_rag_store: {
rag_resources: {
rag_corpus: tool.args.ragCorpus
},
similarity_top_k: tool.args.topK
}
}
});
} else {
toolWarnings.push({
type: "unsupported",
feature: `provider-defined tool ${tool.id}`,
details: "The RAG store tool is not supported with other Gemini models than Gemini 2."
});
}
break;
case "google.google_maps":
if (isGemini2orNewer) {
googleTools2.push({ googleMaps: {} });
} else {
toolWarnings.push({
type: "unsupported",
feature: `provider-defined tool ${tool.id}`,
details: "The Google Maps grounding tool is not supported with Gemini models other than Gemini 2 or newer."
});
}
break;
default:
toolWarnings.push({
type: "unsupported",
feature: `provider-defined tool ${tool.id}`
});
break;
}
});
if (hasFunctionTools && isGemini3orNewer && googleTools2.length > 0) {
const functionDeclarations2 = [];
for (const tool of tools) {
if (tool.type === "function") {
functionDeclarations2.push({
name: tool.name,
description: (_a = tool.description) != null ? _a : "",
parameters: convertJSONSchemaToOpenAPISchema(tool.inputSchema)
});
}
}
const combinedToolConfig = {
functionCallingConfig: { mode: "VALIDATED" },
...!isVertexProvider && {
includeServerSideToolInvocations: true
}
};
if (toolChoice != null) {
switch (toolChoice.type) {
case "auto":
break;
case "none":
combinedToolConfig.functionCallingConfig = { mode: "NONE" };
break;
case "required":
combinedToolConfig.functionCallingConfig = { mode: "ANY" };
break;
case "tool":
combinedToolConfig.functionCallingConfig = {
mode: "ANY",
allowedFunctionNames: [toolChoice.toolName]
};
break;
}
}
return {
tools: [...googleTools2, { functionDeclarations: functionDeclarations2 }],
toolConfig: combinedToolConfig,
toolWarnings
};
}
return {
tools: googleTools2.length > 0 ? googleTools2 : void 0,
toolConfig: void 0,
toolWarnings
};
}
const functionDeclarations = [];
let hasStrictTools = false;
for (const tool of tools) {
switch (tool.type) {
case "function":
functionDeclarations.push({
name: tool.name,
description: (_b = tool.description) != null ? _b : "",
parameters: convertJSONSchemaToOpenAPISchema(tool.inputSchema)
});
if (tool.strict === true) {
hasStrictTools = true;
}
break;
default:
toolWarnings.push({
type: "unsupported",
feature: `function tool ${tool.name}`
});
break;
}
}
if (toolChoice == null) {
return {
tools: [{ functionDeclarations }],
toolConfig: hasStrictTools ? { functionCallingConfig: { mode: "VALIDATED" } } : void 0,
toolWarnings
};
}
const type = toolChoice.type;
switch (type) {
case "auto":
return {
tools: [{ functionDeclarations }],
toolConfig: {
functionCallingConfig: {
mode: hasStrictTools ? "VALIDATED" : "AUTO"
}
},
toolWarnings
};
case "none":
return {
tools: [{ functionDeclarations }],
toolConfig: { functionCallingConfig: { mode: "NONE" } },
toolWarnings
};
case "required":
return {
tools: [{ functionDeclarations }],
toolConfig: {
functionCallingConfig: {
mode: hasStrictTools ? "VALIDATED" : "ANY"
}
},
toolWarnings
};
case "tool":
return {
tools: [{ functionDeclarations }],
toolConfig: {
functionCallingConfig: {
mode: hasStrictTools ? "VALIDATED" : "ANY",
allowedFunctionNames: [toolChoice.toolName]
}
},
toolWarnings
};
default: {
const _exhaustiveCheck = type;
throw new UnsupportedFunctionalityError2({
functionality: `tool choice type: ${_exhaustiveCheck}`
});
}
}
}
// src/google-json-accumulator.ts
var GoogleJSONAccumulator = class {
constructor() {
this.accumulatedArgs = {};
this.jsonText = "";
/**
* Stack representing the currently "open" containers in the JSON output.
* Entry 0 is always the root `{` object once the first value is written.
*/
this.pathStack = [];
/**
* Whether a string value is currently "open" (willContinue was true),
* meaning the closing quote has not yet been emitted.
*/
this.stringOpen = false;
}
/**
* Input: [{jsonPath:"$.brightness",numberValue:50}]
* Output: { currentJSON:{brightness:50}, textDelta:'{"brightness":50' }
*/
processPartialArgs(partialArgs) {
let delta = "";
for (const arg of partialArgs) {
const rawPath = arg.jsonPath.replace(/^\$\./, "");
if (!rawPath) continue;
const segments = parsePath(rawPath);
const existingValue = getNestedValue(this.accumulatedArgs, segments);
const isStringContinuation = arg.stringValue != null && existingValue !== void 0;
if (isStringContinuation) {
const escaped = JSON.stringify(arg.stringValue).slice(1, -1);
setNestedValue(
this.accumulatedArgs,
segments,
existingValue + arg.stringValue
);
delta += escaped;
continue;
}
const resolved = resolvePartialArgValue(arg);
if (resolved == null) continue;
setNestedValue(this.accumulatedArgs, segments, resolved.value);
delta += this.emitNavigationTo(segments, arg, resolved.json);
}
this.jsonText += delta;
return {
currentJSON: this.accumulatedArgs,
textDelta: delta
};
}
/**
* Input: jsonText='{"brightness":50', accumulatedArgs={brightness:50}
* Output: { finalJSON:'{"brightness":50}', closingDelta:'}' }
*/
finalize() {
const finalArgs = JSON.stringify(this.accumulatedArgs);
const closingDelta = finalArgs.slice(this.jsonText.length);
return { finalJSON: finalArgs, closingDelta };
}
/**
* Input: pathStack=[] (first call) or pathStack=[root,...] (subsequent calls)
* Output: '{' (first call) or '' (subsequent calls)
*/
ensureRoot() {
if (this.pathStack.length === 0) {
this.pathStack.push({ segment: "", isArray: false, childCount: 0 });
return "{";
}
return "";
}
/**
* Emits the JSON text fragment needed to navigate from the current open
* path to the new leaf at `targetSegments`, then writes the value.
*
* Input: targetSegments=["recipe","name"], arg={jsonPath:"$.recipe.name",stringValue:"Lasagna"}, valueJson='"Lasagna"'
* Output: '{"recipe":{"name":"Lasagna"'
*/
emitNavigationTo(targetSegments, arg, valueJson) {
let fragment = "";
if (this.stringOpen) {
fragment += '"';
this.stringOpen = false;
}
fragment += this.ensureRoot();
const targetContainerSegments = targetSegments.slice(0, -1);
const leafSegment = targetSegments[targetSegments.length - 1];
const commonDepth = this.findCommonStackDepth(targetContainerSegments);
fragment += this.closeDownTo(commonDepth);
fragment += this.openDownTo(targetContainerSegments, leafSegment);
fragment += this.emitLeaf(leafSegment, arg, valueJson);
return fragment;
}
/**
* Returns the stack depth to preserve when navigating to a new target
* container path. Always >= 1 (the root is never popped).
*
* Input: stack=[root,"recipe","ingredients",0], target=["recipe","ingredients",1]
* Output: 3 (keep root+"recipe"+"ingredients")
*/
findCommonStackDepth(targetContainer) {
const maxDepth = Math.min(
this.pathStack.length - 1,
targetContainer.length
);
let common = 0;
for (let i = 0; i < maxDepth; i++) {
if (this.pathStack[i + 1].segment === targetContainer[i]) {
common++;
} else {
break;
}
}
return common + 1;
}
/**
* Closes containers from the current stack depth back down to `targetDepth`.
*
* Input: this.pathStack=[root,"recipe","ingredients",0], targetDepth=3
* Output: '}'
*/
closeDownTo(targetDepth) {
let fragment = "";
while (this.pathStack.length > targetDepth) {
const entry = this.pathStack.pop();
fragment += entry.isArray ? "]" : "}";
}
return fragment;
}
/**
* Opens containers from the current stack depth down to the full target
* container path, emitting opening `{`, `[`, keys, and commas as needed.
* `leafSegment` is used to determine if the innermost container is an array.
*
* Input: this.pathStack=[root], targetContainer=["recipe","ingredients"], leafSegment=0
* Output: '"recipe":{"ingredients":['
*/
openDownTo(targetContainer, leafSegment) {
let fragment = "";
const startIdx = this.pathStack.length - 1;
for (let i = startIdx; i < targetContainer.length; i++) {
const seg = targetContainer[i];
const parentEntry = this.pathStack[this.pathStack.length - 1];
if (parentEntry.childCount > 0) {
fragment += ",";
}
parentEntry.childCount++;
if (typeof seg === "string") {
fragment += `${JSON.stringify(seg)}:`;
}
const childSeg = i + 1 < targetContainer.length ? targetContainer[i + 1] : leafSegment;
const isArray = typeof childSeg === "number";
fragment += isArray ? "[" : "{";
this.pathStack.push({ segment: seg, isArray, childCount: 0 });
}
return fragment;
}
/**
* Emits the comma, key, and value for a leaf entry in the current container.
*
* Input: leafSegment="name", arg={stringValue:"Lasagna"}, valueJson='"Lasagna"'
* Output: '"name":"Lasagna"' (or ',"name":"Lasagna"' if container.childCount > 0)
*/
emitLeaf(leafSegment, arg, valueJson) {
let fragment = "";
const container = this.pathStack[this.pathStack.length - 1];
if (container.childCount > 0) {
fragment += ",";
}
container.childCount++;
if (typeof leafSegment === "string") {
fragment += `${JSON.stringify(leafSegment)}:`;
}
if (arg.stringValue != null && arg.willContinue) {
fragment += valueJson.slice(0, -1);
this.stringOpen = true;
} else {
fragment += valueJson;
}
return fragment;
}
};
function parsePath(rawPath) {
const segments = [];
for (const part of rawPath.split(".")) {
const bracketIdx = part.indexOf("[");
if (bracketIdx === -1) {
segments.push(part);
} else {
if (bracketIdx > 0) segments.push(part.slice(0, bracketIdx));
for (const m of part.matchAll(/\[(\d+)\]/g)) {
segments.push(parseInt(m[1], 10));
}
}
}
return segments;
}
function getNestedValue(obj, segments) {
let current = obj;
for (const seg of segments) {
if (current == null || typeof current !== "object") return void 0;
current = current[seg];
}
return current;
}
function setNestedValue(obj, segments, value) {
let current = obj;
for (let i = 0; i < segments.length - 1; i++) {
const seg = segments[i];
const nextSeg = segments[i + 1];
if (current[seg] == null) {
current[seg] = typeof nextSeg === "number" ? [] : {};
}
current = current[seg];
}
current[segments[segments.length - 1]] = value;
}
function resolvePartialArgValue(arg) {
var _a, _b;
const value = (_b = (_a = arg.stringValue) != null ? _a : arg.numberValue) != null ? _b : arg.boolValue;
if (value != null) return { value, json: JSON.stringify(value) };
if ("nullValue" in arg) return { value: null, json: "null" };
return void 0;
}
// src/map-google-generative-ai-finish-reason.ts
function mapGoogleGenerativeAIFinishReason({
finishReason,
hasToolCalls
}) {
switch (finishReason) {
case "STOP":
return hasToolCalls ? "tool-calls" : "stop";
case "MAX_TOKENS":
return "length";
case "IMAGE_SAFETY":
case "RECITATION":
case "SAFETY":
case "BLOCKLIST":
case "PROHIBITED_CONTENT":
case "SPII":
return "content-filter";
case "MALFORMED_FUNCTION_CALL":
return "error";
case "FINISH_REASON_UNSPECIFIED":
case "OTHER":
default:
return "other";
}
}
// src/google-generative-ai-language-model.ts
var GoogleGenerativeAILanguageModel = class {
constructor(modelId, config) {
this.specificationVersion = "v3";
var _a;
this.modelId = modelId;
this.config = config;
this.generateId = (_a = config.generateId) != null ? _a : generateId;
}
get provider() {
return this.config.provider;
}
get supportedUrls() {
var _a, _b, _c;
return (_c = (_b = (_a = this.config).supportedUrls) == null ? void 0 : _b.call(_a)) != null ? _c : {};
}
async getArgs({
prompt,
maxOutputTokens,
temperature,
topP,
topK,
frequencyPenalty,
presencePenalty,
stopSequences,
responseFormat,
seed,
tools,
toolChoice,
providerOptions
}, { isStreaming = false } = {}) {
var _a, _b;
const warnings = [];
const providerOptionsName = this.config.provider.includes("vertex") ? "vertex" : "google";
let googleOptions = await parseProviderOptions2({
provider: providerOptionsName,
providerOptions,
schema: googleLanguageModelOptions
});
if (googleOptions == null && providerOptionsName !== "google") {
googleOptions = await parseProviderOptions2({
provider: "google",
providerOptions,
schema: googleLanguageModelOptions
});
}
const isVertexProvider = this.config.provider.startsWith("google.vertex.");
if ((tools == null ? void 0 : tools.some(
(tool) => tool.type === "provider" && tool.id === "google.vertex_rag_store"
)) && !isVertexProvider) {
warnings.push({
type: "other",
message: `The 'vertex_rag_store' tool is only supported with the Google Vertex provider and might not be supported or could behave unexpectedly with the current Google provider (${this.config.provider}).`
});
}
if ((googleOptions == null ? void 0 : googleOptions.streamFunctionCallArguments) && !isVertexProvider) {
warnings.push({
type: "other",
message: `'streamFunctionCallArguments' is only supported on the Vertex AI API and will be ignored with the current Google provider (${this.config.provider}). See https://docs.cloud.google.com/vertex-ai/generative-ai/docs/multimodal/function-calling#streaming-fc`
});
}
if ((googleOptions == null ? void 0 : googleOptions.serviceTier) && isVertexProvider) {
warnings.push({
type: "other",
message: "'serviceTier' is a Gemini API option and is not supported on Vertex AI. Use 'sharedRequestType' (and optionally 'requestType') instead. See https://docs.cloud.google.com/vertex-ai/generative-ai/docs/priority-paygo"
});
}
if (((googleOptions == null ? void 0 : googleOptions.sharedRequestType) || (googleOptions == null ? void 0 : googleOptions.requestType)) && !isVertexProvider) {
warnings.push({
type: "other",
message: `'sharedRequestType' and 'requestType' are Vertex AI options and are ignored with the current Google provider (${this.config.provider}).`
});
}
const vertexPaygoHeaders = isVertexProvider && ((googleOptions == null ? void 0 : googleOptions.sharedRequestType) || (googleOptions == null ? void 0 : googleOptions.requestType)) ? {
...googleOptions.sharedRequestType && {
"X-Vertex-AI-LLM-Shared-Request-Type": googleOptions.sharedRequestType
},
...googleOptions.requestType && {
"X-Vertex-AI-LLM-Request-Type": googleOptions.requestType
}
} : void 0;
cons