अनुरोधों से पहले और बाद में, सर्वर-साइड की कस्टम स्क्रिप्ट चलाना


आपके पास हर उस अनुरोध से पहले और बाद में, सर्वर-साइड की अपनी कस्टम स्क्रिप्ट चलाने का विकल्प होता है जिसे आपका ऐप्लिकेशन, Firebase AI Logic के ज़रिए Gemini API को भेजता है. ऐसा क्लाइंट कोड में बदलाव किए बिना किया जा सकता है. इन स्क्रिप्ट को, Cloud Functions for Firebase पर डिप्लॉय किए गए कॉलबैक-स्टाइल वाले फ़ंक्शन के तौर पर लागू किया जाता है.

इस सुविधा की मदद से, कई काम किए जा सकते हैं. जैसे, प्रॉम्प्ट को मॉडरेट करना, टोकन के इस्तेमाल की सीमा तय करना, आंकड़ों के लिए जनरेट किए गए डेटा को लॉग करना या जवाब के कॉन्टेंट को छिपाना.

दो तरह के इवेंट उपलब्ध हैं:

  • beforeGenerateContent: यह फ़ंक्शन, अनुरोध के Gemini API तक पहुंचने से पहले चलता है. यह फ़ंक्शन, अनुरोध की जांच कर सकता है या उसमें बदलाव कर सकता है. इसके अलावा, गड़बड़ी की सूचना देकर पूरे अनुरोध को ब्लॉक कर सकता है.

  • afterGenerateContent: यह फ़ंक्शन, Gemini API से जवाब वापस भेजे जाने के बाद और क्लाइंट ऐप्लिकेशन को वापस भेजे जाने से पहले चलता है. यह फ़ंक्शन, जवाब की जांच कर सकता है या उसमें बदलाव कर सकता है. साथ ही, जवाब को पूरी तरह से ब्लॉक कर सकता है या सिर्फ़ उसकी निगरानी कर सकता है. जैसे, लॉगिंग या ऑडिटिंग के लिए.

जब आपकी स्क्रिप्ट को Cloud Functions for Firebase में फ़ंक्शन के तौर पर डिप्लॉय किया जाता है, तब उन्हें Firebase AI Logic ट्रिगर के तौर पर रजिस्टर किया जाता है. इसका मतलब है कि वे आपके प्रोजेक्ट में generateContent के हर अनुरोध के लिए Gemini API के ज़रिए Firebase AI Logic पर चलेंगी. इनमें सर्वर प्रॉम्प्ट टेंप्लेट का इस्तेमाल करके किए गए अनुरोध भी शामिल हैं.

ये फ़ंक्शन, Gemini API को किए गए ऐसे अनुरोधों से ट्रिगर नहीं होते हैं जो Firebase AI Logic के ज़रिए नहीं किए गए हैं.

ज़रूरी शर्तें

पहला चरण: Cloud Functions for Firebase के लिए अपना प्रोजेक्ट सेट अप करना

अगर आपने अपने Firebase प्रोजेक्ट में Cloud Functions for Firebase का इस्तेमाल कभी नहीं किया है, तो यहां दिया गया सेटअप पूरा करें.

  1. पक्का करें कि आपका Firebase प्रोजेक्ट, इस्तेमाल के हिसाब से पैसे चुकाने वाले ब्लेज़ प्लान पर हो. Cloud Functions for Firebase का इस्तेमाल करने के लिए, यह ज़रूरी है.

  2. कमांड-लाइन इंटरफ़ेस (सीएलआई) इंस्टॉल करें: gcloud CLI और Firebase सीएलआई

  3. Compute के डिफ़ॉल्ट सेवा खाते को Cloud Build सेवा खाता की भूमिका (roles/cloudbuild.builds.builder) असाइन करें. यह भूमिका, आपके फ़ंक्शन को बनाने के लिए ज़रूरी है. यह gcloud CLI कमांड चलाएं:

    gcloud projects add-iam-policy-binding PROJECT_ID \
      --member="serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com" \
      --role="roles/cloudbuild.builds.builder"
    
  4. अपने Firebase प्रोजेक्ट में Cloud Functions for Firebase को शुरू करें:

    1. Firebase सीएलआई की यह कमांड चलाएं:

      firebase init functions
      
    2. जब कहा जाए, तब TypeScript चुनें.

    3. पक्का करें कि आपके functions/package.json में firebase-functions का वर्शन 6.3.0 या उसके बाद का हो. अपना वर्शन देखने का तरीका यहां दिया गया है:

      npm --prefix functions list firebase-functions
      

