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


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

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

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

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

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

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

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

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

  • Firebase AI Logic सेट अप करें: अगर आपने अब तक ऐसा नहीं किया है, तो Firebase AI Logic शुरुआती निर्देश पढ़ें. इसमें Firebase प्रोजेक्ट को सेट अप करने, अपने ऐप्लिकेशन को Firebase से कनेक्ट करने, और SDK टूल जोड़ने का तरीका बताया गया है. साथ ही, इसमें यह भी बताया गया है कि चुने गए Gemini API प्रोवाइडर के लिए बैकएंड सेवा को कैसे शुरू करें और GenerativeModel इंस्टेंस कैसे बनाएं.

  • ज़रूरी अनुमतियां: पक्का करें कि आपके पास Cloud Functions for Firebase पर डिप्लॉय करने के लिए ज़रूरी IAM अनुमतियां हों.

पहला चरण: 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 से तुलना करें.

  • Throwing blocks the request: If you throw an HttpsError, the request will be rejected.

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

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

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

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

अपना फ़ंक्शन लिखने से पहले, यह जानकारी ज़रूर देखें:

उदाहरण

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

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 सीएलआई का इस्तेमाल करके, इनमें से किसी एक विकल्प की मदद से ऐसा किया जा सकता है:

  • पहला विकल्प: फ़ंक्शन को सीधे तौर पर मिटाना

    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 से पुष्टि करने वाला उसका यूआईडी.
  • 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 अनुरोध: ये फ़ंक्शन, Firebase AI Logic के ज़रिए Gemini API को किए गए generateContent अनुरोधों से ही ट्रिगर किए जा सकते हैं.

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

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

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

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

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

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

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