@ai-sdk/google
Version:
9,242 lines • 327 kB
JavaScript
// src/google-provider.ts
import {
generateId as generateId3,
loadApiKey,
withoutTrailingSlash,
withUserAgentSuffix as withUserAgentSuffix2
} from "@ai-sdk/provider-utils";
// src/version.ts
var VERSION = true ? "4.0.62" : "0.0.0-test";
// src/google-embedding-model.ts
import {
TooManyEmbeddingValuesForCallError
} from "@ai-sdk/provider";
import {
combineHeaders,
createJsonResponseHandler,
lazySchema as lazySchema3,
parseProviderOptions,
postJsonToApi,
resolve,
serializeModelOptions,
WORKFLOW_SERIALIZE,
WORKFLOW_DESERIALIZE,
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(),
details: z.array(z.unknown()).nullish()
})
})
)
);
var googleFailedResponseHandler = createJsonErrorResponseHandler({
errorSchema: googleErrorDataSchema,
errorToMessage: (data) => data.error.message
});
// src/google-embedding-model-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-embedding-model.ts
var GoogleEmbeddingModel = class _GoogleEmbeddingModel {
constructor(modelId, config) {
this.specificationVersion = "v4";
this.maxEmbeddingsPerCall = 100;
this.supportsParallelCalls = true;
this.modelId = modelId;
this.config = config;
}
static [WORKFLOW_SERIALIZE](model) {
return serializeModelOptions({
modelId: model.modelId,
config: model.config
});
}
static [WORKFLOW_DESERIALIZE](options) {
return new _GoogleEmbeddingModel(options.modelId, options.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(
this.config.headers ? await resolve(this.config.headers) : void 0,
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-batch.ts
import {
InvalidArgumentError,
InvalidResponseDataError
} from "@ai-sdk/provider";
import {
combineHeaders as combineHeaders3,
convertAsyncIteratorToReadableStream,
createJsonLinesResponseHandler,
createJsonResponseHandler as createJsonResponseHandler3,
generateId as generateId2,
getFromApi,
lazySchema as lazySchema6,
normalizeBatchRequestCounts,
postJsonToApi as postJsonToApi3,
postToApi,
resolve as resolve3,
safeValidateTypes,
WORKFLOW_DESERIALIZE as WORKFLOW_DESERIALIZE3,
WORKFLOW_SERIALIZE as WORKFLOW_SERIALIZE3,
zodSchema as zodSchema6
} from "@ai-sdk/provider-utils";
import { z as z6 } from "zod/v4";
// src/get-model-path.ts
function getModelPath(modelId) {
return modelId.includes("/") ? modelId : `models/${modelId}`;
}
// src/google-language-model.ts
import {
combineHeaders as combineHeaders2,
createEventSourceResponseHandler,
createJsonResponseHandler as createJsonResponseHandler2,
generateId,
isCustomReasoning,
lazySchema as lazySchema5,
mapReasoningToProviderBudget,
mapReasoningToProviderEffort,
parseProviderOptions as parseProviderOptions2,
postJsonToApi as postJsonToApi2,
resolve as resolve2,
serializeModelOptions as serializeModelOptions2,
WORKFLOW_SERIALIZE as WORKFLOW_SERIALIZE2,
WORKFLOW_DESERIALIZE as WORKFLOW_DESERIALIZE2,
zodSchema as zodSchema5
} from "@ai-sdk/provider-utils";
import { z as z5 } from "zod/v4";
// src/convert-google-usage.ts
import { createNullLanguageModelUsage } from "@ai-sdk/provider-utils";
function convertGoogleUsage(usage) {
var _a, _b, _c, _d;
if (usage == null) {
return createNullLanguageModelUsage();
}
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
import {
UnsupportedFunctionalityError
} from "@ai-sdk/provider";
var recursiveReferenceFunctionalityPrefix = "recursive JSON Schema reference:";
function isRecursiveJSONSchemaReferenceError(error) {
return UnsupportedFunctionalityError.isInstance(error) && error.functionality.startsWith(recursiveReferenceFunctionalityPrefix);
}
function convertJSONSchemaToOpenAPISchema(jsonSchema, isRoot = true) {
const rootSchema = typeof jsonSchema === "object" ? jsonSchema : void 0;
return convertJSONSchemaDefinition(jsonSchema, isRoot, {
definitions: rootSchema == null ? void 0 : rootSchema.definitions,
dollarDefinitions: rootSchema == null ? void 0 : rootSchema.$defs,
resolvingReferences: /* @__PURE__ */ new Set()
});
}
function convertJSONSchemaDefinition(jsonSchema, isRoot, referenceContext) {
if (jsonSchema == null) {
return void 0;
}
if (typeof jsonSchema === "boolean") {
return { type: "boolean", properties: {} };
}
if (jsonSchema.$ref != null) {
return convertJSONSchemaReference({
jsonSchema,
reference: jsonSchema.$ref,
isRoot,
referenceContext
});
}
if (isEmptyObjectSchema(jsonSchema)) {
if (isRoot) {
return void 0;
}
if (jsonSchema.description) {
return { type: "object", description: jsonSchema.description };
}
return { type: "object" };
}
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 (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;
}
}
const values = enumValues != null ? enumValues : constValue !== void 0 ? [constValue] : void 0;
if (values !== void 0) {
addEnumToSchema({ values, type, result });
}
if (properties != null) {
result.properties = Object.entries(properties).reduce(
(acc, [key, value]) => {
acc[key] = convertJSONSchemaDefinition(value, false, referenceContext);
return acc;
},
{}
);
}
if (items) {
result.items = Array.isArray(items) ? items.map(
(item) => convertJSONSchemaDefinition(item, false, referenceContext)
) : convertJSONSchemaDefinition(items, false, referenceContext);
}
if (allOf) {
result.allOf = allOf.map(
(item) => convertJSONSchemaDefinition(item, false, referenceContext)
);
}
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 = convertJSONSchemaDefinition(
nonNullSchemas[0],
false,
referenceContext
);
if (typeof converted === "object") {
result.nullable = true;
Object.assign(result, converted);
}
} else {
result.anyOf = nonNullSchemas.map(
(item) => convertJSONSchemaDefinition(item, false, referenceContext)
);
result.nullable = true;
}
} else {
result.anyOf = anyOf.map(
(item) => convertJSONSchemaDefinition(item, false, referenceContext)
);
}
}
if (oneOf) {
result.oneOf = oneOf.map(
(item) => convertJSONSchemaDefinition(item, false, referenceContext)
);
}
if (minLength !== void 0) {
result.minLength = minLength;
}
return result;
}
function convertJSONSchemaReference({
jsonSchema,
reference,
isRoot,
referenceContext
}) {
const { definition, referenceKey } = getReferencedDefinition(
reference,
referenceContext
);
if (referenceContext.resolvingReferences.has(referenceKey)) {
throw new UnsupportedFunctionalityError({
functionality: `${recursiveReferenceFunctionalityPrefix} ${reference}`,
message: "Google schema conversion does not support recursive JSON Schema references."
});
}
const resolvingReferences = new Set(referenceContext.resolvingReferences);
resolvingReferences.add(referenceKey);
const { $ref: _reference, ...siblingSchema } = jsonSchema;
const resolvedSchema = typeof definition === "boolean" ? definition ? siblingSchema : false : { ...definition, ...siblingSchema };
return convertJSONSchemaDefinition(resolvedSchema, isRoot, {
...referenceContext,
resolvingReferences
});
}
function getReferencedDefinition(reference, referenceContext) {
const definitionSources = [
{
prefix: "#/$defs/",
definitions: referenceContext.dollarDefinitions
},
{
prefix: "#/definitions/",
definitions: referenceContext.definitions
}
];
const source = definitionSources.find(
({ prefix }) => reference.startsWith(prefix)
);
const encodedDefinitionName = source ? reference.slice(source.prefix.length) : void 0;
if (source == null || encodedDefinitionName == null || encodedDefinitionName.length === 0 || encodedDefinitionName.includes("/")) {
throwUnsupportedReference(reference);
}
let decodedDefinitionName;
try {
decodedDefinitionName = decodeURIComponent(encodedDefinitionName);
} catch (e) {
throwUnsupportedReference(reference);
}
if (decodedDefinitionName.includes("/") || /~(?![01])/u.test(decodedDefinitionName) || source.definitions == null) {
throwUnsupportedReference(reference);
}
const definitionName = decodedDefinitionName.replace(
/~[01]/g,
(match) => match === "~1" ? "/" : "~"
);
if (!Object.prototype.hasOwnProperty.call(source.definitions, definitionName)) {
throwUnsupportedReference(reference);
}
return {
definition: source.definitions[definitionName],
referenceKey: `${source.prefix}${definitionName}`
};
}
function throwUnsupportedReference(reference) {
throw new UnsupportedFunctionalityError({
functionality: `JSON Schema reference: ${reference}`,
message: "Google schema conversion only supports references to direct children of root-level $defs or definitions."
});
}
function addEnumToSchema({
values,
type,
result
}) {
const nullable = Array.isArray(type) && type.includes("null") || type === void 0 && values.includes(null);
const enumValues = nullable ? values.filter((value) => value !== null) : values;
if (values.length > 0 && values.every((value) => value === null)) {
const typeAllowsNull = type === void 0 || type === "null" || Array.isArray(type) && type.includes("null");
if (typeAllowsNull) {
result.type = "null";
if (Array.isArray(type)) {
delete result.anyOf;
}
return;
}
}
const enumType = getEnumType({ values: enumValues, type });
if (enumType === void 0) {
throw new UnsupportedFunctionalityError({
functionality: "JSON Schema enum with mixed or unsupported values",
message: "Google does not support this JSON Schema enum. Enum values must share one supported primitive type and match the schema type."
});
}
result.type = enumType;
if (Array.isArray(type)) {
delete result.anyOf;
}
if (nullable) {
result.nullable = true;
}
if (enumType === "string") {
result.enum = enumValues;
} else {
result.format = "enum";
result.enum = enumValues.map(String);
}
}
function getEnumType({
values,
type
}) {
if (values.length === 0) {
return void 0;
}
const typeAllows = (enumType) => type === void 0 || type === enumType || Array.isArray(type) && type.includes(enumType);
if (typeAllows("string") && values.every((value) => typeof value === "string")) {
return "string";
}
if ((typeAllows("number") || typeAllows("integer")) && values.every((value) => typeof value === "number" && Number.isFinite(value))) {
if (typeAllows("number")) {
return "number";
}
if (values.every((value) => Number.isInteger(value))) {
return "integer";
}
}
if (typeAllows("boolean") && values.every((value) => typeof value === "boolean")) {
return "boolean";
}
return void 0;
}
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-messages.ts
import {
UnsupportedFunctionalityError as UnsupportedFunctionalityError2
} from "@ai-sdk/provider";
import {
convertToBase64,
getTopLevelMediaType,
isFullMediaType,
resolveFullMediaType,
resolveProviderReference,
secureJsonParse
} from "@ai-sdk/provider-utils";
var SKIP_THOUGHT_SIGNATURE_VALIDATOR = "skip_thought_signature_validator";
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, includeFunctionCallIds = true) {
const functionResponseParts = [];
const responseTextParts = [];
for (const contentPart of outputValue) {
switch (contentPart.type) {
case "text": {
responseTextParts.push(contentPart.text);
break;
}
case "file": {
if (contentPart.data.type === "data") {
functionResponseParts.push({
inlineData: {
mimeType: resolveFullMediaType({ part: contentPart }),
data: convertToBase64(contentPart.data.data)
}
});
} else if (contentPart.data.type === "url") {
const functionResponsePart = convertUrlToolResultPart(
contentPart.data.url.toString()
);
if (functionResponsePart != null) {
functionResponseParts.push(functionResponsePart);
} else {
responseTextParts.push(JSON.stringify(contentPart));
}
} else {
responseTextParts.push(JSON.stringify(contentPart));
}
break;
}
default: {
responseTextParts.push(JSON.stringify(contentPart));
break;
}
}
}
parts.push({
functionResponse: {
...includeFunctionCallIds && 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, includeFunctionCallIds = true) {
for (const contentPart of outputValue) {
switch (contentPart.type) {
case "text":
parts.push({
functionResponse: {
...includeFunctionCallIds && toolCallId != null ? { id: toolCallId } : {},
name: toolName,
response: {
name: toolName,
content: contentPart.text
}
}
});
break;
case "file": {
if (contentPart.data.type === "data") {
const topLevelMediaType = getTopLevelMediaType(contentPart.mediaType);
parts.push(
{
inlineData: {
mimeType: resolveFullMediaType({ part: contentPart }),
data: convertToBase64(contentPart.data.data)
}
},
{
text: `Tool executed successfully and returned this ${topLevelMediaType === "image" ? "image" : "file"} as a response`
}
);
} else {
parts.push({ text: JSON.stringify(contentPart) });
}
break;
}
default:
parts.push({ text: JSON.stringify(contentPart) });
break;
}
}
}
function convertToGoogleMessages(prompt, options) {
var _a, _b, _c, _d, _e, _f;
const systemInstructionParts = [];
const contents = [];
let systemMessagesAllowed = true;
const isGemmaModel = (_a = options == null ? void 0 : options.isGemmaModel) != null ? _a : false;
const isGemini3Model = (_b = options == null ? void 0 : options.isGemini3Model) != null ? _b : false;
const onWarning = options == null ? void 0 : options.onWarning;
const providerOptionsNames = (_c = options == null ? void 0 : options.providerOptionsNames) != null ? _c : ["google"];
const isVertexLike = !providerOptionsNames.includes("google");
const supportsFunctionResponseParts = (_d = options == null ? void 0 : options.supportsFunctionResponseParts) != null ? _d : true;
const includeFunctionCallIds = (_e = options == null ? void 0 : options.includeFunctionCallIds) != null ? _e : true;
let sentinelInjected = false;
const missingSignatureToolNames = [];
const injectSkipSignature = (toolName) => {
missingSignatureToolNames.push(toolName);
sentinelInjected = true;
return SKIP_THOUGHT_SIGNATURE_VALIDATOR;
};
const readProviderOpts = (part) => {
var _a2, _b2, _c2, _d2, _e2;
for (const name of providerOptionsNames) {
const v = (_a2 = part.providerOptions) == null ? void 0 : _a2[name];
if (v != null) return v;
}
if (isVertexLike) {
return (_b2 = part.providerOptions) == null ? void 0 : _b2.google;
}
return (_e2 = (_c2 = part.providerOptions) == null ? void 0 : _c2.googleVertex) != null ? _e2 : (_d2 = part.providerOptions) == null ? void 0 : _d2.vertex;
};
for (const { role, content } of prompt) {
switch (role) {
case "system": {
if (!systemMessagesAllowed) {
throw new UnsupportedFunctionalityError2({
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": {
switch (part.data.type) {
case "url": {
parts.push({
fileData: {
mimeType: resolveFullMediaType({ part }),
fileUri: part.data.url.toString()
}
});
break;
}
case "reference": {
if (isVertexLike) {
throw new UnsupportedFunctionalityError2({
functionality: "file parts with provider references"
});
}
parts.push({
fileData: {
mimeType: resolveFullMediaType({ part }),
fileUri: resolveProviderReference({
reference: part.data.reference,
provider: "google"
})
}
});
break;
}
case "text": {
parts.push({
inlineData: {
mimeType: isFullMediaType(part.mediaType) ? part.mediaType : "text/plain",
data: convertToBase64(
new TextEncoder().encode(part.data.text)
)
}
});
break;
}
case "data": {
parts.push({
inlineData: {
mimeType: resolveFullMediaType({ part }),
data: convertToBase64(part.data.data)
}
});
break;
}
}
break;
}
}
}
contents.push({ role: "user", parts });
break;
}
case "assistant": {
systemMessagesAllowed = false;
let modelResponseHasSignedFunctionCall = false;
contents.push({
role: "model",
parts: content.map((part) => {
const providerOpts = readProviderOpts(part);
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 "reasoning-file": {
switch (part.data.type) {
case "url": {
throw new UnsupportedFunctionalityError2({
functionality: "File data URLs in assistant messages are not supported"
});
}
case "data": {
return {
inlineData: {
mimeType: part.mediaType,
data: convertToBase64(part.data.data)
},
thought: true,
thoughtSignature
};
}
}
break;
}
case "file": {
switch (part.data.type) {
case "url": {
throw new UnsupportedFunctionalityError2({
functionality: "File data URLs in assistant messages are not supported"
});
}
case "reference": {
if (isVertexLike) {
throw new UnsupportedFunctionalityError2({
functionality: "file parts with provider references"
});
}
return {
fileData: {
mimeType: part.mediaType,
fileUri: resolveProviderReference({
reference: part.data.reference,
provider: "google"
})
},
...(providerOpts == null ? void 0 : providerOpts.thought) === true ? { thought: true } : {},
thoughtSignature
};
}
case "text": {
return {
inlineData: {
mimeType: isFullMediaType(part.mediaType) ? part.mediaType : "text/plain",
data: convertToBase64(
new TextEncoder().encode(part.data.text)
)
},
...(providerOpts == null ? void 0 : providerOpts.thought) === true ? { thought: true } : {},
thoughtSignature
};
}
case "data": {
return {
inlineData: {
mimeType: part.mediaType,
data: convertToBase64(part.data.data)
},
...(providerOpts == null ? void 0 : providerOpts.thought) === true ? { thought: true } : {},
thoughtSignature
};
}
}
break;
}
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;
const isServerToolCall = serverToolCallId != null && serverToolType != null;
const shouldSkipMissingSignatureMitigation = (
// Gemini 3 returns a single signature for a parallel
// function-call response on the first standard function
// call. Subsequent standard function calls in the same
// model response legitimately have no signature.
!isServerToolCall && thoughtSignature == null && modelResponseHasSignedFunctionCall
);
const effectiveThoughtSignature = thoughtSignature != null ? thoughtSignature : isGemini3Model && !shouldSkipMissingSignatureMitigation ? injectSkipSignature(part.toolName) : void 0;
if (!isServerToolCall && thoughtSignature != null) {
modelResponseHasSignedFunctionCall = true;
}
if (isServerToolCall) {
return {
toolCall: {
toolType: serverToolType,
args: typeof part.input === "string" ? secureJsonParse(part.input) : part.input,
id: serverToolCallId
},
thoughtSignature: effectiveThoughtSignature
};
}
return {
functionCall: {
...includeFunctionCallIds && part.toolCallId != null ? { id: part.toolCallId } : {},
name: part.toolName,
args: part.input
},
thoughtSignature: effectiveThoughtSignature
};
}
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 = readProviderOpts(part);
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,
includeFunctionCallIds
);
} else {
appendLegacyToolResultParts(
parts,
part.toolName,
output.value,
part.toolCallId,
includeFunctionCallIds
);
}
} else {
parts.push({
functionResponse: {
...includeFunctionCallIds && part.toolCallId != null ? { id: part.toolCallId } : {},
name: part.toolName,
response: {
name: part.toolName,
content: output.type === "execution-denied" ? (_f = output.reason) != null ? _f : "Tool call 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" });
}
if (sentinelInjected && onWarning != null) {
const uniqueToolNames = Array.from(new Set(missingSignatureToolNames));
onWarning({
type: "other",
message: `Replayed ${missingSignatureToolNames.length} \`functionCall\` part(s) for a Gemini 3 model without a \`thoughtSignature\` (tools: ${uniqueToolNames.map((name) => `\`${name}\``).join(", ")}). Injected the documented \`skip_thought_signature_validator\` sentinel to keep the request from failing with HTTP 400. The likely cause is application code that drops \`providerOptions.google.thoughtSignature\` when persisting or serializing assistant tool-call messages. See https://ai.google.dev/gemini-api/docs/thought-signatures.`
});
}
return {
systemInstruction: systemInstructionParts.length > 0 && !isGemmaModel ? { parts: systemInstructionParts } : void 0,
contents
};
}
// src/google-language-model-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 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. Controls the generation of people in images.
* Vertex AI only.
*/
personGeneration: z4.enum([
"PERSON_GENERATION_UNSPECIFIED",
"ALLOW_ALL",
"ALLOW_ADULT",
"ALLOW_NONE"
]).optional(),
/**
* Optional. Controls whether generation of prominent people
* (celebrities) is allowed. When set together with
* `personGeneration`, `personGeneration` takes precedence.
* Vertex AI only.
*
* https://docs.cloud.google.com/vertex-ai/generative-ai/docs/reference/rest/v1/GenerationConfig
*/
prominentPeople: z4.enum([
"PROMINENT_PEOPLE_UNSPECIFIED",
"ALLOW_PROMINENT_PEOPLE",
"BLOCK_PROMINENT_PEOPLE"
]).optional(),
/**
* Optional. The image output format for generated images.
* Vertex AI only.
*/
imageOutputOptions: z4.object({
mimeType: z4.enum(["image/jpeg", "image/png"]).optional(),
compressionQuality: z4.number().optional()
}).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-model-capabilities.ts
var gemini1ModelPattern = /(^|\/)gemini-1(?:[.-]|$)/i;
var gemini2ModelPattern = /(^|\/)gemini-2(?:[.-]|$)/i;
var gemini25ModelPattern = /(^|\/)gemini-2\.5(?:[.-]|$)/i;
var geminiModelPattern = /(^|\/)gemini-/i;
function isKnownPreGemini2Model(modelId) {
return gemini1ModelPattern.test(modelId) || /(^|\/)gemini-pro(?:-vision)?$/i.test(modelId) || /(^|\/)gemini-robotics-er-1\.5(?:[.-]|$)/i.test(modelId);
}
function getGoogleModelCapabilities(modelId) {
const isGeminiModel = geminiModelPattern.test(modelId);
const isGemini2Model = gemini2ModelPattern.test(modelId);
const isKnownPreGemini2 = isKnownPreGemini2Model(modelId);
const isKnownOlderModel = isKnownPreGemini2 || isGemini2Model;
const usesGemini3Features = isGeminiModel && !isKnownOlderModel;
return {
supportsGemini2Tools: isGeminiModel && !isKnownPreGemini2 || modelId.toLowerCase().includes("nano-banana"),
supportsFileSearch: gemini25ModelPattern.test(modelId) || usesGemini3Features,
usesGemini3Features
};
}
// src/google-prepare-tools.ts
import {
UnsupportedFunctionalityError as UnsupportedFunctionalityError3
} from "@ai-sdk/provider";
function prepareTools({
tools,
toolChoice,
modelId,
isVertexProvider = false
}) {
tools = (tools == null ? void 0 : tools.length) ? tools : void 0;
const toolWarnings = [];
const { supportsGemini2Tools, supportsFileSearch, usesGemini3Features } = getGoogleModelCapabilities(modelId);
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 && !usesGemini3Features) {
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 (supportsGemini2Tools) {
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 (supportsGemini2Tools) {
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 (supportsGemini2Tools) {
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 (supportsGemini2Tools) {
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 (supportsGemini2Tools) {
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 (supportsGemini2Tools) {
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 && usesGemini3Features && googleTools2.length > 0) {
const functionDeclarations2 = [];
for (const tool of tools) {
if (tool.type === "function") {
functionDeclarations2.push(prepareFunctionDeclaration(tool));
}
}
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(prepareFunctionDeclaration(tool));
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: "ANY"
}
},
toolWarnings
};
case "tool":
return {
tools: [{ functionDeclarations }],
toolConfig: {
functionCallingConfig: {
mode: "ANY",
allowedFunctionNames: [toolChoice.toolName]
}
},
toolWarnings
};
default: {
const _exhaustiveCheck = type;
throw new UnsupportedFunctionalityError3({
functionality: `tool choice type: ${_exhaustiveCheck}`
});
}
}
}
function prepareFunctionDeclaration(tool) {
var _a;
const declaration = {
name: tool.name,
description: (_a = tool.description) != null ? _a : ""
};
try {
return {
...declaration,
parameters: convertJSONSchemaToOpenAPISchema(tool.inputSchema)
};
} catch (error) {
if (!isRecursiveJSONSchemaReferenceError(error)) {
throw error;
}
return {
...declaration,
parametersJsonSchema: tool.inputSchema
};
}
}
// 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 pathSegment = targetContainer[i];
const parentEntry = this.pathStack[this.pathStack.length - 1];
if (parentEntry.childCount > 0) {
fragment += ",";
}
parentEntry.childCount++;
if (typeof pathSegment === "string") {
fragment += `${JSON.stringify(pathSegment)}:`;
}
const childSeg = i + 1 < targetContainer.length ? targetContainer[i + 1] : leafSegment;
const isArray = typeof childSeg === "number";
fragment += isArray ? "[" : "{";
this.pathStack.push({ segment: pathSegment, 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;
}
var hasOwn = Object.prototype.hasOwnProperty;
function hasOwnProperty(obj, key) {
return hasOwn.call(obj, key);
}
function defineOwnProperty(obj, key, value) {
Object.defineProperty(obj, key, {
value,
enumerable: true,
configurable: true,
writable: true
});
}
function getNestedValue(obj, segments) {
let current = obj;
for (const pathSegment of segments) {
if (current == null || typeof current !== "object") return void 0;
const currentRecord = current;
if (!hasOwnProperty(currentRecord, pathSegment)) return void 0;
current = currentRecord[pathSegment];
}
return current;
}
function setNestedValue(obj, segments, value) {
let current = obj;
for (let i = 0; i < segments.length - 1; i++) {
const pathSegment = segments[i];
const nextSeg = segments[i + 1];
if (!hasOwnProperty(current, pathSegment) || current[pathSegment] == null) {
defineOwnProperty(
current,
pathSegment,
typeof nextSeg === "number" ? [] : {}
);
}
current = current[pathSegment];
}
defineOwnProperty(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-finish-reason.ts
function mapGoogleFinishReason({
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-language-model.ts
var configurableSafetySettingCategories = [
"HARM_CATEGORY_HATE_SPEECH",
"HARM_CATEGORY_DANGEROUS_CONTENT",
"HARM_CATEGORY_HARASSMENT",
"HARM_CATEGORY_SEXUALLY_EXPLICIT"
];
var gemini25ModelPattern2 = /(^|\/)gemini-2\.5(?:[.-]|$)/i;
var GoogleLanguageModel = class _GoogleLanguageModel {
constructor(modelId, config) {
this.specificationVersion = "v4";
var _a;
this.modelId = modelId;
this.config = config;
this.generateId = (_a = config.generateId) != null ? _a : generateId;
}
static [WORKFLOW_SERIALIZE2](model) {
return serializeModelOptions2({
modelId: model.modelId,
config: model.config
});
}
static [WORKFLOW_DESERIALIZE2](options) {
return new _GoogleLanguageModel(options.modelId, options.config);
}
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,
reasoning,
providerOptions
}, { isStreaming = false } = {}) {
var _a, _b, _c;
const warnings = [];
const providerOptionsNames = this.config.provider.includes("vertex") ? ["googleVertex", "vertex"] : ["google"];
let googleOptions;
for (const name of providerOptionsNames) {
googleOptions = await parseProviderOptions2({
provider: name,
providerOptions,
schema: googleLanguageModelOptions
});
if (googleOptions != null) break;
}
if (googleOptions == null && !providerOptionsNames.includes("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;
const bodyServiceTier = isVertexProvider ? void 0 : googleOptions == null ? void 0 : googleOptions.serviceTier;
let imageConfig = googleOptions == null ? void 0 : googleOptions.imageConfig;
if (imageConfig != null && !isVertexProvider) {
const {
personGeneration,
prominentPeople,
imageOutputOptions,
...geminiApiImageConfig
} = imageConfig;
const droppedImageConfigFields = Object.entries({
personGeneration,
prominentPeople,
imageOutputOptions
}).filter(([, value]) => value != null).map(([key]) => `'imageConfig.${key}'`);
if (droppedImageConfigFields.length > 0) {
warnings.push({
type: "other",
message: `${droppedImageConfigFields.join(", ")} ${droppedImageConfigFields.length === 1 ? "is a Vertex AI option and is" : "are Vertex AI options and are"} ignored with the current Google provider (${this.config.provider}).`
});
imageConfig = geminiApiImageConfig;
}
}
const isGemmaModel = this.modelId.toLowerCase().startsWith("gemma-");
const isGemini25DeveloperApiModel = !isVertexProvider && gemini25ModelPattern2.test(this.modelId);
if (isGemini25DeveloperApiModel && frequencyPenalty != null) {
warnings.push({
type: "unsupported",
feature: "frequencyPenalty"
});
}
if (isGemini25DeveloperApiModel && presencePenalty != null) {
warnings.push({
type: "unsupported",
feature: "presencePenalty"
});
}
const { usesGemini3Features } = getGoogleModelCapabilities(this.modelId);
const { contents, systemInstruction } = convertToGoogleMessages(prompt, {
isGemmaModel,
isGemini3Model: usesGemini3Features,
onWarning: (warning) => warnings.push(warning),
providerOptionsNames,
supportsFunctionResponseParts: usesGemini3Features,
includeFunctionCallIds: !isVertexProvider
});
const {
tools: googleTools2,
toolConfig: googleToolConfig,
toolWarnings
} = prepareTools({
tools,
toolChoice,
modelId: this.modelId,
isVertexProvider
});
const resolvedThinking = resolveThinkingConfig({
reasoning,
modelId: this.modelId,
warnings
});
const thinkingConfig = (googleOptions == null ? void 0 : googleOptions.thinkingConfig) || resolvedThinking ? { ...resolvedThinking, ...googleOptions == null ? void 0 : googleOptions.thinkingConfig } : void 0;
const streamFunctionCallArguments = isStreaming && isVertexProvider ? (_a = googleOptions == null ? void 0 : googleOptions.streamFunctionCallArguments) != null ? _a : false : void 0;
const safetyThreshold = googleOptions == null ? void 0 : googleOptions.threshold;
const safetySettings = (_b = googleOptions == null ? void 0 : googleOptions.safetySettings) != null ? _b : safetyThreshold != null ? configurableSafetySettingCategories.map((category) => ({
category,
threshold: safetyThreshold
})) : void 0;
const toolConfig = googleToolConfig || streamFunctionCallArguments || (googleOptions == null ? void 0 : googleOptions.retrievalConfig) ? {
...googleToolConfig,
...streamFunctionCallArguments && {
functionCallingConfig: {
...googleToolConfig == null ? void 0 : googleToolConfig.functionCallingConfig,
streamFunctionCallArguments: true
}
},
...(googleOptions == null ? void 0 : googleOptions.retrievalConfig) && {
retrievalConfig: googleOptions.retrievalConfig
}
} : void 0;
return {
args: {
generationConfig: {
// standardized settings:
maxOutputTokens,
temperature,
topK,
topP,
frequencyPenalty: isGemini25DeveloperApiModel ? void 0 : frequencyPenalty,
presencePenalty: isGemini25DeveloperApiModel ? void 0 : presencePenalty,
stopSequences,
seed,
// response format:
responseMimeType: (responseFormat == null ? void 0 : responseFormat.type) === "json" ? "application/json" : void 0,
responseSchema: (responseFormat == null ? void 0 : responseFormat.type) === "json" && responseFormat.schema != null && // Google GenAI does not support all OpenAPI Schema features,
// so this is needed as an escape hatch:
// TODO convert into provider option
((_c = googleOptions == null ? void 0 : googleOptions.structuredOutputs) != null ? _c : true) ? convertJSONSchemaToOpenAPISchema(responseFormat.schema) : void 0,
...(googleOptions == null ? void 0 : googleOptions.audioTimestamp) && {
audioTimestamp: googleOptions.audioTimestamp
},
// provider options:
responseModalities: googleOptions == null ? void 0 : googleOptions.responseModalities,
thinkingConfig,
...(googleOptions == null ? void 0 : googleOptions.mediaResolution) && {
mediaResolution: googleOptions.mediaResolution
},
...imageConfig && { imageConfig }
},
contents,
systemInstruction: isGemmaModel ? void 0 : systemInstruction,
safetySettings,
tools: googleTools2,
toolConfig,
cachedContent: googleOptions == null ? void 0 : googleOptions.cachedContent,
labels: googleOptions == null ? void 0 : googleOptions.labels,
serviceTier: bodyServiceTier
},
warnings: [...warnings, ...toolWarnings],
providerOptionsNames,
extraHeaders: vertexPaygoHeaders
};
}
convertGenerateContentResponse({
response,
warnings,
providerOptionsNames
}) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s;
const wrapProviderMetadata = (payload) => Object.fromEntries(
providerOptionsNames.map((name) => [name, payload])
);
const candidate = (_a = response.candidates) == null ? void 0 : _a[0];
const promptBlockReason = (_b = response.promptFeedback) == null ? void 0 : _b.blockReason;
const isPromptBlocked = (candidate == null ? void 0 : candidate.finishReason) == null && promptBlockReason != null;
const rawFinishReason = (_d = (_c = candidate == null ? void 0 : candidate.finishReason) != null ? _c : promptBlockReason) != null ? _d : void 0;
const content = [];
const parts = (_f = (_e = candidate == null ? void 0 : candidate.content) == null ? void 0 : _e.parts) != null ? _f : [];
const usageMetadata = response.usageMetadata;
let lastCodeExecutionToolCallId;
let lastServerToolCallId;
for (const part of parts) {
if ("executableCode" in part && ((_g = part.executableCode) == null ? void 0 : _g.code)) {
const toolCallId = this.config.generateId();
lastCodeExecutionToolCallId = toolCallId;
content.push({
type: "tool-call",
toolCallId,
toolName: "code_execution",
input: JSON.stringify(part.executableCode),
providerExecuted: true
});
} else if ("codeExecutionResult" in part && part.codeExecutionResult) {
content.push({
type: "tool-result",
// Results correspond to the most recent executable code part.
toolCallId: lastCodeExecutionToolCallId,
toolName: "code_execution",
result: {
outcome: part.codeExecutionResult.outcome,
output: (_h = part.codeExecutionResult.output) != null ? _h : ""
}
});
} else if ("text" in part && part.text != null) {
const thoughtSignatureMetadata = part.thoughtSignature ? wrapProviderMetadata({
thoughtSignature: part.thoughtSignature
}) : void 0;
if (part.text.length === 0) {
if (thoughtSignatureMetadata != null && content.length > 0) {
const lastContent = content[content.length - 1];
lastContent.providerMetadata = thoughtSignatureMetadata;
}
} else {
content.push({
type: part.thought === true ? "reasoning" : "text",
text: part.text,
providerMetadata: thoughtSignatureMetadata
});
}
} else if ("functionCall" in part && part.functionCall.name != null) {
content.push({
type: "tool-call",
toolCallId: part.functionCall.id || this.config.generateId(),
toolName: part.functionCall.name,
input: JSON.stringify((_i = part.functionCall.args) != null ? _i : {}),
providerMetadata: part.thoughtSignature ? wrapProviderMetadata({
thoughtSignature: part.thoughtSignature
}) : void 0
});
} else if ("inlineData" in part) {
const hasThought = part.thought === true;
const hasThoughtSignature = !!part.thoughtSignature;
content.push({
type: hasThought ? "reasoning-file" : "file",
data: { type: "data", data: part.inlineData.data },
mediaType: part.inlineData.mimeType,
providerMetadata: hasThoughtSignature ? wrapProviderMetadata({
thoughtSignature: part.thoughtSignature
}) : void 0
});
} else if ("toolCall" in part && part.toolCall) {
const toolCallId = part.toolCall.id || this.config.generateId();
lastServerToolCallId = toolCallId;
content.push({
type: "tool-call",
toolCallId,
toolName: `server:${part.toolCall.toolType}`,
input: JSON.stringify((_j = part.toolCall.args) != null ? _j : {}),
providerExecuted: true,
dynamic: true,
providerMetadata: part.thoughtSignature ? wrapProviderMetadata({
thoughtSignature: part.thoughtSignature,
serverToolCallId: toolCallId,
serverToolType: part.toolCall.toolType
}) : wrapProviderMetadata({
serverToolCallId: toolCallId,
serverToolType: part.toolCall.toolType
})
});
} else if ("toolResponse" in part && part.toolResponse) {
const responseToolCallId = lastServerToolCallId || part.toolResponse.id || this.config.generateId();
content.push({
type: "tool-result",
toolCallId: responseToolCallId,
toolName: `server:${part.toolResponse.toolType}`,
result: (_k = part.toolResponse.response) != null ? _k : {},
providerMetadata: part.thoughtSignature ? wrapProviderMetadata({
thoughtSignature: part.thoughtSignature,
serverToolCallId: responseToolCallId,
serverToolType: part.toolResponse.toolType
}) : wrapProviderMetadata({
serverToolCallId: responseToolCallId,
serverToolType: part.toolResponse.toolType
})
});
lastServerToolCallId = void 0;
}
}
const sources = (_l = extractSources({
groundingMetadata: candidate == null ? void 0 : candidate.groundingMetadata,
generateId: this.config.generateId
})) != null ? _l : [];
for (const source of sources) {
content.push(source);
}
return {
content,
finishReason: {
unified: isPromptBlocked ? "content-filter" : mapGoogleFinishReason({
finishReason: rawFinishReason,
// Only count client-executed tool calls for finish reason determination.
hasToolCalls: content.some(
(part) => part.type === "tool-call" && !part.providerExecuted
)
}),
raw: rawFinishReason
},
usage: convertGoogleUsage(usageMetadata),
warnings,
providerMetadata: wrapProviderMetadata({
promptFeedback: (_m = response.promptFeedback) != null ? _m : null,
groundingMetadata: (_n = candidate == null ? void 0 : candidate.groundingMetadata) != null ? _n : null,
urlContextMetadata: (_o = candidate == null ? void 0 : candidate.urlContextMetadata) != null ? _o : null,
safetyRatings: (_p = candidate == null ? void 0 : candidate.safetyRatings) != null ? _p : null,
usageMetadata: usageMetadata != null ? usageMetadata : null,
finishMessage: (_q = candidate == null ? void 0 : candidate.finishMessage) != null ? _q : null,
serviceTier: (_r = usageMetadata == null ? void 0 : usageMetadata.serviceTier) != null ? _r : null
}),
response: {
// TODO timestamp, model id
id: (_s = response.responseId) != null ? _s : void 0
}
};
}
async doGenerate(options) {
const { args, warnings, providerOptionsNames, extraHeaders } = await this.getArgs(options);
const mergedHeaders = combineHeaders2(
this.config.headers ? await resolve2(this.config.headers) : void 0,
options.headers,
extraHeaders
);
const {
responseHeaders,
value: response,
rawValue: rawResponse
} = await postJsonToApi2({
url: `${this.config.baseURL}/${getModelPath(
this.modelId
)}:generateContent`,
headers: mergedHeaders,
body: args,
failedResponseHandler: googleFailedResponseHandler,
successfulResponseHandler: createJsonResponseHandler2(responseSchema),
abortSignal: options.abortSignal,
fetch: this.config.fetch
});
const result = this.convertGenerateContentResponse({
response,
warnings,
providerOptionsNames
});
return {
...result,
request: { body: args },
response: {
...result.response,
headers: responseHeaders,
body: rawResponse
}
};
}
async doStream(options) {
const { args, warnings, providerOptionsNames, extraHeaders } = await this.getArgs(options, { isStreaming: true });
const wrapProviderMetadata = (payload) => Object.fromEntries(
providerOptionsNames.map((name) => [name, payload])
);
const headers = combineHeaders2(
this.config.headers ? await resolve2(this.config.headers) : void 0,
options.headers,
extraHeaders
);
const { responseHeaders, value: response } = await postJsonToApi2({
url: `${this.config.baseURL}/${getModelPath(
this.modelId
)}:streamGenerateContent?alt=sse`,
headers,
body: args,
failedResponseHandler: googleFailedResponseHandler,
successfulResponseHandler: createEventSourceResponseHandler(chunkSchema),
abortSignal: options.abortSignal,
fetch: this.config.fetch
});
let finishReason = {
unified: "other",
raw: void 0
};
let usage = void 0;
let providerMetadata = void 0;
let lastGroundingMetadata = null;
let lastUrlContextMetadata = null;
const generateId4 = this.config.generateId;
let hasToolCalls = false;
let hasEmittedResponseMetadata = false;
let currentTextBlockId = null;
let currentReasoningBlockId = null;
let blockCounter = 0;
const emittedSourceUrls = /* @__PURE__ */ new Set();
let lastCodeExecutionToolCallId;
let lastServerToolCallId;
const activeStreamingToolCalls = [];
const finishActiveStreamingToolCall = (controller) => {
const active = activeStreamingToolCalls.pop();
if (active == null) {
return;
}
const { finalJSON, closingDelta } = active.accumulator.finalize();
if (closingDelta.length > 0) {
controller.enqueue({
type: "tool-input-delta",
id: active.toolCallId,
delta: closingDelta,
providerMetadata: active.providerMetadata
});
}
controller.enqueue({
type: "tool-input-end",
id: active.toolCallId,
providerMetadata: active.providerMetadata
});
controller.enqueue({
type: "tool-call",
toolCallId: active.toolCallId,
toolName: active.toolName,
input: finalJSON,
providerMetadata: active.providerMetadata
});
hasToolCalls = true;
};
return {
stream: response.pipeThrough(
new TransformStream({
start(controller) {
controller.enqueue({ type: "stream-start", warnings });
},
transform(chunk, controller) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
if (options.includeRawChunks) {
controller.enqueue({ type: "raw", rawValue: chunk.rawValue });
}
if (!chunk.success) {
controller.enqueue({ type: "error", error: chunk.error });
return;
}
const value = chunk.value;
if (!hasEmittedResponseMetadata && value.responseId != null) {
hasEmittedResponseMetadata = true;
controller.enqueue({
type: "response-metadata",
id: value.responseId
});
}
const usageMetadata = value.usageMetadata;
if (usageMetadata != null) {
usage = usageMetadata;
}
const candidate = (_a = value.candidates) == null ? void 0 : _a[0];
if (candidate == null) {
const promptBlockReason2 = (_b = value.promptFeedback) == null ? void 0 : _b.blockReason;
if (promptBlockReason2 != null) {
finishReason = {
unified: "content-filter",
raw: promptBlockReason2
};
providerMetadata = wrapProviderMetadata({
promptFeedback: (_c = value.promptFeedback) != null ? _c : null,
groundingMetadata: lastGroundingMetadata,
urlContextMetadata: lastUrlContextMetadata,
safetyRatings: null,
usageMetadata: usageMetadata != null ? usageMetadata : null,
finishMessage: null,
serviceTier: (_d = usage == null ? void 0 : usage.serviceTier) != null ? _d : null
});
}
return;
}
const content = candidate.content;
if (candidate.groundingMetadata != null) {
lastGroundingMetadata = candidate.groundingMetadata;
}
if (candidate.urlContextMetadata != null) {
lastUrlContextMetadata = candidate.urlContextMetadata;
}
const sources = extractSources({
groundingMetadata: candidate.groundingMetadata,
generateId: generateId4
});
if (sources != null) {
for (const source of sources) {
if (source.sourceType === "url" && !emittedSourceUrls.has(source.url)) {
emittedSourceUrls.add(source.url);
controller.enqueue(source);
}
}
}
if (content != null) {
const parts = (_e = content.parts) != null ? _e : [];
for (const part of parts) {
if ("executableCode" in part && ((_f = part.executableCode) == null ? void 0 : _f.code)) {
const toolCallId = generateId4();
lastCodeExecutionToolCallId = toolCallId;
controller.enqueue({
type: "tool-call",
toolCallId,
toolName: "code_execution",
input: JSON.stringify(part.executableCode),
providerExecuted: true
});
} else if ("codeExecutionResult" in part && part.codeExecutionResult) {
const toolCallId = lastCodeExecutionToolCallId;
if (toolCallId) {
controller.enqueue({
type: "tool-result",
toolCallId,
toolName: "code_execution",
result: {
outcome: part.codeExecutionResult.outcome,
output: (_g = part.codeExecutionResult.output) != null ? _g : ""
}
});
}
} else if ("text" in part && part.text != null) {
const thoughtSignatureMetadata = part.thoughtSignature ? wrapProviderMetadata({
thoughtSignature: part.thoughtSignature
}) : void 0;
if (part.text.length === 0) {
if (thoughtSignatureMetadata != null && currentTextBlockId !== null) {
controller.enqueue({
type: "text-delta",
id: currentTextBlockId,
delta: "",
providerMetadata: thoughtSignatureMetadata
});
}
} else if (part.thought === true) {
if (currentTextBlockId !== null) {
controller.enqueue({
type: "text-end",
id: currentTextBlockId
});
currentTextBlockId = null;
}
if (currentReasoningBlockId === null) {
currentReasoningBlockId = String(blockCounter++);
controller.enqueue({
type: "reasoning-start",
id: currentReasoningBlockId,
providerMetadata: thoughtSignatureMetadata
});
}
controller.enqueue({
type: "reasoning-delta",
id: currentReasoningBlockId,
delta: part.text,
providerMetadata: thoughtSignatureMetadata
});
} else {
if (currentReasoningBlockId !== null) {
controller.enqueue({
type: "reasoning-end",
id: currentReasoningBlockId
});
currentReasoningBlockId = null;
}
if (currentTextBlockId === null) {
currentTextBlockId = String(blockCounter++);
controller.enqueue({
type: "text-start",
id: currentTextBlockId,
providerMetadata: thoughtSignatureMetadata
});
}
controller.enqueue({
type: "text-delta",
id: currentTextBlockId,
delta: part.text,
providerMetadata: thoughtSignatureMetadata
});
}
} else if ("inlineData" in part) {
if (currentTextBlockId !== null) {
controller.enqueue({
type: "text-end",
id: currentTextBlockId
});
currentTextBlockId = null;
}
if (currentReasoningBlockId !== null) {
controller.enqueue({
type: "reasoning-end",
id: currentReasoningBlockId
});
currentReasoningBlockId = null;
}
const hasThought = part.thought === true;
const hasThoughtSignature = !!part.thoughtSignature;
const fileMeta = hasThoughtSignature ? wrapProviderMetadata({
thoughtSignature: part.thoughtSignature
}) : void 0;
controller.enqueue({
type: hasThought ? "reasoning-file" : "file",
mediaType: part.inlineData.mimeType,
data: { type: "data", data: part.inlineData.data },
providerMetadata: fileMeta
});
} else if ("toolCall" in part && part.toolCall) {
const toolCallId = part.toolCall.id || generateId4();
lastServerToolCallId = toolCallId;
const serverMeta = wrapProviderMetadata({
...part.thoughtSignature ? { thoughtSignature: part.thoughtSignature } : {},
serverToolCallId: toolCallId,
serverToolType: part.toolCall.toolType
});
controller.enqueue({
type: "tool-call",
toolCallId,
toolName: `server:${part.toolCall.toolType}`,
input: JSON.stringify((_h = part.toolCall.args) != null ? _h : {}),
providerExecuted: true,
dynamic: true,
providerMetadata: serverMeta
});
} else if ("toolResponse" in part && part.toolResponse) {
const responseToolCallId = lastServerToolCallId || part.toolResponse.id || generateId4();
const serverMeta = wrapProviderMetadata({
...part.thoughtSignature ? { thoughtSignature: part.thoughtSignature } : {},
serverToolCallId: responseToolCallId,
serverToolType: part.toolResponse.toolType
});
controller.enqueue({
type: "tool-result",
toolCallId: responseToolCallId,
toolName: `server:${part.toolResponse.toolType}`,
result: (_i = part.toolResponse.response) != null ? _i : {},
providerMetadata: serverMeta
});
lastServerToolCallId = void 0;
}
}
for (const part of parts) {
if (!("functionCall" in part)) continue;
const providerMeta = part.thoughtSignature ? wrapProviderMetadata({
thoughtSignature: part.thoughtSignature
}) : void 0;
const isStreamingChunk = part.functionCall.partialArgs != null || part.functionCall.name != null && part.functionCall.willContinue === true;
const isTerminalChunk = part.functionCall.name == null && part.functionCall.args == null && part.functionCall.partialArgs == null && part.functionCall.willContinue == null;
const isCompleteCall = part.functionCall.name != null && part.functionCall.args != null && part.functionCall.partialArgs == null;
const isNoArgsCompleteCall = part.functionCall.name != null && part.functionCall.args == null && part.functionCall.partialArgs == null && part.functionCall.willContinue !== true;
if (isStreamingChunk) {
if (part.functionCall.name != null) {
const toolCallId = part.functionCall.id || generateId4();
const accumulator = new GoogleJSONAccumulator();
activeStreamingToolCalls.push({
toolCallId,
toolName: part.functionCall.name,
accumulator,
providerMetadata: providerMeta
});
controller.enqueue({
type: "tool-input-start",
id: toolCallId,
toolName: part.functionCall.name,
providerMetadata: providerMeta
});
if (part.functionCall.partialArgs != null) {
const partialArgs = part.functionCall.partialArgs;
const { textDelta } = accumulator.processPartialArgs(partialArgs);
if (textDelta.length > 0) {
controller.enqueue({
type: "tool-input-delta",
id: toolCallId,
delta: textDelta,
providerMetadata: providerMeta
});
}
if (part.functionCall.willContinue !== true && partialArgs.every((arg) => arg.willContinue !== true)) {
finishActiveStreamingToolCall(controller);
}
}
} else if (part.functionCall.partialArgs != null && activeStreamingToolCalls.length > 0) {
const active = activeStreamingToolCalls[activeStreamingToolCalls.length - 1];
const partialArgs = part.functionCall.partialArgs;
const { textDelta } = active.accumulator.processPartialArgs(partialArgs);
if (textDelta.length > 0) {
controller.enqueue({
type: "tool-input-delta",
id: active.toolCallId,
delta: textDelta,
providerMetadata: providerMeta
});
}
if (part.functionCall.willContinue !== true && partialArgs.every((arg) => arg.willContinue !== true)) {
finishActiveStreamingToolCall(controller);
}
}
} else if (isTerminalChunk && activeStreamingToolCalls.length > 0) {
finishActiveStreamingToolCall(controller);
} else if (isCompleteCall) {
const toolCallId = part.functionCall.id || generateId4();
const toolName = part.functionCall.name;
const args2 = typeof part.functionCall.args === "string" ? part.functionCall.args : JSON.stringify((_j = part.functionCall.args) != null ? _j : {});
controller.enqueue({
type: "tool-input-start",
id: toolCallId,
toolName,
providerMetadata: providerMeta
});
controller.enqueue({
type: "tool-input-delta",
id: toolCallId,
delta: args2,
providerMetadata: providerMeta
});
controller.enqueue({
type: "tool-input-end",
id: toolCallId,
providerMetadata: providerMeta
});
controller.enqueue({
type: "tool-call",
toolCallId,
toolName,
input: args2,
providerMetadata: providerMeta
});
hasToolCalls = true;
} else if (isNoArgsCompleteCall) {
const toolCallId = part.functionCall.id || generateId4();
const toolName = part.functionCall.name;
controller.enqueue({
type: "tool-input-start",
id: toolCallId,
toolName,
providerMetadata: providerMeta
});
controller.enqueue({
type: "tool-input-end",
id: toolCallId,
providerMetadata: providerMeta
});
controller.enqueue({
type: "tool-call",
toolCallId,
toolName,
input: "{}",
providerMetadata: providerMeta
});
hasToolCalls = true;
}
}
}
const promptBlockReason = (_k = value.promptFeedback) == null ? void 0 : _k.blockReason;
const isPromptBlocked = candidate.finishReason == null && promptBlockReason != null;
const rawFinishReason = (_m = (_l = candidate.finishReason) != null ? _l : promptBlockReason) != null ? _m : void 0;
if (rawFinishReason != null) {
finishReason = {
unified: isPromptBlocked ? "content-filter" : mapGoogleFinishReason({
finishReason: rawFinishReason,
hasToolCalls
}),
raw: rawFinishReason
};
providerMetadata = wrapProviderMetadata({
promptFeedback: (_n = value.promptFeedback) != null ? _n : null,
groundingMetadata: lastGroundingMetadata,
urlContextMetadata: lastUrlContextMetadata,
safetyRatings: (_o = candidate.safetyRatings) != null ? _o : null,
usageMetadata: usageMetadata != null ? usageMetadata : null,
finishMessage: (_p = candidate.finishMessage) != null ? _p : null,
serviceTier: (_q = usage == null ? void 0 : usage.serviceTier) != null ? _q : null
});
}
},
flush(controller) {
if (currentTextBlockId !== null) {
controller.enqueue({
type: "text-end",
id: currentTextBlockId
});
}
if (currentReasoningBlockId !== null) {
controller.enqueue({
type: "reasoning-end",
id: currentReasoningBlockId
});
}
controller.enqueue({
type: "finish",
finishReason,
usage: convertGoogleUsage(usage),
providerMetadata
});
}
})
),
response: { headers: responseHeaders },
request: { body: args }
};
}
};
function getMaxOutputTokensForGemini25Model() {
return 65536;
}
function getMaxThinkingTokensForGemini25Model(modelId) {
const id = modelId.toLowerCase();
if (id.includes("2.5-pro") || id.includes("gemini-3-pro-image")) {
return 32768;
}
return 24576;
}
function resolveThinkingConfig({
reasoning,
modelId,
warnings
}) {
if (!isCustomReasoning(reasoning)) {
return void 0;
}
if (getGoogleModelCapabilities(modelId).usesGemini3Features && !modelId.includes("gemini-3-pro-image")) {
return resolveGemini3ThinkingConfig({ reasoning, modelId, warnings });
}
return resolveGemini25ThinkingConfig({ reasoning, modelId, warnings });
}
function resolveGemini3ThinkingConfig({
reasoning,
modelId,
warnings
}) {
const minimumThinkingLevel = getMinimumThinkingLevelForGemini3Model(modelId);
if (reasoning === "none") {
return { thinkingLevel: minimumThinkingLevel };
}
const thinkingLevel = mapReasoningToProviderEffort({
reasoning,
effortMap: {
minimal: minimumThinkingLevel,
low: "low",
medium: "medium",
high: "high",
xhigh: "high"
},
warnings
});
if (thinkingLevel == null) {
return void 0;
}
return { thinkingLevel };
}
function getMinimumThinkingLevelForGemini3Model(modelId) {
var _a;
const modelName = (_a = modelId.split("/").at(-1)) == null ? void 0 : _a.toLowerCase();
if (modelName === "gemini-flash-latest") {
return "low";
}
const versionMatch = /^gemini-(\d+)\.(\d+)-flash(?:$|-(?!lite(?:-|$)))/.exec(
modelName != null ? modelName : ""
);
if (versionMatch == null) {
return "minimal";
}
const majorVersion = Number(versionMatch[1]);
const minorVersion = Number(versionMatch[2]);
return majorVersion > 3 || majorVersion === 3 && minorVersion >= 7 ? "low" : "minimal";
}
function resolveGemini25ThinkingConfig({
reasoning,
modelId,
warnings
}) {
if (reasoning === "none") {
return { thinkingBudget: 0 };
}
const thinkingBudget = mapReasoningToProviderBudget({
reasoning,
maxOutputTokens: getMaxOutputTokensForGemini25Model(),
maxReasoningBudget: getMaxThinkingTokensForGemini25Model(modelId),
minReasoningBudget: 0,
warnings
});
if (thinkingBudget == null) {
return void 0;
}
return { thinkingBudget };
}
function extractSources({
groundingMetadata,
generateId: generateId4
}) {
var _a, _b, _c, _d, _e, _f;
if (!(groundingMetadata == null ? void 0 : groundingMetadata.groundingChunks)) {
return void 0;
}
const sources = [];
for (const chunk of groundingMetadata.groundingChunks) {
if (chunk.web != null) {
sources.push({
type: "source",
sourceType: "url",
id: generateId4(),
url: chunk.web.uri,
title: (_a = chunk.web.title) != null ? _a : void 0
});
} else if (chunk.image != null) {
sources.push({
type: "source",
sourceType: "url",
id: generateId4(),
// Google requires attribution to the source URI, not the actual image URI.
// TODO: add another type in v7 to allow both the image and source URL to be included separately
url: chunk.image.sourceUri,
title: (_b = chunk.image.title) != null ? _b : void 0
});
} else if (chunk.retrievedContext != null) {
const uri = chunk.retrievedContext.uri;
const fileSearchStore = chunk.retrievedContext.fileSearchStore;
if (uri && (uri.startsWith("http://") || uri.startsWith("https://"))) {
sources.push({
type: "source",
sourceType: "url",
id: generateId4(),
url: uri,
title: (_c = chunk.retrievedContext.title) != null ? _c : void 0
});
} else if (uri) {
const title = (_d = chunk.retrievedContext.title) != null ? _d : "Unknown Document";
let mediaType = "application/octet-stream";
let filename = void 0;
if (uri.endsWith(".pdf")) {
mediaType = "application/pdf";
filename = uri.split("/").pop();
} else if (uri.endsWith(".txt")) {
mediaType = "text/plain";
filename = uri.split("/").pop();
} else if (uri.endsWith(".docx")) {
mediaType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
filename = uri.split("/").pop();
} else if (uri.endsWith(".doc")) {
mediaType = "application/msword";
filename = uri.split("/").pop();
} else if (uri.match(/\.(md|markdown)$/)) {
mediaType = "text/markdown";
filename = uri.split("/").pop();
} else {
filename = uri.split("/").pop();
}
sources.push({
type: "source",
sourceType: "document",
id: generateId4(),
mediaType,
title,
filename
});
} else if (fileSearchStore) {
const title = (_e = chunk.retrievedContext.title) != null ? _e : "Unknown Document";
sources.push({
type: "source",
sourceType: "document",
id: generateId4(),
mediaType: "application/octet-stream",
title,
filename: fileSearchStore.split("/").pop()
});
}
} else if (chunk.maps != null) {
if (chunk.maps.uri) {
sources.push({
type: "source",
sourceType: "url",
id: generateId4(),
url: chunk.maps.uri,
title: (_f = chunk.maps.title) != null ? _f : void 0
});
}
}
}
return sources.length > 0 ? sources : void 0;
}
var getGroundingMetadataSchema = () => z5.object({
webSearchQueries: z5.array(z5.string()).nullish(),
imageSearchQueries: z5.array(z5.string()).nullish(),
retrievalQueries: z5.array(z5.string()).nullish(),
searchEntryPoint: z5.object({ renderedContent: z5.string() }).nullish(),
groundingChunks: z5.array(
z5.object({
web: z5.object({ uri: z5.string(), title: z5.string().nullish() }).nullish(),
image: z5.object({
sourceUri: z5.string(),
imageUri: z5.string(),
title: z5.string().nullish(),
domain: z5.string().nullish()
}).nullish(),
retrievedContext: z5.object({
uri: z5.string().nullish(),
title: z5.string().nullish(),
text: z5.string().nullish(),
fileSearchStore: z5.string().nullish()
}).nullish(),
maps: z5.object({
uri: z5.string().nullish(),
title: z5.string().nullish(),
text: z5.string().nullish(),
placeId: z5.string().nullish()
}).nullish()
})
).nullish(),
groundingSupports: z5.array(
z5.object({
segment: z5.object({
startIndex: z5.number().nullish(),
endIndex: z5.number().nullish(),
text: z5.string().nullish()
}).nullish(),
segment_text: z5.string().nullish(),
groundingChunkIndices: z5.array(z5.number()).nullish(),
supportChunkIndices: z5.array(z5.number()).nullish(),
confidenceScores: z5.array(z5.number()).nullish(),
confidenceScore: z5.array(z5.number()).nullish()
})
).nullish(),
retrievalMetadata: z5.union([
z5.object({
webDynamicRetrievalScore: z5.number()
}),
z5.object({})
]).nullish()
});
var partialArgSchema = z5.object({
jsonPath: z5.string(),
stringValue: z5.string().nullish(),
numberValue: z5.number().nullish(),
boolValue: z5.boolean().nullish(),
nullValue: z5.unknown().nullish(),
willContinue: z5.boolean().nullish()
});
var getContentSchema = () => z5.object({
parts: z5.array(
z5.union([
// note: order matters since text can be fully empty
z5.object({
functionCall: z5.object({
id: z5.string().nullish(),
name: z5.string().nullish(),
args: z5.unknown().nullish(),
partialArgs: z5.array(partialArgSchema).nullish(),
willContinue: z5.boolean().nullish()
}),
thoughtSignature: z5.string().nullish()
}),
z5.object({
inlineData: z5.object({
mimeType: z5.string(),
data: z5.string()
}),
thought: z5.boolean().nullish(),
thoughtSignature: z5.string().nullish()
}),
z5.object({
toolCall: z5.object({
toolType: z5.string(),
args: z5.unknown().nullish(),
id: z5.string()
}),
thoughtSignature: z5.string().nullish()
}),
z5.object({
toolResponse: z5.object({
toolType: z5.string(),
response: z5.unknown().nullish(),
id: z5.string()
}),
thoughtSignature: z5.string().nullish()
}),
z5.object({
executableCode: z5.object({
language: z5.string(),
code: z5.string()
}).nullish(),
codeExecutionResult: z5.object({
outcome: z5.string(),
output: z5.string().nullish()
}).nullish(),
text: z5.string().nullish(),
thought: z5.boolean().nullish(),
thoughtSignature: z5.string().nullish()
})
])
).nullish()
});
var getSafetyRatingSchema = () => z5.object({
category: z5.string().nullish(),
probability: z5.string().nullish(),
probabilityScore: z5.number().nullish(),
severity: z5.string().nullish(),
severityScore: z5.number().nullish(),
blocked: z5.boolean().nullish()
});
var tokenDetailsSchema = z5.array(
z5.object({
modality: z5.string(),
tokenCount: z5.number()
}).loose()
).nullish();
var usageSchema = z5.object({
cachedContentTokenCount: z5.number().nullish(),
thoughtsTokenCount: z5.number().nullish(),
promptTokenCount: z5.number().nullish(),
candidatesTokenCount: z5.number().nullish(),
toolUsePromptTokenCount: z5.number().nullish(),
totalTokenCount: z5.number().nullish(),
// https://cloud.google.com/vertex-ai/generative-ai/docs/reference/rest/v1/GenerateContentResponse#TrafficType
trafficType: z5.string().nullish(),
serviceTier: z5.string().nullish(),
// https://ai.google.dev/api/generate-content#Modality
promptTokensDetails: tokenDetailsSchema,
cacheTokensDetails: tokenDetailsSchema,
candidatesTokensDetails: tokenDetailsSchema,
toolUsePromptTokensDetails: tokenDetailsSchema
}).loose();
var getUrlContextMetadataSchema = () => z5.object({
urlMetadata: z5.array(
z5.object({
retrievedUrl: z5.string(),
urlRetrievalStatus: z5.string()
})
).nullish()
});
var responseSchema = lazySchema5(
() => zodSchema5(
z5.object({
responseId: z5.string().nullish(),
candidates: z5.array(
z5.object({
content: getContentSchema().nullish().or(z5.object({}).strict()),
finishReason: z5.string().nullish(),
finishMessage: z5.string().nullish(),
safetyRatings: z5.array(getSafetyRatingSchema()).nullish(),
groundingMetadata: getGroundingMetadataSchema().nullish(),
urlContextMetadata: getUrlContextMetadataSchema().nullish()
})
).nullish(),
usageMetadata: usageSchema.nullish(),
promptFeedback: z5.object({
blockReason: z5.string().nullish(),
safetyRatings: z5.array(getSafetyRatingSchema()).nullish()
}).nullish()
})
)
);
var chunkSchema = lazySchema5(
() => zodSchema5(
z5.object({
responseId: z5.string().nullish(),
candidates: z5.array(
z5.object({
content: getContentSchema().nullish(),
finishReason: z5.string().nullish(),
finishMessage: z5.string().nullish(),
safetyRatings: z5.array(getSafetyRatingSchema()).nullish(),
groundingMetadata: getGroundingMetadataSchema().nullish(),
urlContextMetadata: getUrlContextMetadataSchema().nullish()
})
).nullish(),
usageMetadata: usageSchema.nullish(),
promptFeedback: z5.object({
blockReason: z5.string().nullish(),
safetyRatings: z5.array(getSafetyRatingSchema()).nullish()
}).nullish()
})
)
);
// src/google-batch.ts
var googleBatchInputFileMaxBytes = 2 * 1024 * 1024 * 1024;
var googleBatchInlineCreationMaxBytes = 2e7;
var supportedGoogleBatchContentTypes = /* @__PURE__ */ new Set(["text", "reasoning", "source", "tool-call", "tool-result"]);
var googleRpcStatusSchema = z6.object({
code: z6.union([z6.number(), z6.string()]).nullish(),
message: z6.string().nullish(),
status: z6.string().nullish()
});
var googleBatchStatsSchema = z6.object({
requestCount: z6.union([z6.string(), z6.number()]).nullish(),
successfulRequestCount: z6.union([z6.string(), z6.number()]).nullish(),
failedRequestCount: z6.union([z6.string(), z6.number()]).nullish(),
pendingRequestCount: z6.union([z6.string(), z6.number()]).nullish()
});
var googleBatchOutputSchema = z6.object({
responsesFile: z6.string().nullish(),
inlinedResponses: z6.object({
inlinedResponses: z6.array(
z6.object({
metadata: z6.object({
key: z6.string()
}),
response: z6.unknown().nullish(),
error: googleRpcStatusSchema.nullish()
})
)
}).nullish()
});
var googleBatchOperationSchema = lazySchema6(
() => zodSchema6(
z6.object({
name: z6.string(),
metadata: z6.object({
state: z6.string().nullish(),
createTime: z6.string().nullish(),
batchStats: googleBatchStatsSchema.nullish(),
output: googleBatchOutputSchema.nullish()
}).nullish(),
done: z6.boolean().nullish(),
error: googleRpcStatusSchema.nullish(),
response: googleBatchOutputSchema.nullish()
})
)
);
var googleFileUploadResponseSchema = lazySchema6(
() => zodSchema6(
z6.object({
file: z6.object({
name: z6.string()
})
})
)
);
var googleBatchResultLineSchema = lazySchema6(
() => zodSchema6(
z6.object({
key: z6.string(),
response: z6.unknown().nullish(),
error: googleRpcStatusSchema.nullish()
})
)
);
var googleBatchResponsePreviewSchema = lazySchema6(
() => zodSchema6(
z6.object({
candidates: z6.array(z6.unknown()).nullish(),
promptFeedback: z6.object({
blockReason: z6.string().nullish()
}).nullish()
})
)
);
var GoogleBatchLanguageModel = class _GoogleBatchLanguageModel extends GoogleLanguageModel {
static [WORKFLOW_SERIALIZE3](model) {
return GoogleLanguageModel[WORKFLOW_SERIALIZE3](model);
}
static [WORKFLOW_DESERIALIZE3](options) {
return new _GoogleBatchLanguageModel(options.modelId, options.config);
}
constructor(modelId, config) {
var _a;
super(modelId, config);
this.batchConfig = config;
this.batchGenerateId = (_a = config.generateId) != null ? _a : generateId2;
}
async experimental_doStartBatch(options) {
const warnings = [];
const displayName = `ai-sdk-batch-${this.batchGenerateId()}`;
const inlinedRequests = [];
const inlineBatchBody = {
batch: {
displayName,
...options.webhookUrl != null && {
webhookConfig: { uris: [options.webhookUrl] }
},
inputConfig: {
requests: { requests: inlinedRequests }
}
}
};
const textEncoder = new TextEncoder();
let inlineInputBytes = textEncoder.encode(
JSON.stringify(inlineBatchBody)
).byteLength;
let fileParts;
for (const request of options.requests) {
const preparedRequest = await this.getArgs(request.options);
const inlinedRequest = {
request: preparedRequest.args,
metadata: { key: request.id }
};
if (fileParts == null) {
const requestBytes = textEncoder.encode(
JSON.stringify(inlinedRequest)
).byteLength;
const nextInlineInputBytes = inlineInputBytes + requestBytes + (inlinedRequests.length > 0 ? 1 : 0);
if (nextInlineInputBytes < googleBatchInlineCreationMaxBytes) {
inlinedRequests.push(inlinedRequest);
inlineInputBytes = nextInlineInputBytes;
} else {
fileParts = [];
for (const previousRequest of inlinedRequests) {
fileParts.push(
JSON.stringify({
key: previousRequest.metadata.key,
request: previousRequest.request
}),
"\n"
);
}
inlinedRequests.length = 0;
fileParts.push(
JSON.stringify({
key: request.id,
request: preparedRequest.args
}),
"\n"
);
}
} else {
fileParts.push(
JSON.stringify({
key: request.id,
request: preparedRequest.args
}),
"\n"
);
}
for (const warning of preparedRequest.warnings) {
warnings.push({ requestId: request.id, warning });
}
}
const headers = await this.getHeaders(options.headers);
const createUrl = `${this.batchConfig.baseURL}/${getModelPath(
this.modelId
)}:batchGenerateContent`;
let operation;
if (fileParts == null) {
const { value } = await postJsonToApi3({
url: createUrl,
headers,
body: inlineBatchBody,
failedResponseHandler: googleFailedResponseHandler,
successfulResponseHandler: createJsonResponseHandler3(
googleBatchOperationSchema
),
abortSignal: options.abortSignal,
fetch: this.batchConfig.fetch
});
operation = value;
} else {
const inputFile = new Blob(fileParts, { type: "application/jsonl" });
fileParts.length = 0;
if (inputFile.size > googleBatchInputFileMaxBytes) {
throw new InvalidArgumentError({
argument: "requests",
message: "Google batch input files must not exceed 2 GB."
});
}
const { value: uploadUrl } = await postJsonToApi3({
url: `${this.getBaseOrigin()}/upload/v1beta/files`,
headers: combineHeaders3(headers, {
"X-Goog-Upload-Protocol": "resumable",
"X-Goog-Upload-Command": "start",
"X-Goog-Upload-Header-Content-Length": String(inputFile.size),
"X-Goog-Upload-Header-Content-Type": "application/jsonl"
}),
body: {
file: {
display_name: `${displayName}-input`
}
},
failedResponseHandler: googleFailedResponseHandler,
successfulResponseHandler: googleUploadUrlResponseHandler,
abortSignal: options.abortSignal,
fetch: this.batchConfig.fetch
});
const { value: uploadedFile } = await postToApi({
url: uploadUrl,
headers: {
"X-Goog-Upload-Offset": "0",
"X-Goog-Upload-Command": "upload, finalize",
"Content-Type": "application/jsonl"
},
body: {
content: inputFile,
values: {
byteLength: inputFile.size,
mediaType: "application/jsonl"
}
},
failedResponseHandler: googleFailedResponseHandler,
successfulResponseHandler: createJsonResponseHandler3(
googleFileUploadResponseSchema
),
abortSignal: options.abortSignal,
fetch: this.batchConfig.fetch
});
const { value } = await postJsonToApi3({
url: createUrl,
headers,
body: {
batch: {
displayName,
...options.webhookUrl != null && {
webhookConfig: { uris: [options.webhookUrl] }
},
inputConfig: {
fileName: uploadedFile.file.name
}
}
},
failedResponseHandler: googleFailedResponseHandler,
successfulResponseHandler: createJsonResponseHandler3(
googleBatchOperationSchema
),
abortSignal: options.abortSignal,
fetch: this.batchConfig.fetch
});
operation = value;
}
return {
batchId: operation.name,
...convertGoogleBatchStatus(operation),
warnings
};
}
async experimental_doGetBatchStatus(options) {
return convertGoogleBatchStatus(await this.retrieveBatch(options));
}
async experimental_doGetBatchResults(options) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
const operation = await this.retrieveBatch(options);
const batchStatus = convertGoogleBatchStatus(operation);
if (batchStatus.status === "pending") {
throw new InvalidArgumentError({
argument: "batchId",
message: `Google batch "${options.batchId}" is not complete.`
});
}
const inlinedResponses = (_f = (_c = (_b = (_a = operation.metadata) == null ? void 0 : _a.output) == null ? void 0 : _b.inlinedResponses) == null ? void 0 : _c.inlinedResponses) != null ? _f : (_e = (_d = operation.response) == null ? void 0 : _d.inlinedResponses) == null ? void 0 : _e.inlinedResponses;
if (inlinedResponses != null) {
return convertAsyncIteratorToReadableStream(
this.iterateBatchResults(
inlinedResponses.map((result) => ({
key: result.metadata.key,
response: result.response,
error: result.error
}))
)
);
}
const responsesFile = (_j = (_h = (_g = operation.metadata) == null ? void 0 : _g.output) == null ? void 0 : _h.responsesFile) != null ? _j : (_i = operation.response) == null ? void 0 : _i.responsesFile;
if (responsesFile == null) {
if (batchStatus.status === "completed") {
throw new InvalidResponseDataError({
data: operation,
message: `Google batch "${options.batchId}" completed without batch output.`
});
}
return new ReadableStream({
start(controller) {
controller.close();
}
});
}
const encodedResponsesFile = responsesFile.split("/").map((segment) => encodeURIComponent(segment)).join("/");
const { value: lines } = await getFromApi({
url: `${this.getBaseOrigin()}/download/v1beta/${encodedResponsesFile}:download?alt=media`,
headers: await this.getHeaders(options.headers),
failedResponseHandler: googleFailedResponseHandler,
successfulResponseHandler: createJsonLinesResponseHandler(
googleBatchResultLineSchema
),
abortSignal: options.abortSignal,
fetch: this.batchConfig.fetch,
validateUrl: false
});
return convertAsyncIteratorToReadableStream(
this.iterateBatchResults(lines)
);
}
async retrieveBatch(options) {
const { value: operation } = await getFromApi({
url: `${this.batchConfig.baseURL}/${options.batchId}`,
headers: await this.getHeaders(options.headers),
failedResponseHandler: googleFailedResponseHandler,
successfulResponseHandler: createJsonResponseHandler3(
googleBatchOperationSchema
),
abortSignal: options.abortSignal,
fetch: this.batchConfig.fetch,
validateUrl: false
});
return operation;
}
async *iterateBatchResults(results) {
var _a, _b, _c;
for await (const line of results) {
if (line.error != null) {
const error = convertGoogleRpcError(
line.error,
"Google batch request failed."
);
const status = line.error.status === "CANCELLED" || String(line.error.code) === "1" ? "cancelled" : "failed";
yield { id: line.key, status, error };
continue;
}
if (line.response == null) {
yield {
id: line.key,
status: "failed",
error: {
message: "Google returned a batch result without a response or error.",
code: "invalid_batch_result"
}
};
continue;
}
const preview = await safeValidateTypes({
value: line.response,
schema: googleBatchResponsePreviewSchema
});
if (preview.success && (preview.value.candidates == null || preview.value.candidates.length === 0)) {
const promptFeedback = (_a = preview.value.promptFeedback) != null ? _a : void 0;
const blockReason = (_b = promptFeedback == null ? void 0 : promptFeedback.blockReason) != null ? _b : void 0;
yield {
id: line.key,
status: "failed",
error: {
message: blockReason == null ? "Google returned a batch response without any candidates." : `Google blocked the batch request (${blockReason}).`,
code: blockReason == null ? "invalid_response" : "prompt_blocked",
...blockReason != null ? { type: blockReason } : {}
},
...promptFeedback != null ? {
providerMetadata: {
google: {
promptFeedback: {
blockReason: (_c = promptFeedback.blockReason) != null ? _c : null
}
}
}
} : {}
};
continue;
}
const response = await safeValidateTypes({
value: line.response,
schema: responseSchema
});
if (!response.success) {
yield {
id: line.key,
status: "failed",
error: {
message: "Google returned an invalid GenerateContent batch result.",
code: "invalid_response"
}
};
continue;
}
const result = this.convertGenerateContentResponse({
response: response.value,
warnings: [],
providerOptionsNames: ["google"]
});
const unsupportedPart = result.content.find(
(part) => !supportedGoogleBatchContentTypes.has(part.type)
);
if (unsupportedPart != null) {
yield {
id: line.key,
status: "failed",
error: {
message: `Google returned a "${unsupportedPart.type}" content block, but that content is not supported in AI SDK text batches.`,
code: "unsupported_content"
}
};
continue;
}
yield { id: line.key, status: "succeeded", result };
}
}
async getHeaders(headers) {
return combineHeaders3(
this.batchConfig.headers ? await resolve3(this.batchConfig.headers) : void 0,
headers
);
}
getBaseOrigin() {
return this.batchConfig.baseURL.replace(/\/v1beta$/, "");
}
};
function convertGoogleBatchStatus(operation) {
var _a, _b, _c, _d, _e, _f;
const rawStatus = (_b = (_a = operation.metadata) == null ? void 0 : _a.state) != null ? _b : void 0;
const requestCounts = convertGoogleRequestCounts(
(_c = operation.metadata) == null ? void 0 : _c.batchStats
);
const createdAt = (_e = (_d = operation.metadata) == null ? void 0 : _d.createTime) != null ? _e : void 0;
const error = operation.error != null ? convertGoogleRpcError(operation.error, "Google batch failed.") : void 0;
return {
status: mapGoogleBatchStatus({
rawStatus,
done: (_f = operation.done) != null ? _f : void 0,
hasError: error != null
}),
...rawStatus != null ? { rawStatus } : {},
...requestCounts != null ? { requestCounts } : {},
...error != null ? { error } : {},
...createdAt != null ? { createdAt } : {}
};
}
function mapGoogleBatchStatus({
rawStatus,
done,
hasError
}) {
if (hasError) {
return "failed";
}
if (rawStatus == null) {
return done ? "completed" : "pending";
}
const normalizedStatus = rawStatus.replace(/^(?:BATCH|JOB)_STATE_/, "");
switch (normalizedStatus) {
case "SUCCEEDED":
return "completed";
case "FAILED":
case "CANCELLED":
case "EXPIRED":
return "failed";
case "UNSPECIFIED":
case "PENDING":
case "RUNNING":
default:
return "pending";
}
}
function convertGoogleRequestCounts(counts) {
var _a, _b, _c;
const total = parseCount(counts == null ? void 0 : counts.requestCount);
const completed = parseCount((_a = counts == null ? void 0 : counts.successfulRequestCount) != null ? _a : 0);
const failed = parseCount((_b = counts == null ? void 0 : counts.failedRequestCount) != null ? _b : 0);
const pending = parseCount((_c = counts == null ? void 0 : counts.pendingRequestCount) != null ? _c : 0);
return normalizeBatchRequestCounts({
total,
pending,
completed,
failed
});
}
function parseCount(value) {
const count = typeof value === "string" && /^\d+$/.test(value) ? Number(value) : value;
return typeof count === "number" && Number.isSafeInteger(count) && count >= 0 ? count : void 0;
}
function convertGoogleRpcError(error, fallbackMessage) {
var _a;
return {
message: (_a = error.message) != null ? _a : fallbackMessage,
...error.status != null ? { type: error.status } : {},
...error.code != null ? { code: String(error.code) } : {}
};
}
var googleUploadUrlResponseHandler = async ({
response
}) => {
const uploadUrl = response.headers.get("x-goog-upload-url");
if (uploadUrl == null) {
throw new InvalidResponseDataError({
data: response.headers,
message: "Google did not return a resumable upload URL."
});
}
return { value: uploadUrl };
};
// src/tool/code-execution.ts
import { createProviderExecutedToolFactory } from "@ai-sdk/provider-utils";
import { z as z7 } from "zod/v4";
var codeExecution = createProviderExecutedToolFactory({
id: "google.code_execution",
inputSchema: z7.object({
language: z7.string().describe("The programming language of the code."),
code: z7.string().describe("The code to be executed.")
}),
outputSchema: z7.object({
outcome: z7.string().describe('The outcome of the execution (e.g., "OUTCOME_OK").'),
output: z7.string().describe("The output from the code execution.")
})
});
// src/tool/enterprise-web-search.ts
import {
createProviderExecutedToolFactory as createProviderExecutedToolFactory2,
lazySchema as lazySchema7,
zodSchema as zodSchema7
} from "@ai-sdk/provider-utils";
import { z as z8 } from "zod/v4";
var enterpriseWebSearch = createProviderExecutedToolFactory2({
id: "google.enterprise_web_search",
inputSchema: lazySchema7(() => zodSchema7(z8.object({}))),
outputSchema: lazySchema7(() => zodSchema7(z8.object({})))
});
// src/tool/file-search.ts
import {
createProviderExecutedToolFactory as createProviderExecutedToolFactory3,
lazySchema as lazySchema8,
zodSchema as zodSchema8
} from "@ai-sdk/provider-utils";
import { z as z9 } from "zod/v4";
var fileSearchArgsBaseSchema = z9.looseObject({
/** The names of the file_search_stores to retrieve from.
* Example: `fileSearchStores/my-file-search-store-123`
*/
fileSearchStoreNames: z9.array(z9.string()).describe(
"The names of the file_search_stores to retrieve from. Example: `fileSearchStores/my-file-search-store-123`"
),
/** The number of file search retrieval chunks to retrieve. */
topK: z9.number().int().positive().describe("The number of file search retrieval chunks to retrieve.").optional(),
/** Metadata filter to apply to the file search retrieval documents.
* See https://google.aip.dev/160 for the syntax of the filter expression.
*/
metadataFilter: z9.string().describe(
"Metadata filter to apply to the file search retrieval documents. See https://google.aip.dev/160 for the syntax of the filter expression."
).optional()
});
var fileSearch = createProviderExecutedToolFactory3({
id: "google.file_search",
inputSchema: lazySchema8(() => zodSchema8(z9.object({}))),
outputSchema: lazySchema8(() => zodSchema8(z9.object({})))
});
// src/tool/google-maps.ts
import {
createProviderExecutedToolFactory as createProviderExecutedToolFactory4,
lazySchema as lazySchema9,
zodSchema as zodSchema9
} from "@ai-sdk/provider-utils";
import { z as z10 } from "zod/v4";
var googleMaps = createProviderExecutedToolFactory4({
id: "google.google_maps",
inputSchema: lazySchema9(() => zodSchema9(z10.object({}))),
outputSchema: lazySchema9(() => zodSchema9(z10.object({})))
});
// src/tool/google-search.ts
import {
createProviderExecutedToolFactory as createProviderExecutedToolFactory5,
lazySchema as lazySchema10,
zodSchema as zodSchema10
} from "@ai-sdk/provider-utils";
import { z as z11 } from "zod/v4";
var googleSearchToolArgsBaseSchema = z11.looseObject({
searchTypes: z11.object({
webSearch: z11.object({}).optional(),
imageSearch: z11.object({}).optional()
}).optional(),
timeRangeFilter: z11.object({
startTime: z11.string(),
endTime: z11.string()
}).optional()
});
var googleSearch = createProviderExecutedToolFactory5({
id: "google.google_search",
inputSchema: lazySchema10(() => zodSchema10(z11.object({}))),
outputSchema: lazySchema10(() => zodSchema10(z11.object({})))
});
// src/tool/url-context.ts
import {
createProviderExecutedToolFactory as createProviderExecutedToolFactory6,
lazySchema as lazySchema11,
zodSchema as zodSchema11
} from "@ai-sdk/provider-utils";
import { z as z12 } from "zod/v4";
var urlContext = createProviderExecutedToolFactory6({
id: "google.url_context",
inputSchema: lazySchema11(() => zodSchema11(z12.object({}))),
outputSchema: lazySchema11(() => zodSchema11(z12.object({})))
});
// src/tool/vertex-rag-store.ts
import {
createProviderExecutedToolFactory as createProviderExecutedToolFactory7,
lazySchema as lazySchema12,
zodSchema as zodSchema12
} from "@ai-sdk/provider-utils";
import { z as z13 } from "zod/v4";
var vertexRagStore = createProviderExecutedToolFactory7({
id: "google.vertex_rag_store",
inputSchema: lazySchema12(() => zodSchema12(z13.object({}))),
outputSchema: lazySchema12(() => zodSchema12(z13.object({})))
});
// src/google-tools.ts
var googleTools = {
/**
* Creates a Google search tool that gives Google direct access to real-time web content.
* Must have name "google_search".
*/
googleSearch,
/**
* Creates an Enterprise Web Search tool for grounding responses using a compliance-focused web index.
* Designed for highly-regulated industries (finance, healthcare, public sector).
* Does not log customer data and supports VPC service controls.
* Must have name "enterprise_web_search".
*
* @note Only available on Vertex AI. Requires Gemini 2.0 or newer.
*
* @see https://cloud.google.com/vertex-ai/generative-ai/docs/grounding/web-grounding-enterprise
*/
enterpriseWebSearch,
/**
* Creates a Google Maps grounding tool that gives the model access to Google Maps data.
* Must have name "google_maps".
*
* @see https://ai.google.dev/gemini-api/docs/maps-grounding
* @see https://cloud.google.com/vertex-ai/generative-ai/docs/grounding/grounding-with-google-maps
*/
googleMaps,
/**
* Creates a URL context tool that gives Google direct access to real-time web content.
* Must have name "url_context".
*/
urlContext,
/**
* Enables Retrieval Augmented Generation (RAG) via the Gemini File Search tool.
* Must have name "file_search".
*
* @param fileSearchStoreNames - Fully-qualified File Search store resource names.
* @param metadataFilter - Optional filter expression to restrict the files that can be retrieved.
* @param topK - Optional result limit for the number of chunks returned from File Search.
*
* @see https://ai.google.dev/gemini-api/docs/file-search
*/
fileSearch,
/**
* A tool that enables the model to generate and run Python code.
* Must have name "code_execution".
*
* @note Ensure the selected model supports Code Execution.
* Multi-tool usage with the code execution tool is typically compatible with Gemini >=2 models.
*
* @see https://ai.google.dev/gemini-api/docs/code-execution (Google AI)
* @see https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/code-execution-api (Vertex AI)
*/
codeExecution,
/**
* Creates a Vertex RAG Store tool that enables the model to perform RAG searches against a Vertex RAG Store.
* Must have name "vertex_rag_store".
*/
vertexRagStore
};
// src/google-image-model.ts
import {
convertToBase64 as convertToBase642,
generateId as defaultGenerateId,
parseProviderOptions as parseProviderOptions3,
serializeModelOptions as serializeModelOptions3,
WORKFLOW_SERIALIZE as WORKFLOW_SERIALIZE4,
WORKFLOW_DESERIALIZE as WORKFLOW_DESERIALIZE4
} from "@ai-sdk/provider-utils";
// src/google-image-model-options.ts
import { lazySchema as lazySchema13, zodSchema as zodSchema13 } from "@ai-sdk/provider-utils";
import { z as z14 } from "zod/v4";
var googleImageModelOptionsSchema = lazySchema13(
() => zodSchema13(
z14.object({
/**
* Enable Google Search grounding for Gemini image models. The value is
* forwarded as the args of the `google.tools.googleSearch` provider
* tool on the underlying language-model call. Pass `{}` for defaults.
*
* `generateImage` does not accept a `tools` parameter, so this is the
* dedicated escape hatch for grounding image generation the same way
* `generateText` does.
*/
googleSearch: googleSearchToolArgsBaseSchema.optional()
})
)
);
// src/google-image-model.ts
var GoogleImageModel = class _GoogleImageModel {
constructor(modelId, settings, config) {
this.modelId = modelId;
this.settings = settings;
this.config = config;
this.specificationVersion = "v4";
}
static [WORKFLOW_SERIALIZE4](model) {
return serializeModelOptions3({
modelId: model.modelId,
config: model.config
});
}
static [WORKFLOW_DESERIALIZE4](options) {
return new _GoogleImageModel(options.modelId, {}, options.config);
}
get maxImagesPerCall() {
if (this.settings.maxImagesPerCall != null) {
return this.settings.maxImagesPerCall;
}
return 10;
}
get provider() {
return this.config.provider;
}
async doGenerate(options) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
if (!this.modelId.startsWith("gemini-")) {
throw new Error(
"Google image models other than Gemini are no longer supported. Use a model ID that starts with `gemini-`."
);
}
const {
prompt,
n,
size,
aspectRatio,
seed,
providerOptions,
headers,
abortSignal,
files,
mask
} = options;
const warnings = [];
if (mask != null) {
throw new Error(
"Gemini image models do not support mask-based image editing."
);
}
if (n != null && n > 1) {
throw new Error(
"Gemini image models do not support generating a set number of images per call. Use n=1 or omit the n parameter."
);
}
if (size != null) {
warnings.push({
type: "unsupported",
feature: "size",
details: "This model does not support the `size` option. Use `aspectRatio` instead."
});
}
const userContent = [];
if (prompt != null) {
userContent.push({ type: "text", text: prompt });
}
if (files != null && files.length > 0) {
for (const file of files) {
if (file.type === "url") {
userContent.push({
type: "file",
data: { type: "url", url: new URL(file.url) },
mediaType: "image/*"
});
} else {
userContent.push({
type: "file",
data: {
type: "data",
data: typeof file.data === "string" ? file.data : new Uint8Array(file.data)
},
mediaType: file.mediaType
});
}
}
}
const languageModelPrompt = [
{ role: "user", content: userContent }
];
const googleImageOptions = await parseProviderOptions3({
provider: "google",
providerOptions,
schema: googleImageModelOptionsSchema
});
const {
googleSearch: _strippedGoogleSearch,
responseModalities: _strippedResponseModalities,
imageConfig: userImageConfig,
...passthroughGoogleOptions
} = (_a = providerOptions == null ? void 0 : providerOptions.google) != null ? _a : {};
const languageModel = new GoogleLanguageModel(this.modelId, {
provider: this.config.provider,
baseURL: this.config.baseURL,
headers: (_b = this.config.headers) != null ? _b : {},
fetch: this.config.fetch,
generateId: (_c = this.config.generateId) != null ? _c : defaultGenerateId
});
const result = await languageModel.doGenerate({
prompt: languageModelPrompt,
seed,
providerOptions: {
google: {
...passthroughGoogleOptions,
responseModalities: ["IMAGE"],
imageConfig: aspectRatio != null || userImageConfig != null ? {
...userImageConfig,
...aspectRatio != null ? {
aspectRatio
} : {}
} : void 0
}
},
tools: (googleImageOptions == null ? void 0 : googleImageOptions.googleSearch) != null ? [
{
type: "provider",
id: "google.google_search",
name: "google_search",
args: googleImageOptions.googleSearch
}
] : void 0,
headers,
abortSignal
});
const currentDate = (_f = (_e = (_d = this.config._internal) == null ? void 0 : _d.currentDate) == null ? void 0 : _e.call(_d)) != null ? _f : /* @__PURE__ */ new Date();
const images = [];
for (const part of result.content) {
if (part.type === "file" && part.mediaType.startsWith("image/") && part.data.type === "data") {
images.push(convertToBase642(part.data.data));
}
}
const languageModelGoogleMetadata = (_h = (_g = result.providerMetadata) == null ? void 0 : _g.google) != null ? _h : {};
return {
images,
warnings,
providerMetadata: {
google: {
...languageModelGoogleMetadata,
images: images.map(() => ({}))
}
},
response: {
timestamp: currentDate,
modelId: this.modelId,
headers: (_i = result.response) == null ? void 0 : _i.headers
},
usage: result.usage ? {
inputTokens: result.usage.inputTokens.total,
outputTokens: result.usage.outputTokens.total,
totalTokens: ((_j = result.usage.inputTokens.total) != null ? _j : 0) + ((_k = result.usage.outputTokens.total) != null ? _k : 0)
} : void 0
};
}
};
// src/google-files.ts
import {
AISDKError
} from "@ai-sdk/provider";
import {
combineHeaders as combineHeaders4,
convertInlineFileDataToUint8Array,
createJsonResponseHandler as createJsonResponseHandler4,
delay,
lazySchema as lazySchema14,
parseProviderOptions as parseProviderOptions4,
zodSchema as zodSchema14,
getFromApi as getFromApi2
} from "@ai-sdk/provider-utils";
import { z as z15 } from "zod/v4";
function encodePathSegment(value) {
const encodedValue = encodeURIComponent(value);
return encodedValue === "." ? "%252E" : encodedValue === ".." ? "%252E%252E" : encodedValue;
}
var GoogleFiles = class {
constructor(config) {
this.config = config;
this.specificationVersion = "v4";
}
get provider() {
return this.config.provider;
}
async uploadFile(options) {
var _a, _b, _c, _d;
const googleOptions = await parseProviderOptions4({
provider: "google",
providerOptions: options.providerOptions,
schema: googleFilesUploadOptionsSchema
});
const resolvedHeaders = combineHeaders4(
this.config.headers(),
options.headers
);
const fetchFn = (_a = this.config.fetch) != null ? _a : globalThis.fetch;
const warnings = [];
if (options.filename != null) {
warnings.push({ type: "unsupported", feature: "filename" });
}
const fileBytes = convertInlineFileDataToUint8Array(options.data);
const mediaType = options.mediaType;
const displayName = googleOptions == null ? void 0 : googleOptions.displayName;
const baseOrigin = this.config.baseURL.replace(/\/v1beta$/, "");
const initResponse = await fetchFn(`${baseOrigin}/upload/v1beta/files`, {
method: "POST",
headers: {
...resolvedHeaders,
"X-Goog-Upload-Protocol": "resumable",
"X-Goog-Upload-Command": "start",
"X-Goog-Upload-Header-Content-Length": String(fileBytes.length),
"X-Goog-Upload-Header-Content-Type": mediaType,
"Content-Type": "application/json"
},
body: JSON.stringify({
file: {
...displayName != null ? { display_name: displayName } : {}
}
}),
signal: options.abortSignal
});
if (!initResponse.ok) {
const errorBody = await initResponse.text();
throw new AISDKError({
name: "GOOGLE_FILES_UPLOAD_ERROR",
message: `Failed to initiate resumable upload: ${initResponse.status} ${errorBody}`
});
}
const uploadUrl = initResponse.headers.get("x-goog-upload-url");
if (!uploadUrl) {
throw new AISDKError({
name: "GOOGLE_FILES_UPLOAD_ERROR",
message: "No upload URL returned from initiation request"
});
}
const uploadResponse = await fetchFn(uploadUrl, {
method: "POST",
headers: {
"X-Goog-Upload-Offset": "0",
"X-Goog-Upload-Command": "upload, finalize"
},
body: ensureArrayBufferBacked(fileBytes),
signal: options.abortSignal
});
if (!uploadResponse.ok) {
const errorBody = await uploadResponse.text();
throw new AISDKError({
name: "GOOGLE_FILES_UPLOAD_ERROR",
message: `Failed to upload file data: ${uploadResponse.status} ${errorBody}`
});
}
const uploadResult = await uploadResponse.json();
let file = uploadResult.file;
const pollIntervalMs = (_b = googleOptions == null ? void 0 : googleOptions.pollIntervalMs) != null ? _b : 2e3;
const pollTimeoutMs = (_c = googleOptions == null ? void 0 : googleOptions.pollTimeoutMs) != null ? _c : 3e5;
const startTime = Date.now();
while (file.state === "PROCESSING") {
if (Date.now() - startTime > pollTimeoutMs) {
throw new AISDKError({
name: "GOOGLE_FILES_UPLOAD_TIMEOUT",
message: `File processing timed out after ${pollTimeoutMs}ms`
});
}
await delay(pollIntervalMs, { abortSignal: options.abortSignal });
const fileNameMatch = /^files\/([^/]+)$/.exec(file.name);
const filePath = fileNameMatch != null ? `files/${encodePathSegment(fileNameMatch[1])}` : encodePathSegment(file.name);
const { value: fileStatus } = await getFromApi2({
url: `${this.config.baseURL}/${filePath}`,
validateUrl: false,
headers: combineHeaders4(resolvedHeaders),
successfulResponseHandler: createJsonResponseHandler4(
googleFileResponseSchema
),
failedResponseHandler: googleFailedResponseHandler,
abortSignal: options.abortSignal,
fetch: this.config.fetch
});
file = fileStatus;
}
if (file.state === "FAILED") {
throw new AISDKError({
name: "GOOGLE_FILES_UPLOAD_FAILED",
message: `File processing failed for ${file.name}`
});
}
return {
warnings,
providerReference: { google: file.uri },
mediaType: (_d = file.mimeType) != null ? _d : options.mediaType,
providerMetadata: {
google: {
name: file.name,
displayName: file.displayName,
mimeType: file.mimeType,
sizeBytes: file.sizeBytes,
state: file.state,
uri: file.uri,
...file.createTime != null ? { createTime: file.createTime } : {},
...file.updateTime != null ? { updateTime: file.updateTime } : {},
...file.expirationTime != null ? { expirationTime: file.expirationTime } : {},
...file.sha256Hash != null ? { sha256Hash: file.sha256Hash } : {}
}
}
};
}
};
function ensureArrayBufferBacked(data) {
if (data.buffer instanceof ArrayBuffer) {
return data;
}
return new Uint8Array(data);
}
var googleFileResponseSchema = lazySchema14(
() => zodSchema14(
z15.object({
name: z15.string(),
displayName: z15.string().nullish(),
mimeType: z15.string(),
sizeBytes: z15.string().nullish(),
createTime: z15.string().nullish(),
updateTime: z15.string().nullish(),
expirationTime: z15.string().nullish(),
sha256Hash: z15.string().nullish(),
uri: z15.string(),
state: z15.string()
})
)
);
var googleFilesUploadOptionsSchema = lazySchema14(
() => zodSchema14(
z15.looseObject({
displayName: z15.string().nullish(),
pollIntervalMs: z15.number().positive().nullish(),
pollTimeoutMs: z15.number().positive().nullish()
})
)
);
// src/google-video-model.ts
import {
AISDKError as AISDKError2
} from "@ai-sdk/provider";
import {
combineHeaders as combineHeaders5,
convertUint8ArrayToBase64,
createJsonResponseHandler as createJsonResponseHandler5,
getFromApi as getFromApi3,
isSameOrigin,
parseProviderOptions as parseProviderOptions5,
postJsonToApi as postJsonToApi4,
resolve as resolve4
} from "@ai-sdk/provider-utils";
import { z as z17 } from "zod/v4";
// src/google-video-model-options.ts
import { lazySchema as lazySchema15, zodSchema as zodSchema15 } from "@ai-sdk/provider-utils";
import { z as z16 } from "zod/v4";
var googleVideoModelOptionsSchema = lazySchema15(
() => zodSchema15(
z16.looseObject({
pollIntervalMs: z16.number().positive().nullish(),
pollTimeoutMs: z16.number().positive().nullish(),
personGeneration: z16.enum(["dont_allow", "allow_adult", "allow_all"]).nullish(),
negativePrompt: z16.string().nullish(),
referenceImages: z16.array(
z16.object({
bytesBase64Encoded: z16.string().nullish(),
gcsUri: z16.string().nullish()
})
).nullish()
})
)
);
// src/google-video-model.ts
function getFirstFrameImage(options) {
var _a, _b;
return (_b = (_a = options.frameImages) == null ? void 0 : _a.find((frame) => frame.frameType === "first_frame")) == null ? void 0 : _b.image;
}
function resolveStartImage(options) {
var _a;
return (_a = getFirstFrameImage(options)) != null ? _a : options.image;
}
function getLastFrameImage(options) {
var _a, _b;
return (_b = (_a = options.frameImages) == null ? void 0 : _a.find((frame) => frame.frameType === "last_frame")) == null ? void 0 : _b.image;
}
function getInputReferences(options) {
if (options.frameImages != null && options.frameImages.length > 0) {
return void 0;
}
return options.inputReferences != null && options.inputReferences.length > 0 ? options.inputReferences : void 0;
}
function convertFileToGoogleImage(file, warnings) {
if (file.type === "url") {
if (file.url.startsWith("gs://")) {
return {
gcsUri: file.url,
mimeType: "image/png"
};
}
warnings.push({
type: "unsupported",
feature: "URL-based image input",
details: "Google Generative AI video models require base64-encoded images or GCS URIs. URL will be ignored."
});
return void 0;
}
const base64Data = typeof file.data === "string" ? file.data : convertUint8ArrayToBase64(file.data);
return {
bytesBase64Encoded: base64Data,
mimeType: file.mediaType || "image/png"
};
}
function convertProviderReferenceImage(refImg) {
if (refImg.bytesBase64Encoded) {
return {
image: {
bytesBase64Encoded: refImg.bytesBase64Encoded,
mimeType: "image/png"
},
referenceType: "asset"
};
}
if (refImg.gcsUri) {
return {
image: {
gcsUri: refImg.gcsUri,
mimeType: "image/png"
},
referenceType: "asset"
};
}
return refImg;
}
function convertInputReferenceImage(file, warnings) {
const image = convertFileToGoogleImage(file, warnings);
return image != null ? { image, referenceType: "asset" } : void 0;
}
var GoogleVideoModel = class {
constructor(modelId, config) {
this.modelId = modelId;
this.config = config;
this.specificationVersion = "v4";
}
get provider() {
return this.config.provider;
}
get maxVideosPerCall() {
return 4;
}
async buildRequest(options) {
const warnings = [];
const googleOptions = await parseProviderOptions5({
provider: "google",
providerOptions: options.providerOptions,
schema: googleVideoModelOptionsSchema
});
const instances = [{}];
const instance = instances[0];
if (options.prompt != null) {
instance.prompt = options.prompt;
}
const startImage = resolveStartImage(options);
if (startImage != null) {
const image = convertFileToGoogleImage(startImage, warnings);
if (image != null) {
instance.image = image;
}
}
const lastFrameImage = getLastFrameImage(options);
if (lastFrameImage != null) {
const lastFrame = convertFileToGoogleImage(lastFrameImage, warnings);
if (lastFrame != null) {
instance.lastFrame = lastFrame;
}
}
const inputReferences = getInputReferences(options);
if (inputReferences != null) {
instance.referenceImages = inputReferences.flatMap((reference) => {
const converted = convertInputReferenceImage(reference, warnings);
return converted != null ? [converted] : [];
});
} else if ((googleOptions == null ? void 0 : googleOptions.referenceImages) != null) {
instance.referenceImages = googleOptions.referenceImages.map(
(refImg) => convertProviderReferenceImage(refImg)
);
}
const parameters = {
sampleCount: options.n
};
if (options.aspectRatio) {
parameters.aspectRatio = options.aspectRatio;
}
if (options.resolution) {
const resolutionMap = {
"1280x720": "720p",
"1920x1080": "1080p",
"3840x2160": "4k"
};
parameters.resolution = resolutionMap[options.resolution] || options.resolution;
}
if (options.duration) {
parameters.durationSeconds = options.duration;
}
if (options.seed) {
parameters.seed = options.seed;
}
if (googleOptions != null) {
const opts = googleOptions;
if (opts.personGeneration !== void 0 && opts.personGeneration !== null) {
parameters.personGeneration = opts.personGeneration;
}
if (opts.negativePrompt !== void 0 && opts.negativePrompt !== null) {
parameters.negativePrompt = opts.negativePrompt;
}
for (const [key, value] of Object.entries(opts)) {
if (![
"pollIntervalMs",
"pollTimeoutMs",
"personGeneration",
"negativePrompt",
"referenceImages"
].includes(key)) {
parameters[key] = value;
}
}
}
return { instances, parameters, warnings, googleOptions };
}
async buildCompletedResult(finalOperation, responseHeaders, warnings, currentDate) {
var _a, _b;
const response = finalOperation.response;
if (!((_a = response == null ? void 0 : response.generateVideoResponse) == null ? void 0 : _a.generatedSamples) || response.generateVideoResponse.generatedSamples.length === 0) {
throw new AISDKError2({
name: "GOOGLE_VIDEO_GENERATION_ERROR",
message: `No videos in response. Response: ${JSON.stringify(finalOperation)}`
});
}
const videos = [];
const videoMetadata = [];
const resolvedHeaders = await resolve4(this.config.headers);
const apiKey = resolvedHeaders == null ? void 0 : resolvedHeaders["x-goog-api-key"];
for (const generatedSample of response.generateVideoResponse.generatedSamples) {
if ((_b = generatedSample.video) == null ? void 0 : _b.uri) {
const urlWithAuth = apiKey && isSameOrigin(generatedSample.video.uri, this.config.baseURL) ? `${generatedSample.video.uri}${generatedSample.video.uri.includes("?") ? "&" : "?"}key=${apiKey}` : generatedSample.video.uri;
videos.push({
type: "url",
url: urlWithAuth,
mediaType: "video/mp4"
});
videoMetadata.push({
uri: generatedSample.video.uri
});
}
}
if (videos.length === 0) {
throw new AISDKError2({
name: "GOOGLE_VIDEO_GENERATION_ERROR",
message: "No valid videos in response"
});
}
return {
status: "completed",
videos,
warnings,
response: {
timestamp: currentDate,
modelId: this.modelId,
headers: responseHeaders
},
providerMetadata: {
google: {
videos: videoMetadata
}
}
};
}
async doStart(options) {
var _a, _b, _c;
const currentDate = (_c = (_b = (_a = this.config._internal) == null ? void 0 : _a.currentDate) == null ? void 0 : _b.call(_a)) != null ? _c : /* @__PURE__ */ new Date();
const { instances, parameters, warnings } = await this.buildRequest(options);
const { value: operation, responseHeaders } = await postJsonToApi4({
url: `${this.config.baseURL}/models/${this.modelId}:predictLongRunning`,
headers: combineHeaders5(
await resolve4(this.config.headers),
options.headers
),
body: {
instances,
parameters
},
successfulResponseHandler: createJsonResponseHandler5(
googleOperationSchema
),
failedResponseHandler: googleFailedResponseHandler,
abortSignal: options.abortSignal,
fetch: this.config.fetch
});
const operationName = operation.name;
if (!operationName) {
throw new AISDKError2({
name: "GOOGLE_VIDEO_GENERATION_ERROR",
message: "No operation name returned from API"
});
}
return {
operation: { operationName },
warnings,
response: {
timestamp: currentDate,
modelId: this.modelId,
headers: responseHeaders
}
};
}
async doStatus(options) {
var _a, _b, _c;
const currentDate = (_c = (_b = (_a = this.config._internal) == null ? void 0 : _a.currentDate) == null ? void 0 : _b.call(_a)) != null ? _c : /* @__PURE__ */ new Date();
const { operationName } = options.operation;
const { value: statusOperation, responseHeaders } = await getFromApi3({
url: `${this.config.baseURL}/${operationName}`,
validateUrl: false,
headers: combineHeaders5(
await resolve4(this.config.headers),
options.headers
),
successfulResponseHandler: createJsonResponseHandler5(
googleOperationSchema
),
failedResponseHandler: googleFailedResponseHandler,
abortSignal: options.abortSignal,
fetch: this.config.fetch
});
if (!statusOperation.done) {
return {
status: "pending",
response: {
timestamp: currentDate,
modelId: this.modelId,
headers: responseHeaders
}
};
}
if (statusOperation.error) {
return {
status: "error",
error: `Video generation failed: ${statusOperation.error.message}`,
response: {
timestamp: currentDate,
modelId: this.modelId,
headers: responseHeaders
}
};
}
return this.buildCompletedResult(
statusOperation,
responseHeaders,
[],
currentDate
);
}
};
var googleOperationSchema = z17.object({
name: z17.string().nullish(),
done: z17.boolean().nullish(),
error: z17.object({
code: z17.number().nullish(),
message: z17.string(),
status: z17.string().nullish()
}).nullish(),
response: z17.object({
generateVideoResponse: z17.object({
generatedSamples: z17.array(
z17.object({
video: z17.object({
uri: z17.string().nullish()
}).nullish()
})
).nullish()
}).nullish()
}).nullish()
});
// src/google-speech-model.ts
import {
combineHeaders as combineHeaders6,
convertBase64ToUint8Array,
createJsonResponseHandler as createJsonResponseHandler6,
parseProviderOptions as parseProviderOptions6,
postJsonToApi as postJsonToApi5,
resolve as resolve5,
serializeModelOptions as serializeModelOptions4,
WORKFLOW_DESERIALIZE as WORKFLOW_DESERIALIZE5,
WORKFLOW_SERIALIZE as WORKFLOW_SERIALIZE5
} from "@ai-sdk/provider-utils";
// src/google-speech-api.ts
import { lazySchema as lazySchema16, zodSchema as zodSchema16 } from "@ai-sdk/provider-utils";
import { z as z18 } from "zod/v4";
var googleSpeechResponseSchema = lazySchema16(
() => zodSchema16(
z18.object({
candidates: z18.array(
z18.object({
content: z18.object({
parts: z18.array(
z18.object({
inlineData: z18.object({
mimeType: z18.string().nullish(),
data: z18.string().nullish()
}).nullish()
})
).nullish()
}).nullish()
})
).nullish()
})
)
);
// src/google-speech-model-options.ts
import {
lazySchema as lazySchema17,
zodSchema as zodSchema17
} from "@ai-sdk/provider-utils";
import { z as z19 } from "zod/v4";
var prebuiltVoiceConfigSchema = z19.object({
voiceName: z19.string()
});
var voiceConfigSchema = z19.object({
prebuiltVoiceConfig: prebuiltVoiceConfigSchema
});
var googleSpeechProviderOptionsSchema = lazySchema17(
() => zodSchema17(
z19.object({
/**
* Multi-speaker configuration for dialogue audio. When provided, this
* overrides the top-level `voice`. The Gemini TTS API supports up to two
* speakers; each speaker name must match a name used in the input text.
*
* https://ai.google.dev/gemini-api/docs/speech-generation#multi-speaker
*/
multiSpeakerVoiceConfig: z19.object({
speakerVoiceConfigs: z19.array(
z19.object({
speaker: z19.string(),
voiceConfig: voiceConfigSchema
})
)
}).optional()
})
)
);
// src/google-speech-model.ts
var DEFAULT_VOICE = "Kore";
var DEFAULT_SAMPLE_RATE = 24e3;
var GoogleSpeechModel = class _GoogleSpeechModel {
constructor(modelId, config) {
this.modelId = modelId;
this.config = config;
this.specificationVersion = "v4";
}
static [WORKFLOW_SERIALIZE5](model) {
return serializeModelOptions4({
modelId: model.modelId,
config: model.config
});
}
static [WORKFLOW_DESERIALIZE5](options) {
return new _GoogleSpeechModel(options.modelId, options.config);
}
get provider() {
return this.config.provider;
}
async getArgs({
text,
voice = DEFAULT_VOICE,
outputFormat,
instructions,
speed,
language,
providerOptions
}) {
const warnings = [];
const providerOptionsNames = this.config.provider.includes("vertex") ? ["googleVertex", "vertex"] : ["google"];
let googleOptions;
for (const name of providerOptionsNames) {
googleOptions = await parseProviderOptions6({
provider: name,
providerOptions,
schema: googleSpeechProviderOptionsSchema
});
if (googleOptions != null) {
break;
}
}
if (googleOptions == null && !providerOptionsNames.includes("google")) {
googleOptions = await parseProviderOptions6({
provider: "google",
providerOptions,
schema: googleSpeechProviderOptionsSchema
});
}
const multiSpeakerVoiceConfig = googleOptions == null ? void 0 : googleOptions.multiSpeakerVoiceConfig;
const speechConfig = multiSpeakerVoiceConfig ? { multiSpeakerVoiceConfig } : { voiceConfig: { prebuiltVoiceConfig: { voiceName: voice } } };
let promptText = text;
if (instructions != null) {
if (multiSpeakerVoiceConfig) {
warnings.push({
type: "unsupported",
feature: "instructions",
details: "Google Gemini TTS ignores `instructions` when `multiSpeakerVoiceConfig` is set, because prepending them would break multi-speaker transcript parsing."
});
} else {
promptText = `${instructions}: ${text}`;
}
}
if (speed != null) {
warnings.push({
type: "unsupported",
feature: "speed",
details: "Google Gemini TTS models do not support the `speed` option. It was ignored."
});
}
if (language != null) {
warnings.push({
type: "unsupported",
feature: "language",
details: "Google Gemini TTS models do not support the `language` option. Language is detected automatically from the input text."
});
}
let resolvedOutputFormat = "wav";
if (outputFormat === "pcm") {
resolvedOutputFormat = "pcm";
} else if (outputFormat != null && outputFormat !== "wav") {
warnings.push({
type: "unsupported",
feature: "outputFormat",
details: `Unsupported output format: ${outputFormat}. Using wav instead.`
});
}
const requestBody = {
contents: [{ role: "user", parts: [{ text: promptText }] }],
generationConfig: {
responseModalities: ["AUDIO"],
speechConfig
}
};
return { requestBody, warnings, outputFormat: resolvedOutputFormat };
}
async doGenerate(options) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
const currentDate = (_c = (_b = (_a = this.config._internal) == null ? void 0 : _a.currentDate) == null ? void 0 : _b.call(_a)) != null ? _c : /* @__PURE__ */ new Date();
const { requestBody, warnings, outputFormat } = await this.getArgs(options);
const {
value: response,
responseHeaders,
rawValue: rawResponse
} = await postJsonToApi5({
url: `${this.config.baseURL}/models/${this.modelId}:generateContent`,
headers: combineHeaders6(
this.config.headers ? await resolve5(this.config.headers) : void 0,
options.headers
),
body: requestBody,
failedResponseHandler: googleFailedResponseHandler,
successfulResponseHandler: createJsonResponseHandler6(
googleSpeechResponseSchema
),
abortSignal: options.abortSignal,
fetch: this.config.fetch
});
let base64Audio;
let mimeType;
for (const candidate of (_d = response.candidates) != null ? _d : []) {
for (const part of (_f = (_e = candidate.content) == null ? void 0 : _e.parts) != null ? _f : []) {
if ((_g = part.inlineData) == null ? void 0 : _g.data) {
base64Audio = part.inlineData.data;
mimeType = (_h = part.inlineData.mimeType) != null ? _h : void 0;
break;
}
}
if (base64Audio != null) {
break;
}
}
const sampleRate = (_i = parseSampleRate(mimeType)) != null ? _i : DEFAULT_SAMPLE_RATE;
const pcm = base64Audio != null ? convertBase64ToUint8Array(base64Audio) : new Uint8Array(0);
const audio = outputFormat === "pcm" || pcm.length === 0 ? pcm : addWavHeader(pcm, sampleRate);
if (outputFormat === "pcm" && pcm.length > 0) {
warnings.push({
type: "unsupported",
feature: "outputFormat",
details: `Returning raw PCM audio (signed 16-bit little-endian, mono, ${sampleRate} Hz). These bytes have no container header and are not directly playable; see providerMetadata.google for the sample rate and mime type.`
});
}
return {
audio,
warnings,
request: {
body: JSON.stringify(requestBody)
},
response: {
timestamp: currentDate,
modelId: this.modelId,
headers: responseHeaders,
body: rawResponse
},
providerMetadata: {
google: {
sampleRate,
mimeType: mimeType != null ? mimeType : null
}
}
};
}
};
function parseSampleRate(mimeType) {
if (mimeType == null) {
return void 0;
}
const match = /rate=(\d+)/.exec(mimeType);
return match ? Number.parseInt(match[1], 10) : void 0;
}
function addWavHeader(pcm, sampleRate) {
const numChannels = 1;
const bitsPerSample = 16;
const blockAlign = numChannels * bitsPerSample / 8;
const byteRate = sampleRate * blockAlign;
const dataSize = pcm.length;
const buffer = new ArrayBuffer(44 + dataSize);
const view = new DataView(buffer);
writeAscii(view, 0, "RIFF");
view.setUint32(4, 36 + dataSize, true);
writeAscii(view, 8, "WAVE");
writeAscii(view, 12, "fmt ");
view.setUint32(16, 16, true);
view.setUint16(20, 1, true);
view.setUint16(22, numChannels, true);
view.setUint32(24, sampleRate, true);
view.setUint32(28, byteRate, true);
view.setUint16(32, blockAlign, true);
view.setUint16(34, bitsPerSample, true);
writeAscii(view, 36, "data");
view.setUint32(40, dataSize, true);
const out = new Uint8Array(buffer);
out.set(pcm, 44);
return out;
}
function writeAscii(view, offset, text) {
for (let i = 0; i < text.length; i++) {
view.setUint8(offset + i, text.charCodeAt(i));
}
}
// src/interactions/google-interactions-language-model.ts
import {
combineHeaders as combineHeaders8,
createEventSourceResponseHandler as createEventSourceResponseHandler3,
createJsonResponseHandler as createJsonResponseHandler8,
generateId as defaultGenerateId2,
parseProviderOptions as parseProviderOptions7,
postJsonToApi as postJsonToApi6,
resolve as resolve6,
serializeModelOptions as serializeModelOptions5,
WORKFLOW_DESERIALIZE as WORKFLOW_DESERIALIZE6,
WORKFLOW_SERIALIZE as WORKFLOW_SERIALIZE6
} from "@ai-sdk/provider-utils";
// src/interactions/build-google-interactions-stream-transform.ts
import {
createProviderStreamError
} from "@ai-sdk/provider-utils";
// src/interactions/convert-google-interactions-usage.ts
import { createNullLanguageModelUsage as createNullLanguageModelUsage2 } from "@ai-sdk/provider-utils";
function convertGoogleInteractionsUsage(usage) {
var _a, _b, _c, _d, _e, _f, _g, _h;
if (usage == null) {
return createNullLanguageModelUsage2();
}
const totalInput = (_a = usage.total_input_tokens) != null ? _a : 0;
const totalOutput = (_b = usage.total_output_tokens) != null ? _b : 0;
const totalThought = (_c = usage.total_thought_tokens) != null ? _c : 0;
const totalCached = (_d = usage.total_cached_tokens) != null ? _d : 0;
return {
inputTokens: {
total: (_e = usage.total_input_tokens) != null ? _e : void 0,
noCache: usage.total_input_tokens == null ? void 0 : totalInput - totalCached,
cacheRead: (_f = usage.total_cached_tokens) != null ? _f : void 0,
cacheWrite: void 0
},
outputTokens: {
total: usage.total_output_tokens == null && usage.total_thought_tokens == null ? void 0 : totalOutput + totalThought,
text: (_g = usage.total_output_tokens) != null ? _g : void 0,
reasoning: (_h = usage.total_thought_tokens) != null ? _h : void 0
},
raw: usage
};
}
function getGoogleInteractionsOutputTokensByModality(usage) {
const byModality = usage == null ? void 0 : usage.output_tokens_by_modality;
if (byModality == null) {
return void 0;
}
const result = {};
for (const entry of byModality) {
if ((entry == null ? void 0 : entry.modality) != null && entry.tokens != null) {
result[entry.modality] = entry.tokens;
}
}
return Object.keys(result).length > 0 ? result : void 0;
}
// src/interactions/extract-google-interactions-sources.ts
var KNOWN_DOC_EXTENSIONS = {
pdf: "application/pdf",
txt: "text/plain",
md: "text/markdown",
markdown: "text/markdown",
doc: "application/msword",
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
};
function inferDocMediaType(uriOrName) {
const lower = uriOrName.toLowerCase();
for (const [ext, media] of Object.entries(KNOWN_DOC_EXTENSIONS)) {
if (lower.endsWith(`.${ext}`)) return media;
}
return "application/octet-stream";
}
function basename(uriOrName) {
const parts = uriOrName.split("/");
const last = parts[parts.length - 1];
return last && last.length > 0 ? last : void 0;
}
function annotationToSource({
annotation,
generateId: generateId4
}) {
var _a, _b, _c, _d, _e;
switch (annotation.type) {
case "url_citation": {
const urlCitation = annotation;
if (urlCitation.url == null || urlCitation.url.length === 0) {
return void 0;
}
return {
type: "source",
sourceType: "url",
id: generateId4(),
url: urlCitation.url,
...urlCitation.title != null ? { title: urlCitation.title } : {}
};
}
case "file_citation": {
const fileCitation = annotation;
const uri = (_b = (_a = fileCitation.url) != null ? _a : fileCitation.document_uri) != null ? _b : fileCitation.file_name;
if (uri == null || uri.length === 0) return void 0;
if (uri.startsWith("http://") || uri.startsWith("https://")) {
return {
type: "source",
sourceType: "url",
id: generateId4(),
url: uri,
...fileCitation.file_name != null ? { title: fileCitation.file_name } : {}
};
}
const filename = (_c = fileCitation.file_name) != null ? _c : basename(uri);
const mediaType = inferDocMediaType(uri);
return {
type: "source",
sourceType: "document",
id: generateId4(),
mediaType,
title: (_e = (_d = fileCitation.file_name) != null ? _d : filename) != null ? _e : uri,
...filename != null ? { filename } : {}
};
}
case "place_citation": {
const placeCitation = annotation;
if (placeCitation.url == null || placeCitation.url.length === 0) {
return void 0;
}
return {
type: "source",
sourceType: "url",
id: generateId4(),
url: placeCitation.url,
...placeCitation.name != null ? { title: placeCitation.name } : {}
};
}
default:
return void 0;
}
}
function builtinToolResultToSources({
block,
generateId: generateId4
}) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
const sources = [];
switch (block.type) {
case "url_context_result": {
const result = (_a = block.result) != null ? _a : [];
for (const entry of result) {
if ((entry == null ? void 0 : entry.url) == null || entry.url.length === 0) continue;
if (entry.status != null && entry.status !== "success") continue;
sources.push({
type: "source",
sourceType: "url",
id: generateId4(),
url: entry.url
});
}
break;
}
case "google_search_result": {
const result = (_b = block.result) != null ? _b : [];
for (const entry of result) {
const url = entry == null ? void 0 : entry.url;
if (url == null || url.length === 0) continue;
sources.push({
type: "source",
sourceType: "url",
id: generateId4(),
url,
...entry.title != null ? { title: entry.title } : {}
});
}
break;
}
case "google_maps_result": {
const result = (_c = block.result) != null ? _c : [];
for (const entry of result) {
for (const place of (_d = entry.places) != null ? _d : []) {
if (place.url == null || place.url.length === 0) continue;
sources.push({
type: "source",
sourceType: "url",
id: generateId4(),
url: place.url,
...place.name != null ? { title: place.name } : {}
});
}
}
break;
}
case "file_search_result": {
const result = (_e = block.result) != null ? _e : [];
for (const raw of result) {
if (raw == null || typeof raw !== "object") continue;
const entry = raw;
const uri = (_g = (_f = entry.url) != null ? _f : entry.document_uri) != null ? _g : entry.file_name;
if (uri == null || uri.length === 0) continue;
if (uri.startsWith("http://") || uri.startsWith("https://")) {
sources.push({
type: "source",
sourceType: "url",
id: generateId4(),
url: uri,
...entry.title != null ? { title: entry.title } : {}
});
continue;
}
const filename = (_h = entry.file_name) != null ? _h : basename(uri);
const mediaType = inferDocMediaType(uri);
sources.push({
type: "source",
sourceType: "document",
id: generateId4(),
mediaType,
title: (_k = (_j = (_i = entry.title) != null ? _i : entry.file_name) != null ? _j : filename) != null ? _k : uri,
...filename != null ? { filename } : {}
});
}
break;
}
default:
break;
}
return sources;
}
function annotationsToSources({
annotations,
generateId: generateId4
}) {
var _a;
if (annotations == null) return [];
const seen = /* @__PURE__ */ new Set();
const sources = [];
for (const annotation of annotations) {
const source = annotationToSource({ annotation, generateId: generateId4 });
if (source == null) continue;
const key = source.sourceType === "url" ? `url:${source.url}` : `doc:${(_a = source.filename) != null ? _a : source.title}`;
if (seen.has(key)) continue;
seen.add(key);
sources.push(source);
}
return sources;
}
// src/interactions/map-google-interactions-finish-reason.ts
function mapGoogleInteractionsFinishReason({
status,
hasFunctionCall
}) {
switch (status) {
case "completed":
return hasFunctionCall ? "tool-calls" : "stop";
case "requires_action":
return "tool-calls";
case "failed":
return "error";
case "incomplete":
return "length";
case "cancelled":
return "other";
case "in_progress":
default:
return "other";
}
}
// src/interactions/build-google-interactions-stream-transform.ts
var BUILTIN_TOOL_CALL_TYPES = /* @__PURE__ */ new Set([
"google_search_call",
"code_execution_call",
"url_context_call",
"file_search_call",
"google_maps_call",
"mcp_server_tool_call"
]);
var BUILTIN_TOOL_RESULT_TYPES = /* @__PURE__ */ new Set([
"google_search_result",
"code_execution_result",
"url_context_result",
"file_search_result",
"google_maps_result",
"mcp_server_tool_result"
]);
function builtinToolNameFromCallType(type) {
return type.replace(/_call$/, "");
}
function builtinToolNameFromResultType(type) {
return type.replace(/_result$/, "");
}
function buildGoogleInteractionsStreamTransform({
warnings,
generateId: generateId4,
includeRawChunks,
serviceTier: headerServiceTier
}) {
let interactionId;
let usage;
let serviceTier = headerServiceTier;
let finishStatus;
let hasFunctionCall = false;
const openBlocks = /* @__PURE__ */ new Map();
const emittedSourceKeys = /* @__PURE__ */ new Set();
function sourceKey(source) {
var _a;
return source.sourceType === "url" ? `url:${source.url}` : `doc:${(_a = source.filename) != null ? _a : source.title}`;
}
return new TransformStream({
start(controller) {
controller.enqueue({ type: "stream-start", warnings });
},
transform(chunk, controller) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t;
if (includeRawChunks) {
controller.enqueue({ type: "raw", rawValue: chunk.rawValue });
}
if (!chunk.success) {
finishStatus = "failed";
controller.enqueue({ type: "error", error: chunk.error });
return;
}
const value = chunk.value;
const eventType = value.event_type;
switch (eventType) {
case "interaction.created": {
const event = value;
const interaction = event.interaction;
interactionId = (interaction == null ? void 0 : interaction.id) != null && interaction.id.length > 0 ? interaction.id : void 0;
const created = interaction == null ? void 0 : interaction.created;
let timestamp;
if (typeof created === "string") {
const parsed = new Date(created);
if (!Number.isNaN(parsed.getTime())) {
timestamp = parsed;
}
}
controller.enqueue({
type: "response-metadata",
...interactionId != null ? { id: interactionId } : {},
modelId: interaction == null ? void 0 : interaction.model,
...timestamp ? { timestamp } : {}
});
break;
}
case "step.start": {
const event = value;
const step = event.step;
const index = event.index;
const blockId = `${interactionId != null ? interactionId : "interaction"}:${index}`;
const stepType = step == null ? void 0 : step.type;
if (stepType === "model_output") {
const initial = (_a = step == null ? void 0 : step.content) == null ? void 0 : _a[0];
if ((initial == null ? void 0 : initial.type) === "text") {
openBlocks.set(index, {
kind: "text",
id: blockId,
emittedSourceKeys: /* @__PURE__ */ new Set()
});
controller.enqueue({ type: "text-start", id: blockId });
const initialSources = annotationsToSources({
annotations: initial.annotations,
generateId: generateId4
});
for (const source of initialSources) {
const key = sourceKey(source);
if (emittedSourceKeys.has(key)) continue;
emittedSourceKeys.add(key);
controller.enqueue(source);
}
} else if ((initial == null ? void 0 : initial.type) === "image") {
openBlocks.set(index, {
kind: "image",
id: blockId,
...initial.data != null ? { data: initial.data } : {},
...initial.mime_type != null ? { mimeType: initial.mime_type } : {},
...initial.uri != null ? { uri: initial.uri } : {}
});
} else {
openBlocks.set(index, {
kind: "pending_model_output",
id: blockId
});
}
} else if (stepType === "thought") {
const signature = step == null ? void 0 : step.signature;
openBlocks.set(index, {
kind: "reasoning",
id: blockId,
...signature != null ? { signature } : {}
});
controller.enqueue({ type: "reasoning-start", id: blockId });
if (Array.isArray(step == null ? void 0 : step.summary)) {
for (const item of step.summary) {
if ((item == null ? void 0 : item.type) === "text" && typeof item.text === "string") {
controller.enqueue({
type: "reasoning-delta",
id: blockId,
delta: item.text
});
}
}
}
} else if (stepType === "function_call") {
const toolCallId = (step == null ? void 0 : step.id) || blockId;
const toolName = (_b = step == null ? void 0 : step.name) != null ? _b : "unknown";
hasFunctionCall = true;
const state = {
kind: "function_call",
id: blockId,
toolCallId,
toolName,
argumentsAccum: "",
...(step == null ? void 0 : step.signature) != null ? { signature: step.signature } : {}
};
openBlocks.set(index, state);
controller.enqueue({
type: "tool-input-start",
id: toolCallId,
toolName
});
} else if (stepType != null && BUILTIN_TOOL_CALL_TYPES.has(stepType)) {
const toolName = stepType === "mcp_server_tool_call" ? (_c = step == null ? void 0 : step.name) != null ? _c : "mcp_server_tool" : builtinToolNameFromCallType(stepType);
const toolCallId = (step == null ? void 0 : step.id) || blockId;
const state = {
kind: "builtin_tool_call",
id: blockId,
blockType: stepType,
toolCallId,
toolName,
arguments: (_d = step == null ? void 0 : step.arguments) != null ? _d : {},
callEmitted: false
};
openBlocks.set(index, state);
} else if (stepType != null && BUILTIN_TOOL_RESULT_TYPES.has(stepType)) {
const toolName = stepType === "mcp_server_tool_result" ? (_e = step == null ? void 0 : step.name) != null ? _e : "mcp_server_tool" : builtinToolNameFromResultType(stepType);
const callId = (step == null ? void 0 : step.call_id) || blockId;
const state = {
kind: "builtin_tool_result",
id: blockId,
blockType: stepType,
callId,
toolName,
result: (_f = step == null ? void 0 : step.result) != null ? _f : null,
...(step == null ? void 0 : step.is_error) != null ? { isError: step.is_error } : {},
resultEmitted: false
};
openBlocks.set(index, state);
} else {
openBlocks.set(index, { kind: "unknown", id: blockId });
}
break;
}
case "step.delta": {
const event = value;
let open = openBlocks.get(event.index);
if (open == null) break;
const dtype = (_g = event.delta) == null ? void 0 : _g.type;
if (open.kind === "pending_model_output") {
if (dtype === "text" || dtype === "text_annotation" || dtype === "text_annotation_delta") {
const promoted = {
kind: "text",
id: open.id,
emittedSourceKeys: /* @__PURE__ */ new Set()
};
openBlocks.set(event.index, promoted);
open = promoted;
controller.enqueue({ type: "text-start", id: promoted.id });
}
}
if (dtype === "image" && (open.kind === "pending_model_output" || open.kind === "text" || open.kind === "image")) {
const imageDelta = event.delta;
const google2 = {};
if (interactionId != null) google2.interactionId = interactionId;
const providerMetadata = Object.keys(google2).length > 0 ? { google: google2 } : void 0;
if ((imageDelta == null ? void 0 : imageDelta.data) != null && imageDelta.data.length > 0) {
controller.enqueue({
type: "file",
mediaType: (_h = imageDelta.mime_type) != null ? _h : "image/png",
data: { type: "data", data: imageDelta.data },
...providerMetadata ? { providerMetadata } : {}
});
} else if ((imageDelta == null ? void 0 : imageDelta.uri) != null && imageDelta.uri.length > 0) {
controller.enqueue({
type: "file",
mediaType: (_i = imageDelta.mime_type) != null ? _i : "image/png",
data: { type: "url", url: new URL(imageDelta.uri) },
...providerMetadata ? { providerMetadata } : {}
});
}
if (open.kind === "image") {
open.data = void 0;
open.uri = void 0;
}
break;
}
if (dtype === "video" && (open.kind === "pending_model_output" || open.kind === "text")) {
const videoDelta = event.delta;
const google2 = {};
if (interactionId != null) google2.interactionId = interactionId;
const providerMetadata = Object.keys(google2).length > 0 ? { google: google2 } : void 0;
if ((videoDelta == null ? void 0 : videoDelta.data) != null && videoDelta.data.length > 0) {
controller.enqueue({
type: "file",
mediaType: (_j = videoDelta.mime_type) != null ? _j : "video/mp4",
data: { type: "data", data: videoDelta.data },
...providerMetadata ? { providerMetadata } : {}
});
} else if ((videoDelta == null ? void 0 : videoDelta.uri) != null && videoDelta.uri.length > 0) {
controller.enqueue({
type: "file",
mediaType: (_k = videoDelta.mime_type) != null ? _k : "video/mp4",
data: { type: "url", url: new URL(videoDelta.uri) },
...providerMetadata ? { providerMetadata } : {}
});
}
break;
}
const delta = event.delta;
if (open.kind === "text" && (delta == null ? void 0 : delta.type) === "text") {
const text = (_l = delta.text) != null ? _l : "";
if (text.length > 0) {
controller.enqueue({
type: "text-delta",
id: open.id,
delta: text
});
}
} else if (open.kind === "text" && ((delta == null ? void 0 : delta.type) === "text_annotation" || (delta == null ? void 0 : delta.type) === "text_annotation_delta")) {
const sources = annotationsToSources({
annotations: delta.annotations,
generateId: generateId4
});
for (const source of sources) {
const key = sourceKey(source);
if (emittedSourceKeys.has(key)) continue;
emittedSourceKeys.add(key);
open.emittedSourceKeys.add(key);
controller.enqueue(source);
}
} else if (open.kind === "image" && (delta == null ? void 0 : delta.type) === "image") {
if (delta.data != null) open.data = delta.data;
if (delta.mime_type != null) open.mimeType = delta.mime_type;
if (delta.uri != null) open.uri = delta.uri;
} else if (open.kind === "reasoning") {
if ((delta == null ? void 0 : delta.type) === "thought_summary") {
const item = delta.content;
if ((item == null ? void 0 : item.type) === "text" && typeof item.text === "string") {
controller.enqueue({
type: "reasoning-delta",
id: open.id,
delta: item.text
});
}
} else if ((delta == null ? void 0 : delta.type) === "thought_signature") {
const signature = delta.signature;
if (signature != null) {
open.signature = signature;
}
}
} else if (open.kind === "function_call" && (delta == null ? void 0 : delta.type) === "arguments_delta") {
const slice = typeof delta.arguments === "string" ? delta.arguments : "";
if (slice.length > 0) {
open.argumentsAccum += slice;
controller.enqueue({
type: "tool-input-delta",
id: open.toolCallId,
delta: slice
});
}
if (delta.id != null && delta.id.length > 0) {
open.toolCallId = delta.id;
}
if (delta.signature != null) {
open.signature = delta.signature;
}
hasFunctionCall = true;
} else if (open.kind === "builtin_tool_call" && (delta == null ? void 0 : delta.type) === open.blockType) {
if (delta.id != null && delta.id.length > 0) {
open.toolCallId = delta.id;
}
if (delta.arguments != null && typeof delta.arguments === "object") {
open.arguments = delta.arguments;
}
if (delta.name != null && open.blockType === "mcp_server_tool_call") {
open.toolName = delta.name;
}
} else if (open.kind === "builtin_tool_result" && (delta == null ? void 0 : delta.type) === open.blockType) {
if (delta.call_id != null && delta.call_id.length > 0) {
open.callId = delta.call_id;
}
if (delta.result !== void 0) open.result = delta.result;
if (delta.is_error != null) open.isError = delta.is_error;
if (delta.name != null && open.blockType === "mcp_server_tool_result") {
open.toolName = delta.name;
}
}
break;
}
case "step.stop": {
const event = value;
const open = openBlocks.get(event.index);
if (open == null) break;
if (open.kind === "text") {
const textProviderMetadata = interactionId != null ? { google: { interactionId } } : void 0;
controller.enqueue({
type: "text-end",
id: open.id,
...textProviderMetadata ? { providerMetadata: textProviderMetadata } : {}
});
} else if (open.kind === "reasoning") {
const google2 = {};
if (open.signature != null) google2.signature = open.signature;
if (interactionId != null) google2.interactionId = interactionId;
const providerMetadata = Object.keys(google2).length > 0 ? { google: google2 } : void 0;
controller.enqueue({
type: "reasoning-end",
id: open.id,
...providerMetadata ? { providerMetadata } : {}
});
} else if (open.kind === "image") {
const google2 = {};
if (interactionId != null) google2.interactionId = interactionId;
const providerMetadata = Object.keys(google2).length > 0 ? { google: google2 } : void 0;
if (open.data != null && open.data.length > 0) {
controller.enqueue({
type: "file",
mediaType: (_m = open.mimeType) != null ? _m : "image/png",
data: { type: "data", data: open.data },
...providerMetadata ? { providerMetadata } : {}
});
} else if (open.uri != null && open.uri.length > 0) {
controller.enqueue({
type: "file",
mediaType: (_n = open.mimeType) != null ? _n : "image/png",
data: { type: "url", url: new URL(open.uri) },
...providerMetadata ? { providerMetadata } : {}
});
}
} else if (open.kind === "function_call") {
const accumulated = open.argumentsAccum.length > 0 ? open.argumentsAccum : "{}";
controller.enqueue({
type: "tool-input-end",
id: open.toolCallId
});
const google2 = {};
if (open.signature != null) google2.signature = open.signature;
if (interactionId != null) google2.interactionId = interactionId;
const providerMetadata = Object.keys(google2).length > 0 ? { google: google2 } : void 0;
controller.enqueue({
type: "tool-call",
toolCallId: open.toolCallId,
toolName: open.toolName,
input: accumulated,
...providerMetadata ? { providerMetadata } : {}
});
} else if (open.kind === "builtin_tool_call" && !open.callEmitted) {
controller.enqueue({
type: "tool-call",
toolCallId: open.toolCallId,
toolName: open.toolName,
input: JSON.stringify((_o = open.arguments) != null ? _o : {}),
providerExecuted: true
});
open.callEmitted = true;
} else if (open.kind === "builtin_tool_result" && !open.resultEmitted) {
controller.enqueue({
type: "tool-result",
toolCallId: open.callId,
toolName: open.toolName,
result: (_p = open.result) != null ? _p : null
});
open.resultEmitted = true;
const sources = builtinToolResultToSources({
block: {
type: open.blockType,
call_id: open.callId,
result: open.result
},
generateId: generateId4
});
for (const source of sources) {
const key = sourceKey(source);
if (emittedSourceKeys.has(key)) continue;
emittedSourceKeys.add(key);
controller.enqueue(source);
}
}
openBlocks.delete(event.index);
break;
}
case "interaction.status_update":
case "interaction.in_progress":
case "interaction.requires_action": {
const event = value;
if (event.status != null) {
finishStatus = event.status;
} else if (eventType === "interaction.requires_action") {
finishStatus = "requires_action";
} else {
finishStatus = "in_progress";
}
break;
}
case "interaction.completed": {
const event = value;
const interaction = event.interaction;
if ((interaction == null ? void 0 : interaction.id) != null && interaction.id.length > 0) {
interactionId = interaction.id;
}
if ((interaction == null ? void 0 : interaction.status) != null) {
finishStatus = interaction.status;
}
if ((interaction == null ? void 0 : interaction.usage) != null) {
usage = interaction.usage;
}
if ((interaction == null ? void 0 : interaction.service_tier) != null) {
serviceTier = interaction.service_tier;
}
break;
}
case "error": {
const event = value;
finishStatus = "failed";
controller.enqueue({
type: "error",
error: createProviderStreamError({
message: (_r = (_q = event.error) == null ? void 0 : _q.message) != null ? _r : "Unknown interaction error",
type: event.event_type,
code: (_t = (_s = event.error) == null ? void 0 : _s.code) != null ? _t : void 0,
data: event
})
});
break;
}
default:
break;
}
},
flush(controller) {
const finishReason = {
unified: mapGoogleInteractionsFinishReason({
status: finishStatus,
hasFunctionCall
}),
raw: finishStatus
};
const outputTokensByModality = getGoogleInteractionsOutputTokensByModality(usage);
const providerMetadata = {
google: {
...interactionId != null ? { interactionId } : {},
...serviceTier != null ? { serviceTier } : {},
...outputTokensByModality != null ? { outputTokensByModality } : {}
}
};
controller.enqueue({
type: "finish",
finishReason,
usage: convertGoogleInteractionsUsage(usage),
providerMetadata
});
}
});
}
// src/interactions/convert-to-google-interactions-input.ts
import {
convertToBase64 as convertToBase643,
getTopLevelMediaType as getTopLevelMediaType2,
isFullMediaType as isFullMediaType2,
resolveFullMediaType as resolveFullMediaType2,
resolveProviderReference as resolveProviderReference2,
secureJsonParse as secureJsonParse2
} from "@ai-sdk/provider-utils";
function convertToGoogleInteractionsInput({
prompt,
previousInteractionId,
store,
mediaResolution
}) {
var _a, _b, _c, _d, _e, _f, _g;
const warnings = [];
const incoherentCombo = previousInteractionId != null && store === false;
const shouldCompact = previousInteractionId != null && store !== false;
if (incoherentCombo) {
warnings.push({
type: "other",
message: "google.interactions: providerOptions.google.previousInteractionId was set together with store: false. These are incoherent (the prior interaction cannot be referenced when nothing was stored on the server); the full history will be sent and previous_interaction_id will still be emitted."
});
}
const compactedPrompt = shouldCompact ? compactPromptForPreviousInteraction({
prompt,
previousInteractionId
}) : prompt;
const systemTexts = [];
const steps = [];
for (const message of compactedPrompt) {
switch (message.role) {
case "system": {
systemTexts.push(message.content);
break;
}
case "user": {
const content = [];
for (const part of message.content) {
if (part.type === "text") {
content.push({ type: "text", text: part.text });
} else if (part.type === "file") {
const fileBlock = convertFilePartToContent({
part,
warnings,
mediaResolution
});
if (fileBlock != null) {
content.push(fileBlock);
}
}
}
const merged = mergeAdjacentTextContent(content);
if (merged.length > 0) {
steps.push({ type: "user_input", content: merged });
}
break;
}
case "assistant": {
let pendingModelOutput = [];
const flushModelOutput = () => {
if (pendingModelOutput.length > 0) {
steps.push({ type: "model_output", content: pendingModelOutput });
pendingModelOutput = [];
}
};
for (const part of message.content) {
if (part.type === "text") {
pendingModelOutput.push({ type: "text", text: part.text });
} else if (part.type === "reasoning") {
flushModelOutput();
const signature = (_b = (_a = part.providerOptions) == null ? void 0 : _a.google) == null ? void 0 : _b.signature;
steps.push({
type: "thought",
...signature != null ? { signature } : {},
summary: part.text.length > 0 ? [{ type: "text", text: part.text }] : void 0
});
} else if (part.type === "file") {
const fileBlock = convertFilePartToContent({
part,
warnings,
mediaResolution
});
if (fileBlock != null) {
pendingModelOutput.push(fileBlock);
}
} else if (part.type === "tool-call") {
flushModelOutput();
const signature = (_d = (_c = part.providerOptions) == null ? void 0 : _c.google) == null ? void 0 : _d.signature;
const args = typeof part.input === "string" ? safeParseToolArgs(part.input) : (_e = part.input) != null ? _e : {};
steps.push({
type: "function_call",
id: part.toolCallId,
name: part.toolName,
arguments: args,
...signature != null ? { signature } : {}
});
} else {
warnings.push({
type: "other",
message: `google.interactions: unsupported assistant content part type "${part.type}"; part dropped.`
});
}
}
flushModelOutput();
break;
}
case "tool": {
const content = [];
for (const part of message.content) {
if (part.type !== "tool-result") {
warnings.push({
type: "other",
message: `google.interactions: unsupported tool message part type "${part.type}"; part dropped.`
});
continue;
}
const block = convertToolResultPart({
toolCallId: part.toolCallId,
toolName: part.toolName,
output: part.output,
signature: (_g = (_f = part.providerOptions) == null ? void 0 : _f.google) == null ? void 0 : _g.signature,
warnings
});
content.push(block);
}
if (content.length > 0) {
steps.push({ type: "user_input", content });
}
break;
}
}
}
const systemInstruction = systemTexts.length > 0 ? systemTexts.join("\n\n") : void 0;
return { input: steps, systemInstruction, warnings };
}
function convertFilePartToContent({
part,
warnings,
mediaResolution
}) {
if (part.data.type === "text") {
return {
type: "text",
text: part.data.text
};
}
const topLevel = getTopLevelMediaType2(part.mediaType);
let kind;
switch (topLevel) {
case "image":
kind = "image";
break;
case "audio":
kind = "audio";
break;
case "video":
kind = "video";
break;
case "application":
case "text":
kind = "document";
break;
default:
kind = void 0;
}
if (kind == null) {
warnings.push({
type: "other",
message: `google.interactions: unsupported file media type "${part.mediaType}"; part dropped.`
});
return void 0;
}
const resolutionField = mediaResolution != null && (kind === "image" || kind === "video") ? { resolution: mediaResolution } : {};
switch (part.data.type) {
case "data": {
const mimeType = resolveFullMediaType2({ part });
return {
type: kind,
data: convertToBase643(part.data.data),
mime_type: mimeType,
...resolutionField
};
}
case "url": {
return {
type: kind,
uri: part.data.url.toString(),
...isFullMediaType2(part.mediaType) ? { mime_type: part.mediaType } : {},
...resolutionField
};
}
case "reference": {
const uri = resolveProviderReference2({
reference: part.data.reference,
provider: "google"
});
return {
type: kind,
uri,
...isFullMediaType2(part.mediaType) ? { mime_type: part.mediaType } : {},
...resolutionField
};
}
}
}
function compactPromptForPreviousInteraction({
prompt,
previousInteractionId
}) {
const out = [];
const droppedToolCallIds = /* @__PURE__ */ new Set();
for (const message of prompt) {
if (message.role === "assistant") {
const matchesLinkedInteraction = message.content.some((part) => {
var _a, _b;
const partInteractionId = (_b = (_a = part.providerOptions) == null ? void 0 : _a.google) == null ? void 0 : _b.interactionId;
return partInteractionId === previousInteractionId;
});
if (matchesLinkedInteraction) {
for (const part of message.content) {
if (part.type === "tool-call") {
droppedToolCallIds.add(part.toolCallId);
}
}
continue;
}
out.push(message);
continue;
}
if (message.role === "tool") {
const remaining = message.content.filter((part) => {
if (part.type !== "tool-result") {
return true;
}
return !droppedToolCallIds.has(part.toolCallId);
});
if (remaining.length === 0) {
continue;
}
out.push({
...message,
content: remaining
});
continue;
}
out.push(message);
}
return out;
}
function safeParseToolArgs(input) {
try {
const parsed = secureJsonParse2(input);
if (parsed != null && typeof parsed === "object" && !Array.isArray(parsed)) {
return parsed;
}
return { value: parsed };
} catch (e) {
return { value: input };
}
}
function convertToolResultPart({
toolCallId,
toolName,
output,
signature,
warnings
}) {
var _a;
const base = {
type: "function_result",
call_id: toolCallId,
name: toolName,
...signature != null ? { signature } : {}
};
switch (output.type) {
case "text":
return { ...base, result: output.value };
case "json":
return { ...base, result: JSON.stringify(output.value) };
case "error-text":
return { ...base, is_error: true, result: output.value };
case "error-json":
return { ...base, is_error: true, result: JSON.stringify(output.value) };
case "execution-denied":
return {
...base,
is_error: true,
result: (_a = output.reason) != null ? _a : "Tool execution denied by user."
};
case "content": {
const blocks = [];
for (const item of output.value) {
if (item.type === "text") {
blocks.push({ type: "text", text: item.text });
} else if (item.type === "file") {
const topLevel = getTopLevelMediaType2(item.mediaType);
if (topLevel !== "image") {
warnings.push({
type: "other",
message: `google.interactions: tool-result file with mediaType "${item.mediaType}" is not supported (Interactions \`function_result.result\` accepts only text and image content); part dropped.`
});
continue;
}
const imageBlock = filePartToImageBlock({ part: item, warnings });
if (imageBlock != null) {
blocks.push(imageBlock);
}
} else {
warnings.push({
type: "other",
message: `google.interactions: tool-result content part type "${item.type}" is not supported; part dropped.`
});
}
}
return { ...base, result: blocks };
}
}
}
function filePartToImageBlock({
part,
warnings
}) {
switch (part.data.type) {
case "data": {
const mimeType = isFullMediaType2(part.mediaType) ? part.mediaType : resolveFullMediaType2({
part: {
type: "file",
mediaType: part.mediaType,
data: part.data
}
});
return {
type: "image",
data: convertToBase643(part.data.data),
mime_type: mimeType
};
}
case "url":
return {
type: "image",
uri: part.data.url.toString(),
...isFullMediaType2(part.mediaType) ? { mime_type: part.mediaType } : {}
};
case "reference": {
const uri = resolveProviderReference2({
reference: part.data.reference,
provider: "google"
});
return {
type: "image",
uri,
...isFullMediaType2(part.mediaType) ? { mime_type: part.mediaType } : {}
};
}
case "text": {
warnings.push({
type: "other",
message: 'google.interactions: tool-result image part with `data.type === "text"` is not representable as an image; part dropped.'
});
return void 0;
}
}
}
function mergeAdjacentTextContent(content) {
if (content.length < 2) {
return content;
}
const result = [];
for (const block of content) {
const last = result[result.length - 1];
if (block.type === "text" && last != null && last.type === "text" && last.annotations == null && block.annotations == null) {
const merged = {
type: "text",
text: `${last.text}
${block.text}`
};
result[result.length - 1] = merged;
continue;
}
result.push(block);
}
return result;
}
// src/interactions/google-interactions-api.ts
import {
lazySchema as lazySchema18,
zodSchema as zodSchema18
} from "@ai-sdk/provider-utils";
import { z as z20 } from "zod/v4";
var tokenByModalitySchema = () => z20.object({
modality: z20.string().nullish(),
tokens: z20.number().nullish()
}).loose();
var usageSchema2 = () => z20.object({
total_input_tokens: z20.number().nullish(),
total_output_tokens: z20.number().nullish(),
total_thought_tokens: z20.number().nullish(),
total_cached_tokens: z20.number().nullish(),
total_tool_use_tokens: z20.number().nullish(),
total_tokens: z20.number().nullish(),
input_tokens_by_modality: z20.array(tokenByModalitySchema()).nullish(),
output_tokens_by_modality: z20.array(tokenByModalitySchema()).nullish(),
cached_tokens_by_modality: z20.array(tokenByModalitySchema()).nullish(),
tool_use_tokens_by_modality: z20.array(tokenByModalitySchema()).nullish(),
grounding_tool_count: z20.array(
z20.object({
type: z20.string().nullish(),
count: z20.number().nullish()
}).loose()
).nullish()
}).loose();
var interactionStatusSchema = () => z20.enum([
"in_progress",
"requires_action",
"completed",
"failed",
"cancelled",
"incomplete"
]);
var annotationSchema = () => {
const urlCitation = z20.object({
type: z20.literal("url_citation"),
url: z20.string().nullish(),
title: z20.string().nullish(),
start_index: z20.number().nullish(),
end_index: z20.number().nullish()
}).loose();
const fileCitation = z20.object({
type: z20.literal("file_citation"),
file_name: z20.string().nullish(),
document_uri: z20.string().nullish(),
url: z20.string().nullish(),
page_number: z20.number().nullish(),
media_id: z20.string().nullish(),
start_index: z20.number().nullish(),
end_index: z20.number().nullish(),
custom_metadata: z20.record(z20.string(), z20.unknown()).nullish()
}).loose();
const placeCitation = z20.object({
type: z20.literal("place_citation"),
name: z20.string().nullish(),
url: z20.string().nullish(),
place_id: z20.string().nullish(),
start_index: z20.number().nullish(),
end_index: z20.number().nullish()
}).loose();
return z20.union([
urlCitation,
fileCitation,
placeCitation,
z20.object({ type: z20.string() }).loose()
]);
};
var thoughtSummaryItemSchema = () => z20.object({
type: z20.string(),
text: z20.string().nullish(),
data: z20.string().nullish(),
mime_type: z20.string().nullish()
}).loose();
var contentBlockSchema = () => {
const textContent = z20.object({
type: z20.literal("text"),
text: z20.string(),
annotations: z20.array(annotationSchema()).nullish()
}).loose();
const imageContent = z20.object({
type: z20.literal("image"),
data: z20.string().nullish(),
mime_type: z20.string().nullish(),
resolution: z20.enum(["low", "medium", "high", "ultra_high"]).nullish(),
uri: z20.string().nullish()
}).loose();
const videoContent = z20.object({
type: z20.literal("video"),
data: z20.string().nullish(),
mime_type: z20.string().nullish(),
uri: z20.string().nullish()
}).loose();
return z20.union([
textContent,
imageContent,
videoContent,
z20.object({ type: z20.string() }).loose()
]);
};
var BUILTIN_TOOL_CALL_STEP_TYPES = [
"google_search_call",
"code_execution_call",
"url_context_call",
"file_search_call",
"google_maps_call",
"mcp_server_tool_call"
];
var BUILTIN_TOOL_RESULT_STEP_TYPES = [
"google_search_result",
"code_execution_result",
"url_context_result",
"file_search_result",
"google_maps_result",
"mcp_server_tool_result"
];
var stepSchema = () => {
const userInputStep = z20.object({
type: z20.literal("user_input"),
content: z20.array(contentBlockSchema()).nullish()
}).loose();
const modelOutputStep = z20.object({
type: z20.literal("model_output"),
content: z20.array(contentBlockSchema()).nullish()
}).loose();
const functionCallStep = z20.object({
type: z20.literal("function_call"),
id: z20.string(),
name: z20.string(),
arguments: z20.record(z20.string(), z20.unknown()).nullish(),
signature: z20.string().nullish()
}).loose();
const thoughtStep = z20.object({
type: z20.literal("thought"),
signature: z20.string().nullish(),
summary: z20.array(thoughtSummaryItemSchema()).nullish()
}).loose();
const builtinToolCallStep = z20.object({
type: z20.enum(BUILTIN_TOOL_CALL_STEP_TYPES),
id: z20.string(),
arguments: z20.record(z20.string(), z20.unknown()).nullish(),
name: z20.string().nullish(),
server_name: z20.string().nullish(),
search_type: z20.string().nullish(),
signature: z20.string().nullish()
}).loose();
const builtinToolResultStep = z20.object({
type: z20.enum(BUILTIN_TOOL_RESULT_STEP_TYPES),
call_id: z20.string(),
result: z20.unknown().nullish(),
is_error: z20.boolean().nullish(),
name: z20.string().nullish(),
server_name: z20.string().nullish(),
signature: z20.string().nullish()
}).loose();
return z20.union([
userInputStep,
modelOutputStep,
functionCallStep,
thoughtStep,
builtinToolCallStep,
builtinToolResultStep,
z20.object({ type: z20.string() }).loose()
]);
};
var googleInteractionsResponseSchema = lazySchema18(
() => zodSchema18(
z20.object({
/*
* `id` is omitted from the response body when `store: false` (fully
* stateless mode) — there is no server-side interaction record for the
* client to reference. `nullish` lets the schema accept that shape.
*/
id: z20.string().nullish(),
created: z20.string().nullish(),
updated: z20.string().nullish(),
status: interactionStatusSchema(),
model: z20.string().nullish(),
agent: z20.string().nullish(),
steps: z20.array(stepSchema()).nullish(),
usage: usageSchema2().nullish(),
service_tier: z20.string().nullish(),
previous_interaction_id: z20.string().nullish(),
response_modalities: z20.array(z20.string()).nullish()
}).loose()
)
);
var googleInteractionsEventSchema = lazySchema18(
() => zodSchema18(
(() => {
const status = interactionStatusSchema();
const annotation = annotationSchema();
const thoughtSummaryItem = thoughtSummaryItemSchema();
const interactionCreatedEvent = z20.object({
event_type: z20.literal("interaction.created"),
event_id: z20.string().nullish(),
interaction: z20.object({
/*
* `id` is omitted when `store: false` (fully stateless mode);
* see the matching note on `googleInteractionsResponseSchema.id`.
*/
id: z20.string().nullish(),
created: z20.string().nullish(),
model: z20.string().nullish(),
agent: z20.string().nullish(),
status: status.nullish()
}).loose()
}).loose();
const stepStartEvent = z20.object({
event_type: z20.literal("step.start"),
event_id: z20.string().nullish(),
index: z20.number(),
step: stepSchema()
}).loose();
const stepDeltaText = z20.object({
type: z20.literal("text"),
text: z20.string()
}).loose();
const stepDeltaThoughtSummary = z20.object({
type: z20.literal("thought_summary"),
content: thoughtSummaryItem.nullish()
}).loose();
const stepDeltaThoughtSignature = z20.object({
type: z20.literal("thought_signature"),
signature: z20.string().nullish()
}).loose();
const stepDeltaArgumentsDelta = z20.object({
type: z20.literal("arguments_delta"),
arguments: z20.string().nullish(),
id: z20.string().nullish(),
signature: z20.string().nullish()
}).loose();
const stepDeltaTextAnnotation = z20.object({
type: z20.enum(["text_annotation_delta", "text_annotation"]),
annotations: z20.array(annotation).nullish()
}).loose();
const stepDeltaImage = z20.object({
type: z20.literal("image"),
data: z20.string().nullish(),
mime_type: z20.string().nullish(),
resolution: z20.enum(["low", "medium", "high", "ultra_high"]).nullish(),
uri: z20.string().nullish()
}).loose();
const stepDeltaVideo = z20.object({
type: z20.literal("video"),
data: z20.string().nullish(),
mime_type: z20.string().nullish(),
uri: z20.string().nullish()
}).loose();
const stepDeltaBuiltinToolCall = z20.object({
type: z20.enum(BUILTIN_TOOL_CALL_STEP_TYPES),
id: z20.string().nullish(),
arguments: z20.record(z20.string(), z20.unknown()).nullish(),
name: z20.string().nullish(),
server_name: z20.string().nullish(),
search_type: z20.string().nullish(),
signature: z20.string().nullish()
}).loose();
const stepDeltaBuiltinToolResult = z20.object({
type: z20.enum(BUILTIN_TOOL_RESULT_STEP_TYPES),
call_id: z20.string().nullish(),
result: z20.unknown().nullish(),
is_error: z20.boolean().nullish(),
name: z20.string().nullish(),
server_name: z20.string().nullish(),
signature: z20.string().nullish()
}).loose();
const stepDeltaUnknown = z20.object({ type: z20.string() }).loose();
const stepDeltaUnion = z20.union([
stepDeltaText,
stepDeltaImage,
stepDeltaVideo,
stepDeltaThoughtSummary,
stepDeltaThoughtSignature,
stepDeltaArgumentsDelta,
stepDeltaTextAnnotation,
stepDeltaBuiltinToolCall,
stepDeltaBuiltinToolResult,
stepDeltaUnknown
]);
const stepDeltaEvent = z20.object({
event_type: z20.literal("step.delta"),
event_id: z20.string().nullish(),
index: z20.number(),
delta: stepDeltaUnion
}).loose();
const stepStopEvent = z20.object({
event_type: z20.literal("step.stop"),
event_id: z20.string().nullish(),
index: z20.number()
}).loose();
const interactionStatusUpdateEvent = z20.object({
event_type: z20.literal("interaction.status_update"),
event_id: z20.string().nullish(),
interaction_id: z20.string().nullish(),
status: status.nullish()
}).loose();
const interactionInProgressEvent = z20.object({
event_type: z20.literal("interaction.in_progress"),
event_id: z20.string().nullish(),
interaction_id: z20.string().nullish(),
status: status.nullish()
}).loose();
const interactionRequiresActionEvent = z20.object({
event_type: z20.literal("interaction.requires_action"),
event_id: z20.string().nullish(),
interaction_id: z20.string().nullish(),
status: status.nullish()
}).loose();
const interactionCompletedEvent = z20.object({
event_type: z20.literal("interaction.completed"),
event_id: z20.string().nullish(),
interaction: z20.object({
id: z20.string().nullish(),
status: status.nullish(),
usage: usageSchema2().nullish(),
service_tier: z20.string().nullish()
}).loose()
}).loose();
const errorEvent = z20.object({
event_type: z20.literal("error"),
event_id: z20.string().nullish(),
error: z20.object({
code: z20.string().nullish(),
message: z20.string().nullish()
}).loose().nullish()
}).loose();
const unknownEvent = z20.object({ event_type: z20.string() }).loose();
return z20.union([
interactionCreatedEvent,
stepStartEvent,
stepDeltaEvent,
stepStopEvent,
interactionStatusUpdateEvent,
interactionInProgressEvent,
interactionRequiresActionEvent,
interactionCompletedEvent,
errorEvent,
unknownEvent
]);
})()
)
);
// src/interactions/google-interactions-language-model-options.ts
import {
lazySchema as lazySchema19,
zodSchema as zodSchema19
} from "@ai-sdk/provider-utils";
import { z as z21 } from "zod/v4";
var googleInteractionsLanguageModelOptions = lazySchema19(
() => zodSchema19(
z21.object({
previousInteractionId: z21.string().nullish(),
store: z21.boolean().nullish(),
agent: z21.string().nullish(),
agentConfig: z21.union([
z21.object({
type: z21.literal("dynamic")
}).loose(),
z21.object({
type: z21.literal("deep-research"),
thinkingSummaries: z21.enum(["auto", "none"]).nullish(),
visualization: z21.enum(["off", "auto"]).nullish(),
collaborativePlanning: z21.boolean().nullish()
})
]).nullish(),
thinkingLevel: z21.enum(["minimal", "low", "medium", "high"]).nullish(),
thinkingSummaries: z21.enum(["auto", "none"]).nullish(),
/**
* Output-format entries that map directly to the API's `response_format`
* array. Use this to request image, audio, video, or non-JSON text
* outputs with modality-specific controls.
*
* Entries are sent in order. The AI SDK call-level `responseFormat: {
* type: 'json', schema }` still drives JSON-mode and adds a matching
* text entry automatically; entries listed here are appended.
*/
responseFormat: z21.array(
z21.union([
z21.object({
type: z21.literal("text"),
mimeType: z21.string().nullish(),
schema: z21.unknown().nullish()
}).loose(),
z21.object({
type: z21.literal("image"),
mimeType: z21.string().nullish(),
aspectRatio: z21.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"
]).nullish(),
imageSize: z21.enum(["1K", "2K", "4K", "512"]).nullish()
}).loose(),
z21.object({
type: z21.literal("audio"),
mimeType: z21.string().nullish()
}).loose(),
z21.object({
type: z21.literal("video"),
aspectRatio: z21.enum(["16:9", "9:16"]).nullish(),
resolution: z21.enum(["360p", "720p", "1080p", "4k"]).nullish(),
duration: z21.string().nullish(),
delivery: z21.enum(["inline", "uri"]).nullish(),
gcsUri: z21.string().nullish()
}).loose()
])
).nullish(),
/**
* @deprecated Use `responseFormat` with a `{ type: 'image', ... }`
* entry instead. Retained for backwards compatibility; the SDK
* translates it into a matching `response_format` image entry and
* emits a warning when set.
*/
imageConfig: z21.object({
aspectRatio: z21.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"
]).nullish(),
imageSize: z21.enum(["1K", "2K", "4K", "512"]).nullish()
}).nullish(),
mediaResolution: z21.enum(["low", "medium", "high", "ultra_high"]).nullish(),
responseModalities: z21.array(z21.enum(["text", "image", "audio", "video", "document"])).nullish(),
serviceTier: z21.enum(["flex", "standard", "priority"]).nullish(),
/**
* Alternative to AI SDK `system` message. If both are set, the AI SDK
* `system` message wins and a warning is emitted.
*/
systemInstruction: z21.string().nullish(),
/**
* Per-block signature for round-tripping `thought.signature` and
* `function_call.signature` blocks. Set by the SDK on output reasoning /
* tool-call parts; passed back unchanged on input parts so the API
* accepts the prior turn.
*/
signature: z21.string().nullish(),
/**
* Set by the SDK on output assistant messages. The converter uses it to
* decide which messages to drop when compacting under
* `previousInteractionId`.
*/
interactionId: z21.string().nullish(),
/**
* Maximum time, in milliseconds, to poll a background interaction (agent
* call) before giving up. Defaults to 30 minutes. Long-running agents
* such as deep research can take tens of minutes — increase if needed.
*/
pollingTimeoutMs: z21.number().int().positive().nullish(),
/**
* Run the interaction in the background. Required for agents whose
* server-side workflow cannot complete within a single request/response.
* When `true`, the POST returns with a non-terminal status and the SDK
* polls `GET /interactions/{id}` until the work completes. Some agents
* reject `true`; see the agent's documentation for which mode it
* requires.
*/
background: z21.boolean().nullish(),
/**
* Environment configuration for the agent sandbox. Only applies to agent
* calls (`google.interactions({ agent })`); ignored on model-id calls.
*
* - `"remote"`: provision a fresh sandbox for this call.
* - any other string: an existing `environment_id` to reuse.
* - object: provision a fresh sandbox and optionally preload `sources`
* and/or constrain outbound traffic via `network`.
*/
environment: z21.union([
z21.string(),
z21.object({
type: z21.literal("remote"),
sources: z21.array(
z21.union([
z21.object({
type: z21.literal("gcs"),
source: z21.string(),
target: z21.string().nullish()
}),
z21.object({
type: z21.literal("repository"),
source: z21.string(),
target: z21.string().nullish()
}),
z21.object({
type: z21.literal("inline"),
content: z21.string(),
target: z21.string()
})
])
).nullish(),
network: z21.union([
z21.literal("disabled"),
z21.object({
allowlist: z21.array(
z21.object({
domain: z21.string(),
transform: z21.array(z21.record(z21.string(), z21.string())).nullish()
})
)
})
]).nullish()
})
]).nullish()
})
)
);
// src/interactions/parse-google-interactions-outputs.ts
function googleProviderMetadata({
signature,
interactionId
}) {
const google2 = {};
if (signature != null) {
google2.signature = signature;
}
if (interactionId != null) {
google2.interactionId = interactionId;
}
return Object.keys(google2).length > 0 ? { providerMetadata: { google: google2 } } : {};
}
var BUILTIN_TOOL_CALL_TYPES2 = /* @__PURE__ */ new Set([
"google_search_call",
"code_execution_call",
"url_context_call",
"file_search_call",
"google_maps_call",
"mcp_server_tool_call"
]);
var BUILTIN_TOOL_RESULT_TYPES2 = /* @__PURE__ */ new Set([
"google_search_result",
"code_execution_result",
"url_context_result",
"file_search_result",
"google_maps_result",
"mcp_server_tool_result"
]);
function builtinToolNameFromCallType2(type) {
return type.replace(/_call$/, "");
}
function builtinToolNameFromResultType2(type) {
return type.replace(/_result$/, "");
}
function parseGoogleInteractionsOutputs({
steps,
generateId: generateId4,
interactionId
}) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
const content = [];
let hasFunctionCall = false;
if (steps == null) {
return { content, hasFunctionCall };
}
for (const step of steps) {
if (step == null || typeof step !== "object") continue;
const type = step.type;
if (typeof type !== "string") continue;
switch (type) {
case "user_input": {
break;
}
case "model_output": {
const blocks = (_a = step.content) != null ? _a : [];
for (const block of blocks) {
if (block == null || typeof block !== "object") continue;
const blockType = block.type;
if (blockType === "text") {
const text = (_b = block.text) != null ? _b : "";
const annotations = block.annotations;
content.push({
type: "text",
text,
...googleProviderMetadata({ interactionId })
});
const sources = annotationsToSources({ annotations, generateId: generateId4 });
for (const source of sources) {
content.push(source);
}
} else if (blockType === "image") {
const image = block;
if (image.data != null && image.data.length > 0) {
content.push({
type: "file",
mediaType: (_c = image.mime_type) != null ? _c : "image/png",
data: { type: "data", data: image.data },
...googleProviderMetadata({ interactionId })
});
} else if (image.uri != null && image.uri.length > 0) {
content.push({
type: "file",
mediaType: (_d = image.mime_type) != null ? _d : "image/png",
data: { type: "url", url: new URL(image.uri) },
...googleProviderMetadata({ interactionId })
});
}
} else if (blockType === "video") {
const video = block;
if (video.data != null && video.data.length > 0) {
content.push({
type: "file",
mediaType: (_e = video.mime_type) != null ? _e : "video/mp4",
data: { type: "data", data: video.data },
...googleProviderMetadata({ interactionId })
});
} else if (video.uri != null && video.uri.length > 0) {
content.push({
type: "file",
mediaType: (_f = video.mime_type) != null ? _f : "video/mp4",
data: { type: "url", url: new URL(video.uri) },
...googleProviderMetadata({ interactionId })
});
}
}
}
break;
}
case "thought": {
const thought = step;
const summary = Array.isArray(thought.summary) ? thought.summary : [];
const text = summary.filter(
(item) => (item == null ? void 0 : item.type) === "text" && typeof item.text === "string"
).map((item) => item.text).join("\n");
content.push({
type: "reasoning",
text,
...googleProviderMetadata({
signature: thought.signature,
interactionId
})
});
break;
}
case "function_call": {
hasFunctionCall = true;
const call = step;
content.push({
type: "tool-call",
toolCallId: call.id,
toolName: call.name,
input: JSON.stringify((_g = call.arguments) != null ? _g : {}),
...googleProviderMetadata({
signature: call.signature,
interactionId
})
});
break;
}
default: {
if (BUILTIN_TOOL_CALL_TYPES2.has(type)) {
const call = step;
const toolName = type === "mcp_server_tool_call" ? (_h = call.name) != null ? _h : "mcp_server_tool" : builtinToolNameFromCallType2(type);
const input = JSON.stringify((_i = call.arguments) != null ? _i : {});
content.push({
type: "tool-call",
toolCallId: call.id || generateId4(),
toolName,
input,
providerExecuted: true
});
} else if (BUILTIN_TOOL_RESULT_TYPES2.has(type)) {
const result = step;
const toolName = type === "mcp_server_tool_result" ? (_j = result.name) != null ? _j : "mcp_server_tool" : builtinToolNameFromResultType2(type);
content.push({
type: "tool-result",
toolCallId: result.call_id || generateId4(),
toolName,
result: (_k = result.result) != null ? _k : null
});
const sources = builtinToolResultToSources({
block: step,
generateId: generateId4
});
for (const source of sources) {
content.push(source);
}
}
break;
}
}
}
return { content, hasFunctionCall };
}
// src/interactions/poll-google-interactions.ts
import {
createJsonResponseHandler as createJsonResponseHandler7,
delay as delay2,
getFromApi as getFromApi4,
isAbortError
} from "@ai-sdk/provider-utils";
// src/interactions/cancel-google-interaction.ts
import {
combineHeaders as combineHeaders7,
getRuntimeEnvironmentUserAgent,
withUserAgentSuffix
} from "@ai-sdk/provider-utils";
var getOriginalFetch = () => globalThis.fetch;
async function cancelGoogleInteraction({
baseURL,
interactionId,
headers,
fetch: fetch2 = getOriginalFetch()
}) {
if (interactionId == null || interactionId.length === 0) {
return;
}
const url = `${baseURL}/interactions/${encodeURIComponent(interactionId)}/cancel`;
try {
const response = await fetch2(url, {
method: "POST",
headers: withUserAgentSuffix(
combineHeaders7({ "Content-Type": "application/json" }, headers),
getRuntimeEnvironmentUserAgent()
),
body: "{}"
});
try {
await response.text();
} catch (e) {
}
} catch (e) {
}
}
// src/interactions/poll-google-interactions.ts
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled", "incomplete"]);
function isTerminalStatus(status) {
return status != null && TERMINAL_STATUSES.has(status);
}
var DEFAULT_INITIAL_DELAY_MS = 1e3;
var DEFAULT_MAX_DELAY_MS = 1e4;
var DEFAULT_TIMEOUT_MS = 30 * 60 * 1e3;
async function pollGoogleInteractionUntilTerminal({
baseURL,
interactionId,
headers,
fetch: fetch2,
abortSignal,
initialDelayMs = DEFAULT_INITIAL_DELAY_MS,
maxDelayMs = DEFAULT_MAX_DELAY_MS,
timeoutMs = DEFAULT_TIMEOUT_MS
}) {
if (interactionId == null || interactionId.length === 0) {
throw new Error(
"google.interactions: cannot poll a background interaction without an id. The POST response did not include an interaction id."
);
}
const startedAt = Date.now();
let nextDelayMs = initialDelayMs;
const url = `${baseURL}/interactions/${encodeURIComponent(interactionId)}`;
const cancelOnServer = () => cancelGoogleInteraction({ baseURL, interactionId, headers, fetch: fetch2 });
try {
while (true) {
if (abortSignal == null ? void 0 : abortSignal.aborted) {
await cancelOnServer();
throw new DOMException("Polling was aborted", "AbortError");
}
if (Date.now() - startedAt > timeoutMs) {
throw new Error(
`google.interactions: timed out polling interaction ${interactionId} after ${timeoutMs}ms.`
);
}
await delay2(nextDelayMs, { abortSignal });
const {
value: response,
rawValue: rawResponse,
responseHeaders
} = await getFromApi4({
url,
validateUrl: false,
headers,
failedResponseHandler: googleFailedResponseHandler,
successfulResponseHandler: createJsonResponseHandler7(
googleInteractionsResponseSchema
),
abortSignal,
fetch: fetch2
});
if (isTerminalStatus(response.status)) {
return { response, rawResponse, responseHeaders };
}
nextDelayMs = Math.min(nextDelayMs * 2, maxDelayMs);
}
} catch (error) {
if (isAbortError(error)) {
await cancelOnServer();
}
throw error;
}
}
// src/interactions/prepare-google-interactions-tools.ts
function prepareGoogleInteractionsTools({
tools,
toolChoice
}) {
var _a, _b, _c, _d;
const toolWarnings = [];
const normalized = (tools == null ? void 0 : tools.length) ? tools : void 0;
if (normalized == null) {
return { tools: void 0, toolChoice: void 0, toolWarnings };
}
const interactionsTools = [];
for (const tool of normalized) {
if (tool.type === "function") {
interactionsTools.push({
type: "function",
name: tool.name,
description: (_a = tool.description) != null ? _a : "",
parameters: tool.inputSchema
});
continue;
}
if (tool.type === "provider") {
const args = (_b = tool.args) != null ? _b : {};
switch (tool.id) {
case "google.google_search": {
const searchTypesArg = args.searchTypes;
let search_types;
if (searchTypesArg != null && typeof searchTypesArg === "object") {
const list = [];
if (searchTypesArg.webSearch != null) list.push("web_search");
if (searchTypesArg.imageSearch != null) list.push("image_search");
if (list.length > 0) {
search_types = list;
}
}
interactionsTools.push({
type: "google_search",
...search_types != null ? { search_types } : {}
});
break;
}
case "google.code_execution": {
interactionsTools.push({ type: "code_execution" });
break;
}
case "google.url_context": {
interactionsTools.push({ type: "url_context" });
break;
}
case "google.file_search": {
interactionsTools.push({
type: "file_search",
...args.fileSearchStoreNames != null ? {
file_search_store_names: args.fileSearchStoreNames
} : {},
...args.topK != null ? { top_k: args.topK } : {},
...args.metadataFilter != null ? { metadata_filter: args.metadataFilter } : {}
});
break;
}
case "google.google_maps": {
interactionsTools.push({
type: "google_maps",
...args.latitude != null ? { latitude: args.latitude } : {},
...args.longitude != null ? { longitude: args.longitude } : {},
...args.enableWidget != null ? { enable_widget: args.enableWidget } : {}
});
break;
}
case "google.computer_use": {
interactionsTools.push({
type: "computer_use",
environment: (_c = args.environment) != null ? _c : "browser",
...args.excludedPredefinedFunctions != null ? {
excludedPredefinedFunctions: args.excludedPredefinedFunctions
} : {}
});
break;
}
case "google.mcp_server": {
interactionsTools.push({
type: "mcp_server",
...args.name != null ? { name: args.name } : {},
...args.url != null ? { url: args.url } : {},
...args.headers != null ? { headers: args.headers } : {},
...args.allowedTools != null ? { allowed_tools: args.allowedTools } : {}
});
break;
}
case "google.retrieval": {
const vertexAiSearchConfig = (_d = args.vertexAiSearchConfig) != null ? _d : void 0;
interactionsTools.push({
type: "retrieval",
...args.retrievalTypes != null ? {
retrieval_types: args.retrievalTypes
} : { retrieval_types: ["vertex_ai_search"] },
...vertexAiSearchConfig != null ? { vertex_ai_search_config: vertexAiSearchConfig } : {}
});
break;
}
default: {
toolWarnings.push({
type: "unsupported",
feature: `provider-defined tool ${tool.id}`,
details: `provider-defined tool ${tool.id} is not supported by google.interactions; tool dropped.`
});
break;
}
}
continue;
}
toolWarnings.push({
type: "unsupported",
feature: `tool of type ${tool.type}`,
details: "Only function tools and google.* provider-defined tools are supported by google.interactions; tool dropped."
});
}
const hasFunctionTool = interactionsTools.some((t) => t.type === "function");
let mappedToolChoice;
if (toolChoice != null && hasFunctionTool) {
switch (toolChoice.type) {
case "auto":
mappedToolChoice = "auto";
break;
case "required":
mappedToolChoice = "any";
break;
case "none":
mappedToolChoice = "none";
break;
case "tool":
mappedToolChoice = {
allowed_tools: {
mode: "validated",
tools: [toolChoice.toolName]
}
};
break;
}
}
return {
tools: interactionsTools.length > 0 ? interactionsTools : void 0,
toolChoice: mappedToolChoice,
toolWarnings
};
}
// src/interactions/stream-google-interactions.ts
import {
createEventSourceResponseHandler as createEventSourceResponseHandler2,
delay as delay3,
getFromApi as getFromApi5,
isAbortError as isAbortError2
} from "@ai-sdk/provider-utils";
var DEFAULT_MAX_RETRIES = 3;
var DEFAULT_RETRY_DELAY_MS = 500;
function streamGoogleInteractionEvents({
baseURL,
interactionId,
headers,
fetch: fetch2,
abortSignal,
maxRetries = DEFAULT_MAX_RETRIES,
retryDelayMs = DEFAULT_RETRY_DELAY_MS
}) {
if (interactionId.length === 0) {
throw new Error(
"google.interactions: cannot stream a background interaction without an id."
);
}
const eventSourceHeaders = {
...headers,
accept: "text/event-stream"
};
let lastEventId;
let complete = false;
let attempt = 0;
let receivedAnyEventThisAttempt = false;
let currentReader;
const internalAbort = new AbortController();
const upstreamAbortHandler = () => internalAbort.abort();
if (abortSignal != null) {
if (abortSignal.aborted) {
internalAbort.abort();
} else {
abortSignal.addEventListener("abort", upstreamAbortHandler, {
once: true
});
}
}
const effectiveSignal = internalAbort.signal;
function buildUrl() {
const base = `${baseURL}/interactions/${encodeURIComponent(interactionId)}`;
const params = new URLSearchParams({ stream: "true" });
if (lastEventId != null) {
params.set("last_event_id", lastEventId);
}
return `${base}?${params.toString()}`;
}
async function openReader() {
const { value: stream } = await getFromApi5({
url: buildUrl(),
validateUrl: false,
headers: eventSourceHeaders,
failedResponseHandler: googleFailedResponseHandler,
successfulResponseHandler: createEventSourceResponseHandler2(
googleInteractionsEventSchema
),
abortSignal: effectiveSignal,
fetch: fetch2
});
return stream.getReader();
}
return new ReadableStream({
async start(controller) {
try {
while (!complete && !effectiveSignal.aborted) {
if (currentReader == null) {
try {
currentReader = await openReader();
receivedAnyEventThisAttempt = false;
} catch (error) {
if (isAbortError2(error) || effectiveSignal.aborted) {
controller.error(error);
return;
}
attempt++;
if (attempt >= maxRetries) {
controller.error(error);
return;
}
await delay3(retryDelayMs * attempt, {
abortSignal: effectiveSignal
});
continue;
}
}
try {
const { done, value } = await currentReader.read();
if (done) {
currentReader = void 0;
if (complete) break;
if (!receivedAnyEventThisAttempt) {
attempt++;
if (attempt >= maxRetries) {
controller.error(
new Error(
"google.interactions: SSE stream closed without producing any events."
)
);
return;
}
await delay3(retryDelayMs * attempt, {
abortSignal: effectiveSignal
});
} else {
attempt = 0;
}
continue;
}
receivedAnyEventThisAttempt = true;
if (value.success) {
const streamEvent = value.value;
if (typeof streamEvent.event_id === "string" && streamEvent.event_id.length > 0) {
lastEventId = streamEvent.event_id;
}
if (streamEvent.event_type === "interaction.completed" || streamEvent.event_type === "error") {
complete = true;
}
}
controller.enqueue(value);
} catch (error) {
if (isAbortError2(error) || effectiveSignal.aborted) {
controller.error(error);
return;
}
currentReader = void 0;
attempt++;
if (attempt >= maxRetries) {
controller.error(error);
return;
}
await delay3(retryDelayMs * attempt, {
abortSignal: effectiveSignal
});
}
}
controller.close();
} catch (error) {
controller.error(error);
} finally {
if (abortSignal != null) {
abortSignal.removeEventListener("abort", upstreamAbortHandler);
}
currentReader == null ? void 0 : currentReader.cancel().catch(() => {
});
currentReader = void 0;
if (effectiveSignal.aborted && !complete) {
await cancelGoogleInteraction({
baseURL,
interactionId,
headers,
fetch: fetch2
});
}
}
},
cancel() {
internalAbort.abort();
currentReader == null ? void 0 : currentReader.cancel().catch(() => {
});
currentReader = void 0;
}
});
}
// src/interactions/synthesize-google-interactions-agent-stream.ts
function synthesizeGoogleInteractionsAgentStream({
response,
warnings,
generateId: generateId4,
includeRawChunks,
headerServiceTier
}) {
return new ReadableStream({
start(controller) {
var _a, _b, _c;
controller.enqueue({ type: "stream-start", warnings });
const interactionId = typeof response.id === "string" && response.id.length > 0 ? response.id : void 0;
let timestamp;
const created = response.created;
if (typeof created === "string") {
const parsed = new Date(created);
if (!Number.isNaN(parsed.getTime())) {
timestamp = parsed;
}
}
controller.enqueue({
type: "response-metadata",
...interactionId != null ? { id: interactionId } : {},
modelId: (_a = response.model) != null ? _a : void 0,
...timestamp ? { timestamp } : {}
});
if (includeRawChunks) {
controller.enqueue({ type: "raw", rawValue: response });
}
const { content, hasFunctionCall } = parseGoogleInteractionsOutputs({
steps: (_b = response.steps) != null ? _b : null,
generateId: generateId4,
interactionId
});
let blockCounter = 0;
const nextBlockId = () => `${interactionId != null ? interactionId : "agent"}:${blockCounter++}`;
for (const part of content) {
switch (part.type) {
case "text": {
const id = nextBlockId();
const providerMetadata2 = part.providerMetadata;
controller.enqueue({ type: "text-start", id });
if (part.text.length > 0) {
controller.enqueue({ type: "text-delta", id, delta: part.text });
}
controller.enqueue({
type: "text-end",
id,
...providerMetadata2 ? { providerMetadata: providerMetadata2 } : {}
});
break;
}
case "reasoning": {
const id = nextBlockId();
const providerMetadata2 = part.providerMetadata;
controller.enqueue({ type: "reasoning-start", id });
if (part.text.length > 0) {
controller.enqueue({
type: "reasoning-delta",
id,
delta: part.text
});
}
controller.enqueue({
type: "reasoning-end",
id,
...providerMetadata2 ? { providerMetadata: providerMetadata2 } : {}
});
break;
}
case "tool-call": {
const providerMetadata2 = part.providerMetadata;
controller.enqueue({
type: "tool-input-start",
id: part.toolCallId,
toolName: part.toolName,
...part.providerExecuted ? { providerExecuted: part.providerExecuted } : {}
});
controller.enqueue({
type: "tool-input-delta",
id: part.toolCallId,
delta: part.input
});
controller.enqueue({
type: "tool-input-end",
id: part.toolCallId
});
controller.enqueue({
type: "tool-call",
toolCallId: part.toolCallId,
toolName: part.toolName,
input: part.input,
...part.providerExecuted ? { providerExecuted: part.providerExecuted } : {},
...providerMetadata2 ? { providerMetadata: providerMetadata2 } : {}
});
break;
}
case "tool-result": {
controller.enqueue({
type: "tool-result",
toolCallId: part.toolCallId,
toolName: part.toolName,
result: part.result
});
break;
}
case "source":
case "file": {
controller.enqueue(part);
break;
}
default:
break;
}
}
const serviceTier = (_c = response.service_tier) != null ? _c : headerServiceTier;
const finishReason = {
unified: mapGoogleInteractionsFinishReason({
status: response.status,
hasFunctionCall
}),
raw: response.status
};
const providerMetadata = {
google: {
...interactionId != null ? { interactionId } : {},
...serviceTier != null ? { serviceTier } : {}
}
};
controller.enqueue({
type: "finish",
finishReason,
usage: convertGoogleInteractionsUsage(response.usage),
providerMetadata
});
controller.close();
}
});
}
// src/interactions/google-interactions-language-model.ts
var GoogleInteractionsLanguageModel = class _GoogleInteractionsLanguageModel {
constructor(modelOrAgent, config) {
this.specificationVersion = "v4";
if (typeof modelOrAgent === "string") {
this.modelId = modelOrAgent;
this.agent = void 0;
} else if ("managedAgent" in modelOrAgent) {
this.modelId = modelOrAgent.managedAgent;
this.agent = modelOrAgent.managedAgent;
} else {
this.modelId = modelOrAgent.agent;
this.agent = modelOrAgent.agent;
}
this.config = config;
}
static [WORKFLOW_SERIALIZE6](model) {
return {
...serializeModelOptions5({
modelId: model.modelId,
config: model.config
}),
agent: model.agent
};
}
static [WORKFLOW_DESERIALIZE6](options) {
return new _GoogleInteractionsLanguageModel(
options.agent != null ? { agent: options.agent } : options.modelId,
options.config
);
}
get provider() {
return this.config.provider;
}
get supportedUrls() {
if (this.config.supportedUrls) {
return this.config.supportedUrls();
}
return {
"image/*": [/^https?:\/\/.+/],
"application/pdf": [/^https?:\/\/.+/],
"audio/*": [/^https?:\/\/.+/],
"video/*": [
/^https?:\/\/(www\.)?youtube\.com\/watch\?v=.+/,
/^https?:\/\/youtu\.be\/.+/,
/^gs:\/\/.+/
]
};
}
async getArgs(options) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F;
const warnings = [];
const googleOptions = await parseProviderOptions7({
provider: "google",
providerOptions: options.providerOptions,
schema: googleInteractionsLanguageModelOptions
});
const isAgent = this.agent != null;
if (!isAgent) {
if (options.frequencyPenalty != null) {
warnings.push({
type: "unsupported",
feature: "frequencyPenalty"
});
}
if (options.presencePenalty != null) {
warnings.push({
type: "unsupported",
feature: "presencePenalty"
});
}
}
const hasTools = options.tools != null && options.tools.length > 0;
let toolsForBody;
let toolChoiceForBody;
if (hasTools) {
const prepared = prepareGoogleInteractionsTools({
tools: options.tools,
toolChoice: options.toolChoice
});
toolsForBody = prepared.tools;
toolChoiceForBody = prepared.toolChoice;
warnings.push(...prepared.toolWarnings);
}
const responseFormatEntries = [];
if (((_a = options.responseFormat) == null ? void 0 : _a.type) === "json") {
if (isAgent) {
warnings.push({
type: "other",
message: "google.interactions: structured output (responseFormat) is not supported when an agent is set; responseFormat will be ignored."
});
} else {
const entry = {
type: "text",
mime_type: "application/json",
...options.responseFormat.schema != null ? { schema: options.responseFormat.schema } : {}
};
responseFormatEntries.push(entry);
}
}
if ((googleOptions == null ? void 0 : googleOptions.responseFormat) != null) {
for (const entry of googleOptions.responseFormat) {
if (entry.type === "text") {
responseFormatEntries.push(
pruneUndefined({
type: "text",
mime_type: (_b = entry.mimeType) != null ? _b : void 0,
schema: (_c = entry.schema) != null ? _c : void 0
})
);
} else if (entry.type === "image") {
responseFormatEntries.push(
pruneUndefined({
type: "image",
mime_type: (_d = entry.mimeType) != null ? _d : void 0,
aspect_ratio: (_e = entry.aspectRatio) != null ? _e : void 0,
image_size: (_f = entry.imageSize) != null ? _f : void 0
})
);
} else if (entry.type === "audio") {
responseFormatEntries.push(
pruneUndefined({
type: "audio",
mime_type: (_g = entry.mimeType) != null ? _g : void 0
})
);
} else if (entry.type === "video") {
responseFormatEntries.push(
pruneUndefined({
type: "video",
aspect_ratio: (_h = entry.aspectRatio) != null ? _h : void 0,
resolution: (_i = entry.resolution) != null ? _i : void 0,
duration: (_j = entry.duration) != null ? _j : void 0,
delivery: (_k = entry.delivery) != null ? _k : void 0,
gcs_uri: (_l = entry.gcsUri) != null ? _l : void 0
})
);
}
}
}
const {
input,
systemInstruction: convertedSystemInstruction,
warnings: convWarnings
} = convertToGoogleInteractionsInput({
prompt: options.prompt,
previousInteractionId: (_m = googleOptions == null ? void 0 : googleOptions.previousInteractionId) != null ? _m : void 0,
store: (_n = googleOptions == null ? void 0 : googleOptions.store) != null ? _n : void 0,
mediaResolution: (_o = googleOptions == null ? void 0 : googleOptions.mediaResolution) != null ? _o : void 0
});
warnings.push(...convWarnings);
let systemInstruction = convertedSystemInstruction;
const optionSystemInstruction = (_p = googleOptions == null ? void 0 : googleOptions.systemInstruction) != null ? _p : void 0;
if (systemInstruction != null && optionSystemInstruction != null) {
warnings.push({
type: "other",
message: "google.interactions: both AI SDK system message and providerOptions.google.systemInstruction were set; using the AI SDK system message."
});
} else if (systemInstruction == null && optionSystemInstruction != null) {
systemInstruction = optionSystemInstruction;
}
let generationConfig;
if (isAgent) {
const droppedFields = [];
if (options.temperature != null) droppedFields.push("temperature");
if (options.topP != null) droppedFields.push("topP");
if (options.topK != null) droppedFields.push("topK");
if (options.frequencyPenalty != null)
droppedFields.push("frequencyPenalty");
if (options.presencePenalty != null)
droppedFields.push("presencePenalty");
if (options.seed != null) droppedFields.push("seed");
if (options.stopSequences != null && options.stopSequences.length > 0) {
droppedFields.push("stopSequences");
}
if (options.maxOutputTokens != null)
droppedFields.push("maxOutputTokens");
if ((googleOptions == null ? void 0 : googleOptions.thinkingLevel) != null)
droppedFields.push("thinkingLevel");
if ((googleOptions == null ? void 0 : googleOptions.thinkingSummaries) != null) {
droppedFields.push("thinkingSummaries");
}
if ((googleOptions == null ? void 0 : googleOptions.imageConfig) != null) droppedFields.push("imageConfig");
if (droppedFields.length > 0) {
warnings.push({
type: "other",
message: `google.interactions: ${droppedFields.join(", ")} ${droppedFields.length === 1 ? "is" : "are"} not supported when an agent is set; use providerOptions.google.agentConfig instead. Dropped from the request body.`
});
}
generationConfig = void 0;
} else {
generationConfig = pruneUndefined({
temperature: (_q = options.temperature) != null ? _q : void 0,
top_p: (_r = options.topP) != null ? _r : void 0,
top_k: (_s = options.topK) != null ? _s : void 0,
seed: (_t = options.seed) != null ? _t : void 0,
stop_sequences: options.stopSequences != null && options.stopSequences.length > 0 ? options.stopSequences : void 0,
max_output_tokens: (_u = options.maxOutputTokens) != null ? _u : void 0,
thinking_level: (_v = googleOptions == null ? void 0 : googleOptions.thinkingLevel) != null ? _v : void 0,
thinking_summaries: (_w = googleOptions == null ? void 0 : googleOptions.thinkingSummaries) != null ? _w : void 0,
tool_choice: toolChoiceForBody
});
if ((googleOptions == null ? void 0 : googleOptions.imageConfig) != null) {
const alreadyHasImageEntry = responseFormatEntries.some(
(entry) => entry.type === "image"
);
warnings.push({
type: "other",
message: alreadyHasImageEntry ? "google.interactions: providerOptions.google.imageConfig is deprecated and was ignored because providerOptions.google.responseFormat already supplies an image entry. Use responseFormat exclusively." : 'google.interactions: providerOptions.google.imageConfig is deprecated. Use providerOptions.google.responseFormat with a { type: "image", ... } entry instead.'
});
if (!alreadyHasImageEntry) {
responseFormatEntries.push({
type: "image",
mime_type: "image/png",
...googleOptions.imageConfig.aspectRatio != null ? { aspect_ratio: googleOptions.imageConfig.aspectRatio } : {},
...googleOptions.imageConfig.imageSize != null ? { image_size: googleOptions.imageConfig.imageSize } : {}
});
}
}
}
let agentConfig;
if (isAgent && (googleOptions == null ? void 0 : googleOptions.agentConfig) != null) {
const agentConfigOptions = googleOptions.agentConfig;
if (agentConfigOptions.type === "deep-research") {
agentConfig = pruneUndefined({
type: "deep-research",
thinking_summaries: (_x = agentConfigOptions.thinkingSummaries) != null ? _x : void 0,
visualization: (_y = agentConfigOptions.visualization) != null ? _y : void 0,
collaborative_planning: (_z = agentConfigOptions.collaborativePlanning) != null ? _z : void 0
});
} else if (agentConfigOptions.type === "dynamic") {
agentConfig = { type: "dynamic" };
}
}
let environment;
if ((googleOptions == null ? void 0 : googleOptions.environment) != null) {
if (!isAgent) {
warnings.push({
type: "other",
message: "google.interactions: environment is only supported when an agent is set; environment will be omitted from the request body."
});
} else if (typeof googleOptions.environment === "string") {
environment = googleOptions.environment;
} else {
const environmentOptions = googleOptions.environment;
const sources = (_A = environmentOptions.sources) == null ? void 0 : _A.map((source) => {
var _a2;
if (source.type === "inline") {
return {
type: "inline",
content: source.content,
target: source.target
};
}
return pruneUndefined({
type: source.type,
source: source.source,
target: (_a2 = source.target) != null ? _a2 : void 0
});
});
let network;
if (environmentOptions.network === "disabled") {
network = "disabled";
} else if (environmentOptions.network != null) {
network = {
allowlist: environmentOptions.network.allowlist.map(
(entry) => {
var _a2;
return pruneUndefined({
domain: entry.domain,
transform: (_a2 = entry.transform) != null ? _a2 : void 0
});
}
)
};
}
environment = pruneUndefined({
type: "remote",
sources: sources != null && sources.length > 0 ? sources : void 0,
network
});
}
}
const args = pruneUndefined({
...isAgent ? { agent: this.agent } : { model: this.modelId },
input,
system_instruction: systemInstruction,
tools: toolsForBody,
response_format: responseFormatEntries.length > 0 ? responseFormatEntries : void 0,
response_modalities: (googleOptions == null ? void 0 : googleOptions.responseModalities) != null ? googleOptions.responseModalities : void 0,
previous_interaction_id: (_B = googleOptions == null ? void 0 : googleOptions.previousInteractionId) != null ? _B : void 0,
service_tier: (_C = googleOptions == null ? void 0 : googleOptions.serviceTier) != null ? _C : void 0,
store: (_D = googleOptions == null ? void 0 : googleOptions.store) != null ? _D : void 0,
generation_config: generationConfig != null && Object.keys(generationConfig).length > 0 ? generationConfig : void 0,
agent_config: agentConfig,
environment,
background: (_E = googleOptions == null ? void 0 : googleOptions.background) != null ? _E : void 0
});
return {
args,
warnings,
isAgent,
isBackground: (googleOptions == null ? void 0 : googleOptions.background) === true,
pollingTimeoutMs: (_F = googleOptions == null ? void 0 : googleOptions.pollingTimeoutMs) != null ? _F : void 0
};
}
async doGenerate(options) {
var _a, _b, _c, _d, _e, _f;
const { args, warnings, isAgent, pollingTimeoutMs } = await this.getArgs(options);
const url = `${this.config.baseURL}/interactions`;
const mergedHeaders = combineHeaders8(
this.config.headers ? await resolve6(this.config.headers) : void 0,
options.headers
);
const postResult = await postJsonToApi6({
url,
headers: mergedHeaders,
body: args,
failedResponseHandler: googleFailedResponseHandler,
successfulResponseHandler: createJsonResponseHandler8(
googleInteractionsResponseSchema
),
abortSignal: options.abortSignal,
fetch: this.config.fetch
});
let {
responseHeaders,
value: response,
rawValue: rawResponse
} = postResult;
if (isAgent && !isTerminalStatus(response.status)) {
const polled = await pollGoogleInteractionUntilTerminal({
baseURL: this.config.baseURL,
interactionId: response.id,
headers: mergedHeaders,
fetch: this.config.fetch,
abortSignal: options.abortSignal,
timeoutMs: pollingTimeoutMs
});
response = polled.response;
rawResponse = polled.rawResponse;
responseHeaders = (_a = polled.responseHeaders) != null ? _a : responseHeaders;
}
const interactionId = typeof response.id === "string" && response.id.length > 0 ? response.id : void 0;
const { content, hasFunctionCall } = parseGoogleInteractionsOutputs({
steps: (_b = response.steps) != null ? _b : null,
generateId: (_c = this.config.generateId) != null ? _c : defaultGenerateId2,
interactionId
});
const finishReason = {
unified: mapGoogleInteractionsFinishReason({
status: response.status,
hasFunctionCall
}),
raw: response.status
};
const serviceTier = (_e = (_d = response.service_tier) != null ? _d : responseHeaders == null ? void 0 : responseHeaders["x-gemini-service-tier"]) != null ? _e : void 0;
const outputTokensByModality = getGoogleInteractionsOutputTokensByModality(
response.usage
);
const providerMetadata = {
google: {
...interactionId != null ? { interactionId } : {},
...serviceTier != null ? { serviceTier } : {},
...outputTokensByModality != null ? { outputTokensByModality } : {}
}
};
let timestamp;
if (typeof response.created === "string") {
const parsed = new Date(response.created);
if (!Number.isNaN(parsed.getTime())) {
timestamp = parsed;
}
}
return {
content,
finishReason,
usage: convertGoogleInteractionsUsage(response.usage),
warnings,
providerMetadata,
request: { body: args },
response: {
headers: responseHeaders,
body: rawResponse,
...interactionId != null ? { id: interactionId } : {},
...timestamp ? { timestamp } : {},
modelId: (_f = response.model) != null ? _f : void 0
}
};
}
async doStream(options) {
var _a;
const { args, warnings, isBackground, pollingTimeoutMs } = await this.getArgs(options);
const url = `${this.config.baseURL}/interactions`;
const mergedHeaders = combineHeaders8(
this.config.headers ? await resolve6(this.config.headers) : void 0,
options.headers
);
if (isBackground) {
return this.doStreamBackground({
args,
warnings,
url,
mergedHeaders,
options,
pollingTimeoutMs
});
}
const body = { ...args, stream: true };
const { responseHeaders, value: response } = await postJsonToApi6({
url,
headers: mergedHeaders,
body,
failedResponseHandler: googleFailedResponseHandler,
successfulResponseHandler: createEventSourceResponseHandler3(
googleInteractionsEventSchema
),
abortSignal: options.abortSignal,
fetch: this.config.fetch
});
const headerServiceTier = responseHeaders == null ? void 0 : responseHeaders["x-gemini-service-tier"];
const transform = buildGoogleInteractionsStreamTransform({
warnings,
generateId: (_a = this.config.generateId) != null ? _a : defaultGenerateId2,
includeRawChunks: options.includeRawChunks,
serviceTier: headerServiceTier
});
return {
stream: response.pipeThrough(transform),
request: { body },
response: { headers: responseHeaders }
};
}
/*
* Drive the streaming surface for agent calls. Agents require
* `background: true`, which is incompatible with `stream: true` on POST.
*
* Approach:
* 1. POST `/interactions` with `background: true`. The response includes
* the interaction id and an initial (usually non-terminal) status.
* 2. If the POST status is already terminal (rare), synthesize a stream
* from the polled outputs and we're done.
* 3. Otherwise open `GET /interactions/{id}?stream=true` and pipe the
* SSE events through `buildGoogleInteractionsStreamTransform` so the
* consumer receives text deltas / thinking summaries / tool events as
* they happen instead of all at once at the end.
*
* The SSE connection can drop while the agent idles between events
* (`UND_ERR_BODY_TIMEOUT`); `streamGoogleInteractionEvents` handles the
* reconnect-with-`last_event_id` loop transparently.
*/
async doStreamBackground({
args,
warnings,
url,
mergedHeaders,
options,
pollingTimeoutMs
}) {
var _a, _b;
const postResult = await postJsonToApi6({
url,
headers: mergedHeaders,
body: args,
failedResponseHandler: googleFailedResponseHandler,
successfulResponseHandler: createJsonResponseHandler8(
googleInteractionsResponseSchema
),
abortSignal: options.abortSignal,
fetch: this.config.fetch
});
const { responseHeaders: postHeaders, value: postResponse } = postResult;
const interactionId = postResponse.id;
if (interactionId == null || interactionId.length === 0) {
throw new Error(
"google.interactions: background POST response did not include an interaction id; cannot stream the result."
);
}
const headerServiceTier = postHeaders == null ? void 0 : postHeaders["x-gemini-service-tier"];
if (isTerminalStatus(postResponse.status)) {
const synthesized = synthesizeGoogleInteractionsAgentStream({
response: postResponse,
warnings,
generateId: (_a = this.config.generateId) != null ? _a : defaultGenerateId2,
includeRawChunks: options.includeRawChunks,
headerServiceTier
});
return {
stream: synthesized,
request: { body: args },
response: { headers: postHeaders }
};
}
void pollingTimeoutMs;
const events = streamGoogleInteractionEvents({
baseURL: this.config.baseURL,
interactionId,
headers: mergedHeaders,
fetch: this.config.fetch,
abortSignal: options.abortSignal
});
const transform = buildGoogleInteractionsStreamTransform({
warnings,
generateId: (_b = this.config.generateId) != null ? _b : defaultGenerateId2,
includeRawChunks: options.includeRawChunks,
serviceTier: headerServiceTier
});
return {
stream: events.pipeThrough(transform),
request: { body: args },
response: { headers: postHeaders }
};
}
};
function pruneUndefined(obj) {
const result = {};
for (const [key, value] of Object.entries(obj)) {
if (value === void 0) continue;
result[key] = value;
}
return result;
}
// src/get-realtime-base-url.ts
function getRealtimeBaseURL(baseURL) {
const url = new URL(baseURL);
const pathSegments = url.pathname.split("/");
const version = pathSegments.at(-1);
if (version === "v1beta" || version === "v1alpha") {
pathSegments.pop();
url.pathname = pathSegments.join("/") || "/";
}
return url;
}
function getRealtimeWebSocketURL(baseURL, webSocketPath) {
const url = getRealtimeBaseURL(baseURL);
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
url.pathname = `${url.pathname.replace(/\/$/, "")}/ws/${webSocketPath}`;
return url;
}
// src/realtime/google-realtime-event-mapper.ts
import { isRecord, safeParseJSON } from "@ai-sdk/provider-utils";
var GoogleRealtimeEventMapper = class {
constructor() {
this.turnCounter = 0;
this.hasAudio = false;
this.hasText = false;
this.hasTranscript = false;
this.turnClosed = false;
this.inputAudioRate = 16e3;
}
get responseId() {
return `google-resp-${this.turnCounter}`;
}
get itemId() {
return `google-item-${this.turnCounter}`;
}
/**
* Rolls over to the next turn lazily, only once new model content actually
* arrives. `turnComplete` merely marks the current turn closed; the counter
* is not advanced until the next response begins. This keeps a transcript
* that arrives shortly after `turnComplete` attached to the turn it belongs
* to, since Google delivers transcription independently with no guaranteed
* ordering relative to `turnComplete`.
*/
beginTurnIfClosed() {
if (!this.turnClosed) return;
this.turnCounter++;
this.hasAudio = false;
this.hasText = false;
this.hasTranscript = false;
this.turnClosed = false;
}
parseServerEvent(raw) {
var _a, _b;
const data = raw;
if (data.setupComplete != null) {
return { type: "session-created", raw };
}
if (data.toolCall != null) {
this.beginTurnIfClosed();
const functionCalls = (_a = data.toolCall.functionCalls) != null ? _a : [];
return functionCalls.flatMap((functionCall) => {
var _a2;
const args = JSON.stringify((_a2 = functionCall.args) != null ? _a2 : {});
return [
{
type: "function-call-arguments-delta",
responseId: this.responseId,
itemId: this.itemId,
callId: functionCall.id,
delta: args,
raw
},
{
type: "function-call-arguments-done",
responseId: this.responseId,
itemId: this.itemId,
callId: functionCall.id,
name: functionCall.name,
arguments: args,
raw
}
];
});
}
if (data.toolCallCancellation != null) {
return {
type: "custom",
rawType: "toolCallCancellation",
raw
};
}
if (data.goAway != null) {
return {
type: "custom",
rawType: "goAway",
raw
};
}
if (data.sessionResumptionUpdate != null) {
return {
type: "custom",
rawType: "sessionResumptionUpdate",
raw
};
}
if (data.serverContent != null) {
return this.parseServerContent(data.serverContent, raw);
}
if (((_b = data.inputTranscription) == null ? void 0 : _b.text) != null) {
return {
type: "input-transcription-completed",
itemId: `google-input-${this.turnCounter}`,
transcript: data.inputTranscription.text,
raw
};
}
return { type: "custom", rawType: String(Object.keys(data)[0]), raw };
}
parseServerContent(serverContent, raw) {
var _a, _b, _c, _d;
const events = [];
if (serverContent.interrupted) {
events.push({
type: "speech-started",
raw
});
}
if ((_a = serverContent.modelTurn) == null ? void 0 : _a.parts) {
this.beginTurnIfClosed();
for (const part of serverContent.modelTurn.parts) {
if ((_b = part.inlineData) == null ? void 0 : _b.data) {
this.hasAudio = true;
events.push({
type: "audio-delta",
responseId: this.responseId,
itemId: this.itemId,
delta: part.inlineData.data,
raw
});
}
if (part.text) {
this.hasText = true;
events.push({
type: "text-delta",
responseId: this.responseId,
itemId: this.itemId,
delta: part.text,
raw
});
}
}
}
if ((_c = serverContent.outputTranscription) == null ? void 0 : _c.text) {
this.hasTranscript = true;
events.push({
type: "audio-transcript-delta",
responseId: this.responseId,
itemId: this.itemId,
delta: serverContent.outputTranscription.text,
raw
});
}
if ((_d = serverContent.inputTranscription) == null ? void 0 : _d.text) {
events.push({
type: "input-transcription-completed",
itemId: `google-input-${this.turnCounter}`,
transcript: serverContent.inputTranscription.text,
raw
});
}
if (serverContent.generationComplete) {
events.push({
type: "custom",
rawType: "generationComplete",
raw
});
}
if (serverContent.turnComplete) {
if (this.hasAudio) {
events.push({
type: "audio-done",
responseId: this.responseId,
itemId: this.itemId,
raw
});
}
if (this.hasText) {
events.push({
type: "text-done",
responseId: this.responseId,
itemId: this.itemId,
raw
});
}
if (this.hasTranscript) {
events.push({
type: "audio-transcript-done",
responseId: this.responseId,
itemId: this.itemId,
raw
});
}
events.push({
type: "response-done",
responseId: this.responseId,
status: "completed",
raw
});
this.turnClosed = true;
}
if (events.length === 0) {
return { type: "custom", rawType: "serverContent", raw };
}
return events.length === 1 ? events[0] : events;
}
serializeClientEvent(event, modelId) {
var _a;
switch (event.type) {
case "session-update":
if (((_a = event.config.inputAudioFormat) == null ? void 0 : _a.rate) != null) {
this.inputAudioRate = event.config.inputAudioFormat.rate;
}
return {
setup: buildGoogleSessionConfig(event.config, modelId)
};
case "input-audio-append":
return {
realtimeInput: {
audio: {
data: event.audio,
mimeType: `audio/pcm;rate=${this.inputAudioRate}`
}
}
};
case "input-audio-commit":
return {
realtimeInput: {
audioStreamEnd: true
}
};
case "input-audio-clear":
case "response-create":
case "response-cancel":
case "conversation-item-truncate":
return null;
case "conversation-item-create": {
const item = event.item;
switch (item.type) {
case "text-message":
return {
realtimeInput: {
text: item.text
}
};
case "function-call-output":
return serializeFunctionCallOutput(item);
case "audio-message":
return null;
}
break;
}
}
return null;
}
};
async function serializeFunctionCallOutput(item) {
const parseResult = await safeParseJSON({ text: item.output });
const response = parseResult.success ? parseResult.value : {};
return {
toolResponse: {
functionResponses: [
{
id: item.callId,
name: item.name,
response
}
]
}
};
}
function buildGoogleSessionConfig(config, modelId) {
const setup = {
model: getModelPath(modelId)
};
const generationConfig = {};
if ((config == null ? void 0 : config.outputModalities) != null) {
generationConfig.responseModalities = config.outputModalities.map(
(m) => m.toUpperCase()
);
} else {
generationConfig.responseModalities = ["AUDIO"];
}
if ((config == null ? void 0 : config.voice) != null) {
generationConfig.speechConfig = {
voiceConfig: {
prebuiltVoiceConfig: {
voiceName: config.voice
}
}
};
}
setup.generationConfig = generationConfig;
if ((config == null ? void 0 : config.instructions) != null) {
setup.systemInstruction = {
parts: [{ text: config.instructions }]
};
}
if ((config == null ? void 0 : config.tools) != null && config.tools.length > 0) {
setup.tools = [
{
functionDeclarations: config.tools.map((tool) => ({
name: tool.name,
description: tool.description,
parameters: convertJSONSchemaToOpenAPISchema(tool.parameters)
}))
}
];
}
if ((config == null ? void 0 : config.inputAudioTranscription) != null) {
setup.inputAudioTranscription = {};
}
if ((config == null ? void 0 : config.outputAudioTranscription) != null) {
setup.outputAudioTranscription = {};
}
if ((config == null ? void 0 : config.providerOptions) == null) {
return setup;
}
const { google: google2, ...providerOptions } = config.providerOptions;
Object.assign(setup, providerOptions);
const googleOptions = isRecord(google2) ? google2 : void 0;
if ((googleOptions == null ? void 0 : googleOptions.translationConfig) != null) {
const target = isRecord(setup.generationConfig) ? setup.generationConfig : generationConfig;
setup.generationConfig = {
...target,
translationConfig: googleOptions.translationConfig
};
}
return setup;
}
// src/realtime/google-realtime-model.ts
var realtimeWebSocketPath = "google.ai.generativelanguage.v1alpha.GenerativeService.BidiGenerateContentConstrained";
function getAuthTokensURL(baseURL) {
const url = getRealtimeBaseURL(baseURL);
url.pathname = `${url.pathname.replace(/\/$/, "")}/v1alpha/auth_tokens`;
return url.toString();
}
function getWebSocketURL(baseURL) {
return getRealtimeWebSocketURL(baseURL, realtimeWebSocketPath).toString();
}
var GoogleRealtimeModel = class {
constructor(modelId, config) {
this.specificationVersion = "v4";
this.mapper = new GoogleRealtimeEventMapper();
this.modelId = modelId;
this.provider = config.provider;
this.config = config;
}
async doCreateClientSecret(options) {
var _a, _b;
const fetchFn = (_a = this.config.fetch) != null ? _a : fetch;
const headers = this.config.headers();
const apiKey = headers["x-goog-api-key"];
if (!apiKey) {
throw new Error(
"Google Generative AI API key is required for realtime token creation."
);
}
const now = Date.now();
const openWindowMs = ((_b = options.expiresAfterSeconds) != null ? _b : 60) * 1e3;
const newSessionExpireTime = new Date(now + openWindowMs).toISOString();
const expireTime = new Date(
now + openWindowMs + 30 * 60 * 1e3
).toISOString();
const setupPayload = buildGoogleSessionConfig(
options.sessionConfig,
this.modelId
);
const response = await fetchFn(
`${getAuthTokensURL(this.config.baseURL)}?key=${encodeURIComponent(apiKey)}`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
// `uses: 0` means no limit is applied to how many times the token can
// start a session (per the AuthToken spec). An unset value would
// default to 1, which breaks WebSocket reconnects within the session.
uses: 0,
expireTime,
newSessionExpireTime,
bidiGenerateContentSetup: setupPayload
})
}
);
if (!response.ok) {
const text = await response.text();
throw new Error(
`Google realtime auth token request failed: ${response.status} ${text}`
);
}
const data = await response.json();
return {
token: data.name,
url: getWebSocketURL(this.config.baseURL),
expiresAt: data.expireTime ? Math.floor(new Date(data.expireTime).getTime() / 1e3) : void 0
};
}
getWebSocketConfig(options) {
return {
url: `${options.url}?access_token=${encodeURIComponent(options.token)}`
};
}
parseServerEvent(raw) {
return this.mapper.parseServerEvent(raw);
}
serializeClientEvent(event) {
return this.mapper.serializeClientEvent(event, this.modelId);
}
buildSessionConfig(config) {
return buildGoogleSessionConfig(config, this.modelId);
}
};
// src/transcription/google-transcription-model.ts
import {
InvalidArgumentError as InvalidArgumentError2
} from "@ai-sdk/provider";
import {
combineHeaders as combineHeaders9,
connectToWebSocket,
convertToBase64 as convertToBase644,
createJsonResponseHandler as createJsonResponseHandler9,
parseProviderOptions as parseProviderOptions8,
postJsonToApi as postJsonToApi7,
resolve as resolve7,
safeParseJSON as safeParseJSON2,
serializeModelOptions as serializeModelOptions6,
waitForWebSocketBufferDrain,
WORKFLOW_DESERIALIZE as WORKFLOW_DESERIALIZE7,
WORKFLOW_SERIALIZE as WORKFLOW_SERIALIZE7
} from "@ai-sdk/provider-utils";
import { z as z23 } from "zod/v4";
// src/transcription/google-transcription-model-options.ts
import { z as z22 } from "zod/v4";
var googleTranscriptionModelOptions = z22.object({
/**
* BCP-47 language codes providing hints about the languages present in the
* audio. If omitted or empty, defaults to automatic language detection.
*/
languageCodes: z22.array(z22.string()).optional(),
/**
* Custom vocabulary phrases, which bias the speech recognition model
* toward recognizing specific terms.
*/
customVocabulary: z22.array(z22.string()).optional(),
/**
* Enables word-level timestamp generation.
*/
wordTimestamp: z22.boolean().optional(),
/**
* Enables speaker diarization.
*/
diarization: z22.boolean().optional(),
/**
* Transcription output formatting mode.
*
* - `VERBATIM` (default): exact literal transcript preserving filler
* words, repetitions, and false starts.
* - `SMART`: cleans up and structures the transcript in real time —
* disfluency removal, inline self-corrections, structured formatting
* (lists, numbers, dates, paragraph breaks), and grammar/casing polish.
*/
mode: z22.enum(["SMART", "VERBATIM"]).optional()
});
// src/transcription/google-transcription-model.ts
var liveWebSocketPath = "google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent";
var defaultFinishGraceMs = 3e3;
function getLiveWebSocketURL(baseURL, apiKey) {
const url = getRealtimeWebSocketURL(baseURL, liveWebSocketPath);
url.searchParams.set("key", apiKey);
return url;
}
function isLiveTranscriptionModelId(modelId) {
return modelId.includes("-live");
}
var GoogleTranscriptionModel = class _GoogleTranscriptionModel {
constructor(modelId, config) {
this.modelId = modelId;
this.config = config;
this.specificationVersion = "v4";
}
static [WORKFLOW_SERIALIZE7](model) {
return serializeModelOptions6({
modelId: model.modelId,
config: model.config
});
}
static [WORKFLOW_DESERIALIZE7](options) {
return new _GoogleTranscriptionModel(options.modelId, options.config);
}
get provider() {
return this.config.provider;
}
async parseOptions(providerOptions) {
return parseProviderOptions8({
provider: "google",
providerOptions,
schema: googleTranscriptionModelOptions
});
}
async doGenerate(options) {
var _a, _b, _c, _d, _e, _f;
if (isLiveTranscriptionModelId(this.modelId)) {
throw new InvalidArgumentError2({
argument: "modelId",
message: `Model '${this.modelId}' only supports streaming transcription. Use experimental_streamTranscribe, or a unary model such as 'gemini-3.5-transcribe'.`
});
}
const currentDate = (_c = (_b = (_a = this.config._internal) == null ? void 0 : _a.currentDate) == null ? void 0 : _b.call(_a)) != null ? _c : /* @__PURE__ */ new Date();
const warnings = [];
const googleOptions = await this.parseOptions(options.providerOptions);
const transcriptionConfig = buildTranscriptionConfig(googleOptions);
const requestBody = {
model: this.modelId,
input: [
{
type: "audio",
data: convertToBase644(options.audio),
mime_type: options.mediaType
}
],
...transcriptionConfig != null ? { generation_config: { transcription_config: transcriptionConfig } } : {}
};
const {
value: response,
responseHeaders,
rawValue: rawResponse
} = await postJsonToApi7({
url: `${this.config.baseURL}/interactions`,
headers: combineHeaders9(
this.config.headers ? await resolve7(this.config.headers) : void 0,
options.headers
),
body: requestBody,
failedResponseHandler: googleFailedResponseHandler,
successfulResponseHandler: createJsonResponseHandler9(
googleInteractionsTranscriptionResponseSchema
),
abortSignal: options.abortSignal,
fetch: this.config.fetch
});
let text = "";
const segments = [];
for (const step of (_d = response.steps) != null ? _d : []) {
for (const content of (_e = step.content) != null ? _e : []) {
if (content.type !== "text" || content.text == null) continue;
text += content.text;
for (const annotation of (_f = content.annotations) != null ? _f : []) {
if (annotation.type !== "word_info") continue;
const startSecond = parseOffsetSeconds(annotation.start_offset);
const endSecond = parseOffsetSeconds(annotation.end_offset);
if (annotation.text == null || startSecond == null || endSecond == null) {
continue;
}
segments.push({ text: annotation.text, startSecond, endSecond });
}
}
}
return {
text,
segments,
language: void 0,
durationInSeconds: void 0,
warnings,
response: {
timestamp: currentDate,
modelId: this.modelId,
headers: responseHeaders,
body: rawResponse
},
...response.usage != null ? {
providerMetadata: {
google: { usage: response.usage }
}
} : {}
};
}
async doStream(options) {
var _a, _b, _c, _d, _e, _f, _g;
if (!isLiveTranscriptionModelId(this.modelId)) {
throw new InvalidArgumentError2({
argument: "modelId",
message: `Model '${this.modelId}' does not support streaming transcription. Use a live model such as 'gemini-3.5-transcribe-live'.`
});
}
const currentDate = (_c = (_b = (_a = this.config._internal) == null ? void 0 : _a.currentDate) == null ? void 0 : _b.call(_a)) != null ? _c : /* @__PURE__ */ new Date();
const warnings = [];
const googleOptions = await this.parseOptions(options.providerOptions);
validateLiveInputAudioFormat(options.inputAudioFormat);
const headers = combineHeaders9(
this.config.headers ? await resolve7(this.config.headers) : void 0,
options.headers
);
let apiKey;
for (const [key, value] of Object.entries(headers)) {
if (key.toLowerCase() === "x-goog-api-key" && value != null) {
apiKey = value;
}
}
if (apiKey == null) {
throw new Error(
"Google Generative AI API key is required for streaming transcription."
);
}
const webSocketHeaders = Object.fromEntries(
Object.entries(headers).filter(
([key]) => key.toLowerCase() !== "x-goog-api-key"
)
);
const setup = {
model: getModelPath(this.modelId),
inputAudioTranscription: (_d = buildAudioTranscriptionConfig(googleOptions)) != null ? _d : {}
};
return {
request: { body: setup },
response: {
timestamp: currentDate,
modelId: this.modelId
},
stream: createGoogleLiveTranscriptionStream({
webSocket: this.config.webSocket,
url: getLiveWebSocketURL(this.config.baseURL, apiKey),
headers: webSocketHeaders,
setup,
inputAudioRate: (_e = options.inputAudioFormat.rate) != null ? _e : 16e3,
finishGraceMs: (_g = (_f = this.config._internal) == null ? void 0 : _f.finishGraceMs) != null ? _g : defaultFinishGraceMs,
warnings,
audio: options.audio,
abortSignal: options.abortSignal,
includeRawChunks: options.includeRawChunks
})
};
}
};
function createGoogleLiveTranscriptionStream({
webSocket,
url,
headers,
setup,
inputAudioRate,
finishGraceMs,
warnings,
audio,
abortSignal,
includeRawChunks
}) {
let finished = false;
let cleanup = () => {
};
return new ReadableStream({
start: (controller) => {
let audioReader;
let connection;
let resolveSetupComplete;
const setupComplete = new Promise((resolve8) => {
resolveSetupComplete = resolve8;
});
let segmentCounter = 0;
let segmentBuffer = "";
let fullText = "";
let latestInterim = "";
let language;
let audioEnded = false;
let usageMetadata;
let finishTimer;
const segmentId = () => `google-segment-${segmentCounter}`;
const cancelPendingFinish = () => {
if (finishTimer != null) {
clearTimeout(finishTimer);
finishTimer = void 0;
}
};
const schedulePendingFinish = () => {
if (finished || !audioEnded) return;
cancelPendingFinish();
finishTimer = setTimeout(() => {
finishTimer = void 0;
finish();
}, finishGraceMs);
};
cleanup = (closeCode) => {
cancelPendingFinish();
if (audioReader != null) {
void audioReader.cancel().catch(() => {
});
} else {
void audio.cancel().catch(() => {
});
}
connection == null ? void 0 : connection.close(closeCode);
};
const finishWithError = (error) => {
if (finished) return;
finished = true;
cleanup();
controller.error(error);
};
const completeSegment = () => {
if (segmentBuffer === "") {
if (latestInterim === "") return;
segmentBuffer = latestInterim;
}
latestInterim = "";
controller.enqueue({
type: "transcript-final",
id: segmentId(),
text: segmentBuffer
});
fullText += fullText === "" ? segmentBuffer : ` ${segmentBuffer}`;
segmentBuffer = "";
segmentCounter++;
};
const finish = () => {
if (finished) return;
completeSegment();
finished = true;
controller.enqueue({
type: "finish",
text: fullText,
segments: [],
language,
durationInSeconds: void 0,
...usageMetadata != null ? { providerMetadata: { google: { usageMetadata } } } : {}
});
controller.close();
cleanup(1e3);
};
const sendAudio = async (socket) => {
audioReader = audio.getReader();
try {
while (true) {
const { done, value } = await audioReader.read();
if (done || finished) break;
socket.send(
JSON.stringify({
realtimeInput: {
audio: {
data: convertToBase644(value),
mimeType: `audio/pcm;rate=${inputAudioRate}`
}
}
})
);
await waitForWebSocketBufferDrain(socket);
}
} finally {
audioReader.releaseLock();
audioReader = void 0;
}
if (!finished) {
socket.send(
JSON.stringify({ realtimeInput: { audioStreamEnd: true } })
);
audioEnded = true;
schedulePendingFinish();
}
};
connection = connectToWebSocket({
url,
headers,
webSocket,
abortSignal,
onAbort: finishWithError,
onProcessingError: finishWithError,
onOpen: (socket) => {
controller.enqueue({ type: "stream-start", warnings });
socket.send(JSON.stringify({ setup }));
void setupComplete.then(() => finished ? void 0 : sendAudio(socket)).catch(finishWithError);
},
onMessageText: async (text) => {
var _a, _b;
if (finished) return;
const parsed = await safeParseJSON2({ text });
if (!parsed.success) return;
const message = parsed.value;
if (includeRawChunks) {
controller.enqueue({ type: "raw", rawValue: message });
}
if (message.setupComplete != null) {
resolveSetupComplete();
}
if (message.usageMetadata != null) {
usageMetadata = message.usageMetadata;
}
if (message.error != null) {
finishWithError(
new Error((_a = message.error.message) != null ? _a : "Google Live API error")
);
return;
}
const serverContent = message.serverContent;
const interim = serverContent == null ? void 0 : serverContent.interimInputTranscription;
if (interim == null ? void 0 : interim.text) {
schedulePendingFinish();
latestInterim = interim.text;
controller.enqueue({
type: "transcript-partial",
id: segmentId(),
text: interim.text
});
}
const transcription = (_b = serverContent == null ? void 0 : serverContent.inputTranscription) != null ? _b : message.inputTranscription;
if (transcription != null) {
if (transcription.languageCode != null) {
language = transcription.languageCode;
}
if (transcription.text) {
schedulePendingFinish();
latestInterim = "";
segmentBuffer += transcription.text;
controller.enqueue({
type: "transcript-delta",
id: segmentId(),
delta: transcription.text
});
}
if (transcription.finished === true) {
completeSegment();
}
}
if (serverContent == null ? void 0 : serverContent.turnComplete) {
completeSegment();
}
const interactionStatus = serverContent == null ? void 0 : serverContent.interactionStatus;
if (audioEnded && (interactionStatus === "IDLE" || interactionStatus === "REQUIRES_ACTION" || (serverContent == null ? void 0 : serverContent.turnComplete) === true && interactionStatus == null)) {
finish();
}
},
onSocketError: () => {
finishWithError(new Error("Google Live transcription error"));
},
onClose: ({ code, reason }) => {
if (finished) return;
if (audioEnded) {
finish();
return;
}
finishWithError(
new Error(
`Google Live transcription WebSocket closed unexpectedly before finishing (code ${code != null ? code : "unknown"}${reason ? `, reason: ${reason}` : ""}).`
)
);
}
});
},
cancel: () => {
if (finished) return;
finished = true;
cleanup();
}
});
}
function buildAudioTranscriptionConfig(options) {
if (options == null) return void 0;
const config = {};
if (options.languageCodes != null) {
config.languageCodes = options.languageCodes;
}
if (options.customVocabulary != null) {
config.customVocabulary = options.customVocabulary;
}
if (options.wordTimestamp != null) {
config.wordTimestamp = options.wordTimestamp;
}
if (options.diarization != null) {
config.diarization = options.diarization;
}
if (options.mode != null) {
config.mode = options.mode;
}
return Object.keys(config).length > 0 ? config : void 0;
}
function buildTranscriptionConfig(options) {
var _a;
if (options == null) return void 0;
const config = {};
if (options.languageCodes != null) {
config.language_codes = options.languageCodes;
}
if (options.customVocabulary != null) {
config.custom_vocabulary = options.customVocabulary;
}
if (options.mode != null || options.diarization === true || options.wordTimestamp === true) {
config.mode = {
type: ((_a = options.mode) != null ? _a : "VERBATIM").toLowerCase(),
...options.diarization === true ? { diarization_mode: "speaker" } : {},
...options.wordTimestamp === true ? { timestamp_granularities: ["word"] } : {}
};
}
return Object.keys(config).length > 0 ? config : void 0;
}
function parseOffsetSeconds(offset) {
if (offset == null) return void 0;
const parsed = Number.parseFloat(offset);
return Number.isFinite(parsed) ? parsed : void 0;
}
function validateLiveInputAudioFormat(inputAudioFormat) {
if (inputAudioFormat.type !== "audio/pcm" || inputAudioFormat.rate != null && inputAudioFormat.rate !== 16e3) {
throw new InvalidArgumentError2({
argument: "inputAudioFormat",
message: "The Gemini Live transcription API only supports 16kHz 16-bit PCM input audio."
});
}
}
var googleInteractionsWordAnnotationSchema = z23.object({
type: z23.string().nullish(),
text: z23.string().nullish(),
speaker: z23.string().nullish(),
start_offset: z23.string().nullish(),
end_offset: z23.string().nullish()
});
var googleInteractionsTranscriptionResponseSchema = z23.object({
status: z23.string().nullish(),
steps: z23.array(
z23.object({
type: z23.string().nullish(),
content: z23.array(
z23.object({
type: z23.string().nullish(),
text: z23.string().nullish(),
annotations: z23.array(googleInteractionsWordAnnotationSchema).nullish()
})
).nullish()
})
).nullish(),
usage: z23.record(z23.string(), z23.unknown()).nullish()
});
// src/speech-translation/google-speech-translation-model.ts
import {
InvalidArgumentError as InvalidArgumentError3
} from "@ai-sdk/provider";
import {
connectToWebSocket as connectToWebSocket2,
combineHeaders as combineHeaders10,
convertBase64ToUint8Array as convertBase64ToUint8Array2,
convertToBase64 as convertToBase645,
parseProviderOptions as parseProviderOptions9,
safeParseJSON as safeParseJSON3,
serializeModelOptions as serializeModelOptions7,
WORKFLOW_DESERIALIZE as WORKFLOW_DESERIALIZE8,
WORKFLOW_SERIALIZE as WORKFLOW_SERIALIZE8,
waitForWebSocketBufferDrain as waitForWebSocketBufferDrain2
} from "@ai-sdk/provider-utils";
// src/speech-translation/google-speech-translation-model-options.ts
import {
lazySchema as lazySchema20,
zodSchema as zodSchema20
} from "@ai-sdk/provider-utils";
import { z as z24 } from "zod/v4";
var googleSpeechTranslationModelOptions = lazySchema20(
() => zodSchema20(
z24.object({
/**
* Whether input audio already in the target language should be echoed
* instead of producing silence.
*/
echoTargetLanguage: z24.boolean().optional()
})
)
);
// src/speech-translation/google-speech-translation-model.ts
var liveWebSocketPath2 = "google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent";
var defaultFinishGraceMs2 = 1e3;
var googleLiveOutputAudioRate = 24e3;
var pcm16SilenceAmplitudeThreshold = 128;
function getLiveWebSocketURL2(baseURL, apiKey) {
const url = getRealtimeWebSocketURL(baseURL, liveWebSocketPath2);
url.searchParams.set("key", apiKey);
return url;
}
var GoogleSpeechTranslationModel = class _GoogleSpeechTranslationModel {
constructor(modelId, config) {
this.specificationVersion = "v4";
this.modelId = modelId;
this.config = config;
}
static [WORKFLOW_SERIALIZE8](model) {
return serializeModelOptions7({
modelId: model.modelId,
config: model.config
});
}
static [WORKFLOW_DESERIALIZE8](options) {
return new _GoogleSpeechTranslationModel(options.modelId, options.config);
}
get provider() {
return this.config.provider;
}
async doStream(options) {
var _a, _b, _c, _d, _e, _f;
if (options.targetLanguage == null) {
throw new InvalidArgumentError3({
argument: "targetLanguage",
message: `targetLanguage is required for translation model '${this.modelId}'.`
});
}
const currentDate = (_c = (_b = (_a = this.config._internal) == null ? void 0 : _a.currentDate) == null ? void 0 : _b.call(_a)) != null ? _c : /* @__PURE__ */ new Date();
const googleOptions = await parseProviderOptions9({
provider: "google",
providerOptions: options.providerOptions,
schema: googleSpeechTranslationModelOptions
});
const warnings = [];
validateGoogleSpeechTranslationInputAudioFormat(options.inputAudioFormat);
if (options.sourceLanguage != null) {
warnings.push({
type: "unsupported",
feature: "sourceLanguage",
details: "The Gemini Live translation API auto-detects the source language and does not accept a source language."
});
}
if (options.outputAudioFormat != null) {
warnings.push({
type: "unsupported",
feature: "outputAudioFormat",
details: "The Gemini Live API always outputs 24kHz 16-bit PCM audio and does not accept an output audio format."
});
}
const headers = combineHeaders10(this.config.headers(), options.headers);
let apiKey;
for (const [key, value] of Object.entries(headers)) {
if (key.toLowerCase() === "x-goog-api-key" && value != null) {
apiKey = value;
}
}
if (apiKey == null) {
throw new Error(
"Google Generative AI API key is required for streaming translation."
);
}
const webSocketHeaders = Object.fromEntries(
Object.entries(headers).filter(
([key]) => key.toLowerCase() !== "x-goog-api-key"
)
);
const setup = buildGoogleLiveSpeechTranslationSetup({
modelId: this.modelId,
targetLanguage: options.targetLanguage,
providerOptions: googleOptions
});
return {
request: { body: setup },
response: {
timestamp: currentDate,
modelId: this.modelId
},
stream: createGoogleLiveSpeechTranslationStream({
webSocket: this.config.webSocket,
url: getLiveWebSocketURL2(this.config.baseURL, apiKey),
headers: webSocketHeaders,
setup,
inputAudioRate: (_d = options.inputAudioFormat.rate) != null ? _d : 16e3,
finishGraceMs: (_f = (_e = this.config._internal) == null ? void 0 : _e.finishGraceMs) != null ? _f : defaultFinishGraceMs2,
warnings,
audio: options.audio,
abortSignal: options.abortSignal,
includeRawChunks: options.includeRawChunks
})
};
}
};
function createGoogleLiveSpeechTranslationStream({
webSocket,
url,
headers,
setup,
inputAudioRate,
finishGraceMs,
warnings,
audio,
abortSignal,
includeRawChunks
}) {
let finished = false;
let cleanup = () => {
};
return new ReadableStream({
start: (controller) => {
let audioReader;
let connection;
let resolveSetupComplete;
const setupComplete = new Promise((resolve8) => {
resolveSetupComplete = resolve8;
});
let turnCounter = 0;
let sourceText = "";
let sourceTurnBuffer = "";
let translationText = "";
let translationTurnBuffer = "";
let audioEnded = false;
let usage;
let openTurn = false;
let sawTurnComplete = false;
let trailingSilenceMs = 0;
let finishTimer;
const itemId = () => `google-item-${turnCounter}`;
const cancelPendingFinish = () => {
if (finishTimer != null) {
clearTimeout(finishTimer);
finishTimer = void 0;
}
};
const schedulePendingFinish = () => {
if (finished || finishTimer != null) return;
finishTimer = setTimeout(() => {
finishTimer = void 0;
finish();
}, finishGraceMs);
};
const onTurnActivity = () => {
openTurn = true;
trailingSilenceMs = 0;
cancelPendingFinish();
};
cleanup = (closeCode) => {
cancelPendingFinish();
if (audioReader != null) {
void audioReader.cancel().catch(() => {
});
} else {
void audio.cancel().catch(() => {
});
}
connection == null ? void 0 : connection.close(closeCode);
};
const finishWithError = (error) => {
if (finished) return;
finished = true;
cleanup();
controller.error(error);
};
const finish = () => {
if (finished) return;
if (sourceTurnBuffer !== "" || translationTurnBuffer !== "") {
completeTurn();
}
finished = true;
controller.enqueue({
type: "finish",
sourceText,
outputText: translationText,
usage
});
controller.close();
cleanup(1e3);
};
const completeTurn = () => {
if (sourceTurnBuffer !== "") {
controller.enqueue({
type: "source-transcript-final",
id: itemId(),
text: sourceTurnBuffer
});
sourceText += sourceTurnBuffer;
sourceTurnBuffer = "";
}
if (translationTurnBuffer !== "") {
controller.enqueue({
type: "output-text-final",
id: itemId(),
text: translationTurnBuffer
});
translationText += translationTurnBuffer;
translationTurnBuffer = "";
}
turnCounter++;
};
const sendAudio = async (socket) => {
audioReader = audio.getReader();
try {
while (true) {
const { done, value } = await audioReader.read();
if (done || finished) break;
socket.send(
JSON.stringify({
realtimeInput: {
audio: {
data: convertToBase645(value),
mimeType: `audio/pcm;rate=${inputAudioRate}`
}
}
})
);
await waitForWebSocketBufferDrain2(socket);
}
} finally {
audioReader.releaseLock();
audioReader = void 0;
}
if (!finished) {
socket.send(
JSON.stringify({ realtimeInput: { audioStreamEnd: true } })
);
audioEnded = true;
if (sawTurnComplete && !openTurn) {
schedulePendingFinish();
}
}
};
connection = connectToWebSocket2({
url,
headers,
webSocket,
abortSignal,
onAbort: finishWithError,
onProcessingError: finishWithError,
onOpen: (socket) => {
controller.enqueue({ type: "stream-start", warnings });
socket.send(JSON.stringify({ setup }));
void setupComplete.then(() => finished ? void 0 : sendAudio(socket)).catch(finishWithError);
},
onMessageText: async (text) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
if (finished) return;
const parsed = await safeParseJSON3({ text });
if (!parsed.success) return;
const message = parsed.value;
if (includeRawChunks) {
controller.enqueue({ type: "raw", rawValue: message });
}
if (message.setupComplete != null) {
resolveSetupComplete();
}
if (message.usageMetadata != null) {
usage = accumulateGoogleLiveUsage(usage, message.usageMetadata);
}
if (message.error != null) {
finishWithError(
new Error((_a = message.error.message) != null ? _a : "Google Live API error")
);
return;
}
const inputTranscriptionText = (_e = (_c = (_b = message.serverContent) == null ? void 0 : _b.inputTranscription) == null ? void 0 : _c.text) != null ? _e : (_d = message.inputTranscription) == null ? void 0 : _d.text;
if (inputTranscriptionText) {
onTurnActivity();
sourceTurnBuffer += inputTranscriptionText;
controller.enqueue({
type: "source-transcript-delta",
id: itemId(),
delta: inputTranscriptionText
});
}
const serverContent = message.serverContent;
if (serverContent == null) {
return;
}
for (const part of (_g = (_f = serverContent.modelTurn) == null ? void 0 : _f.parts) != null ? _g : []) {
if ((_h = part.inlineData) == null ? void 0 : _h.data) {
controller.enqueue({
type: "audio",
id: itemId(),
audio: part.inlineData.data
});
const silenceDurationMs = getPcm16SilenceDurationMs(
part.inlineData.data
);
if (audioEnded && silenceDurationMs != null) {
trailingSilenceMs += silenceDurationMs;
if (trailingSilenceMs >= finishGraceMs) {
finish();
return;
}
} else {
onTurnActivity();
}
}
}
if ((_i = serverContent.outputTranscription) == null ? void 0 : _i.text) {
onTurnActivity();
translationTurnBuffer += serverContent.outputTranscription.text;
controller.enqueue({
type: "output-text-delta",
id: itemId(),
delta: serverContent.outputTranscription.text
});
}
if (serverContent.turnComplete) {
completeTurn();
openTurn = false;
sawTurnComplete = true;
if (audioEnded) {
schedulePendingFinish();
}
}
},
onSocketError: () => {
finishWithError(new Error("Google Live translation error"));
},
onClose: ({ code, reason }) => {
if (finished) return;
if (finishTimer != null) {
finish();
return;
}
finishWithError(
new Error(
`Google Live translation WebSocket closed unexpectedly before finishing (code ${code != null ? code : "unknown"}${reason ? `, reason: ${reason}` : ""}).`
)
);
}
});
},
cancel: () => {
if (finished) return;
finished = true;
cleanup();
}
});
}
function accumulateGoogleLiveUsage(usage, usageMetadata) {
var _a, _b;
let inputAudioTokens = usage == null ? void 0 : usage.inputAudioTokens;
let outputAudioTokens = usage == null ? void 0 : usage.outputAudioTokens;
for (const detail of (_a = usageMetadata.promptTokensDetails) != null ? _a : []) {
if (detail.modality === "AUDIO" && detail.tokenCount != null) {
inputAudioTokens = (inputAudioTokens != null ? inputAudioTokens : 0) + detail.tokenCount;
}
}
for (const detail of (_b = usageMetadata.responseTokensDetails) != null ? _b : []) {
if (detail.modality === "AUDIO" && detail.tokenCount != null) {
outputAudioTokens = (outputAudioTokens != null ? outputAudioTokens : 0) + detail.tokenCount;
}
}
if (inputAudioTokens == null && outputAudioTokens == null) {
return usage;
}
return {
...usage,
...inputAudioTokens != null ? { inputAudioTokens } : {},
...outputAudioTokens != null ? { outputAudioTokens } : {}
};
}
function getPcm16SilenceDurationMs(audio) {
let bytes;
try {
bytes = convertBase64ToUint8Array2(audio);
} catch (e) {
return void 0;
}
if (bytes.byteLength < 2) {
return void 0;
}
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
const sampleCount = Math.floor(bytes.byteLength / 2);
for (let i = 0; i < sampleCount; i++) {
if (Math.abs(view.getInt16(i * 2, true)) > pcm16SilenceAmplitudeThreshold) {
return void 0;
}
}
return sampleCount / googleLiveOutputAudioRate * 1e3;
}
function buildGoogleLiveSpeechTranslationSetup({
modelId,
targetLanguage,
providerOptions
}) {
return {
model: getModelPath(modelId),
generationConfig: {
responseModalities: ["AUDIO"],
translationConfig: {
targetLanguageCode: targetLanguage,
...(providerOptions == null ? void 0 : providerOptions.echoTargetLanguage) != null ? { echoTargetLanguage: providerOptions.echoTargetLanguage } : {}
}
},
inputAudioTranscription: {},
outputAudioTranscription: {}
};
}
function validateGoogleSpeechTranslationInputAudioFormat(inputAudioFormat) {
if (inputAudioFormat.type !== "audio/pcm" || inputAudioFormat.rate != null && inputAudioFormat.rate !== 16e3) {
throw new InvalidArgumentError3({
argument: "inputAudioFormat",
message: "The Gemini Live translation API only supports 16kHz 16-bit PCM input audio."
});
}
}
// src/google-provider.ts
var DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
var googleFilesUrlPattern = /^https:\/\/generativelanguage\.googleapis\.com\/v1beta\/files\/.*$/;
var supportedExternalUrlMediaTypes = [
"text/html",
"text/css",
"text/plain",
"text/xml",
"text/csv",
"text/rtf",
"text/javascript",
"application/json",
"application/pdf",
"image/bmp",
"image/jpeg",
"image/png",
"image/webp",
"video/mp4",
"video/mpeg",
"video/quicktime",
"video/avi",
"video/x-flv",
"video/mpg",
"video/webm",
"video/wmv",
"video/3gpp"
];
var externalHttpsUrlPattern = /^https:\/\/.*$/;
function supportsExternalFileUrls(modelId) {
return /(^|\/)gemini-/.test(modelId) && !/(^|\/)gemini-2\.0/.test(modelId);
}
function createGoogle(options = {}) {
var _a, _b;
const baseURL = (_a = withoutTrailingSlash(options.baseURL)) != null ? _a : DEFAULT_BASE_URL;
const providerName = (_b = options.name) != null ? _b : "google.generative-ai";
const getHeaders = () => withUserAgentSuffix2(
{
"x-goog-api-key": loadApiKey({
apiKey: options.apiKey,
environmentVariableName: "GOOGLE_GENERATIVE_AI_API_KEY",
description: "Google Generative AI"
}),
...options.headers
},
`ai-sdk/google/${VERSION}`
);
const createChatModel = (modelId) => {
var _a2;
return new GoogleBatchLanguageModel(modelId, {
provider: providerName,
baseURL,
headers: getHeaders,
generateId: (_a2 = options.generateId) != null ? _a2 : generateId3,
supportedUrls: () => ({
"*": [
// Default Google Generative Language "files" endpoint
// e.g. https://generativelanguage.googleapis.com/v1beta/files/...
googleFilesUrlPattern,
// Configured Google Generative Language "files" endpoint
new RegExp(`^${baseURL}/files/.*$`),
// YouTube URLs (public or unlisted videos)
new RegExp(
`^https://(?:www\\.)?youtube\\.com/watch\\?v=[\\w-]+(?:&[\\w=&.-]*)?$`
),
new RegExp(`^https://youtu\\.be/[\\w-]+(?:\\?[\\w=&.-]*)?$`)
],
...supportsExternalFileUrls(modelId) ? Object.fromEntries(
supportedExternalUrlMediaTypes.map((mediaType) => [
mediaType,
[externalHttpsUrlPattern]
])
) : {}
}),
fetch: options.fetch
});
};
const createEmbeddingModel = (modelId) => new GoogleEmbeddingModel(modelId, {
provider: providerName,
baseURL,
headers: getHeaders,
fetch: options.fetch
});
const createImageModel = (modelId, settings = {}) => new GoogleImageModel(modelId, settings, {
provider: providerName,
baseURL,
headers: getHeaders,
fetch: options.fetch
});
const createFiles = () => new GoogleFiles({
provider: providerName,
baseURL,
headers: getHeaders,
fetch: options.fetch
});
const createVideoModel = (modelId) => {
var _a2;
return new GoogleVideoModel(modelId, {
provider: providerName,
baseURL,
headers: getHeaders,
fetch: options.fetch,
generateId: (_a2 = options.generateId) != null ? _a2 : generateId3
});
};
const createRealtimeModel = (modelId) => new GoogleRealtimeModel(modelId, {
provider: `${providerName}.realtime`,
baseURL,
headers: getHeaders,
fetch: options.fetch
});
const createSpeechTranslationModel = (modelId) => new GoogleSpeechTranslationModel(modelId, {
provider: `${providerName}.speech-translation`,
baseURL,
headers: getHeaders,
webSocket: options.webSocket
});
const createSpeechModel = (modelId) => new GoogleSpeechModel(modelId, {
provider: `${providerName}.speech`,
baseURL,
headers: getHeaders,
fetch: options.fetch
});
const createTranscriptionModel = (modelId) => new GoogleTranscriptionModel(modelId, {
provider: `${providerName}.transcription`,
baseURL,
headers: getHeaders,
fetch: options.fetch,
webSocket: options.webSocket
});
const experimentalRealtimeFactory = Object.assign(
(modelId) => createRealtimeModel(modelId),
{
getToken: async (tokenOptions) => {
const model = createRealtimeModel(tokenOptions.model);
const secret = await model.doCreateClientSecret({
sessionConfig: tokenOptions.sessionConfig,
expiresAfterSeconds: tokenOptions.expiresAfterSeconds
});
return {
token: secret.token,
url: secret.url,
expiresAt: secret.expiresAt
};
}
}
);
const createInteractionsModel = (modelIdOrAgent) => {
var _a2;
return new GoogleInteractionsLanguageModel(
modelIdOrAgent,
{
provider: `${providerName}.interactions`,
baseURL,
headers: getHeaders,
generateId: (_a2 = options.generateId) != null ? _a2 : generateId3,
fetch: options.fetch
}
);
};
const provider = function(modelId) {
if (new.target) {
throw new Error(
"The Google Generative AI model function cannot be called with the new keyword."
);
}
return createChatModel(modelId);
};
provider.specificationVersion = "v4";
provider.languageModel = createChatModel;
provider.chat = createChatModel;
provider.generativeAI = createChatModel;
provider.embedding = createEmbeddingModel;
provider.embeddingModel = createEmbeddingModel;
provider.textEmbedding = createEmbeddingModel;
provider.textEmbeddingModel = createEmbeddingModel;
provider.image = createImageModel;
provider.imageModel = createImageModel;
provider.video = createVideoModel;
provider.videoModel = createVideoModel;
provider.experimental_realtime = experimentalRealtimeFactory;
provider.files = createFiles;
provider.speech = createSpeechModel;
provider.speechModel = createSpeechModel;
provider.transcription = createTranscriptionModel;
provider.transcriptionModel = createTranscriptionModel;
provider.translation = createSpeechTranslationModel;
provider.speechTranslationModel = createSpeechTranslationModel;
provider.interactions = createInteractionsModel;
provider.tools = googleTools;
return provider;
}
var google = createGoogle();
export {
GoogleRealtimeModel as Experimental_GoogleRealtimeModel,
GoogleSpeechTranslationModel as Experimental_GoogleSpeechTranslationModel,
GoogleSpeechTranslationModel as Experimental_GoogleTranslationModel,
GoogleTranscriptionModel,
VERSION,
createGoogle,
createGoogle as createGoogleGenerativeAI,
google
};
//# sourceMappingURL=index.js.map