@mastra/core
Version:
1,485 lines (1,477 loc) • 259 kB
JavaScript
import { GatewayManager, defaultGateways, createOpenAIWebSocketFetch } from './chunk-J7CHY2GW.js';
import { modelSupportsTemperature, GatewayRegistry } from './chunk-26L3FC2R.js';
import { lazySchema, zodSchema, createJsonErrorResponseHandler, createProviderDefinedToolFactoryWithOutputSchema, createProviderDefinedToolFactory, lazyValidator, withoutTrailingSlash, loadOptionalSetting, generateId, parseProviderOptions, combineHeaders, resolve, postJsonToApi, createJsonResponseHandler, createEventSourceResponseHandler, withUserAgentSuffix, loadApiKey, UnsupportedFunctionalityError, convertToBase64, APICallError, validateTypes, createOpenAICompatible, createOpenAI, MASTRA_USER_AGENT, TooManyEmbeddingValuesForCallError, InvalidResponseDataError, isParsableJson, convertBase64ToUint8Array, mediaTypeToExtension, postFormDataToApi, createBinaryResponseHandler, MASTRA_GATEWAY_STREAM_TRANSPORT, InvalidPromptError } from './chunk-DRULWNQ6.js';
import { AISDKV5LanguageModel, createStreamFromGenerateResult } from './chunk-52EXWWED.js';
import { RequestContext } from './chunk-W4DLMY73.js';
import { createHash } from 'crypto';
import { z } from 'zod/v4';
// src/stream/types.ts
var ChunkFrom = /* @__PURE__ */ ((ChunkFrom2) => {
ChunkFrom2["AGENT"] = "AGENT";
ChunkFrom2["USER"] = "USER";
ChunkFrom2["SYSTEM"] = "SYSTEM";
ChunkFrom2["WORKFLOW"] = "WORKFLOW";
ChunkFrom2["NETWORK"] = "NETWORK";
return ChunkFrom2;
})(ChunkFrom || {});
var MASTRA_MODEL_STREAM_TRANSPORT = /* @__PURE__ */ Symbol.for("@mastra/core.modelStreamTransport");
function attachModelStreamTransport(target, transport) {
if (!transport) return;
Object.defineProperty(target, MASTRA_MODEL_STREAM_TRANSPORT, {
configurable: true,
value: transport
});
}
function readModelStreamTransport(target) {
return target?.[MASTRA_MODEL_STREAM_TRANSPORT];
}
// src/llm/model/aisdk/v6/model.ts
function remapToolsToV3(options) {
if (!options.tools?.length) {
return options;
}
const remappedTools = options.tools.map((tool) => {
if (tool.type === "provider-defined") {
return { ...tool, type: "provider" };
}
return tool;
});
return {
...options,
tools: remappedTools
};
}
var AISDKV6LanguageModel = class {
/**
* The language model must specify which language model interface version it implements.
*/
specificationVersion = "v3";
/**
* Name of the provider for logging purposes.
*/
provider;
/**
* Provider-specific model ID for logging purposes.
*/
modelId;
/**
* Supported URL patterns by media type for the provider.
*
* The keys are media type patterns or full media types (e.g. `*\/*` for everything, `audio/*`, `video/*`, or `application/pdf`).
* and the values are arrays of regular expressions that match the URL paths.
* The matching should be against lower-case URLs.
* Matched URLs are supported natively by the model and are not downloaded.
* @returns A map of supported URL patterns by media type (as a promise or a plain object).
*/
supportedUrls;
#model;
constructor(config) {
this.#model = config;
this.provider = this.#model.provider;
this.modelId = this.#model.modelId;
this.supportedUrls = this.#model.supportedUrls;
}
async doGenerate(options) {
const result = await this.#model.doGenerate(remapToolsToV3(options));
return {
...result,
request: result.request,
response: result.response,
stream: createStreamFromGenerateResult(result)
};
}
async doStream(options) {
return await this.#model.doStream(remapToolsToV3(options));
}
/**
* Custom serialization for tracing/observability spans.
* `#model` is already a true JS private field and not enumerable, so
* the wrapped provider SDK client can't leak. This method makes the
* safe shape explicit and avoids walking `supportedUrls` (a
* PromiseLike / regex map that isn't useful in spans).
*/
serializeForSpan() {
return {
specificationVersion: this.specificationVersion,
modelId: this.modelId,
provider: this.provider
};
}
};
// src/llm/model/aisdk/v7/model.ts
function remapToolsToV4(options) {
if (!options.tools?.length) {
return options;
}
const remappedTools = options.tools.map((tool) => {
if (tool.type === "provider-defined") {
return { ...tool, type: "provider" };
}
return tool;
});
return {
...options,
tools: remappedTools
};
}
var AISDKV7LanguageModel = class {
/**
* The language model must specify which language model interface version it implements.
*/
specificationVersion = "v4";
/**
* Name of the provider for logging purposes.
*/
provider;
/**
* Provider-specific model ID for logging purposes.
*/
modelId;
/**
* Supported URL patterns by media type for the provider.
*
* The keys are media type patterns or full media types (e.g. `*\/*` for everything, `audio/*`, `video/*`, or `application/pdf`).
* and the values are arrays of regular expressions that match the URL paths.
* The matching should be against lower-case URLs.
* Matched URLs are supported natively by the model and are not downloaded.
* @returns A map of supported URL patterns by media type (as a promise or a plain object).
*/
supportedUrls;
#model;
constructor(config) {
this.#model = config;
this.provider = this.#model.provider;
this.modelId = this.#model.modelId;
this.supportedUrls = this.#model.supportedUrls;
}
async doGenerate(options) {
const result = await this.#model.doGenerate(remapToolsToV4(options));
return {
...result,
request: result.request,
response: result.response,
stream: createStreamFromGenerateResult(result)
};
}
async doStream(options) {
return await this.#model.doStream(remapToolsToV4(options));
}
/**
* Custom serialization for tracing/observability spans.
* `#model` is already a true JS private field and not enumerable, so
* the wrapped provider SDK client can't leak. This method makes the
* safe shape explicit and avoids walking `supportedUrls` (a
* PromiseLike / regex map that isn't useful in spans).
*/
serializeForSpan() {
return {
specificationVersion: this.specificationVersion,
modelId: this.modelId,
provider: this.provider
};
}
};
// src/llm/model/router.ts
function isLanguageModelV4(model) {
return model.specificationVersion === "v4";
}
function isLanguageModelV3(model) {
return model.specificationVersion === "v3";
}
var OPENAI_WS_ALLOWLIST = /* @__PURE__ */ new Set(["openai"]);
var OPENAI_API_HOST = "api.openai.com";
function createGatewayModelCache() {
return {
modelInstances: /* @__PURE__ */ new Map(),
webSocketFetches: /* @__PURE__ */ new Map(),
gatewayStreamTransports: /* @__PURE__ */ new Map()
};
}
function getOpenAITransport(providerOptions, providerId) {
const transportOptions = providerId === "azure-openai" ? providerOptions?.azure : providerOptions?.openai;
return {
transport: transportOptions?.transport ?? "fetch",
websocket: transportOptions?.websocket
};
}
function isOpenAIBaseUrl(baseURL) {
if (!baseURL) return true;
try {
const hostname = new URL(baseURL).hostname;
return hostname === OPENAI_API_HOST;
} catch {
return false;
}
}
function stableHeaderKey(headers) {
if (!headers) return "";
const entries = Object.entries(headers);
if (entries.length === 0) return "";
return JSON.stringify(entries.sort(([a], [b]) => a.localeCompare(b)));
}
function mergeHeaders(baseHeaders, authHeaders) {
if (!baseHeaders && !authHeaders) return void 0;
return { ...baseHeaders, ...authHeaders };
}
var ModelRouterLanguageModel = class _ModelRouterLanguageModel {
specificationVersion = "v2";
defaultObjectGenerationMode = "json";
supportsStructuredOutputs = true;
supportsImageUrls = true;
/**
* Supported URL patterns by media type for the provider.
* This is a lazy promise that resolves the underlying model's supportedUrls.
* Models like Mistral define which URL patterns they support (e.g., application/pdf for https URLs).
*
* @see https://github.com/mastra-ai/mastra/issues/12152
*/
supportedUrls;
modelId;
provider;
gatewayId;
config;
gateway;
_supportedUrlsPromise = null;
instanceGatewayCache = createGatewayModelCache();
#lastStreamTransport;
#manager;
constructor(config, customGateways) {
let normalizedConfig;
if (typeof config === "string") {
normalizedConfig = { id: config };
} else if ("providerId" in config && "modelId" in config) {
normalizedConfig = {
id: `${config.providerId}/${config.modelId}`,
url: config.url,
apiKey: config.apiKey,
headers: config.headers
};
} else {
normalizedConfig = {
id: config.id,
url: config.url,
apiKey: config.apiKey,
headers: config.headers
};
}
const parsedConfig = {
...normalizedConfig,
routerId: normalizedConfig.id
};
this.#manager = new GatewayManager([...customGateways ?? [], ...defaultGateways]);
const resolved = this.#manager.resolveModelId(normalizedConfig.id);
this.gateway = resolved.gateway;
this.gatewayId = resolved.gatewayId;
this.provider = resolved.providerId || "openai-compatible";
if (resolved.providerId && resolved.modelId !== normalizedConfig.id) {
parsedConfig.id = resolved.modelId;
}
this.modelId = parsedConfig.id;
this.config = parsedConfig;
const self = this;
this.supportedUrls = {
then(onfulfilled, onrejected) {
return self._resolveSupportedUrls().then(onfulfilled, onrejected);
}
};
}
/**
* Lazily resolves the underlying model's supportedUrls.
* This is cached to avoid multiple model resolutions.
* @internal
*/
async _resolveSupportedUrls() {
if (this._supportedUrlsPromise) {
return this._supportedUrlsPromise;
}
this._supportedUrlsPromise = this._fetchSupportedUrls();
return this._supportedUrlsPromise;
}
/**
* Fetches supportedUrls from the underlying model.
* @internal
*/
async _fetchSupportedUrls() {
let apiKey;
try {
const resolved = this.#manager.resolveModelId(this.config.routerId);
const auth = await this.resolveAuth(resolved.providerId, resolved.modelId);
apiKey = auth.apiKey ?? "";
const model = await this.resolveLanguageModel({
apiKey,
auth,
headers: mergeHeaders(this.config.headers, auth.headers),
...resolved
});
const modelSupportedUrls = model.supportedUrls;
if (!modelSupportedUrls) {
return {};
}
if (typeof modelSupportedUrls.then === "function") {
const resolved2 = await modelSupportedUrls;
return resolved2 ?? {};
}
return modelSupportedUrls ?? {};
} catch {
return {};
}
}
/** @internal */
_getStreamTransport() {
return this.#lastStreamTransport;
}
/**
* Custom serialization for tracing/observability spans.
* Excludes `config` (holds apiKey, headers, url) and `gateway`
* (may hold proxy credentials or cached tokens) so they cannot leak
* into telemetry backends.
*/
serializeForSpan() {
return {
specificationVersion: this.specificationVersion,
modelId: this.modelId,
provider: this.provider,
gatewayId: this.gatewayId
};
}
getGatewayCache() {
let cache = _ModelRouterLanguageModel.gatewayCaches.get(this.gateway);
if (!cache) {
cache = createGatewayModelCache();
_ModelRouterLanguageModel.gatewayCaches.set(this.gateway, cache);
}
return cache;
}
shouldUseInstanceGatewayCache(auth) {
return this.config.apiKey !== void 0 || auth.source === "explicit" || auth.source === "gateway";
}
setStreamTransportHandle({
resolvedTransport,
transport,
responsesWebSocket
}) {
if (resolvedTransport !== "websocket") {
this.#lastStreamTransport = void 0;
return;
}
if (!transport) {
this.#lastStreamTransport = void 0;
return;
}
this.#lastStreamTransport = {
type: transport.type,
close: transport.close,
closeOnFinish: responsesWebSocket?.closeOnFinish ?? true
};
}
async resolveAuth(_providerId, _modelId) {
if (this.config.url) {
return { apiKey: this.config.apiKey ?? "", headers: this.config.headers, source: "explicit" };
}
const explicitHeaders = this.config.headers;
if (this.config.apiKey) {
return { apiKey: this.config.apiKey, headers: explicitHeaders, source: "explicit" };
}
const gatewayAuth = await this.#manager.resolveAuth(this.config.routerId);
return explicitHeaders ? { ...gatewayAuth, headers: { ...explicitHeaders, ...gatewayAuth.headers } } : gatewayAuth;
}
setStreamTransportFromCache({
cache,
resolvedTransport,
key,
responsesWebSocket
}) {
const wsFetch = cache.webSocketFetches.get(key);
const gatewayTransport = cache.gatewayStreamTransports.get(key);
const transport = wsFetch ? { type: "openai-websocket", close: () => wsFetch.close() } : gatewayTransport;
this.setStreamTransportHandle({ resolvedTransport, transport, responsesWebSocket });
}
stripUnsupportedSamplingParams(options) {
const supports = modelSupportsTemperature(this.config.routerId);
if (supports !== false) return options;
const { temperature, topP, topK, ...rest } = options;
if (temperature === void 0 && topP === void 0 && topK === void 0) return options;
return rest;
}
async doGenerate(options) {
const resolved = this.#manager.resolveModelId(this.config.routerId);
let auth;
try {
auth = await this.resolveAuth(resolved.providerId, resolved.modelId);
} catch (error) {
return {
stream: new ReadableStream({
start(controller) {
controller.enqueue({
type: "error",
error
});
controller.close();
}
})
};
}
const sanitizedOptions = this.stripUnsupportedSamplingParams(options);
const model = await this.resolveLanguageModel({
apiKey: auth.apiKey ?? "",
auth,
headers: mergeHeaders(this.config.headers, auth.headers),
...resolved
});
if (isLanguageModelV4(model)) {
const aiSDKV7Model = new AISDKV7LanguageModel(model);
return aiSDKV7Model.doGenerate(sanitizedOptions);
}
if (isLanguageModelV3(model)) {
const aiSDKV6Model = new AISDKV6LanguageModel(model);
return aiSDKV6Model.doGenerate(sanitizedOptions);
}
const aiSDKV5Model = new AISDKV5LanguageModel(model);
return aiSDKV5Model.doGenerate(sanitizedOptions);
}
async doStream(options) {
const resolved = this.#manager.resolveModelId(this.config.routerId);
let auth;
try {
auth = await this.resolveAuth(resolved.providerId, resolved.modelId);
} catch (error) {
return {
stream: new ReadableStream({
start(controller) {
controller.enqueue({
type: "error",
error
});
controller.close();
}
})
};
}
const sanitizedOptions = this.stripUnsupportedSamplingParams(options);
const { transport, websocket } = getOpenAITransport(
sanitizedOptions.providerOptions,
resolved.providerId
);
const requestedTransport = transport === "auto" ? "websocket" : transport;
const allowWebSocket = requestedTransport === "websocket" && !this.config.url && (this.gatewayId === "models.dev" && OPENAI_WS_ALLOWLIST.has(this.provider) || this.gatewayId === "azure-openai");
const resolvedTransport = allowWebSocket ? "websocket" : "fetch";
const model = await this.resolveLanguageModel({
apiKey: auth.apiKey ?? "",
auth,
headers: mergeHeaders(this.config.headers, auth.headers),
transport: resolvedTransport,
responsesWebSocket: websocket,
...resolved
});
const streamTransport = this.#lastStreamTransport;
if (isLanguageModelV4(model)) {
const aiSDKV7Model = new AISDKV7LanguageModel(model);
const streamResult2 = await aiSDKV7Model.doStream(sanitizedOptions);
attachModelStreamTransport(streamResult2, streamTransport);
return streamResult2;
}
if (isLanguageModelV3(model)) {
const aiSDKV6Model = new AISDKV6LanguageModel(model);
const streamResult2 = await aiSDKV6Model.doStream(sanitizedOptions);
attachModelStreamTransport(streamResult2, streamTransport);
return streamResult2;
}
const aiSDKV5Model = new AISDKV5LanguageModel(model);
const streamResult = await aiSDKV5Model.doStream(sanitizedOptions);
attachModelStreamTransport(streamResult, streamTransport);
return streamResult;
}
async resolveLanguageModel({
modelId,
providerId,
apiKey,
auth,
headers,
transport,
responsesWebSocket
}) {
const resolvedTransport = transport ?? "fetch";
const websocketKey = resolvedTransport === "websocket" ? `${responsesWebSocket?.url ?? ""}:${stableHeaderKey(responsesWebSocket?.headers)}` : "";
const useInstanceCache = this.shouldUseInstanceGatewayCache(auth);
const cache = useInstanceCache ? this.instanceGatewayCache : this.getGatewayCache();
const authScopeKey = useInstanceCache ? `${auth.source ?? ""}` : "";
const key = createHash("sha256").update(
JSON.stringify([
this.gatewayId,
modelId,
providerId,
this.config.url || "",
stableHeaderKey(headers),
resolvedTransport,
websocketKey,
authScopeKey
])
).digest("hex");
if (cache.modelInstances.has(key)) {
this.setStreamTransportFromCache({ cache, resolvedTransport, key, responsesWebSocket });
return cache.modelInstances.get(key);
}
if (this.config.url) {
const modelInstance2 = createOpenAICompatible({
name: providerId,
apiKey,
baseURL: this.config.url,
headers,
supportsStructuredOutputs: true
}).chatModel(modelId);
cache.modelInstances.set(key, modelInstance2);
this.setStreamTransportHandle({ resolvedTransport, responsesWebSocket });
return modelInstance2;
}
if (resolvedTransport === "websocket" && providerId === "openai" && this.gatewayId === "models.dev") {
const baseURL = await this.gateway.buildUrl(this.config.routerId, process.env);
if (isOpenAIBaseUrl(baseURL)) {
const { modelInstance: modelInstance2, wsFetch } = this.resolveOpenAIWebSocketModel({
modelId,
apiKey,
baseURL,
headers,
responsesWebSocket
});
cache.modelInstances.set(key, modelInstance2);
cache.webSocketFetches.set(key, wsFetch);
this.setStreamTransportFromCache({ cache, resolvedTransport, key, responsesWebSocket });
return modelInstance2;
}
}
const modelInstance = await this.gateway.resolveLanguageModel({
modelId,
providerId,
apiKey,
headers,
transport: resolvedTransport,
responsesWebSocket
});
const gatewayTransport = readGatewayStreamTransport(modelInstance);
cache.modelInstances.set(key, modelInstance);
if (gatewayTransport) {
cache.gatewayStreamTransports.set(key, gatewayTransport);
}
this.setStreamTransportHandle({ resolvedTransport, transport: gatewayTransport, responsesWebSocket });
return modelInstance;
}
resolveOpenAIWebSocketModel({
modelId,
apiKey,
baseURL,
headers,
responsesWebSocket
}) {
const wsFetch = createOpenAIWebSocketFetch({
url: responsesWebSocket?.url,
headers: responsesWebSocket?.headers
});
const modelInstance = createOpenAI({
apiKey,
baseURL,
headers,
fetch: wsFetch
}).responses(modelId);
return { modelInstance, wsFetch };
}
static _clearCachesForTests() {
_ModelRouterLanguageModel.gatewayCaches = /* @__PURE__ */ new WeakMap();
}
static gatewayCaches = /* @__PURE__ */ new WeakMap();
};
function readGatewayStreamTransport(model) {
return model[MASTRA_GATEWAY_STREAM_TRANSPORT];
}
// src/llm/model/aisdk/v4/model.ts
var AISDKV4LegacyLanguageModel = class {
specificationVersion = "v1";
provider;
modelId;
defaultObjectGenerationMode;
supportsImageUrls;
supportsStructuredOutputs;
#model;
constructor(config) {
this.#model = config;
this.provider = config.provider;
this.modelId = config.modelId;
this.defaultObjectGenerationMode = config.defaultObjectGenerationMode;
this.supportsImageUrls = config.supportsImageUrls;
this.supportsStructuredOutputs = config.supportsStructuredOutputs;
}
supportsUrl(url) {
return this.#model.supportsUrl?.(url) ?? false;
}
doGenerate(options) {
return this.#model.doGenerate(options);
}
doStream(options) {
return this.#model.doStream(options);
}
/**
* Custom serialization for tracing/observability spans.
* `#model` is already a true JS private field and not enumerable, so
* the wrapped provider SDK client can't leak. This method makes the
* safe shape explicit.
*/
serializeForSpan() {
return {
specificationVersion: this.specificationVersion,
modelId: this.modelId,
provider: this.provider
};
}
};
// src/llm/model/resolve-model.ts
function isOpenAICompatibleObjectConfig(modelConfig) {
if (typeof modelConfig === "object" && "specificationVersion" in modelConfig) return false;
if (typeof modelConfig === "object" && !("model" in modelConfig)) {
if ("id" in modelConfig) return true;
if ("providerId" in modelConfig && "modelId" in modelConfig) return true;
}
return false;
}
async function resolveModelConfig(modelConfig, requestContext = new RequestContext(), mastra) {
if (typeof modelConfig === "function") {
modelConfig = await modelConfig({ requestContext, mastra });
}
if (modelConfig instanceof ModelRouterLanguageModel || modelConfig instanceof AISDKV4LegacyLanguageModel || modelConfig instanceof AISDKV5LanguageModel || modelConfig instanceof AISDKV6LanguageModel || modelConfig instanceof AISDKV7LanguageModel) {
return modelConfig;
}
if (typeof modelConfig === "object" && "specificationVersion" in modelConfig) {
if (modelConfig.specificationVersion === "v2") {
return new AISDKV5LanguageModel(modelConfig);
}
if (modelConfig.specificationVersion === "v3") {
return new AISDKV6LanguageModel(modelConfig);
}
if (modelConfig.specificationVersion === "v4") {
return new AISDKV7LanguageModel(modelConfig);
}
if (modelConfig.specificationVersion === "v1") {
return new AISDKV4LegacyLanguageModel(modelConfig);
}
if (typeof modelConfig.doStream === "function" && typeof modelConfig.doGenerate === "function") {
return new AISDKV5LanguageModel(modelConfig);
}
return modelConfig;
}
const gatewayRecord = mastra?.listGateways();
const customGateways = gatewayRecord ? Object.values(gatewayRecord) : void 0;
if (typeof modelConfig === "string" || isOpenAICompatibleObjectConfig(modelConfig)) {
return new ModelRouterLanguageModel(modelConfig, customGateways);
}
throw new Error("Invalid model configuration provided");
}
// src/llm/model/model-auth-resolver.ts
function mergeAuthHeaders(auth) {
if (!auth?.bearerToken) return auth;
return {
...auth,
headers: {
...auth.headers,
Authorization: `Bearer ${auth.bearerToken}`
}
};
}
function hasExplicitAuth(explicit) {
return Boolean(
explicit?.apiKey || explicit?.bearerToken || explicit?.headers && Object.keys(explicit.headers).length > 0
);
}
async function resolveModelAuth({
gateway,
request,
explicit
}) {
if (hasExplicitAuth(explicit)) {
return mergeAuthHeaders({ ...explicit, source: "explicit" }) ?? { source: "explicit" };
}
const gatewayAuth = mergeAuthHeaders(await gateway.resolveAuth?.(request));
if (gatewayAuth?.apiKey || gatewayAuth?.headers || gatewayAuth?.bearerToken) {
return { ...gatewayAuth, source: gatewayAuth.source ?? "gateway" };
}
return {
apiKey: await gateway.getApiKey(request.routerId),
source: "legacy"
};
}
var VERSION = "2.0.72" ;
var googleErrorDataSchema = lazySchema(
() => zodSchema(
z.object({
error: z.object({
code: z.number().nullable(),
message: z.string(),
status: z.string()
})
})
)
);
var googleFailedResponseHandler = createJsonErrorResponseHandler({
errorSchema: googleErrorDataSchema,
errorToMessage: (data) => data.error.message
});
var googleEmbeddingContentPartSchema = z.union([
z.object({ text: z.string() }),
z.object({
inlineData: z.object({
mimeType: z.string(),
data: z.string()
})
})
]);
var googleGenerativeAIEmbeddingProviderOptions = lazySchema(
() => zodSchema(
z.object({
/**
* Optional. Optional reduced dimension for the output embedding.
* If set, excessive values in the output embedding are truncated from the end.
*/
outputDimensionality: z.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: z.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: z.array(z.array(googleEmbeddingContentPartSchema).min(1).nullable()).optional()
})
)
);
var GoogleGenerativeAIEmbeddingModel = class {
constructor(modelId, config) {
this.specificationVersion = "v2";
this.maxEmbeddingsPerCall = 2048;
this.supportsParallelCalls = true;
this.modelId = modelId;
this.config = config;
}
get provider() {
return this.config.provider;
}
async doEmbed({
values,
headers,
abortSignal,
providerOptions
}) {
const googleOptions = await parseProviderOptions({
provider: "google",
providerOptions,
schema: googleGenerativeAIEmbeddingProviderOptions
});
if (values.length > this.maxEmbeddingsPerCall) {
throw new TooManyEmbeddingValuesForCallError({
provider: this.provider,
modelId: this.modelId,
maxEmbeddingsPerCall: this.maxEmbeddingsPerCall,
values
});
}
const mergedHeaders = combineHeaders(
await resolve(this.config.headers),
headers
);
const multimodalContent = googleOptions == null ? void 0 : googleOptions.content;
if (multimodalContent != null && multimodalContent.length !== values.length) {
throw new Error(
`The number of multimodal content entries (${multimodalContent.length}) must match the number of values (${values.length}).`
);
}
if (values.length === 1) {
const valueParts = multimodalContent == null ? void 0 : multimodalContent[0];
const textPart = values[0] ? [{ text: values[0] }] : [];
const parts = valueParts != null ? [...textPart, ...valueParts] : [{ text: values[0] }];
const {
responseHeaders: responseHeaders2,
value: response2,
rawValue: rawValue2
} = await postJsonToApi({
url: `${this.config.baseURL}/models/${this.modelId}:embedContent`,
headers: mergedHeaders,
body: {
model: `models/${this.modelId}`,
content: {
parts
},
outputDimensionality: googleOptions == null ? void 0 : googleOptions.outputDimensionality,
taskType: googleOptions == null ? void 0 : googleOptions.taskType
},
failedResponseHandler: googleFailedResponseHandler,
successfulResponseHandler: createJsonResponseHandler(
googleGenerativeAISingleEmbeddingResponseSchema
),
abortSignal,
fetch: this.config.fetch
});
return {
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 {
embeddings: response.embeddings.map((item) => item.values),
usage: void 0,
response: { headers: responseHeaders, body: rawValue }
};
}
};
var googleGenerativeAITextEmbeddingResponseSchema = lazySchema(
() => zodSchema(
z.object({
embeddings: z.array(z.object({ values: z.array(z.number()) }))
})
)
);
var googleGenerativeAISingleEmbeddingResponseSchema = lazySchema(
() => zodSchema(
z.object({
embedding: z.object({ values: z.array(z.number()) })
})
)
);
function convertJSONSchemaToOpenAPISchema(jsonSchema, isRoot = true) {
if (jsonSchema == null) {
return void 0;
}
if (isEmptyObjectSchema(jsonSchema)) {
if (isRoot) {
return void 0;
}
if (typeof jsonSchema === "object" && jsonSchema.description) {
return { type: "object", description: jsonSchema.description };
}
return { type: "object" };
}
if (typeof jsonSchema === "boolean") {
return { type: "boolean", properties: {} };
}
const {
type,
description,
required,
properties,
items,
allOf,
anyOf,
oneOf,
format,
const: constValue,
minLength,
enum: enumValues
} = jsonSchema;
const result = {};
if (description) result.description = description;
if (required) result.required = required;
if (format) result.format = format;
if (constValue !== void 0) {
result.enum = [constValue];
}
if (type) {
if (Array.isArray(type)) {
const hasNull = type.includes("null");
const nonNullTypes = type.filter((t) => t !== "null");
if (nonNullTypes.length === 0) {
result.type = "null";
} else {
result.anyOf = nonNullTypes.map((t) => ({ type: t }));
if (hasNull) {
result.nullable = true;
}
}
} else {
result.type = type;
}
}
if (enumValues !== void 0) {
result.enum = enumValues;
}
if (properties != null) {
result.properties = Object.entries(properties).reduce(
(acc, [key, value]) => {
acc[key] = convertJSONSchemaToOpenAPISchema(value, false);
return acc;
},
{}
);
}
if (items) {
result.items = Array.isArray(items) ? items.map((item) => convertJSONSchemaToOpenAPISchema(item, false)) : convertJSONSchemaToOpenAPISchema(items, false);
}
if (allOf) {
result.allOf = allOf.map(
(item) => convertJSONSchemaToOpenAPISchema(item, false)
);
}
if (anyOf) {
if (anyOf.some(
(schema) => typeof schema === "object" && (schema == null ? void 0 : schema.type) === "null"
)) {
const nonNullSchemas = anyOf.filter(
(schema) => !(typeof schema === "object" && (schema == null ? void 0 : schema.type) === "null")
);
if (nonNullSchemas.length === 1) {
const converted = convertJSONSchemaToOpenAPISchema(
nonNullSchemas[0],
false
);
if (typeof converted === "object") {
result.nullable = true;
Object.assign(result, converted);
}
} else {
result.anyOf = nonNullSchemas.map(
(item) => convertJSONSchemaToOpenAPISchema(item, false)
);
result.nullable = true;
}
} else {
result.anyOf = anyOf.map(
(item) => convertJSONSchemaToOpenAPISchema(item, false)
);
}
}
if (oneOf) {
result.oneOf = oneOf.map(
(item) => convertJSONSchemaToOpenAPISchema(item, false)
);
}
if (minLength !== void 0) {
result.minLength = minLength;
}
return result;
}
function isEmptyObjectSchema(jsonSchema) {
return jsonSchema != null && typeof jsonSchema === "object" && jsonSchema.type === "object" && (jsonSchema.properties == null || Object.keys(jsonSchema.properties).length === 0) && !jsonSchema.additionalProperties;
}
function convertToGoogleGenerativeAIMessages(prompt, options) {
var _a, _b;
const systemInstructionParts = [];
const contents = [];
let systemMessagesAllowed = true;
const isGemmaModel = (_a = options == null ? void 0 : options.isGemmaModel) != null ? _a : false;
const supportsFunctionResponseParts = (_b = options == null ? void 0 : options.supportsFunctionResponseParts) != null ? _b : true;
for (const { role, content } of prompt) {
switch (role) {
case "system": {
if (!systemMessagesAllowed) {
throw new UnsupportedFunctionalityError({
functionality: "system messages are only supported at the beginning of the conversation"
});
}
systemInstructionParts.push({ text: content });
break;
}
case "user": {
systemMessagesAllowed = false;
const parts = [];
for (const part of content) {
switch (part.type) {
case "text": {
parts.push({ text: part.text });
break;
}
case "file": {
const mediaType = part.mediaType === "image/*" ? "image/jpeg" : part.mediaType;
parts.push(
part.data instanceof URL ? {
fileData: {
mimeType: mediaType,
fileUri: part.data.toString()
}
} : {
inlineData: {
mimeType: mediaType,
data: convertToBase64(part.data)
}
}
);
break;
}
}
}
contents.push({ role: "user", parts });
break;
}
case "assistant": {
systemMessagesAllowed = false;
contents.push({
role: "model",
parts: content.map((part) => {
var _a2, _b2, _c;
const thoughtSignature = ((_b2 = (_a2 = part.providerOptions) == null ? void 0 : _a2.google) == null ? void 0 : _b2.thoughtSignature) != null ? String((_c = part.providerOptions.google) == null ? void 0 : _c.thoughtSignature) : void 0;
switch (part.type) {
case "text": {
return part.text.length === 0 ? void 0 : {
text: part.text,
thoughtSignature
};
}
case "reasoning": {
return part.text.length === 0 ? void 0 : {
text: part.text,
thought: true,
thoughtSignature
};
}
case "file": {
if (part.mediaType !== "image/png") {
throw new UnsupportedFunctionalityError({
functionality: "Only PNG images are supported in assistant messages"
});
}
if (part.data instanceof URL) {
throw new UnsupportedFunctionalityError({
functionality: "File data URLs in assistant messages are not supported"
});
}
return {
inlineData: {
mimeType: part.mediaType,
data: convertToBase64(part.data)
}
};
}
case "tool-call": {
return {
functionCall: {
name: part.toolName,
args: part.input
},
thoughtSignature
};
}
}
}).filter((part) => part !== void 0)
});
break;
}
case "tool": {
systemMessagesAllowed = false;
const parts = [];
for (const part of content) {
const output = part.output;
if (output.type === "content") {
if (supportsFunctionResponseParts) {
appendToolResultParts({ parts, part, output });
} else {
appendLegacyToolResultParts({ parts, part, output });
}
} else {
parts.push({
functionResponse: {
name: part.toolName,
response: {
name: part.toolName,
content: output.value
}
}
});
}
}
contents.push({
role: "user",
parts
});
break;
}
}
}
if (isGemmaModel && systemInstructionParts.length > 0 && contents.length > 0 && contents[0].role === "user") {
const systemText = systemInstructionParts.map((part) => part.text).join("\n\n");
contents[0].parts.unshift({ text: systemText + "\n\n" });
}
return {
systemInstruction: systemInstructionParts.length > 0 && !isGemmaModel ? { parts: systemInstructionParts } : void 0,
contents
};
}
function appendToolResultParts({
parts,
part,
output
}) {
const responseTextParts = [];
const functionResponseParts = [];
for (const contentPart of output.value) {
switch (contentPart.type) {
case "text":
responseTextParts.push(contentPart.text);
break;
case "media":
functionResponseParts.push({
inlineData: {
mimeType: contentPart.mediaType,
data: contentPart.data
}
});
break;
}
}
const responseText = responseTextParts.length > 0 ? responseTextParts.join("\n") : "Tool executed successfully.";
parts.push({
functionResponse: {
name: part.toolName,
response: {
name: part.toolName,
content: responseText
},
...functionResponseParts.length > 0 ? { parts: functionResponseParts } : {}
}
});
}
function appendLegacyToolResultParts({
parts,
part,
output
}) {
for (const contentPart of output.value) {
switch (contentPart.type) {
case "text":
parts.push({
functionResponse: {
name: part.toolName,
response: {
name: part.toolName,
content: contentPart.text
}
}
});
break;
case "media":
parts.push(
{
inlineData: {
mimeType: contentPart.mediaType,
data: contentPart.data
}
},
{
text: "Tool executed successfully and returned this image as a response"
}
);
break;
default:
parts.push({ text: JSON.stringify(contentPart) });
break;
}
}
}
function getModelPath(modelId) {
return modelId.includes("/") ? modelId : `models/${modelId}`;
}
var googleGenerativeAIProviderOptions = lazySchema(
() => zodSchema(
z.object({
responseModalities: z.array(z.enum(["TEXT", "IMAGE"])).optional(),
thinkingConfig: z.object({
thinkingBudget: z.number().optional(),
includeThoughts: z.boolean().optional(),
// https://ai.google.dev/gemini-api/docs/gemini-3?thinking=high#thinking_level
thinkingLevel: z.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: z.string().optional(),
/**
* Optional. Enable structured output. Default is true.
*
* This is useful when the JSON Schema contains elements that are
* not supported by the OpenAPI schema version that
* Google Generative AI uses. You can use this to disable
* structured outputs if you need to.
*/
structuredOutputs: z.boolean().optional(),
/**
* Optional. A list of unique safety settings for blocking unsafe content.
*/
safetySettings: z.array(
z.object({
category: z.enum([
"HARM_CATEGORY_UNSPECIFIED",
"HARM_CATEGORY_HATE_SPEECH",
"HARM_CATEGORY_DANGEROUS_CONTENT",
"HARM_CATEGORY_HARASSMENT",
"HARM_CATEGORY_SEXUALLY_EXPLICIT",
"HARM_CATEGORY_CIVIC_INTEGRITY"
]),
threshold: z.enum([
"HARM_BLOCK_THRESHOLD_UNSPECIFIED",
"BLOCK_LOW_AND_ABOVE",
"BLOCK_MEDIUM_AND_ABOVE",
"BLOCK_ONLY_HIGH",
"BLOCK_NONE",
"OFF"
])
})
).optional(),
threshold: z.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: z.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: z.record(z.string(), z.string()).optional(),
/**
* Optional. If specified, the media resolution specified will be used.
*
* https://ai.google.dev/api/generate-content#MediaResolution
*/
mediaResolution: z.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: z.object({
aspectRatio: z.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: z.enum(["1K", "2K", "4K", "512"]).optional()
}).optional(),
/**
* Optional. Configuration for grounding retrieval.
* Used to provide location context for Google Maps and Google Search grounding.
*
* https://cloud.google.com/vertex-ai/generative-ai/docs/grounding/grounding-with-google-maps
*/
retrievalConfig: z.object({
latLng: z.object({
latitude: z.number(),
longitude: z.number()
}).optional()
}).optional(),
/**
* Optional. The service tier to use for the request.
*/
serviceTier: z.enum(["standard", "flex", "priority"]).optional()
})
)
);
var VertexServiceTierMap = {
standard: "SERVICE_TIER_STANDARD",
flex: "SERVICE_TIER_FLEX",
priority: "SERVICE_TIER_PRIORITY"
};
function prepareTools({
tools,
toolChoice,
modelId
}) {
var _a;
tools = (tools == null ? void 0 : tools.length) ? tools : void 0;
const toolWarnings = [];
const isLatest = [
"gemini-flash-latest",
"gemini-flash-lite-latest",
"gemini-pro-latest"
].some((id) => id === modelId);
const isGemini2orNewer = modelId.includes("gemini-2") || modelId.includes("gemini-3") || modelId.includes("nano-banana") || isLatest;
const supportsFileSearch = modelId.includes("gemini-2.5") || modelId.includes("gemini-3");
if (tools == null) {
return { tools: void 0, toolConfig: void 0, toolWarnings };
}
const hasFunctionTools = tools.some((tool) => tool.type === "function");
const hasProviderDefinedTools = tools.some(
(tool) => tool.type === "provider-defined"
);
if (hasFunctionTools && hasProviderDefinedTools) {
const functionTools = tools.filter((tool) => tool.type === "function");
toolWarnings.push({
type: "unsupported-tool",
tool: tools.find((tool) => tool.type === "function"),
details: `Cannot mix function tools with provider-defined tools in the same request. Falling back to provider-defined tools only. The following function tools will be ignored: ${functionTools.map((t) => t.name).join(", ")}. Please use either function tools or provider-defined tools, but not both.`
});
}
if (hasProviderDefinedTools) {
const googleTools2 = [];
const providerDefinedTools = tools.filter(
(tool) => tool.type === "provider-defined"
);
providerDefinedTools.forEach((tool) => {
switch (tool.id) {
case "google.google_search":
if (isGemini2orNewer) {
googleTools2.push({ googleSearch: { ...tool.args } });
} else {
toolWarnings.push({
type: "unsupported-tool",
tool,
details: "Google Search requires Gemini 2.0 or newer."
});
}
break;
case "google.enterprise_web_search":
if (isGemini2orNewer) {
googleTools2.push({ enterpriseWebSearch: {} });
} else {
toolWarnings.push({
type: "unsupported-tool",
tool,
details: "Enterprise Web Search requires Gemini 2.0 or newer."
});
}
break;
case "google.url_context":
if (isGemini2orNewer) {
googleTools2.push({ urlContext: {} });
} else {
toolWarnings.push({
type: "unsupported-tool",
tool,
details: "The URL context tool is not supported with other Gemini models than Gemini 2."
});
}
break;
case "google.code_execution":
if (isGemini2orNewer) {
googleTools2.push({ codeExecution: {} });
} else {
toolWarnings.push({
type: "unsupported-tool",
tool,
details: "The code execution tools 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-tool",
tool,
details: "The file search tool is only supported with Gemini 2.5 models."
});
}
break;
case "google.vertex_rag_store":
if (isGemini2orNewer) {
googleTools2.push({
retrieval: {
vertex_rag_store: {
rag_resources: {
rag_corpus: tool.args.ragCorpus
},
similarity_top_k: tool.args.topK
}
}
});
} else {
toolWarnings.push({
type: "unsupported-tool",
tool,
details: "The RAG store tool is not supported with other Gemini models than Gemini 2."
});
}
break;
case "google.google_maps":
if (isGemini2orNewer) {
googleTools2.push({ googleMaps: {} });
} else {
toolWarnings.push({
type: "unsupported-tool",
tool,
details: "The Google Maps grounding tool is not supported with Gemini models other than Gemini 2 or newer."
});
}
break;
default:
toolWarnings.push({ type: "unsupported-tool", tool });
break;
}
});
return {
tools: googleTools2.length > 0 ? googleTools2 : void 0,
toolConfig: void 0,
toolWarnings