दूसरा चरण: अपने फ़ंक्शन लिखना

अनुरोध से पहले फ़ंक्शन लिखें (beforeGenerateContent) अनुरोध के बाद फ़ंक्शन लिखें (afterGenerateContent)

अनुरोध से पहले का फ़ंक्शन लिखना (beforeGenerateContent)

beforeGenerateContent इवेंट टाइप के साथ, यह फ़ंक्शन तब ट्रिगर होता है, जब Firebase AI Logic प्रॉक्सी को generateContent अनुरोध मिलता है. यह फ़ंक्शन, Gemini API को अनुरोध भेजे जाने से पहले अनुरोध के ख़िलाफ़ काम करता है. यह फ़ंक्शन, अनुरोध में बदलाव कर सकता है या अनुरोध को पूरी तरह से ब्लॉक कर सकता है.

फ़ंक्शन लिखने से पहले, पक्का करें कि आपने यह जानकारी पढ़ ली हो:

उदाहरण

यहां प्री-अनुरोध फ़ंक्शन का एक उदाहरण दिया गया है. यह फ़ंक्शन ये काम करता है:

  • इससे यह तय होता है कि फ़ंक्शन सिर्फ़ तब चलना चाहिए, जब अनुरोध किसी खास Gemini API प्रोवाइडर के लिए हो.

  • यह फ़ंक्शन, ब्लॉक किए गए विषयों के लिए प्रॉम्प्ट की जांच करता है. साथ ही, गड़बड़ी का मैसेज दिखाकर अनुरोध को अस्वीकार कर देता है.

  • यह विकल्प, टेक्स्ट जनरेट करने वाले मॉडल के लिए ज़्यादा से ज़्यादा आउटपुट टोकन की सीमा तय करता है.

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,
      ),
    },
  };
});

अनुरोध से पहले के फ़ंक्शन के लिए ज़रूरी बातें

  • Gemini API सेवा देने वाली कंपनी का नाम बताएं: event.data.request, Gemini Developer API या Agent Platform Gemini API (formerly Vertex AI) के लिए हो सकता है. इन अलग-अलग एपीआई के लिए अनुरोध ऑब्जेक्ट अलग-अलग तरह के होते हैं. अनुरोध ऑब्जेक्ट के साथ सुरक्षित तरीके से काम करने के लिए, आपको event.data.api की जांच करनी होगी. उदाहरण के लिए, इसकी तुलना geminiV1Beta या vertexV1Beta1 से करें.

  • फेंकने पर अनुरोध अस्वीकार कर दिया जाता है: अगर आपने HttpsError फेंका है, तो अनुरोध अस्वीकार कर दिया जाएगा.

  • पूरा अनुरोध वापस भेजें: अगर आपका फ़ंक्शन अनुरोध में बदलाव करता है, तो आपको बदलाव किया गया पूरा अनुरोध ऑब्जेक्ट वापस भेजना होगा. कोई वैल्यू न लौटाने (या undefined) पर, अनुरोध में कोई बदलाव नहीं होता.

  • लेटेंसी की जांच करें: आपके फ़ंक्शन के आधार पर, इसमें लेटेंसी बढ़ सकती है और उपयोगकर्ता अनुभव पर असर पड़ सकता है.

पोस्ट-अनुरोध फ़ंक्शन लिखना (afterGenerateContent)

afterGenerateContent इवेंट टाइप के साथ, यह फ़ंक्शन तब ट्रिगर होता है, जब Firebase AI Logic प्रॉक्सी को generateContent अनुरोध से जवाब मिलता है. यह फ़ंक्शन, जवाब को क्लाइंट ऐप्लिकेशन को भेजने से पहले, जवाब के साथ काम करता है. यह फ़ंक्शन, इस्तेमाल की जानकारी को लॉग कर सकता है, जवाब में बदलाव कर सकता है या जवाब को पूरी तरह से ब्लॉक कर सकता है.

फ़ंक्शन लिखने से पहले, पक्का करें कि आपने यह जानकारी पढ़ ली हो:

उदाहरण

यहां उदाहरण के तौर पर post-request फ़ंक्शन दिया गया है. यह फ़ंक्शन, टोकन के इस्तेमाल और अनुरोध पूरा होने की वजह को लॉग करता है:

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.
});

