EventBuzz_Console/node_modules/@firebase/ai/dist/index.node.cjs.js

2658 lines
100 KiB
JavaScript

'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var app = require('@firebase/app');
var component = require('@firebase/component');
var util = require('@firebase/util');
var logger$1 = require('@firebase/logger');
var tslib = require('tslib');
var name = "@firebase/ai";
var version = "1.4.1";
/**
* @license
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const AI_TYPE = 'AI';
const DEFAULT_LOCATION = 'us-central1';
const DEFAULT_BASE_URL = 'https://firebasevertexai.googleapis.com';
const DEFAULT_API_VERSION = 'v1beta';
const PACKAGE_VERSION = version;
const LANGUAGE_TAG = 'gl-js';
const DEFAULT_FETCH_TIMEOUT_MS = 180 * 1000;
/**
* @license
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Possible roles.
* @public
*/
const POSSIBLE_ROLES = ['user', 'model', 'function', 'system'];
/**
* Harm categories that would cause prompts or candidates to be blocked.
* @public
*/
exports.HarmCategory = void 0;
(function (HarmCategory) {
HarmCategory["HARM_CATEGORY_HATE_SPEECH"] = "HARM_CATEGORY_HATE_SPEECH";
HarmCategory["HARM_CATEGORY_SEXUALLY_EXPLICIT"] = "HARM_CATEGORY_SEXUALLY_EXPLICIT";
HarmCategory["HARM_CATEGORY_HARASSMENT"] = "HARM_CATEGORY_HARASSMENT";
HarmCategory["HARM_CATEGORY_DANGEROUS_CONTENT"] = "HARM_CATEGORY_DANGEROUS_CONTENT";
})(exports.HarmCategory || (exports.HarmCategory = {}));
/**
* Threshold above which a prompt or candidate will be blocked.
* @public
*/
exports.HarmBlockThreshold = void 0;
(function (HarmBlockThreshold) {
/**
* Content with `NEGLIGIBLE` will be allowed.
*/
HarmBlockThreshold["BLOCK_LOW_AND_ABOVE"] = "BLOCK_LOW_AND_ABOVE";
/**
* Content with `NEGLIGIBLE` and `LOW` will be allowed.
*/
HarmBlockThreshold["BLOCK_MEDIUM_AND_ABOVE"] = "BLOCK_MEDIUM_AND_ABOVE";
/**
* Content with `NEGLIGIBLE`, `LOW`, and `MEDIUM` will be allowed.
*/
HarmBlockThreshold["BLOCK_ONLY_HIGH"] = "BLOCK_ONLY_HIGH";
/**
* All content will be allowed.
*/
HarmBlockThreshold["BLOCK_NONE"] = "BLOCK_NONE";
/**
* All content will be allowed. This is the same as `BLOCK_NONE`, but the metadata corresponding
* to the {@link HarmCategory} will not be present in the response.
*/
HarmBlockThreshold["OFF"] = "OFF";
})(exports.HarmBlockThreshold || (exports.HarmBlockThreshold = {}));
/**
* This property is not supported in the Gemini Developer API ({@link GoogleAIBackend}).
*
* @public
*/
exports.HarmBlockMethod = void 0;
(function (HarmBlockMethod) {
/**
* The harm block method uses both probability and severity scores.
*/
HarmBlockMethod["SEVERITY"] = "SEVERITY";
/**
* The harm block method uses the probability score.
*/
HarmBlockMethod["PROBABILITY"] = "PROBABILITY";
})(exports.HarmBlockMethod || (exports.HarmBlockMethod = {}));
/**
* Probability that a prompt or candidate matches a harm category.
* @public
*/
exports.HarmProbability = void 0;
(function (HarmProbability) {
/**
* Content has a negligible chance of being unsafe.
*/
HarmProbability["NEGLIGIBLE"] = "NEGLIGIBLE";
/**
* Content has a low chance of being unsafe.
*/
HarmProbability["LOW"] = "LOW";
/**
* Content has a medium chance of being unsafe.
*/
HarmProbability["MEDIUM"] = "MEDIUM";
/**
* Content has a high chance of being unsafe.
*/
HarmProbability["HIGH"] = "HIGH";
})(exports.HarmProbability || (exports.HarmProbability = {}));
/**
* Harm severity levels.
* @public
*/
exports.HarmSeverity = void 0;
(function (HarmSeverity) {
/**
* Negligible level of harm severity.
*/
HarmSeverity["HARM_SEVERITY_NEGLIGIBLE"] = "HARM_SEVERITY_NEGLIGIBLE";
/**
* Low level of harm severity.
*/
HarmSeverity["HARM_SEVERITY_LOW"] = "HARM_SEVERITY_LOW";
/**
* Medium level of harm severity.
*/
HarmSeverity["HARM_SEVERITY_MEDIUM"] = "HARM_SEVERITY_MEDIUM";
/**
* High level of harm severity.
*/
HarmSeverity["HARM_SEVERITY_HIGH"] = "HARM_SEVERITY_HIGH";
/**
* Harm severity is not supported.
*
* @remarks
* The GoogleAI backend does not support `HarmSeverity`, so this value is used as a fallback.
*/
HarmSeverity["HARM_SEVERITY_UNSUPPORTED"] = "HARM_SEVERITY_UNSUPPORTED";
})(exports.HarmSeverity || (exports.HarmSeverity = {}));
/**
* Reason that a prompt was blocked.
* @public
*/
exports.BlockReason = void 0;
(function (BlockReason) {
/**
* Content was blocked by safety settings.
*/
BlockReason["SAFETY"] = "SAFETY";
/**
* Content was blocked, but the reason is uncategorized.
*/
BlockReason["OTHER"] = "OTHER";
/**
* Content was blocked because it contained terms from the terminology blocklist.
*/
BlockReason["BLOCKLIST"] = "BLOCKLIST";
/**
* Content was blocked due to prohibited content.
*/
BlockReason["PROHIBITED_CONTENT"] = "PROHIBITED_CONTENT";
})(exports.BlockReason || (exports.BlockReason = {}));
/**
* Reason that a candidate finished.
* @public
*/
exports.FinishReason = void 0;
(function (FinishReason) {
/**
* Natural stop point of the model or provided stop sequence.
*/
FinishReason["STOP"] = "STOP";
/**
* The maximum number of tokens as specified in the request was reached.
*/
FinishReason["MAX_TOKENS"] = "MAX_TOKENS";
/**
* The candidate content was flagged for safety reasons.
*/
FinishReason["SAFETY"] = "SAFETY";
/**
* The candidate content was flagged for recitation reasons.
*/
FinishReason["RECITATION"] = "RECITATION";
/**
* Unknown reason.
*/
FinishReason["OTHER"] = "OTHER";
/**
* The candidate content contained forbidden terms.
*/
FinishReason["BLOCKLIST"] = "BLOCKLIST";
/**
* The candidate content potentially contained prohibited content.
*/
FinishReason["PROHIBITED_CONTENT"] = "PROHIBITED_CONTENT";
/**
* The candidate content potentially contained Sensitive Personally Identifiable Information (SPII).
*/
FinishReason["SPII"] = "SPII";
/**
* The function call generated by the model was invalid.
*/
FinishReason["MALFORMED_FUNCTION_CALL"] = "MALFORMED_FUNCTION_CALL";
})(exports.FinishReason || (exports.FinishReason = {}));
/**
* @public
*/
exports.FunctionCallingMode = void 0;
(function (FunctionCallingMode) {
/**
* Default model behavior; model decides to predict either a function call
* or a natural language response.
*/
FunctionCallingMode["AUTO"] = "AUTO";
/**
* Model is constrained to always predicting a function call only.
* If `allowed_function_names` is set, the predicted function call will be
* limited to any one of `allowed_function_names`, else the predicted
* function call will be any one of the provided `function_declarations`.
*/
FunctionCallingMode["ANY"] = "ANY";
/**
* Model will not predict any function call. Model behavior is same as when
* not passing any function declarations.
*/
FunctionCallingMode["NONE"] = "NONE";
})(exports.FunctionCallingMode || (exports.FunctionCallingMode = {}));
/**
* Content part modality.
* @public
*/
exports.Modality = void 0;
(function (Modality) {
/**
* Unspecified modality.
*/
Modality["MODALITY_UNSPECIFIED"] = "MODALITY_UNSPECIFIED";
/**
* Plain text.
*/
Modality["TEXT"] = "TEXT";
/**
* Image.
*/
Modality["IMAGE"] = "IMAGE";
/**
* Video.
*/
Modality["VIDEO"] = "VIDEO";
/**
* Audio.
*/
Modality["AUDIO"] = "AUDIO";
/**
* Document (for example, PDF).
*/
Modality["DOCUMENT"] = "DOCUMENT";
})(exports.Modality || (exports.Modality = {}));
/**
* Generation modalities to be returned in generation responses.
*
* @beta
*/
const ResponseModality = {
/**
* Text.
* @beta
*/
TEXT: 'TEXT',
/**
* Image.
* @beta
*/
IMAGE: 'IMAGE'
};
/**
* @license
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Contains the list of OpenAPI data types
* as defined by the
* {@link https://swagger.io/docs/specification/data-models/data-types/ | OpenAPI specification}
* @public
*/
exports.SchemaType = void 0;
(function (SchemaType) {
/** String type. */
SchemaType["STRING"] = "string";
/** Number type. */
SchemaType["NUMBER"] = "number";
/** Integer type. */
SchemaType["INTEGER"] = "integer";
/** Boolean type. */
SchemaType["BOOLEAN"] = "boolean";
/** Array type. */
SchemaType["ARRAY"] = "array";
/** Object type. */
SchemaType["OBJECT"] = "object";
})(exports.SchemaType || (exports.SchemaType = {}));
/**
* @license
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* A filter level controlling how aggressively to filter sensitive content.
*
* Text prompts provided as inputs and images (generated or uploaded) through Imagen on Vertex AI
* are assessed against a list of safety filters, which include 'harmful categories' (for example,
* `violence`, `sexual`, `derogatory`, and `toxic`). This filter level controls how aggressively to
* filter out potentially harmful content from responses. See the {@link http://firebase.google.com/docs/vertex-ai/generate-images | documentation }
* and the {@link https://cloud.google.com/vertex-ai/generative-ai/docs/image/responsible-ai-imagen#safety-filters | Responsible AI and usage guidelines}
* for more details.
*
* @beta
*/
exports.ImagenSafetyFilterLevel = void 0;
(function (ImagenSafetyFilterLevel) {
/**
* The most aggressive filtering level; most strict blocking.
*/
ImagenSafetyFilterLevel["BLOCK_LOW_AND_ABOVE"] = "block_low_and_above";
/**
* Blocks some sensitive prompts and responses.
*/
ImagenSafetyFilterLevel["BLOCK_MEDIUM_AND_ABOVE"] = "block_medium_and_above";
/**
* Blocks few sensitive prompts and responses.
*/
ImagenSafetyFilterLevel["BLOCK_ONLY_HIGH"] = "block_only_high";
/**
* The least aggressive filtering level; blocks very few sensitive prompts and responses.
*
* Access to this feature is restricted and may require your case to be reviewed and approved by
* Cloud support.
*/
ImagenSafetyFilterLevel["BLOCK_NONE"] = "block_none";
})(exports.ImagenSafetyFilterLevel || (exports.ImagenSafetyFilterLevel = {}));
/**
* A filter level controlling whether generation of images containing people or faces is allowed.
*
* See the <a href="http://firebase.google.com/docs/vertex-ai/generate-images">personGeneration</a>
* documentation for more details.
*
* @beta
*/
exports.ImagenPersonFilterLevel = void 0;
(function (ImagenPersonFilterLevel) {
/**
* Disallow generation of images containing people or faces; images of people are filtered out.
*/
ImagenPersonFilterLevel["BLOCK_ALL"] = "dont_allow";
/**
* Allow generation of images containing adults only; images of children are filtered out.
*
* Generation of images containing people or faces may require your use case to be
* reviewed and approved by Cloud support; see the {@link https://cloud.google.com/vertex-ai/generative-ai/docs/image/responsible-ai-imagen#person-face-gen | Responsible AI and usage guidelines}
* for more details.
*/
ImagenPersonFilterLevel["ALLOW_ADULT"] = "allow_adult";
/**
* Allow generation of images containing adults only; images of children are filtered out.
*
* Generation of images containing people or faces may require your use case to be
* reviewed and approved by Cloud support; see the {@link https://cloud.google.com/vertex-ai/generative-ai/docs/image/responsible-ai-imagen#person-face-gen | Responsible AI and usage guidelines}
* for more details.
*/
ImagenPersonFilterLevel["ALLOW_ALL"] = "allow_all";
})(exports.ImagenPersonFilterLevel || (exports.ImagenPersonFilterLevel = {}));
/**
* Aspect ratios for Imagen images.
*
* To specify an aspect ratio for generated images, set the `aspectRatio` property in your
* {@link ImagenGenerationConfig}.
*
* See the the {@link http://firebase.google.com/docs/vertex-ai/generate-images | documentation }
* for more details and examples of the supported aspect ratios.
*
* @beta
*/
exports.ImagenAspectRatio = void 0;
(function (ImagenAspectRatio) {
/**
* Square (1:1) aspect ratio.
*/
ImagenAspectRatio["SQUARE"] = "1:1";
/**
* Landscape (3:4) aspect ratio.
*/
ImagenAspectRatio["LANDSCAPE_3x4"] = "3:4";
/**
* Portrait (4:3) aspect ratio.
*/
ImagenAspectRatio["PORTRAIT_4x3"] = "4:3";
/**
* Landscape (16:9) aspect ratio.
*/
ImagenAspectRatio["LANDSCAPE_16x9"] = "16:9";
/**
* Portrait (9:16) aspect ratio.
*/
ImagenAspectRatio["PORTRAIT_9x16"] = "9:16";
})(exports.ImagenAspectRatio || (exports.ImagenAspectRatio = {}));
/**
* @license
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* An enum-like object containing constants that represent the supported backends
* for the Firebase AI SDK.
* This determines which backend service (Vertex AI Gemini API or Gemini Developer API)
* the SDK will communicate with.
*
* These values are assigned to the `backendType` property within the specific backend
* configuration objects ({@link GoogleAIBackend} or {@link VertexAIBackend}) to identify
* which service to target.
*
* @public
*/
const BackendType = {
/**
* Identifies the backend service for the Vertex AI Gemini API provided through Google Cloud.
* Use this constant when creating a {@link VertexAIBackend} configuration.
*/
VERTEX_AI: 'VERTEX_AI',
/**
* Identifies the backend service for the Gemini Developer API ({@link https://ai.google/ | Google AI}).
* Use this constant when creating a {@link GoogleAIBackend} configuration.
*/
GOOGLE_AI: 'GOOGLE_AI'
}; // Using 'as const' makes the string values literal types
/**
* @license
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Abstract base class representing the configuration for an AI service backend.
* This class should not be instantiated directly. Use its subclasses; {@link GoogleAIBackend} for
* the Gemini Developer API (via {@link https://ai.google/ | Google AI}), and
* {@link VertexAIBackend} for the Vertex AI Gemini API.
*
* @public
*/
class Backend {
/**
* Protected constructor for use by subclasses.
* @param type - The backend type.
*/
constructor(type) {
this.backendType = type;
}
}
/**
* Configuration class for the Gemini Developer API.
*
* Use this with {@link AIOptions} when initializing the AI service via
* {@link getAI | getAI()} to specify the Gemini Developer API as the backend.
*
* @public
*/
class GoogleAIBackend extends Backend {
/**
* Creates a configuration object for the Gemini Developer API backend.
*/
constructor() {
super(BackendType.GOOGLE_AI);
}
}
/**
* Configuration class for the Vertex AI Gemini API.
*
* Use this with {@link AIOptions} when initializing the AI service via
* {@link getAI | getAI()} to specify the Vertex AI Gemini API as the backend.
*
* @public
*/
class VertexAIBackend extends Backend {
/**
* Creates a configuration object for the Vertex AI backend.
*
* @param location - The region identifier, defaulting to `us-central1`;
* see {@link https://firebase.google.com/docs/vertex-ai/locations#available-locations | Vertex AI locations}
* for a list of supported locations.
*/
constructor(location = DEFAULT_LOCATION) {
super(BackendType.VERTEX_AI);
if (!location) {
this.location = DEFAULT_LOCATION;
}
else {
this.location = location;
}
}
}
/**
* @license
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class AIService {
constructor(app, backend, authProvider, appCheckProvider) {
this.app = app;
this.backend = backend;
const appCheck = appCheckProvider === null || appCheckProvider === void 0 ? void 0 : appCheckProvider.getImmediate({ optional: true });
const auth = authProvider === null || authProvider === void 0 ? void 0 : authProvider.getImmediate({ optional: true });
this.auth = auth || null;
this.appCheck = appCheck || null;
if (backend instanceof VertexAIBackend) {
this.location = backend.location;
}
else {
this.location = '';
}
}
_delete() {
return Promise.resolve();
}
}
/**
* @license
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Error class for the Firebase AI SDK.
*
* @public
*/
class AIError extends util.FirebaseError {
/**
* Constructs a new instance of the `AIError` class.
*
* @param code - The error code from {@link AIErrorCode}.
* @param message - A human-readable message describing the error.
* @param customErrorData - Optional error data.
*/
constructor(code, message, customErrorData) {
// Match error format used by FirebaseError from ErrorFactory
const service = AI_TYPE;
const fullCode = `${service}/${code}`;
const fullMessage = `${service}: ${message} (${fullCode})`;
super(code, fullMessage);
this.code = code;
this.customErrorData = customErrorData;
// FirebaseError initializes a stack trace, but it assumes the error is created from the error
// factory. Since we break this assumption, we set the stack trace to be originating from this
// constructor.
// This is only supported in V8.
if (Error.captureStackTrace) {
// Allows us to initialize the stack trace without including the constructor itself at the
// top level of the stack trace.
Error.captureStackTrace(this, AIError);
}
// Allows instanceof AIError in ES5/ES6
// https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
// TODO(dlarocque): Replace this with `new.target`: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-newtarget
// which we can now use since we no longer target ES5.
Object.setPrototypeOf(this, AIError.prototype);
// Since Error is an interface, we don't inherit toString and so we define it ourselves.
this.toString = () => fullMessage;
}
}
/**
* @license
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Encodes a {@link Backend} into a string that will be used to uniquely identify {@link AI}
* instances by backend type.
*
* @internal
*/
function encodeInstanceIdentifier(backend) {
if (backend instanceof GoogleAIBackend) {
return `${AI_TYPE}/googleai`;
}
else if (backend instanceof VertexAIBackend) {
return `${AI_TYPE}/vertexai/${backend.location}`;
}
else {
throw new AIError("error" /* AIErrorCode.ERROR */, `Invalid backend: ${JSON.stringify(backend.backendType)}`);
}
}
/**
* Decodes an instance identifier string into a {@link Backend}.
*
* @internal
*/
function decodeInstanceIdentifier(instanceIdentifier) {
const identifierParts = instanceIdentifier.split('/');
if (identifierParts[0] !== AI_TYPE) {
throw new AIError("error" /* AIErrorCode.ERROR */, `Invalid instance identifier, unknown prefix '${identifierParts[0]}'`);
}
const backendType = identifierParts[1];
switch (backendType) {
case 'vertexai':
const location = identifierParts[2];
if (!location) {
throw new AIError("error" /* AIErrorCode.ERROR */, `Invalid instance identifier, unknown location '${instanceIdentifier}'`);
}
return new VertexAIBackend(location);
case 'googleai':
return new GoogleAIBackend();
default:
throw new AIError("error" /* AIErrorCode.ERROR */, `Invalid instance identifier string: '${instanceIdentifier}'`);
}
}
/**
* @license
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Base class for Firebase AI model APIs.
*
* Instances of this class are associated with a specific Firebase AI {@link Backend}
* and provide methods for interacting with the configured generative model.
*
* @public
*/
class AIModel {
/**
* Constructs a new instance of the {@link AIModel} class.
*
* This constructor should only be called from subclasses that provide
* a model API.
*
* @param ai - an {@link AI} instance.
* @param modelName - The name of the model being used. It can be in one of the following formats:
* - `my-model` (short name, will resolve to `publishers/google/models/my-model`)
* - `models/my-model` (will resolve to `publishers/google/models/my-model`)
* - `publishers/my-publisher/models/my-model` (fully qualified model name)
*
* @throws If the `apiKey` or `projectId` fields are missing in your
* Firebase config.
*
* @internal
*/
constructor(ai, modelName) {
var _a, _b, _c, _d, _e, _f;
if (!((_b = (_a = ai.app) === null || _a === void 0 ? void 0 : _a.options) === null || _b === void 0 ? void 0 : _b.apiKey)) {
throw new AIError("no-api-key" /* AIErrorCode.NO_API_KEY */, `The "apiKey" field is empty in the local Firebase config. Firebase AI requires this field to contain a valid API key.`);
}
else if (!((_d = (_c = ai.app) === null || _c === void 0 ? void 0 : _c.options) === null || _d === void 0 ? void 0 : _d.projectId)) {
throw new AIError("no-project-id" /* AIErrorCode.NO_PROJECT_ID */, `The "projectId" field is empty in the local Firebase config. Firebase AI requires this field to contain a valid project ID.`);
}
else if (!((_f = (_e = ai.app) === null || _e === void 0 ? void 0 : _e.options) === null || _f === void 0 ? void 0 : _f.appId)) {
throw new AIError("no-app-id" /* AIErrorCode.NO_APP_ID */, `The "appId" field is empty in the local Firebase config. Firebase AI requires this field to contain a valid app ID.`);
}
else {
this._apiSettings = {
apiKey: ai.app.options.apiKey,
project: ai.app.options.projectId,
appId: ai.app.options.appId,
automaticDataCollectionEnabled: ai.app.automaticDataCollectionEnabled,
location: ai.location,
backend: ai.backend
};
if (app._isFirebaseServerApp(ai.app) && ai.app.settings.appCheckToken) {
const token = ai.app.settings.appCheckToken;
this._apiSettings.getAppCheckToken = () => {
return Promise.resolve({ token });
};
}
else if (ai.appCheck) {
this._apiSettings.getAppCheckToken = () => ai.appCheck.getToken();
}
if (ai.auth) {
this._apiSettings.getAuthToken = () => ai.auth.getToken();
}
this.model = AIModel.normalizeModelName(modelName, this._apiSettings.backend.backendType);
}
}
/**
* Normalizes the given model name to a fully qualified model resource name.
*
* @param modelName - The model name to normalize.
* @returns The fully qualified model resource name.
*
* @internal
*/
static normalizeModelName(modelName, backendType) {
if (backendType === BackendType.GOOGLE_AI) {
return AIModel.normalizeGoogleAIModelName(modelName);
}
else {
return AIModel.normalizeVertexAIModelName(modelName);
}
}
/**
* @internal
*/
static normalizeGoogleAIModelName(modelName) {
return `models/${modelName}`;
}
/**
* @internal
*/
static normalizeVertexAIModelName(modelName) {
let model;
if (modelName.includes('/')) {
if (modelName.startsWith('models/')) {
// Add 'publishers/google' if the user is only passing in 'models/model-name'.
model = `publishers/google/${modelName}`;
}
else {
// Any other custom format (e.g. tuned models) must be passed in correctly.
model = modelName;
}
}
else {
// If path is not included, assume it's a non-tuned model.
model = `publishers/google/models/${modelName}`;
}
return model;
}
}
/**
* @license
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const logger = new logger$1.Logger('@firebase/vertexai');
/**
* @license
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var Task;
(function (Task) {
Task["GENERATE_CONTENT"] = "generateContent";
Task["STREAM_GENERATE_CONTENT"] = "streamGenerateContent";
Task["COUNT_TOKENS"] = "countTokens";
Task["PREDICT"] = "predict";
})(Task || (Task = {}));
class RequestUrl {
constructor(model, task, apiSettings, stream, requestOptions) {
this.model = model;
this.task = task;
this.apiSettings = apiSettings;
this.stream = stream;
this.requestOptions = requestOptions;
}
toString() {
const url = new URL(this.baseUrl); // Throws if the URL is invalid
url.pathname = `/${this.apiVersion}/${this.modelPath}:${this.task}`;
url.search = this.queryParams.toString();
return url.toString();
}
get baseUrl() {
var _a;
return ((_a = this.requestOptions) === null || _a === void 0 ? void 0 : _a.baseUrl) || DEFAULT_BASE_URL;
}
get apiVersion() {
return DEFAULT_API_VERSION; // TODO: allow user-set options if that feature becomes available
}
get modelPath() {
if (this.apiSettings.backend instanceof GoogleAIBackend) {
return `projects/${this.apiSettings.project}/${this.model}`;
}
else if (this.apiSettings.backend instanceof VertexAIBackend) {
return `projects/${this.apiSettings.project}/locations/${this.apiSettings.backend.location}/${this.model}`;
}
else {
throw new AIError("error" /* AIErrorCode.ERROR */, `Invalid backend: ${JSON.stringify(this.apiSettings.backend)}`);
}
}
get queryParams() {
const params = new URLSearchParams();
if (this.stream) {
params.set('alt', 'sse');
}
return params;
}
}
/**
* Log language and "fire/version" to x-goog-api-client
*/
function getClientHeaders() {
const loggingTags = [];
loggingTags.push(`${LANGUAGE_TAG}/${PACKAGE_VERSION}`);
loggingTags.push(`fire/${PACKAGE_VERSION}`);
return loggingTags.join(' ');
}
async function getHeaders(url) {
const headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('x-goog-api-client', getClientHeaders());
headers.append('x-goog-api-key', url.apiSettings.apiKey);
if (url.apiSettings.automaticDataCollectionEnabled) {
headers.append('X-Firebase-Appid', url.apiSettings.appId);
}
if (url.apiSettings.getAppCheckToken) {
const appCheckToken = await url.apiSettings.getAppCheckToken();
if (appCheckToken) {
headers.append('X-Firebase-AppCheck', appCheckToken.token);
if (appCheckToken.error) {
logger.warn(`Unable to obtain a valid App Check token: ${appCheckToken.error.message}`);
}
}
}
if (url.apiSettings.getAuthToken) {
const authToken = await url.apiSettings.getAuthToken();
if (authToken) {
headers.append('Authorization', `Firebase ${authToken.accessToken}`);
}
}
return headers;
}
async function constructRequest(model, task, apiSettings, stream, body, requestOptions) {
const url = new RequestUrl(model, task, apiSettings, stream, requestOptions);
return {
url: url.toString(),
fetchOptions: {
method: 'POST',
headers: await getHeaders(url),
body
}
};
}
async function makeRequest(model, task, apiSettings, stream, body, requestOptions) {
const url = new RequestUrl(model, task, apiSettings, stream, requestOptions);
let response;
let fetchTimeoutId;
try {
const request = await constructRequest(model, task, apiSettings, stream, body, requestOptions);
// Timeout is 180s by default
const timeoutMillis = (requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.timeout) != null && requestOptions.timeout >= 0
? requestOptions.timeout
: DEFAULT_FETCH_TIMEOUT_MS;
const abortController = new AbortController();
fetchTimeoutId = setTimeout(() => abortController.abort(), timeoutMillis);
request.fetchOptions.signal = abortController.signal;
response = await fetch(request.url, request.fetchOptions);
if (!response.ok) {
let message = '';
let errorDetails;
try {
const json = await response.json();
message = json.error.message;
if (json.error.details) {
message += ` ${JSON.stringify(json.error.details)}`;
errorDetails = json.error.details;
}
}
catch (e) {
// ignored
}
if (response.status === 403 &&
errorDetails.some((detail) => detail.reason === 'SERVICE_DISABLED') &&
errorDetails.some((detail) => {
var _a, _b;
return (_b = (_a = detail.links) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.description.includes('Google developers console API activation');
})) {
throw new AIError("api-not-enabled" /* AIErrorCode.API_NOT_ENABLED */, `The Firebase AI SDK requires the Firebase AI ` +
`API ('firebasevertexai.googleapis.com') to be enabled in your ` +
`Firebase project. Enable this API by visiting the Firebase Console ` +
`at https://console.firebase.google.com/project/${url.apiSettings.project}/genai/ ` +
`and clicking "Get started". If you enabled this API recently, ` +
`wait a few minutes for the action to propagate to our systems and ` +
`then retry.`, {
status: response.status,
statusText: response.statusText,
errorDetails
});
}
throw new AIError("fetch-error" /* AIErrorCode.FETCH_ERROR */, `Error fetching from ${url}: [${response.status} ${response.statusText}] ${message}`, {
status: response.status,
statusText: response.statusText,
errorDetails
});
}
}
catch (e) {
let err = e;
if (e.code !== "fetch-error" /* AIErrorCode.FETCH_ERROR */ &&
e.code !== "api-not-enabled" /* AIErrorCode.API_NOT_ENABLED */ &&
e instanceof Error) {
err = new AIError("error" /* AIErrorCode.ERROR */, `Error fetching from ${url.toString()}: ${e.message}`);
err.stack = e.stack;
}
throw err;
}
finally {
if (fetchTimeoutId) {
clearTimeout(fetchTimeoutId);
}
}
return response;
}
/**
* @license
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Creates an EnhancedGenerateContentResponse object that has helper functions and
* other modifications that improve usability.
*/
function createEnhancedContentResponse(response) {
/**
* The Vertex AI backend omits default values.
* This causes the `index` property to be omitted from the first candidate in the
* response, since it has index 0, and 0 is a default value.
* See: https://github.com/firebase/firebase-js-sdk/issues/8566
*/
if (response.candidates && !response.candidates[0].hasOwnProperty('index')) {
response.candidates[0].index = 0;
}
const responseWithHelpers = addHelpers(response);
return responseWithHelpers;
}
/**
* Adds convenience helper methods to a response object, including stream
* chunks (as long as each chunk is a complete GenerateContentResponse JSON).
*/
function addHelpers(response) {
response.text = () => {
if (response.candidates && response.candidates.length > 0) {
if (response.candidates.length > 1) {
logger.warn(`This response had ${response.candidates.length} ` +
`candidates. Returning text from the first candidate only. ` +
`Access response.candidates directly to use the other candidates.`);
}
if (hadBadFinishReason(response.candidates[0])) {
throw new AIError("response-error" /* AIErrorCode.RESPONSE_ERROR */, `Response error: ${formatBlockErrorMessage(response)}. Response body stored in error.response`, {
response
});
}
return getText(response);
}
else if (response.promptFeedback) {
throw new AIError("response-error" /* AIErrorCode.RESPONSE_ERROR */, `Text not available. ${formatBlockErrorMessage(response)}`, {
response
});
}
return '';
};
response.inlineDataParts = () => {
if (response.candidates && response.candidates.length > 0) {
if (response.candidates.length > 1) {
logger.warn(`This response had ${response.candidates.length} ` +
`candidates. Returning data from the first candidate only. ` +
`Access response.candidates directly to use the other candidates.`);
}
if (hadBadFinishReason(response.candidates[0])) {
throw new AIError("response-error" /* AIErrorCode.RESPONSE_ERROR */, `Response error: ${formatBlockErrorMessage(response)}. Response body stored in error.response`, {
response
});
}
return getInlineDataParts(response);
}
else if (response.promptFeedback) {
throw new AIError("response-error" /* AIErrorCode.RESPONSE_ERROR */, `Data not available. ${formatBlockErrorMessage(response)}`, {
response
});
}
return undefined;
};
response.functionCalls = () => {
if (response.candidates && response.candidates.length > 0) {
if (response.candidates.length > 1) {
logger.warn(`This response had ${response.candidates.length} ` +
`candidates. Returning function calls from the first candidate only. ` +
`Access response.candidates directly to use the other candidates.`);
}
if (hadBadFinishReason(response.candidates[0])) {
throw new AIError("response-error" /* AIErrorCode.RESPONSE_ERROR */, `Response error: ${formatBlockErrorMessage(response)}. Response body stored in error.response`, {
response
});
}
return getFunctionCalls(response);
}
else if (response.promptFeedback) {
throw new AIError("response-error" /* AIErrorCode.RESPONSE_ERROR */, `Function call not available. ${formatBlockErrorMessage(response)}`, {
response
});
}
return undefined;
};
return response;
}
/**
* Returns all text found in all parts of first candidate.
*/
function getText(response) {
var _a, _b, _c, _d;
const textStrings = [];
if ((_b = (_a = response.candidates) === null || _a === void 0 ? void 0 : _a[0].content) === null || _b === void 0 ? void 0 : _b.parts) {
for (const part of (_d = (_c = response.candidates) === null || _c === void 0 ? void 0 : _c[0].content) === null || _d === void 0 ? void 0 : _d.parts) {
if (part.text) {
textStrings.push(part.text);
}
}
}
if (textStrings.length > 0) {
return textStrings.join('');
}
else {
return '';
}
}
/**
* Returns {@link FunctionCall}s associated with first candidate.
*/
function getFunctionCalls(response) {
var _a, _b, _c, _d;
const functionCalls = [];
if ((_b = (_a = response.candidates) === null || _a === void 0 ? void 0 : _a[0].content) === null || _b === void 0 ? void 0 : _b.parts) {
for (const part of (_d = (_c = response.candidates) === null || _c === void 0 ? void 0 : _c[0].content) === null || _d === void 0 ? void 0 : _d.parts) {
if (part.functionCall) {
functionCalls.push(part.functionCall);
}
}
}
if (functionCalls.length > 0) {
return functionCalls;
}
else {
return undefined;
}
}
/**
* Returns {@link InlineDataPart}s in the first candidate if present.
*
* @internal
*/
function getInlineDataParts(response) {
var _a, _b, _c, _d;
const data = [];
if ((_b = (_a = response.candidates) === null || _a === void 0 ? void 0 : _a[0].content) === null || _b === void 0 ? void 0 : _b.parts) {
for (const part of (_d = (_c = response.candidates) === null || _c === void 0 ? void 0 : _c[0].content) === null || _d === void 0 ? void 0 : _d.parts) {
if (part.inlineData) {
data.push(part);
}
}
}
if (data.length > 0) {
return data;
}
else {
return undefined;
}
}
const badFinishReasons = [exports.FinishReason.RECITATION, exports.FinishReason.SAFETY];
function hadBadFinishReason(candidate) {
return (!!candidate.finishReason &&
badFinishReasons.includes(candidate.finishReason));
}
function formatBlockErrorMessage(response) {
var _a, _b, _c;
let message = '';
if ((!response.candidates || response.candidates.length === 0) &&
response.promptFeedback) {
message += 'Response was blocked';
if ((_a = response.promptFeedback) === null || _a === void 0 ? void 0 : _a.blockReason) {
message += ` due to ${response.promptFeedback.blockReason}`;
}
if ((_b = response.promptFeedback) === null || _b === void 0 ? void 0 : _b.blockReasonMessage) {
message += `: ${response.promptFeedback.blockReasonMessage}`;
}
}
else if ((_c = response.candidates) === null || _c === void 0 ? void 0 : _c[0]) {
const firstCandidate = response.candidates[0];
if (hadBadFinishReason(firstCandidate)) {
message += `Candidate was blocked due to ${firstCandidate.finishReason}`;
if (firstCandidate.finishMessage) {
message += `: ${firstCandidate.finishMessage}`;
}
}
}
return message;
}
/**
* Convert a generic successful fetch response body to an Imagen response object
* that can be returned to the user. This converts the REST APIs response format to our
* APIs representation of a response.
*
* @internal
*/
async function handlePredictResponse(response) {
var _a;
const responseJson = await response.json();
const images = [];
let filteredReason = undefined;
// The backend should always send a non-empty array of predictions if the response was successful.
if (!responseJson.predictions || ((_a = responseJson.predictions) === null || _a === void 0 ? void 0 : _a.length) === 0) {
throw new AIError("response-error" /* AIErrorCode.RESPONSE_ERROR */, 'No predictions or filtered reason received from Vertex AI. Please report this issue with the full error details at https://github.com/firebase/firebase-js-sdk/issues.');
}
for (const prediction of responseJson.predictions) {
if (prediction.raiFilteredReason) {
filteredReason = prediction.raiFilteredReason;
}
else if (prediction.mimeType && prediction.bytesBase64Encoded) {
images.push({
mimeType: prediction.mimeType,
bytesBase64Encoded: prediction.bytesBase64Encoded
});
}
else if (prediction.mimeType && prediction.gcsUri) {
images.push({
mimeType: prediction.mimeType,
gcsURI: prediction.gcsUri
});
}
else {
throw new AIError("response-error" /* AIErrorCode.RESPONSE_ERROR */, `Predictions array in response has missing properties. Response: ${JSON.stringify(responseJson)}`);
}
}
return { images, filteredReason };
}
/**
* @license
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* This SDK supports both the Vertex AI Gemini API and the Gemini Developer API (using Google AI).
* The public API prioritizes the format used by the Vertex AI Gemini API.
* We avoid having two sets of types by translating requests and responses between the two API formats.
* This translation allows developers to switch between the Vertex AI Gemini API and the Gemini Developer API
* with minimal code changes.
*
* In here are functions that map requests and responses between the two API formats.
* Requests in the Vertex AI format are mapped to the Google AI format before being sent.
* Responses from the Google AI backend are mapped back to the Vertex AI format before being returned to the user.
*/
/**
* Maps a Vertex AI {@link GenerateContentRequest} to a format that can be sent to Google AI.
*
* @param generateContentRequest The {@link GenerateContentRequest} to map.
* @returns A {@link GenerateContentResponse} that conforms to the Google AI format.
*
* @throws If the request contains properties that are unsupported by Google AI.
*
* @internal
*/
function mapGenerateContentRequest(generateContentRequest) {
var _a, _b;
(_a = generateContentRequest.safetySettings) === null || _a === void 0 ? void 0 : _a.forEach(safetySetting => {
if (safetySetting.method) {
throw new AIError("unsupported" /* AIErrorCode.UNSUPPORTED */, 'SafetySetting.method is not supported in the the Gemini Developer API. Please remove this property.');
}
});
if ((_b = generateContentRequest.generationConfig) === null || _b === void 0 ? void 0 : _b.topK) {
const roundedTopK = Math.round(generateContentRequest.generationConfig.topK);
if (roundedTopK !== generateContentRequest.generationConfig.topK) {
logger.warn('topK in GenerationConfig has been rounded to the nearest integer to match the format for requests to the Gemini Developer API.');
generateContentRequest.generationConfig.topK = roundedTopK;
}
}
return generateContentRequest;
}
/**
* Maps a {@link GenerateContentResponse} from Google AI to the format of the
* {@link GenerateContentResponse} that we get from VertexAI that is exposed in the public API.
*
* @param googleAIResponse The {@link GenerateContentResponse} from Google AI.
* @returns A {@link GenerateContentResponse} that conforms to the public API's format.
*
* @internal
*/
function mapGenerateContentResponse(googleAIResponse) {
const generateContentResponse = {
candidates: googleAIResponse.candidates
? mapGenerateContentCandidates(googleAIResponse.candidates)
: undefined,
prompt: googleAIResponse.promptFeedback
? mapPromptFeedback(googleAIResponse.promptFeedback)
: undefined,
usageMetadata: googleAIResponse.usageMetadata
};
return generateContentResponse;
}
/**
* Maps a Vertex AI {@link CountTokensRequest} to a format that can be sent to Google AI.
*
* @param countTokensRequest The {@link CountTokensRequest} to map.
* @param model The model to count tokens with.
* @returns A {@link CountTokensRequest} that conforms to the Google AI format.
*
* @internal
*/
function mapCountTokensRequest(countTokensRequest, model) {
const mappedCountTokensRequest = {
generateContentRequest: Object.assign({ model }, countTokensRequest)
};
return mappedCountTokensRequest;
}
/**
* Maps a Google AI {@link GoogleAIGenerateContentCandidate} to a format that conforms
* to the Vertex AI API format.
*
* @param candidates The {@link GoogleAIGenerateContentCandidate} to map.
* @returns A {@link GenerateContentCandidate} that conforms to the Vertex AI format.
*
* @throws If any {@link Part} in the candidates has a `videoMetadata` property.
*
* @internal
*/
function mapGenerateContentCandidates(candidates) {
const mappedCandidates = [];
let mappedSafetyRatings;
if (mappedCandidates) {
candidates.forEach(candidate => {
var _a;
// Map citationSources to citations.
let citationMetadata;
if (candidate.citationMetadata) {
citationMetadata = {
citations: candidate.citationMetadata.citationSources
};
}
// Assign missing candidate SafetyRatings properties to their defaults if undefined.
if (candidate.safetyRatings) {
mappedSafetyRatings = candidate.safetyRatings.map(safetyRating => {
var _a, _b, _c;
return Object.assign(Object.assign({}, safetyRating), { severity: (_a = safetyRating.severity) !== null && _a !== void 0 ? _a : exports.HarmSeverity.HARM_SEVERITY_UNSUPPORTED, probabilityScore: (_b = safetyRating.probabilityScore) !== null && _b !== void 0 ? _b : 0, severityScore: (_c = safetyRating.severityScore) !== null && _c !== void 0 ? _c : 0 });
});
}
// videoMetadata is not supported.
// Throw early since developers may send a long video as input and only expect to pay
// for inference on a small portion of the video.
if ((_a = candidate.content) === null || _a === void 0 ? void 0 : _a.parts.some(part => part === null || part === void 0 ? void 0 : part.videoMetadata)) {
throw new AIError("unsupported" /* AIErrorCode.UNSUPPORTED */, 'Part.videoMetadata is not supported in the Gemini Developer API. Please remove this property.');
}
const mappedCandidate = {
index: candidate.index,
content: candidate.content,
finishReason: candidate.finishReason,
finishMessage: candidate.finishMessage,
safetyRatings: mappedSafetyRatings,
citationMetadata,
groundingMetadata: candidate.groundingMetadata
};
mappedCandidates.push(mappedCandidate);
});
}
return mappedCandidates;
}
function mapPromptFeedback(promptFeedback) {
// Assign missing SafetyRating properties to their defaults if undefined.
const mappedSafetyRatings = [];
promptFeedback.safetyRatings.forEach(safetyRating => {
var _a, _b, _c;
mappedSafetyRatings.push({
category: safetyRating.category,
probability: safetyRating.probability,
severity: (_a = safetyRating.severity) !== null && _a !== void 0 ? _a : exports.HarmSeverity.HARM_SEVERITY_UNSUPPORTED,
probabilityScore: (_b = safetyRating.probabilityScore) !== null && _b !== void 0 ? _b : 0,
severityScore: (_c = safetyRating.severityScore) !== null && _c !== void 0 ? _c : 0,
blocked: safetyRating.blocked
});
});
const mappedPromptFeedback = {
blockReason: promptFeedback.blockReason,
safetyRatings: mappedSafetyRatings,
blockReasonMessage: promptFeedback.blockReasonMessage
};
return mappedPromptFeedback;
}
/**
* @license
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const responseLineRE = /^data\: (.*)(?:\n\n|\r\r|\r\n\r\n)/;
/**
* Process a response.body stream from the backend and return an
* iterator that provides one complete GenerateContentResponse at a time
* and a promise that resolves with a single aggregated
* GenerateContentResponse.
*
* @param response - Response from a fetch call
*/
function processStream(response, apiSettings) {
const inputStream = response.body.pipeThrough(new TextDecoderStream('utf8', { fatal: true }));
const responseStream = getResponseStream(inputStream);
const [stream1, stream2] = responseStream.tee();
return {
stream: generateResponseSequence(stream1, apiSettings),
response: getResponsePromise(stream2, apiSettings)
};
}
async function getResponsePromise(stream, apiSettings) {
const allResponses = [];
const reader = stream.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) {
let generateContentResponse = aggregateResponses(allResponses);
if (apiSettings.backend.backendType === BackendType.GOOGLE_AI) {
generateContentResponse = mapGenerateContentResponse(generateContentResponse);
}
return createEnhancedContentResponse(generateContentResponse);
}
allResponses.push(value);
}
}
function generateResponseSequence(stream, apiSettings) {
return tslib.__asyncGenerator(this, arguments, function* generateResponseSequence_1() {
const reader = stream.getReader();
while (true) {
const { value, done } = yield tslib.__await(reader.read());
if (done) {
break;
}
let enhancedResponse;
if (apiSettings.backend.backendType === BackendType.GOOGLE_AI) {
enhancedResponse = createEnhancedContentResponse(mapGenerateContentResponse(value));
}
else {
enhancedResponse = createEnhancedContentResponse(value);
}
yield yield tslib.__await(enhancedResponse);
}
});
}
/**
* Reads a raw stream from the fetch response and join incomplete
* chunks, returning a new stream that provides a single complete
* GenerateContentResponse in each iteration.
*/
function getResponseStream(inputStream) {
const reader = inputStream.getReader();
const stream = new ReadableStream({
start(controller) {
let currentText = '';
return pump();
function pump() {
return reader.read().then(({ value, done }) => {
if (done) {
if (currentText.trim()) {
controller.error(new AIError("parse-failed" /* AIErrorCode.PARSE_FAILED */, 'Failed to parse stream'));
return;
}
controller.close();
return;
}
currentText += value;
let match = currentText.match(responseLineRE);
let parsedResponse;
while (match) {
try {
parsedResponse = JSON.parse(match[1]);
}
catch (e) {
controller.error(new AIError("parse-failed" /* AIErrorCode.PARSE_FAILED */, `Error parsing JSON response: "${match[1]}`));
return;
}
controller.enqueue(parsedResponse);
currentText = currentText.substring(match[0].length);
match = currentText.match(responseLineRE);
}
return pump();
});
}
}
});
return stream;
}
/**
* Aggregates an array of `GenerateContentResponse`s into a single
* GenerateContentResponse.
*/
function aggregateResponses(responses) {
const lastResponse = responses[responses.length - 1];
const aggregatedResponse = {
promptFeedback: lastResponse === null || lastResponse === void 0 ? void 0 : lastResponse.promptFeedback
};
for (const response of responses) {
if (response.candidates) {
for (const candidate of response.candidates) {
// Index will be undefined if it's the first index (0), so we should use 0 if it's undefined.
// See: https://github.com/firebase/firebase-js-sdk/issues/8566
const i = candidate.index || 0;
if (!aggregatedResponse.candidates) {
aggregatedResponse.candidates = [];
}
if (!aggregatedResponse.candidates[i]) {
aggregatedResponse.candidates[i] = {
index: candidate.index
};
}
// Keep overwriting, the last one will be final
aggregatedResponse.candidates[i].citationMetadata =
candidate.citationMetadata;
aggregatedResponse.candidates[i].finishReason = candidate.finishReason;
aggregatedResponse.candidates[i].finishMessage =
candidate.finishMessage;
aggregatedResponse.candidates[i].safetyRatings =
candidate.safetyRatings;
/**
* Candidates should always have content and parts, but this handles
* possible malformed responses.
*/
if (candidate.content && candidate.content.parts) {
if (!aggregatedResponse.candidates[i].content) {
aggregatedResponse.candidates[i].content = {
role: candidate.content.role || 'user',
parts: []
};
}
const newPart = {};
for (const part of candidate.content.parts) {
if (part.text !== undefined) {
// The backend can send empty text parts. If these are sent back
// (e.g. in chat history), the backend will respond with an error.
// To prevent this, ignore empty text parts.
if (part.text === '') {
continue;
}
newPart.text = part.text;
}
if (part.functionCall) {
newPart.functionCall = part.functionCall;
}
if (Object.keys(newPart).length === 0) {
throw new AIError("invalid-content" /* AIErrorCode.INVALID_CONTENT */, 'Part should have at least one property, but there are none. This is likely caused ' +
'by a malformed response from the backend.');
}
aggregatedResponse.candidates[i].content.parts.push(newPart);
}
}
}
}
}
return aggregatedResponse;
}
/**
* @license
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
async function generateContentStream(apiSettings, model, params, requestOptions) {
if (apiSettings.backend.backendType === BackendType.GOOGLE_AI) {
params = mapGenerateContentRequest(params);
}
const response = await makeRequest(model, Task.STREAM_GENERATE_CONTENT, apiSettings,
/* stream */ true, JSON.stringify(params), requestOptions);
return processStream(response, apiSettings); // TODO: Map streaming responses
}
async function generateContent(apiSettings, model, params, requestOptions) {
if (apiSettings.backend.backendType === BackendType.GOOGLE_AI) {
params = mapGenerateContentRequest(params);
}
const response = await makeRequest(model, Task.GENERATE_CONTENT, apiSettings,
/* stream */ false, JSON.stringify(params), requestOptions);
const generateContentResponse = await processGenerateContentResponse(response, apiSettings);
const enhancedResponse = createEnhancedContentResponse(generateContentResponse);
return {
response: enhancedResponse
};
}
async function processGenerateContentResponse(response, apiSettings) {
const responseJson = await response.json();
if (apiSettings.backend.backendType === BackendType.GOOGLE_AI) {
return mapGenerateContentResponse(responseJson);
}
else {
return responseJson;
}
}
/**
* @license
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function formatSystemInstruction(input) {
// null or undefined
if (input == null) {
return undefined;
}
else if (typeof input === 'string') {
return { role: 'system', parts: [{ text: input }] };
}
else if (input.text) {
return { role: 'system', parts: [input] };
}
else if (input.parts) {
if (!input.role) {
return { role: 'system', parts: input.parts };
}
else {
return input;
}
}
}
function formatNewContent(request) {
let newParts = [];
if (typeof request === 'string') {
newParts = [{ text: request }];
}
else {
for (const partOrString of request) {
if (typeof partOrString === 'string') {
newParts.push({ text: partOrString });
}
else {
newParts.push(partOrString);
}
}
}
return assignRoleToPartsAndValidateSendMessageRequest(newParts);
}
/**
* When multiple Part types (i.e. FunctionResponsePart and TextPart) are
* passed in a single Part array, we may need to assign different roles to each
* part. Currently only FunctionResponsePart requires a role other than 'user'.
* @private
* @param parts Array of parts to pass to the model
* @returns Array of content items
*/
function assignRoleToPartsAndValidateSendMessageRequest(parts) {
const userContent = { role: 'user', parts: [] };
const functionContent = { role: 'function', parts: [] };
let hasUserContent = false;
let hasFunctionContent = false;
for (const part of parts) {
if ('functionResponse' in part) {
functionContent.parts.push(part);
hasFunctionContent = true;
}
else {
userContent.parts.push(part);
hasUserContent = true;
}
}
if (hasUserContent && hasFunctionContent) {
throw new AIError("invalid-content" /* AIErrorCode.INVALID_CONTENT */, 'Within a single message, FunctionResponse cannot be mixed with other type of Part in the request for sending chat message.');
}
if (!hasUserContent && !hasFunctionContent) {
throw new AIError("invalid-content" /* AIErrorCode.INVALID_CONTENT */, 'No Content is provided for sending chat message.');
}
if (hasUserContent) {
return userContent;
}
return functionContent;
}
function formatGenerateContentInput(params) {
let formattedRequest;
if (params.contents) {
formattedRequest = params;
}
else {
// Array or string
const content = formatNewContent(params);
formattedRequest = { contents: [content] };
}
if (params.systemInstruction) {
formattedRequest.systemInstruction = formatSystemInstruction(params.systemInstruction);
}
return formattedRequest;
}
/**
* Convert the user-defined parameters in {@link ImagenGenerationParams} to the format
* that is expected from the REST API.
*
* @internal
*/
function createPredictRequestBody(prompt, { gcsURI, imageFormat, addWatermark, numberOfImages = 1, negativePrompt, aspectRatio, safetyFilterLevel, personFilterLevel }) {
// Properties that are undefined will be omitted from the JSON string that is sent in the request.
const body = {
instances: [
{
prompt
}
],
parameters: {
storageUri: gcsURI,
negativePrompt,
sampleCount: numberOfImages,
aspectRatio,
outputOptions: imageFormat,
addWatermark,
safetyFilterLevel,
personGeneration: personFilterLevel,
includeRaiReason: true
}
};
return body;
}
/**
* @license
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// https://ai.google.dev/api/rest/v1beta/Content#part
const VALID_PART_FIELDS = [
'text',
'inlineData',
'functionCall',
'functionResponse'
];
const VALID_PARTS_PER_ROLE = {
user: ['text', 'inlineData'],
function: ['functionResponse'],
model: ['text', 'functionCall'],
// System instructions shouldn't be in history anyway.
system: ['text']
};
const VALID_PREVIOUS_CONTENT_ROLES = {
user: ['model'],
function: ['model'],
model: ['user', 'function'],
// System instructions shouldn't be in history.
system: []
};
function validateChatHistory(history) {
let prevContent = null;
for (const currContent of history) {
const { role, parts } = currContent;
if (!prevContent && role !== 'user') {
throw new AIError("invalid-content" /* AIErrorCode.INVALID_CONTENT */, `First Content should be with role 'user', got ${role}`);
}
if (!POSSIBLE_ROLES.includes(role)) {
throw new AIError("invalid-content" /* AIErrorCode.INVALID_CONTENT */, `Each item should include role field. Got ${role} but valid roles are: ${JSON.stringify(POSSIBLE_ROLES)}`);
}
if (!Array.isArray(parts)) {
throw new AIError("invalid-content" /* AIErrorCode.INVALID_CONTENT */, `Content should have 'parts' but property with an array of Parts`);
}
if (parts.length === 0) {
throw new AIError("invalid-content" /* AIErrorCode.INVALID_CONTENT */, `Each Content should have at least one part`);
}
const countFields = {
text: 0,
inlineData: 0,
functionCall: 0,
functionResponse: 0
};
for (const part of parts) {
for (const key of VALID_PART_FIELDS) {
if (key in part) {
countFields[key] += 1;
}
}
}
const validParts = VALID_PARTS_PER_ROLE[role];
for (const key of VALID_PART_FIELDS) {
if (!validParts.includes(key) && countFields[key] > 0) {
throw new AIError("invalid-content" /* AIErrorCode.INVALID_CONTENT */, `Content with role '${role}' can't contain '${key}' part`);
}
}
if (prevContent) {
const validPreviousContentRoles = VALID_PREVIOUS_CONTENT_ROLES[role];
if (!validPreviousContentRoles.includes(prevContent.role)) {
throw new AIError("invalid-content" /* AIErrorCode.INVALID_CONTENT */, `Content with role '${role}' can't follow '${prevContent.role}'. Valid previous roles: ${JSON.stringify(VALID_PREVIOUS_CONTENT_ROLES)}`);
}
}
prevContent = currContent;
}
}
/**
* @license
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Do not log a message for this error.
*/
const SILENT_ERROR = 'SILENT_ERROR';
/**
* ChatSession class that enables sending chat messages and stores
* history of sent and received messages so far.
*
* @public
*/
class ChatSession {
constructor(apiSettings, model, params, requestOptions) {
this.model = model;
this.params = params;
this.requestOptions = requestOptions;
this._history = [];
this._sendPromise = Promise.resolve();
this._apiSettings = apiSettings;
if (params === null || params === void 0 ? void 0 : params.history) {
validateChatHistory(params.history);
this._history = params.history;
}
}
/**
* Gets the chat history so far. Blocked prompts are not added to history.
* Neither blocked candidates nor the prompts that generated them are added
* to history.
*/
async getHistory() {
await this._sendPromise;
return this._history;
}
/**
* Sends a chat message and receives a non-streaming
* {@link GenerateContentResult}
*/
async sendMessage(request) {
var _a, _b, _c, _d, _e;
await this._sendPromise;
const newContent = formatNewContent(request);
const generateContentRequest = {
safetySettings: (_a = this.params) === null || _a === void 0 ? void 0 : _a.safetySettings,
generationConfig: (_b = this.params) === null || _b === void 0 ? void 0 : _b.generationConfig,
tools: (_c = this.params) === null || _c === void 0 ? void 0 : _c.tools,
toolConfig: (_d = this.params) === null || _d === void 0 ? void 0 : _d.toolConfig,
systemInstruction: (_e = this.params) === null || _e === void 0 ? void 0 : _e.systemInstruction,
contents: [...this._history, newContent]
};
let finalResult = {};
// Add onto the chain.
this._sendPromise = this._sendPromise
.then(() => generateContent(this._apiSettings, this.model, generateContentRequest, this.requestOptions))
.then(result => {
var _a, _b;
if (result.response.candidates &&
result.response.candidates.length > 0) {
this._history.push(newContent);
const responseContent = {
parts: ((_a = result.response.candidates) === null || _a === void 0 ? void 0 : _a[0].content.parts) || [],
// Response seems to come back without a role set.
role: ((_b = result.response.candidates) === null || _b === void 0 ? void 0 : _b[0].content.role) || 'model'
};
this._history.push(responseContent);
}
else {
const blockErrorMessage = formatBlockErrorMessage(result.response);
if (blockErrorMessage) {
logger.warn(`sendMessage() was unsuccessful. ${blockErrorMessage}. Inspect response object for details.`);
}
}
finalResult = result;
});
await this._sendPromise;
return finalResult;
}
/**
* Sends a chat message and receives the response as a
* {@link GenerateContentStreamResult} containing an iterable stream
* and a response promise.
*/
async sendMessageStream(request) {
var _a, _b, _c, _d, _e;
await this._sendPromise;
const newContent = formatNewContent(request);
const generateContentRequest = {
safetySettings: (_a = this.params) === null || _a === void 0 ? void 0 : _a.safetySettings,
generationConfig: (_b = this.params) === null || _b === void 0 ? void 0 : _b.generationConfig,
tools: (_c = this.params) === null || _c === void 0 ? void 0 : _c.tools,
toolConfig: (_d = this.params) === null || _d === void 0 ? void 0 : _d.toolConfig,
systemInstruction: (_e = this.params) === null || _e === void 0 ? void 0 : _e.systemInstruction,
contents: [...this._history, newContent]
};
const streamPromise = generateContentStream(this._apiSettings, this.model, generateContentRequest, this.requestOptions);
// Add onto the chain.
this._sendPromise = this._sendPromise
.then(() => streamPromise)
// This must be handled to avoid unhandled rejection, but jump
// to the final catch block with a label to not log this error.
.catch(_ignored => {
throw new Error(SILENT_ERROR);
})
.then(streamResult => streamResult.response)
.then(response => {
if (response.candidates && response.candidates.length > 0) {
this._history.push(newContent);
const responseContent = Object.assign({}, response.candidates[0].content);
// Response seems to come back without a role set.
if (!responseContent.role) {
responseContent.role = 'model';
}
this._history.push(responseContent);
}
else {
const blockErrorMessage = formatBlockErrorMessage(response);
if (blockErrorMessage) {
logger.warn(`sendMessageStream() was unsuccessful. ${blockErrorMessage}. Inspect response object for details.`);
}
}
})
.catch(e => {
// Errors in streamPromise are already catchable by the user as
// streamPromise is returned.
// Avoid duplicating the error message in logs.
if (e.message !== SILENT_ERROR) {
// Users do not have access to _sendPromise to catch errors
// downstream from streamPromise, so they should not throw.
logger.error(e);
}
});
return streamPromise;
}
}
/**
* @license
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
async function countTokens(apiSettings, model, params, requestOptions) {
let body = '';
if (apiSettings.backend.backendType === BackendType.GOOGLE_AI) {
const mappedParams = mapCountTokensRequest(params, model);
body = JSON.stringify(mappedParams);
}
else {
body = JSON.stringify(params);
}
const response = await makeRequest(model, Task.COUNT_TOKENS, apiSettings, false, body, requestOptions);
return response.json();
}
/**
* @license
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Class for generative model APIs.
* @public
*/
class GenerativeModel extends AIModel {
constructor(ai, modelParams, requestOptions) {
super(ai, modelParams.model);
this.generationConfig = modelParams.generationConfig || {};
this.safetySettings = modelParams.safetySettings || [];
this.tools = modelParams.tools;
this.toolConfig = modelParams.toolConfig;
this.systemInstruction = formatSystemInstruction(modelParams.systemInstruction);
this.requestOptions = requestOptions || {};
}
/**
* Makes a single non-streaming call to the model
* and returns an object containing a single {@link GenerateContentResponse}.
*/
async generateContent(request) {
const formattedParams = formatGenerateContentInput(request);
return generateContent(this._apiSettings, this.model, Object.assign({ generationConfig: this.generationConfig, safetySettings: this.safetySettings, tools: this.tools, toolConfig: this.toolConfig, systemInstruction: this.systemInstruction }, formattedParams), this.requestOptions);
}
/**
* Makes a single streaming call to the model
* and returns an object containing an iterable stream that iterates
* over all chunks in the streaming response as well as
* a promise that returns the final aggregated response.
*/
async generateContentStream(request) {
const formattedParams = formatGenerateContentInput(request);
return generateContentStream(this._apiSettings, this.model, Object.assign({ generationConfig: this.generationConfig, safetySettings: this.safetySettings, tools: this.tools, toolConfig: this.toolConfig, systemInstruction: this.systemInstruction }, formattedParams), this.requestOptions);
}
/**
* Gets a new {@link ChatSession} instance which can be used for
* multi-turn chats.
*/
startChat(startChatParams) {
return new ChatSession(this._apiSettings, this.model, Object.assign({ tools: this.tools, toolConfig: this.toolConfig, systemInstruction: this.systemInstruction, generationConfig: this.generationConfig, safetySettings: this.safetySettings }, startChatParams), this.requestOptions);
}
/**
* Counts the tokens in the provided request.
*/
async countTokens(request) {
const formattedParams = formatGenerateContentInput(request);
return countTokens(this._apiSettings, this.model, formattedParams);
}
}
/**
* @license
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Class for Imagen model APIs.
*
* This class provides methods for generating images using the Imagen model.
*
* @example
* ```javascript
* const imagen = new ImagenModel(
* ai,
* {
* model: 'imagen-3.0-generate-002'
* }
* );
*
* const response = await imagen.generateImages('A photo of a cat');
* if (response.images.length > 0) {
* console.log(response.images[0].bytesBase64Encoded);
* }
* ```
*
* @beta
*/
class ImagenModel extends AIModel {
/**
* Constructs a new instance of the {@link ImagenModel} class.
*
* @param ai - an {@link AI} instance.
* @param modelParams - Parameters to use when making requests to Imagen.
* @param requestOptions - Additional options to use when making requests.
*
* @throws If the `apiKey` or `projectId` fields are missing in your
* Firebase config.
*/
constructor(ai, modelParams, requestOptions) {
const { model, generationConfig, safetySettings } = modelParams;
super(ai, model);
this.requestOptions = requestOptions;
this.generationConfig = generationConfig;
this.safetySettings = safetySettings;
}
/**
* Generates images using the Imagen model and returns them as
* base64-encoded strings.
*
* @param prompt - A text prompt describing the image(s) to generate.
* @returns A promise that resolves to an {@link ImagenGenerationResponse}
* object containing the generated images.
*
* @throws If the request to generate images fails. This happens if the
* prompt is blocked.
*
* @remarks
* If the prompt was not blocked, but one or more of the generated images were filtered, the
* returned object will have a `filteredReason` property.
* If all images are filtered, the `images` array will be empty.
*
* @beta
*/
async generateImages(prompt) {
const body = createPredictRequestBody(prompt, Object.assign(Object.assign({}, this.generationConfig), this.safetySettings));
const response = await makeRequest(this.model, Task.PREDICT, this._apiSettings,
/* stream */ false, JSON.stringify(body), this.requestOptions);
return handlePredictResponse(response);
}
/**
* Generates images to Cloud Storage for Firebase using the Imagen model.
*
* @internal This method is temporarily internal.
*
* @param prompt - A text prompt describing the image(s) to generate.
* @param gcsURI - The URI of file stored in a Cloud Storage for Firebase bucket.
* This should be a directory. For example, `gs://my-bucket/my-directory/`.
* @returns A promise that resolves to an {@link ImagenGenerationResponse}
* object containing the URLs of the generated images.
*
* @throws If the request fails to generate images fails. This happens if
* the prompt is blocked.
*
* @remarks
* If the prompt was not blocked, but one or more of the generated images were filtered, the
* returned object will have a `filteredReason` property.
* If all images are filtered, the `images` array will be empty.
*/
async generateImagesGCS(prompt, gcsURI) {
const body = createPredictRequestBody(prompt, Object.assign(Object.assign({ gcsURI }, this.generationConfig), this.safetySettings));
const response = await makeRequest(this.model, Task.PREDICT, this._apiSettings,
/* stream */ false, JSON.stringify(body), this.requestOptions);
return handlePredictResponse(response);
}
}
/**
* @license
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Parent class encompassing all Schema types, with static methods that
* allow building specific Schema types. This class can be converted with
* `JSON.stringify()` into a JSON string accepted by Vertex AI REST endpoints.
* (This string conversion is automatically done when calling SDK methods.)
* @public
*/
class Schema {
constructor(schemaParams) {
// eslint-disable-next-line guard-for-in
for (const paramKey in schemaParams) {
this[paramKey] = schemaParams[paramKey];
}
// Ensure these are explicitly set to avoid TS errors.
this.type = schemaParams.type;
this.nullable = schemaParams.hasOwnProperty('nullable')
? !!schemaParams.nullable
: false;
}
/**
* Defines how this Schema should be serialized as JSON.
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#tojson_behavior
* @internal
*/
toJSON() {
const obj = {
type: this.type
};
for (const prop in this) {
if (this.hasOwnProperty(prop) && this[prop] !== undefined) {
if (prop !== 'required' || this.type === exports.SchemaType.OBJECT) {
obj[prop] = this[prop];
}
}
}
return obj;
}
static array(arrayParams) {
return new ArraySchema(arrayParams, arrayParams.items);
}
static object(objectParams) {
return new ObjectSchema(objectParams, objectParams.properties, objectParams.optionalProperties);
}
// eslint-disable-next-line id-blacklist
static string(stringParams) {
return new StringSchema(stringParams);
}
static enumString(stringParams) {
return new StringSchema(stringParams, stringParams.enum);
}
static integer(integerParams) {
return new IntegerSchema(integerParams);
}
// eslint-disable-next-line id-blacklist
static number(numberParams) {
return new NumberSchema(numberParams);
}
// eslint-disable-next-line id-blacklist
static boolean(booleanParams) {
return new BooleanSchema(booleanParams);
}
}
/**
* Schema class for "integer" types.
* @public
*/
class IntegerSchema extends Schema {
constructor(schemaParams) {
super(Object.assign({ type: exports.SchemaType.INTEGER }, schemaParams));
}
}
/**
* Schema class for "number" types.
* @public
*/
class NumberSchema extends Schema {
constructor(schemaParams) {
super(Object.assign({ type: exports.SchemaType.NUMBER }, schemaParams));
}
}
/**
* Schema class for "boolean" types.
* @public
*/
class BooleanSchema extends Schema {
constructor(schemaParams) {
super(Object.assign({ type: exports.SchemaType.BOOLEAN }, schemaParams));
}
}
/**
* Schema class for "string" types. Can be used with or without
* enum values.
* @public
*/
class StringSchema extends Schema {
constructor(schemaParams, enumValues) {
super(Object.assign({ type: exports.SchemaType.STRING }, schemaParams));
this.enum = enumValues;
}
/**
* @internal
*/
toJSON() {
const obj = super.toJSON();
if (this.enum) {
obj['enum'] = this.enum;
}
return obj;
}
}
/**
* Schema class for "array" types.
* The `items` param should refer to the type of item that can be a member
* of the array.
* @public
*/
class ArraySchema extends Schema {
constructor(schemaParams, items) {
super(Object.assign({ type: exports.SchemaType.ARRAY }, schemaParams));
this.items = items;
}
/**
* @internal
*/
toJSON() {
const obj = super.toJSON();
obj.items = this.items.toJSON();
return obj;
}
}
/**
* Schema class for "object" types.
* The `properties` param must be a map of `Schema` objects.
* @public
*/
class ObjectSchema extends Schema {
constructor(schemaParams, properties, optionalProperties = []) {
super(Object.assign({ type: exports.SchemaType.OBJECT }, schemaParams));
this.properties = properties;
this.optionalProperties = optionalProperties;
}
/**
* @internal
*/
toJSON() {
const obj = super.toJSON();
obj.properties = Object.assign({}, this.properties);
const required = [];
if (this.optionalProperties) {
for (const propertyKey of this.optionalProperties) {
if (!this.properties.hasOwnProperty(propertyKey)) {
throw new AIError("invalid-schema" /* AIErrorCode.INVALID_SCHEMA */, `Property "${propertyKey}" specified in "optionalProperties" does not exist.`);
}
}
}
for (const propertyKey in this.properties) {
if (this.properties.hasOwnProperty(propertyKey)) {
obj.properties[propertyKey] = this.properties[propertyKey].toJSON();
if (!this.optionalProperties.includes(propertyKey)) {
required.push(propertyKey);
}
}
}
if (required.length > 0) {
obj.required = required;
}
delete obj.optionalProperties;
return obj;
}
}
/**
* @license
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Defines the image format for images generated by Imagen.
*
* Use this class to specify the desired format (JPEG or PNG) and compression quality
* for images generated by Imagen. This is typically included as part of
* {@link ImagenModelParams}.
*
* @example
* ```javascript
* const imagenModelParams = {
* // ... other ImagenModelParams
* imageFormat: ImagenImageFormat.jpeg(75) // JPEG with a compression level of 75.
* }
* ```
*
* @beta
*/
class ImagenImageFormat {
constructor() {
this.mimeType = 'image/png';
}
/**
* Creates an {@link ImagenImageFormat} for a JPEG image.
*
* @param compressionQuality - The level of compression (a number between 0 and 100).
* @returns An {@link ImagenImageFormat} object for a JPEG image.
*
* @beta
*/
static jpeg(compressionQuality) {
if (compressionQuality &&
(compressionQuality < 0 || compressionQuality > 100)) {
logger.warn(`Invalid JPEG compression quality of ${compressionQuality} specified; the supported range is [0, 100].`);
}
return { mimeType: 'image/jpeg', compressionQuality };
}
/**
* Creates an {@link ImagenImageFormat} for a PNG image.
*
* @returns An {@link ImagenImageFormat} object for a PNG image.
*
* @beta
*/
static png() {
return { mimeType: 'image/png' };
}
}
/**
* @license
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @deprecated Use the new {@link AIModel} instead. The Vertex AI in Firebase SDK has been
* replaced with the Firebase AI SDK to accommodate the evolving set of supported features and
* services. For migration details, see the {@link https://firebase.google.com/docs/vertex-ai/migrate-to-latest-sdk | migration guide}.
*
* Base class for Firebase AI model APIs.
*
* @public
*/
const VertexAIModel = AIModel;
/**
* @deprecated Use the new {@link AIError} instead. The Vertex AI in Firebase SDK has been
* replaced with the Firebase AI SDK to accommodate the evolving set of supported features and
* services. For migration details, see the {@link https://firebase.google.com/docs/vertex-ai/migrate-to-latest-sdk | migration guide}.
*
* Error class for the Firebase AI SDK.
*
* @public
*/
const VertexAIError = AIError;
/**
* @deprecated Use the new {@link getAI | getAI()} instead. The Vertex AI in Firebase SDK has been
* replaced with the Firebase AI SDK to accommodate the evolving set of supported features and
* services. For migration details, see the {@link https://firebase.google.com/docs/vertex-ai/migrate-to-latest-sdk | migration guide}.
*
* Returns a {@link VertexAI} instance for the given app, configured to use the
* Vertex AI Gemini API. This instance will be
* configured to use the Vertex AI Gemini API.
*
* @param app - The {@link @firebase/app#FirebaseApp} to use.
* @param options - Options to configure the Vertex AI instance, including the location.
*
* @public
*/
function getVertexAI(app$1 = app.getApp(), options) {
app$1 = util.getModularInstance(app$1);
// Dependencies
const AIProvider = app._getProvider(app$1, AI_TYPE);
const backend = new VertexAIBackend(options === null || options === void 0 ? void 0 : options.location);
const identifier = encodeInstanceIdentifier(backend);
return AIProvider.getImmediate({
identifier
});
}
/**
* Returns the default {@link AI} instance that is associated with the provided
* {@link @firebase/app#FirebaseApp}. If no instance exists, initializes a new instance with the
* default settings.
*
* @example
* ```javascript
* const ai = getAI(app);
* ```
*
* @example
* ```javascript
* // Get an AI instance configured to use the Gemini Developer API (via Google AI).
* const ai = getAI(app, { backend: new GoogleAIBackend() });
* ```
*
* @example
* ```javascript
* // Get an AI instance configured to use the Vertex AI Gemini API.
* const ai = getAI(app, { backend: new VertexAIBackend() });
* ```
*
* @param app - The {@link @firebase/app#FirebaseApp} to use.
* @param options - {@link AIOptions} that configure the AI instance.
* @returns The default {@link AI} instance for the given {@link @firebase/app#FirebaseApp}.
*
* @public
*/
function getAI(app$1 = app.getApp(), options = { backend: new GoogleAIBackend() }) {
app$1 = util.getModularInstance(app$1);
// Dependencies
const AIProvider = app._getProvider(app$1, AI_TYPE);
const identifier = encodeInstanceIdentifier(options.backend);
return AIProvider.getImmediate({
identifier
});
}
/**
* Returns a {@link GenerativeModel} class with methods for inference
* and other functionality.
*
* @public
*/
function getGenerativeModel(ai, modelParams, requestOptions) {
if (!modelParams.model) {
throw new AIError("no-model" /* AIErrorCode.NO_MODEL */, `Must provide a model name. Example: getGenerativeModel({ model: 'my-model-name' })`);
}
return new GenerativeModel(ai, modelParams, requestOptions);
}
/**
* Returns an {@link ImagenModel} class with methods for using Imagen.
*
* Only Imagen 3 models (named `imagen-3.0-*`) are supported.
*
* @param ai - An {@link AI} instance.
* @param modelParams - Parameters to use when making Imagen requests.
* @param requestOptions - Additional options to use when making requests.
*
* @throws If the `apiKey` or `projectId` fields are missing in your
* Firebase config.
*
* @beta
*/
function getImagenModel(ai, modelParams, requestOptions) {
if (!modelParams.model) {
throw new AIError("no-model" /* AIErrorCode.NO_MODEL */, `Must provide a model name. Example: getImagenModel({ model: 'my-model-name' })`);
}
return new ImagenModel(ai, modelParams, requestOptions);
}
/**
* The Firebase AI Web SDK.
*
* @packageDocumentation
*/
function registerAI() {
app._registerComponent(new component.Component(AI_TYPE, (container, { instanceIdentifier }) => {
if (!instanceIdentifier) {
throw new AIError("error" /* AIErrorCode.ERROR */, 'AIService instance identifier is undefined.');
}
const backend = decodeInstanceIdentifier(instanceIdentifier);
// getImmediate for FirebaseApp will always succeed
const app = container.getProvider('app').getImmediate();
const auth = container.getProvider('auth-internal');
const appCheckProvider = container.getProvider('app-check-internal');
return new AIService(app, backend, auth, appCheckProvider);
}, "PUBLIC" /* ComponentType.PUBLIC */).setMultipleInstances(true));
app.registerVersion(name, version, 'node');
// BUILD_TARGET will be replaced by values like esm2017, cjs2017, etc during the compilation
app.registerVersion(name, version, 'cjs2017');
}
registerAI();
exports.AIError = AIError;
exports.AIModel = AIModel;
exports.ArraySchema = ArraySchema;
exports.Backend = Backend;
exports.BackendType = BackendType;
exports.BooleanSchema = BooleanSchema;
exports.ChatSession = ChatSession;
exports.GenerativeModel = GenerativeModel;
exports.GoogleAIBackend = GoogleAIBackend;
exports.ImagenImageFormat = ImagenImageFormat;
exports.ImagenModel = ImagenModel;
exports.IntegerSchema = IntegerSchema;
exports.NumberSchema = NumberSchema;
exports.ObjectSchema = ObjectSchema;
exports.POSSIBLE_ROLES = POSSIBLE_ROLES;
exports.ResponseModality = ResponseModality;
exports.Schema = Schema;
exports.StringSchema = StringSchema;
exports.VertexAIBackend = VertexAIBackend;
exports.VertexAIError = VertexAIError;
exports.VertexAIModel = VertexAIModel;
exports.getAI = getAI;
exports.getGenerativeModel = getGenerativeModel;
exports.getImagenModel = getImagenModel;
exports.getVertexAI = getVertexAI;
//# sourceMappingURL=index.node.cjs.js.map