@firebase/vertexai
Version:
A Firebase SDK for VertexAI
1,257 lines (1,242 loc) • 79.4 kB
JavaScript
import { _isFirebaseServerApp, _getProvider, getApp, _registerComponent, registerVersion } from '@firebase/app';
import { Component } from '@firebase/component';
import { FirebaseError, getModularInstance } from '@firebase/util';
import { Logger } from '@firebase/logger';
import { __asyncGenerator, __await } from 'tslib';
var name = "@firebase/vertexai";
var version = "1.1.0";
/**
* @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 VERTEX_TYPE = 'vertexAI';
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.
*/
class VertexAIService {
constructor(app, authProvider, appCheckProvider, options) {
var _a;
this.app = app;
this.options = options;
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;
this.location = ((_a = this.options) === null || _a === void 0 ? void 0 : _a.location) || DEFAULT_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 Vertex AI in Firebase SDK.
*
* @public
*/
class VertexAIError extends FirebaseError {
/**
* Constructs a new instance of the `VertexAIError` class.
*
* @param code - The error code from <code>{@link VertexAIErrorCode}</code>.
* @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 = VERTEX_TYPE;
const serviceName = 'VertexAI';
const fullCode = `${service}/${code}`;
const fullMessage = `${serviceName}: ${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, VertexAIError);
}
// Allows instanceof VertexAIError 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, VertexAIError.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.
*/
/**
* Base class for Vertex AI in Firebase model APIs.
*
* @public
*/
class VertexAIModel {
/**
* Constructs a new instance of the {@link VertexAIModel} class.
*
* This constructor should only be called from subclasses that provide
* a model API.
*
* @param vertexAI - An instance of the Vertex AI in Firebase SDK.
* @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(vertexAI, modelName) {
var _a, _b, _c, _d;
this.model = VertexAIModel.normalizeModelName(modelName);
if (!((_b = (_a = vertexAI.app) === null || _a === void 0 ? void 0 : _a.options) === null || _b === void 0 ? void 0 : _b.apiKey)) {
throw new VertexAIError("no-api-key" /* VertexAIErrorCode.NO_API_KEY */, `The "apiKey" field is empty in the local Firebase config. Firebase VertexAI requires this field to contain a valid API key.`);
}
else if (!((_d = (_c = vertexAI.app) === null || _c === void 0 ? void 0 : _c.options) === null || _d === void 0 ? void 0 : _d.projectId)) {
throw new VertexAIError("no-project-id" /* VertexAIErrorCode.NO_PROJECT_ID */, `The "projectId" field is empty in the local Firebase config. Firebase VertexAI requires this field to contain a valid project ID.`);
}
else {
this._apiSettings = {
apiKey: vertexAI.app.options.apiKey,
project: vertexAI.app.options.projectId,
location: vertexAI.location
};
if (_isFirebaseServerApp(vertexAI.app) &&
vertexAI.app.settings.appCheckToken) {
const token = vertexAI.app.settings.appCheckToken;
this._apiSettings.getAppCheckToken = () => {
return Promise.resolve({ token });
};
}
else if (vertexAI.appCheck) {
this._apiSettings.getAppCheckToken = () => vertexAI.appCheck.getToken();
}
if (vertexAI.auth) {
this._apiSettings.getAuthToken = () => vertexAI.auth.getToken();
}
}
}
/**
* 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.
*/
static normalizeModelName(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('@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() {
var _a;
// TODO: allow user-set option if that feature becomes available
const apiVersion = DEFAULT_API_VERSION;
const baseUrl = ((_a = this.requestOptions) === null || _a === void 0 ? void 0 : _a.baseUrl) || DEFAULT_BASE_URL;
let url = `${baseUrl}/${apiVersion}`;
url += `/projects/${this.apiSettings.project}`;
url += `/locations/${this.apiSettings.location}`;
url += `/${this.model}`;
url += `:${this.task}`;
if (this.stream) {
url += '?alt=sse';
}
return url;
}
/**
* If the model needs to be passed to the backend, it needs to
* include project and location path.
*/
get fullModelString() {
let modelString = `projects/${this.apiSettings.project}`;
modelString += `/locations/${this.apiSettings.location}`;
modelString += `/${this.model}`;
return modelString;
}
}
/**
* 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.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 VertexAIError("api-not-enabled" /* VertexAIErrorCode.API_NOT_ENABLED */, `The Vertex AI in Firebase SDK requires the Vertex AI in Firebase ` +
`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 VertexAIError("fetch-error" /* VertexAIErrorCode.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" /* VertexAIErrorCode.FETCH_ERROR */ &&
e.code !== "api-not-enabled" /* VertexAIErrorCode.API_NOT_ENABLED */ &&
e instanceof Error) {
err = new VertexAIError("error" /* VertexAIErrorCode.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.
*/
/**
* Possible roles.
* @public
*/
const POSSIBLE_ROLES = ['user', 'model', 'function', 'system'];
/**
* Harm categories that would cause prompts or candidates to be blocked.
* @public
*/
var HarmCategory;
(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";
})(HarmCategory || (HarmCategory = {}));
/**
* Threshold above which a prompt or candidate will be blocked.
* @public
*/
var HarmBlockThreshold;
(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";
})(HarmBlockThreshold || (HarmBlockThreshold = {}));
/**
* @public
*/
var HarmBlockMethod;
(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";
})(HarmBlockMethod || (HarmBlockMethod = {}));
/**
* Probability that a prompt or candidate matches a harm category.
* @public
*/
var HarmProbability;
(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";
})(HarmProbability || (HarmProbability = {}));
/**
* Harm severity levels.
* @public
*/
var HarmSeverity;
(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";
})(HarmSeverity || (HarmSeverity = {}));
/**
* Reason that a prompt was blocked.
* @public
*/
var BlockReason;
(function (BlockReason) {
// Content was blocked by safety settings.
BlockReason["SAFETY"] = "SAFETY";
// Content was blocked, but the reason is uncategorized.
BlockReason["OTHER"] = "OTHER";
})(BlockReason || (BlockReason = {}));
/**
* Reason that a candidate finished.
* @public
*/
var FinishReason;
(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";
})(FinishReason || (FinishReason = {}));
/**
* @public
*/
var FunctionCallingMode;
(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";
})(FunctionCallingMode || (FunctionCallingMode = {}));
/**
* Content part modality.
* @public
*/
var Modality;
(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";
})(Modality || (Modality = {}));
/**
* @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
*/
var SchemaType;
(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";
})(SchemaType || (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
*/
var ImagenSafetyFilterLevel;
(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";
})(ImagenSafetyFilterLevel || (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"><code>personGeneration</code></a>
* documentation for more details.
*
* @beta
*/
var ImagenPersonFilterLevel;
(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";
})(ImagenPersonFilterLevel || (ImagenPersonFilterLevel = {}));
/**
* Aspect ratios for Imagen images.
*
* To specify an aspect ratio for generated images, set the `aspectRatio` property in your
* <code>{@link ImagenGenerationConfig}</code>.
*
* 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
*/
var ImagenAspectRatio;
(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";
})(ImagenAspectRatio || (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.
*/
/**
* 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 VertexAIError("response-error" /* VertexAIErrorCode.RESPONSE_ERROR */, `Response error: ${formatBlockErrorMessage(response)}. Response body stored in error.response`, {
response
});
}
return getText(response);
}
else if (response.promptFeedback) {
throw new VertexAIError("response-error" /* VertexAIErrorCode.RESPONSE_ERROR */, `Text not available. ${formatBlockErrorMessage(response)}`, {
response
});
}
return '';
};
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 VertexAIError("response-error" /* VertexAIErrorCode.RESPONSE_ERROR */, `Response error: ${formatBlockErrorMessage(response)}. Response body stored in error.response`, {
response
});
}
return getFunctionCalls(response);
}
else if (response.promptFeedback) {
throw new VertexAIError("response-error" /* VertexAIErrorCode.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 <code>{@link FunctionCall}</code>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;
}
}
const badFinishReasons = [FinishReason.RECITATION, 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 VertexAIError("response-error" /* VertexAIErrorCode.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 VertexAIError("response-error" /* VertexAIErrorCode.RESPONSE_ERROR */, `Predictions array in response has missing properties. Response: ${JSON.stringify(responseJson)}`);
}
}
return { images, filteredReason };
}
/**
* @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) {
const inputStream = response.body.pipeThrough(new TextDecoderStream('utf8', { fatal: true }));
const responseStream = getResponseStream(inputStream);
const [stream1, stream2] = responseStream.tee();
return {
stream: generateResponseSequence(stream1),
response: getResponsePromise(stream2)
};
}
async function getResponsePromise(stream) {
const allResponses = [];
const reader = stream.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) {
const enhancedResponse = createEnhancedContentResponse(aggregateResponses(allResponses));
return enhancedResponse;
}
allResponses.push(value);
}
}
function generateResponseSequence(stream) {
return __asyncGenerator(this, arguments, function* generateResponseSequence_1() {
const reader = stream.getReader();
while (true) {
const { value, done } = yield __await(reader.read());
if (done) {
break;
}
const enhancedResponse = createEnhancedContentResponse(value);
yield yield __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 VertexAIError("parse-failed" /* VertexAIErrorCode.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 VertexAIError("parse-failed" /* VertexAIErrorCode.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 VertexAIError("invalid-content" /* VertexAIErrorCode.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) {
const response = await makeRequest(model, Task.STREAM_GENERATE_CONTENT, apiSettings,
/* stream */ true, JSON.stringify(params), requestOptions);
return processStream(response);
}
async function generateContent(apiSettings, model, params, requestOptions) {
const response = await makeRequest(model, Task.GENERATE_CONTENT, apiSettings,
/* stream */ false, JSON.stringify(params), requestOptions);
const responseJson = await response.json();
const enhancedResponse = createEnhancedContentResponse(responseJson);
return {
response: enhancedResponse
};
}
/**
* @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 VertexAIError("invalid-content" /* VertexAIErrorCode.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 VertexAIError("invalid-content" /* VertexAIErrorCode.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 <code>{@link ImagenGenerationParams}</code> 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 a