अनुरोध के बाद के फ़ंक्शन के लिए ध्यान रखने वाली मुख्य बातें

  • Gemini API सेवा देने वाली कंपनी का नाम बताएं: event.data.response, Gemini Developer API या Agent Platform Gemini API (formerly Vertex AI) के लिए हो सकता है. अनुरोध ऑब्जेक्ट को सुरक्षित तरीके से कास्ट करने और उसके साथ काम करने के लिए, आपको event.data.api की जांच करनी होगी. उदाहरण के लिए, geminiV1Beta या vertexV1Beta1 से तुलना करें.

  • लेटेंसी की जांच करें: आपके फ़ंक्शन के आधार पर, इसमें लेटेंसी बढ़ सकती है और उपयोगकर्ता अनुभव पर असर पड़ सकता है.

तीसरा चरण: फ़ंक्शन डिप्लॉय करना

अपने फ़ंक्शन को Firebase पर डिप्लॉय करने से, Firebase AI Logic सेवा एजेंट को इन फ़ंक्शन को शुरू करने की अनुमति मिलती है. साथ ही, हर फ़ंक्शन को Firebase AI Logic ट्रिगर के तौर पर रजिस्टर किया जाता है.

  1. Firebase सीएलआई का इस्तेमाल करके, अपने फ़ंक्शन डिप्लॉय करें:

    firebase deploy --only functions
    
  2. डिप्लॉय करने के बाद, पुष्टि करें कि आपके फ़ंक्शन Firebase पर डिप्लॉय हो गए हैं:

    firebase functions:list
    
  3. अगर आपको अपने फ़ंक्शन को दोहराना है, तो:

    अपनी प्रोजेक्ट डायरेक्ट्री में फ़ंक्शन को अपडेट करें. इसके बाद, firebase deploy --only functions को फिर से चलाएं.

किसी फ़ंक्शन को चलने से रोकना

इनमें से किसी फ़ंक्शन को बंद करने के लिए, उसे हमारे सर्वर से मिटाना होगा और Firebase AI Logic ट्रिगर के तौर पर अनरजिस्टर करना होगा. इसके लिए, Firebase CLI का इस्तेमाल करें. साथ ही, इनमें से कोई एक विकल्प चुनें:

  • पहला विकल्प: फ़ंक्शन को परोक्ष रूप से मिटाना

    1. अपने प्रोजेक्ट डायरेक्ट्री के कोडबेस से फ़ंक्शन हटाएं.

    2. Firebase सीएलआई की यह कमांड चलाएं:

      firebase deploy --only functions
      
  • दूसरा विकल्प: फ़ंक्शन को साफ़ तौर पर मिटाना

    1. अपने प्रोजेक्ट डायरेक्ट्री के कोडबेस से फ़ंक्शन हटाएं.

    2. Firebase सीएलआई की यह कमांड चलाएं:

      firebase functions:delete FUNCTION_NAME
      
फ़ंक्शन के मौजूद न होने की वजह से, अनुरोध पूरा नहीं हो पाता.



इवेंट डेटा का रेफ़रंस

beforeGenerateContent और afterGenerateContent, दोनों को AIBlockingEvent ऑब्जेक्ट मिलता है. इसमें अनुरोध के बारे में कॉन्टेक्स्ट और मेटाडेटा होता है.

टॉप-लेवल के अनुरोध का मेटाडेटा (AIBlockingEvent)

टॉप-लेवल AIBlockingEvent ऑब्जेक्ट, कॉल करने वाले और ट्रिगर करने वाले एनवायरमेंट के बारे में जानकारी देता है:

  • event.authType: कॉलर के लिए पुष्टि करने की स्थिति: "app_user", "unauthenticated" या "unknown".
  • event.authId: अगर कॉलर ने साइन इन किया है, तो उसका Firebase Authentication यूआईडी.
  • event.authClaims: कॉलर के कस्टम ऑथराइज़ेशन के दावे. अगर कोई दावा मौजूद है, तो उसे शामिल करें.
  • event.appId: यह अनुरोध करने वाले ऐप्लिकेशन का Firebase ऐप्लिकेशन आईडी है.
  • event.androidPackageName / event.iosBundleId: कॉल करने वाले ऐप्लिकेशन का पैकेज नाम या बंडल आईडी. यह जानकारी, Android या Apple प्लैटफ़ॉर्म पर लागू होती है.
  • event.data: इवेंट पेलोड, जो प्री-रिक्वेस्ट और पोस्ट-रिक्वेस्ट फ़ंक्शन के बीच अलग-अलग होता है:

