Ejecuta secuencias de comandos personalizadas del servidor antes y después de las solicitudes


Puedes ejecutar tus propias secuencias de comandos personalizadas del servidor antes y después de cada solicitud que tu app envíe a Gemini API a través de Firebase AI Logic, sin cambiar el código del cliente. Implementas estas secuencias de comandos como funciones de estilo de devolución de llamada implementadas en Cloud Functions for Firebase.

Con esta capacidad, puedes realizar acciones como moderar instrucciones, limitar el uso de tokens, registrar generaciones para estadísticas o redactar contenido de respuesta.

Hay dos tipos de eventos disponibles:

  • beforeGenerateContent: Se ejecuta antes de que una solicitud llegue al Gemini API. La función puede inspeccionar o modificar la solicitud, o bien bloquearla por completo arrojando un error.

  • afterGenerateContent: Se ejecuta después de que se envía la respuesta desde el Gemini API y antes de que se devuelva a la app cliente. La función puede inspeccionar o modificar la respuesta, bloquearla por completo o solo observarla (como para el registro o la auditoría).

Una vez que tus secuencias de comandos se implementen como funciones en Cloud Functions for Firebase, se registrarán como activadores de Firebase AI Logic, lo que significa que se ejecutarán para cada solicitud de generateContent en tu proyecto a Gemini API a través de Firebase AI Logic (incluidas las solicitudes realizadas con plantillas de instrucciones del servidor).

Estas funciones no se activan con las solicitudes realizadas a Gemini API que no se realizan a través de Firebase AI Logic.

Requisitos previos

Paso 1: Configura tu proyecto para Cloud Functions for Firebase

Si nunca usaste Cloud Functions for Firebase en tu proyecto de Firebase, completa la siguiente configuración.

  1. Asegúrate de que tu proyecto de Firebase tenga el plan de precios Blaze de pago por uso (obligatorio para usar Cloud Functions for Firebase).

  2. Instala las interfaces de línea de comandos (CLI): gcloud CLI y CLI de Firebase

  3. Otorga a la cuenta de servicio de Compute predeterminada el rol de cuenta de servicio de Cloud Build (roles/cloudbuild.builds.builder) que necesita para compilar tu función. Ejecuta el siguiente comando de gcloud CLI:

    gcloud projects add-iam-policy-binding PROJECT_ID \
      --member="serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com" \
      --role="roles/cloudbuild.builds.builder"
    
  4. Inicializa Cloud Functions for Firebase en tu proyecto de Firebase:

    1. Ejecuta el siguiente comando de la CLI de Firebase:

      firebase init functions
      
    2. Cuando se te solicite, elige TypeScript.

    3. Asegúrate de que firebase-functions en tu functions/package.json sea la versión 6.3.0 o una posterior. Sigue estos pasos para verificar la versión:

      npm --prefix functions list firebase-functions
      

Paso 2: Escribe tus funciones

Escribir una función de solicitud previa (beforeGenerateContent) Escribir una función de solicitud posterior (afterGenerateContent)

Escribe una función de solicitud previa (beforeGenerateContent)

Con el tipo de evento beforeGenerateContent, la función se activa cuando el proxy Firebase AI Logic recibe una solicitud generateContent. La función se ejecuta en la solicitud antes de que se envíe a Gemini API. La función puede modificar la solicitud o bloquearla por completo.

Asegúrate de revisar la siguiente información antes de escribir tu función:

Ejemplo

Aquí tienes un ejemplo de función de solicitud previa que hace lo siguiente:

  • Especifica que la función solo debe ejecutarse cuando la solicitud es para un proveedor de Gemini API específico.

  • Inspecciona la instrucción en busca de temas bloqueados y rechaza la solicitud arrojando un error.

  • Limita la cantidad máxima de tokens de salida para los modelos generativos de texto.

import { logger } from "firebase-functions";
import {
  beforeGenerateContent,
  HttpsError,
  vertexV1Beta1,
  type VertexV1Beta1GenerateContentRequest,
} from "firebase-functions/v2/ai";

const BLOCKED_TOPICS = ["weapon", "explosive", "self-harm"];
const MAX_OUTPUT_TOKENS = 4000;

