অনুরোধের আগে ও পরে কাস্টম সার্ভার-সাইড স্ক্রিপ্ট রান করা


আপনার অ্যাপ 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 প্রোজেক্ট যখন ব্যবহার করবেন তখন পেমেন্ট করুন Blaze প্রাইসিং প্ল্যানে (Cloud Functions for Firebase ব্যবহার করার জন্য প্রয়োজন) আছে কিনা তা নিশ্চিত করুন।

  2. কমান্ড-লাইন ইন্টারফেস (CLI) ইনস্টল করুন: gcloud CLI এবং Firebase CLI

    আপনার প্রোজেক্ট ফাইল যোগ করেন, তাহলে আরও দ্রুত শুরু করার জন্য এটি একটি সুবিধাজনক বিকল্প হয়ে ওঠে।
  3. ডিফল্ট কম্পিউট পরিষেবা অ্যাকাউন্টকে 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 CLI কমান্ড রান করুন:

      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)-এর জন্য হতে পারে। এইসব আলাদা API-এর জন্য অনুরোধ অবজেক্টের আকার আলাদা হয়। অনুরোধ অবজেক্টের সাথে নিরাপদে কাজ করতে, আপনাকে অবশ্যই event.data.api চেক করতে হবে (যেমন, যথাক্রমে geminiV1Beta বা vertexV1Beta1-এর সাথে তুলনা করুন)।

  • থ্রো করলে অনুরোধ ব্লক হয়ে যায়: আপনি HttpsError থ্রো করলে, অনুরোধ বাতিল হয়ে যাবে।

  • সম্পূর্ণ অনুরোধ ফেরত দিন: আপনার ফাংশন অনুরোধটি পরিবর্তন করলে, আপনাকে অবশ্যই সম্পূর্ণ, পরিবর্তিত অনুরোধ অবজেক্ট ফেরত দিতে হবে। কিছুই ফেরত না দিলে (বা 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 CLI ব্যবহার করে আপনার ফাংশন ডিপ্লয় করুন:

    firebase deploy --only functions
    
  2. ডিপ্লয় করার পরে, আপনার ফাংশন Firebase-এ ডিপ্লয় করা হয়েছে কিনা তা কনফার্ম করুন:

    firebase functions:list
    
  3. আপনার ফাংশন ইটারেট করতে হলে:

    আপনার প্রোজেক্ট ডিরেক্টরিতে ফাংশন আপডেট করুন এবং তারপরে firebase deploy --only functions আবার রান করুন।

কোনও ফাংশন রান করা বন্ধ করুন

এইসব ফাংশনের মধ্যে কোনও একটি চালানো বন্ধ করতে, এটি আমাদের সার্ভার থেকে মুছে ফেলতে হবে এবং Firebase AI Logic ট্রিগার হিসেবে অনিবন্ধিত করতে হবে। আপনি নিম্নলিখিত যেকোনও বিকল্পের সাথে Firebase CLI ব্যবহার করে এটি করতে পারবেন:

  • বিকল্প ১: ফাংশনটি ইমপ্লিসিটলি মুছে দিন

    1. আপনার প্রোজেক্ট ডিরেক্টরি কোডবেস থেকে ফাংশনটি সরান।

    2. নিম্নলিখিত Firebase CLI কমান্ড রান করুন:

      firebase deploy --only functions
      
  • বিকল্প ২: ফাংশনটি স্পষ্টভাবে মুছে দিন

    1. আপনার প্রোজেক্ট ডিরেক্টরি কোডবেস থেকে ফাংশনটি সরান।

    2. নিম্নলিখিত Firebase CLI কমান্ড রান করুন:

      firebase functions:delete FUNCTION_NAME
      



ইভেন্ট ডেটার রেফারেন্স

beforeGenerateContent এবং afterGenerateContent উভয়ই একটি AIBlockingEvent অবজেক্ট পায় যাতে অনুরোধ সম্পর্কে কনটেক্সট এবং মেটাডেটা থাকে।

টপ-লেভেল অনুরোধের মেটাডেটা (AIBlockingEvent)

টপ-লেভেল AIBlockingEvent অবজেক্ট কলার ও ট্রিগার করা এনভায়রনমেন্ট সম্পর্কে তথ্য প্রদান করে:

  • event.authType: কলারের জন্য যাচাইকরণের স্ট্যাটাস: "app_user", "unauthenticated", অথবা "unknown".
  • event.authId: কলারের Firebase Authentication UID, যদি সাইন-ইন করা থাকে।
  • 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-এর মাধ্যমে Gemini API-এ Firebase AI Logic-এর অনুরোধের মাধ্যমে ট্রিগার করা যেতে পারে।

    নিম্নলিখিতগুলি এই ফাংশনগুলিকে ট্রিগার করবে না এবং সেই অনুরোধের জন্য ফাংশনগুলি নীরবে বাইপাস করা হবে:

    • generateContentStream-এ করা অনুরোধ এইসব ফাংশন ট্রিগার করবে না।

    • Gemini Live API-এ করা অনুরোধ এইসব ফাংশন ট্রিগার করবে না।

  • ক্লায়েন্ট-সাইড কোডে কোনও পরিবর্তন করতে হয় না: এই ফাংশনগুলি রান করার সময় আপনি যে generateContent অনুরোধ ব্যবহার করছেন তা নিশ্চিত করা ছাড়া, আপনার ক্লায়েন্ট-সাইড কোডবেসে কোনও পরিবর্তন করতে হয় না।

    এইসব ফাংশন আমাদের সার্ভারে ডিপ্লয় করা হয় এবং সেগুলি Firebase AI Logic ট্রিগার হিসেবে রেজিস্টার করা হয় যাতে Firebase AI Logic প্রক্সি সার্ভার-সাইডে অনুরোধ ও উত্তর ইন্টারসেপ্ট করতে পারে।

  • প্রোজেক্ট-লেভেল স্কোপ: আপনি প্রতিটি Firebase প্রোজেক্টে সর্বাধিক একটি beforeGenerateContent ফাংশন এবং একটি afterGenerateContent ফাংশন ডিপ্লয় করতে পারবেন।

  • ডিফল্ট লোকেশন: এই ফাংশনগুলি ডিফল্টভাবে us-central1-এ ডিপ্লয় করা হবে (ফাংশনের জন্য লোকেশন সম্পর্কে জানুন)। তবে, আপনি যেখানেই ফাংশন ডিপ্লয় করুন না কেন, ফাংশনটি global অঞ্চলে Firebase AI Logic ট্রিগার হিসেবে রেজিস্টার করা হবে।