अनुरोध से पहले के इवेंट डेटा (beforeGenerateContent)

beforeGenerateContent फ़ंक्शन में, event.data को BeforeGenerateContentData ऑब्जेक्ट से भरा जाता है:

  • event.data.api: Gemini API की सेवा देने वाली कंपनी: geminiV1Beta (Gemini Developer API) या vertexV1Beta1 (Agent Platform Gemini API (formerly Vertex AI)).
  • event.data.model: मॉडल के संसाधन का पूरा पाथ (उदाहरण के लिए, projects/{PROJECT_ID}/locations/global/publishers/google/models/gemini-3.8-flash).
  • event.data.template: इस्तेमाल किए गए सर्वर प्रॉम्प्ट टेंप्लेट (PromptTemplateInfo) के बारे में मेटाडेटा. यह जानकारी तब लागू होती है, जब सर्वर प्रॉम्प्ट टेंप्लेट का इस्तेमाल किया गया हो.
  • event.data.request: यह आउटगोइंग अनुरोध का पेलोड होता है. ऑब्जेक्ट टाइप और प्रॉपर्टी, Gemini API प्रोवाइडर पर निर्भर करती हैं:

अनुरोध के बाद का इवेंट डेटा (afterGenerateContent)

afterGenerateContent फ़ंक्शन में, event.data को AfterGenerateContentData ऑब्जेक्ट से भरा जाता है. यह ऑब्जेक्ट, BeforeGenerateContentData (api, model, template, और request उपलब्ध कराता है) को बढ़ाता है. साथ ही, इसमें मॉडल का जवाब जोड़ता है:

  • event.data.response: मॉडल के जवाब का पेलोड. ऑब्जेक्ट टाइप और प्रॉपर्टी, Gemini API प्रोवाइडर पर निर्भर करती हैं:



सीमाएं और व्यवहार

इन फ़ंक्शन को लागू करते समय, इन व्यवहारों और सीमाओं को ध्यान में रखें:

  • सिर्फ़ generateContent अनुरोध: इन फ़ंक्शन को सिर्फ़ generateContent अनुरोधों के ज़रिए ट्रिगर किया जा सकता है. ये अनुरोध, Firebase AI Logic के ज़रिए Gemini API को भेजे जाते हैं.

    इन अनुरोधों के लिए, ये फ़ंक्शन ट्रिगर नहीं होंगे. साथ ही, इन फ़ंक्शन को साइलेंटली बायपास कर दिया जाएगा:

    • generateContentStream के अनुरोधों से, ये फ़ंक्शन ट्रिगर नहीं होंगे.

    • Gemini Live API के अनुरोधों से, ये फ़ंक्शन ट्रिगर नहीं होंगे.

  • क्लाइंट-साइड कोड में कोई बदलाव नहीं किया गया है: यह पक्का करने के अलावा कि इन फ़ंक्शन को चलाने के लिए, generateContent अनुरोधों का इस्तेमाल किया जा रहा है, आपको अपने क्लाइंट-साइड कोडबेस में कोई बदलाव करने की ज़रूरत नहीं है.

    इन फ़ंक्शन को हमारे सर्वर पर डिप्लॉय किया जाता है. साथ ही, इन्हें Firebase AI Logic ट्रिगर के तौर पर रजिस्टर किया जाता है, ताकि Firebase AI Logic प्रॉक्सी, सर्वर-साइड पर अनुरोधों और जवाबों को इंटरसेप्ट कर सके.

  • प्रोजेक्ट-लेवल का स्कोप: हर Firebase प्रोजेक्ट के लिए, ज़्यादा से ज़्यादा एक beforeGenerateContent फ़ंक्शन और एक afterGenerateContent फ़ंक्शन डिप्लॉय किया जा सकता है.

  • डिफ़ॉल्ट जगहें: ये फ़ंक्शन डिफ़ॉल्ट रूप से us-central1 पर डिप्लॉय किए जाएंगे. फ़ंक्शन के लिए जगहों के बारे में जानें. हालांकि, फ़ंक्शन को global क्षेत्र में Firebase AI Logic ट्रिगर के तौर पर रजिस्टर किया जाएगा. इससे कोई फ़र्क़ नहीं पड़ता कि आपने फ़ंक्शन को कहां डिप्लॉय किया है.