export const guardPrompts = beforeGenerateContent((event) => {
  // 1. Optional: If you want the function to only run for a specific Gemini API provider, specify it here.
  if (event.data.api !== vertexV1Beta1) return;
  const request = event.data.request as VertexV1Beta1GenerateContentRequest;

  // 2. Read the prompt: contents[] -> parts[] -> text
  const prompt = (request.contents ?? [])
    .flatMap((c) => c.parts ?? [])
    .map((p) => ("text" in p ? p.text : "") ?? "")
    .join(" ")
    .toLowerCase();

  // 3. Throwing rejects the request. The request is never sent to the Gemini API.
  const blocked = BLOCKED_TOPICS.find((t) => prompt.includes(t));
  if (blocked) {
    logger.warn("Blocked a prompt", { topic: blocked });
    throw new HttpsError("invalid-argument", `We don't return content about ${blocked}.`);
  }

  logger.info("Allowing generation", {
    model: event.data.model,
    authType: event.authType,
    authId: event.authId,
    appId: event.appId,
  });

  // 4. The next step truncates the response, but that will break images.
  if (event.data.model.includes("image")) return;

  // 5. Return the WHOLE request, edited. Returning nothing leaves it untouched.
  return {
    ...request,
    generationConfig: {
      ...request.generationConfig,
      maxOutputTokens: Math.min(
        request.generationConfig?.maxOutputTokens ?? MAX_OUTPUT_TOKENS,
        MAX_OUTPUT_TOKENS,
      ),
    },
  };
});

Consideraciones clave para las funciones de solicitud previa

  • Especifica el proveedor de Gemini API: event.data.request puede ser para Gemini Developer API o Agent Platform Gemini API (formerly Vertex AI). Los objetos de solicitud para estas diferentes APIs tienen diferentes formas. Para trabajar de forma segura con el objeto de solicitud, debes verificar event.data.api (por ejemplo, compararlo con geminiV1Beta o vertexV1Beta1, respectivamente).

  • La generación de excepciones bloquea la solicitud: Si generas una HttpsError, se rechazará la solicitud.

  • Devuelve la solicitud completa: Si tu función modifica la solicitud, debes devolver el objeto de solicitud completo y modificado. Si no se devuelve nada (o se devuelve undefined), la solicitud no se modifica.

  • Prueba la latencia: Según lo que haga tu función, podría agregar latencia y afectar la experiencia del usuario.

Escribe una función posterior a la solicitud (afterGenerateContent)

Con el tipo de evento afterGenerateContent, la función se activa cuando el proxy Firebase AI Logic recibe una respuesta de una solicitud generateContent. La función se ejecuta en la respuesta antes de que se devuelva a la app cliente. La función puede registrar el uso, modificar la respuesta o bloquearla por completo.

Asegúrate de revisar la siguiente información antes de escribir tu función:

Ejemplo

Este es un ejemplo de una función post-request que registra el uso de tokens y el motivo de finalización:

import { logger } from "firebase-functions";
import {
  afterGenerateContent,
  vertexV1Beta1,
  type VertexV1Beta1GenerateContentResponse,
} from "firebase-functions/v2/ai";

export const recordGenerationUsage = afterGenerateContent((event) => {
  // Optional: If you want the function to only run for a specific Gemini API provider, specify it here.
  if (event.data.api !== vertexV1Beta1) return;
  const response = event.data.response as VertexV1Beta1GenerateContentResponse;

  logger.info("Generation finished", {
    model: event.data.model,
    promptTokens: response.usageMetadata?.promptTokenCount,
    totalTokens: response.usageMetadata?.totalTokenCount,
    finishReason: response.candidates?.[0]?.finishReason,
  });

  // To leave the response untouched, return nothing.
  // To modify the response, return a modified response object here.
});

Consideraciones clave para las funciones posteriores a la solicitud

  • Especifica el proveedor de Gemini API: event.data.response puede ser para Gemini Developer API o Agent Platform Gemini API (formerly Vertex AI). Para transmitir y trabajar de forma segura con el objeto de solicitud, debes verificar event.data.api (por ejemplo, compararlo con geminiV1Beta o vertexV1Beta1, respectivamente).

  • Prueba la latencia: Según lo que haga tu función, podría agregar latencia y afectar la experiencia del usuario.

Paso 3: Implementa tus funciones

Cuando implementas tus funciones en Firebase, se le otorga permiso al agente de servicio de Firebase AI Logic para que invoque estas funciones y se registra cada función como un activador de Firebase AI Logic.

  1. Implementa tus funciones con Firebase CLI:

    firebase deploy --only functions
    
  2. Después de la implementación, confirma que tus funciones se hayan implementado en Firebase:

    firebase functions:list
    
  3. Si necesitas iterar en tu función, haz lo siguiente:

    Actualiza la función en el directorio de tu proyecto y, luego, vuelve a ejecutar firebase deploy --only functions.

Detén la ejecución de una función

Para detener la ejecución de una de estas funciones, se debe borrar de nuestros servidores y se debe cancelar su registro como activador de Firebase AI Logic. Puedes hacerlo con la CLI de Firebase con cualquiera de las siguientes opciones:

  • Opción 1: Borra la función de forma implícita

    1. Quita la función de la base de código del directorio de tu proyecto.

    2. Ejecuta el siguiente comando de la CLI de Firebase:

      firebase deploy --only functions
      
  • Opción 2: Borra la función de forma explícita

    1. Quita la función de la base de código del directorio de tu proyecto.

    2. Ejecuta el siguiente comando de la CLI de Firebase:

      firebase functions:delete FUNCTION_NAME
      



Referencia de datos de eventos

Tanto beforeGenerateContent como afterGenerateContent reciben un objeto AIBlockingEvent que contiene el contexto y los metadatos sobre la solicitud.

Metadatos de solicitud de nivel superior (AIBlockingEvent)

El objeto AIBlockingEvent de nivel superior proporciona información sobre el emisor y el entorno de activación:

  • event.authType: Es el estado de autenticación del emisor: "app_user", "unauthenticated" o "unknown".
  • event.authId: Es el UID de Firebase Authentication del emisor, si accedió.
  • event.authClaims: Las reclamaciones de autenticación personalizadas del llamador, si las hay.
  • event.appId: Es el ID de la app de Firebase que realizó la solicitud.
  • event.androidPackageName/event.iosBundleId: Es el nombre del paquete o el ID del paquete de la app que realiza la llamada (aplicable para las plataformas de Android o Apple, respectivamente).
  • event.data: Es la carga útil del evento, que difiere entre las funciones previas y posteriores a la solicitud:

Datos de eventos previos a la solicitud (beforeGenerateContent)

En una función beforeGenerateContent, event.data se propaga con un objeto BeforeGenerateContentData:

  • event.data.api: Es el proveedor de Gemini API: geminiV1Beta (Gemini Developer API) o vertexV1Beta1 (Agent Platform Gemini API (formerly Vertex AI)).
  • event.data.model: Es la ruta de acceso completa del recurso del modelo (por ejemplo, projects/{PROJECT_ID}/locations/global/publishers/google/models/gemini-3.8-flash).
  • event.data.template: Son los metadatos sobre la plantilla de instrucciones del servidor que se usó (PromptTemplateInfo), si corresponde.
  • event.data.request: Es la carga útil de la solicitud saliente. El tipo de objeto y las propiedades dependen del proveedor de Gemini API:

Datos de eventos posteriores a la solicitud (afterGenerateContent)

En una función afterGenerateContent, event.data se propaga con un objeto AfterGenerateContentData. Este objeto extiende BeforeGenerateContentData (proporciona api, model, template y request) y agrega la respuesta del modelo:



Limitaciones y comportamientos

Cuando implementes estas funciones, ten en cuenta los siguientes comportamientos y limitaciones:

  • Solo solicitudes generateContent: Estas funciones solo se pueden activar con solicitudes generateContent a Gemini API a través de Firebase AI Logic.

    Las siguientes acciones no activarán estas funciones, y las funciones se omitirán de forma silenciosa para esa solicitud:

    • Las solicitudes a generateContentStream no activarán estas funciones.

    • Las solicitudes a Gemini Live API no activarán estas funciones.

  • No se requieren cambios en el código del cliente: Además de asegurarte de usar solicitudes generateContent cuando quieras ejecutar estas funciones, no se requieren cambios en la base de código del cliente.

    Estas funciones se implementan en nuestros servidores y se registran como activadores de Firebase AI Logic para que el proxy de Firebase AI Logic pueda interceptar solicitudes y respuestas del lado del servidor.

  • Permiso a nivel del proyecto: Puedes implementar, como máximo, una función beforeGenerateContent y una función afterGenerateContent por proyecto de Firebase.

  • Ubicaciones predeterminadas: Estas funciones se implementarán en us-central1 de forma predeterminada (obtén más información sobre las ubicaciones de las funciones). Sin embargo, la función se registrará como un activador de Firebase AI Logic en la región global, sin importar dónde la implementes.