जर्नल पर वापस जाएं
15 मिनट पठन

React में जीरो-लेटेंसी ऑन-डिवाइस AI: Chrome Gemini Nano और Prompt API प्रोडक्शन में

Chrome के इनबिल्ट Gemini Nano APIs, window.LanguageModel, लोकल PII सैनिटाइजेशन और हाइब्रिड फॉलबैक के साथ जीरो-लेटेंसी, प्राइवेसी-फर्स्ट React इंटरफेस का निर्माण।

React.jsTypeScriptIn-Browser LLMsAI ArchitectureGDPR & PII Management
React में जीरो-लेटेंसी ऑन-डिवाइस AI: Chrome Gemini Nano और Prompt API प्रोडक्शन में

React में जीरो-लेटेंसी ऑन-डिवाइस AI: Chrome Gemini Nano और Prompt API प्रोडक्शन में

आधुनिक वेब एप्लिकेशन में हर कीस्ट्रोक के साथ तुरंत फीडबैक की उम्मीद होती है। जब टीमें ऑटो-कम्प्लीशन, टोन एडजस्टमेंट या रियल-टाइम समरी के लिए टेक्स्ट फील्ड्स में लार्ज लैंग्वेज मॉडल्स को जोड़ती हैं, तो उनका सामना एक अनिवार्य भौतिक सीमा से होता है: नेटवर्क राउंडट्रिप।

ब्राउज़र से क्लाउड LLM गेटवे तक पहले टोकन (TTFT) के लिए 300ms से 1500ms का समय लगता है। यह लेटेंसी एक अलग चैट पैनल के लिए स्वीकार्य हो सकती है, लेकिन यह टेक्स्ट एडिटर्स और इनपुट फॉर्म्स में टाइपिंग के प्रवाह को बाधित करती है। इसके अलावा क्लाउड इन्फरेंस से रिकरिंग टोकन लागत, कोल्ड स्टार्ट्स, रेट लिमिट्स और संवेदनशील यूजर डेटा के क्लाइंट से बाहर जाने पर सुरक्षा संबंधी जोखिम भी जुड़े होते हैं।

Chrome का इन-बिल्ट AI इस समीकरण को बदल देता है क्योंकि यह Gemini Nano को सीधे ब्राउज़र रनटाइम में एम्बेड करता है। यह लेख प्रोडक्शन में लोकल मॉडल्स को चलाने के लिए आवश्यक आर्किटेक्चर, सेशन लाइफसाइकिल, क्लाइंट-साइड डेटा क्लीनिंग और React इंटीग्रेशन पैटर्न्स को विस्तार से प्रस्तुत करता है।

CODE
+-------------------------------------------------------------------------+
| Browser Context (Main Thread)                                           |
|                                                                         |
|  [ User Input ] ---> [ Local PII Sanitizer ] ---> [ useBrowserAI Hook ] |
|                             |                              |            |
|                             v                              v            |
|                     (Cleaned Prompt)             (Streaming Tokens)     |
|                                                            |            |
|                                                            v            |
|                                                  [ <SmartTextArea /> ]  |
+------------------------------------------------------------|------------+
                                                             | IPC / Mojo
+------------------------------------------------------------v------------+
| Chrome Optimization Guide Process (On-Device Runtime)                   |
|                                                                         |
|  [ window.LanguageModel / ai ] <---> [ Gemini Nano Neural Weights ]     |
|  - Sub-20ms TTFT                     - Zero Network Egress              |
|  - Local Session Cache               - GPU/NPU Silicon (LiteRT/Metal)   |
+-------------------------------------------------------------------------+

इन-ब्राउज़र इन्फरेंस मॉडल

Chrome, Gemini Nano को ऑन-डिवाइस न्यूरल फाउंडेशन मॉडल के रूप में वितरित करता है जिसे ब्राउज़र की Optimization Guide सर्विस प्रबंधित करती है। WebAssembly या WebGPU पोर्ट्स के विपरीत, जहाँ यूज़र्स को पहले पेज लोड पर 1GB से 4GB मॉडल वेट्स डाउनलोड करने पड़ते हैं, Chrome बैकग्राउंड Component Updater के माध्यम से बाइनरी डिस्ट्रीब्यूशन को संभालता है।

इन्फरेंस वेब कंटेंट स्क्रिप्ट्स से अलग एक सैंडबॉक्स्ड यूटिलिटी प्रोसेस में चलता है। जावास्क्रिप्ट एक्ज़ीक्यूशन कॉन्टेक्स्ट स्टैंडर्ड ग्लोबल API window.LanguageModel (तथा ट्रांसिशनल नेमस्पेस window.ai.languageModel) के जरिए स्ट्रक्चर्ड इंटर-प्रोसेस कम्युनिकेशन (IPC) से मॉडल से जुड़ता है।

प्रमुख आर्किटेक्चरल विशेषताएँ

  1. स्पष्ट डेटा गोपनीयता: रॉ प्रॉम्प्ट टेक्स्ट कभी भी नेटवर्क सॉकेट को पार नहीं करता। स्वास्थ्य और वित्त जैसे विनियमित क्षेत्रों के लिए लोकल इन्फरेंस बिना किसी डेटा लीकेज के कड़े नियमों का पालन करता है।
  2. 20ms से कम TTFT: क्योंकि मॉडल लोकल मेमोरी में रहता है और ऑन-डिवाइस हार्डवेयर एक्सेलेरेटर्स (Apple Silicon Metal, Windows DirectML, Vulkan) का उपयोग करता है, टोकन जेनरेशन बिना किसी रुकावट के तुरंत शुरू होता है।
  3. शून्य वैरिएबल इन्फ्रास्ट्रक्चर लागत: सभी वर्कलोड्स क्लाइंट सिलिकॉन पर चलते हैं। लाखों यूज़र्स के आने पर भी इन्फरेंस API का खर्च 0$ रहता है।
  4. ऑफलाइन क्षमता: कमजोर मोबाइल नेटवर्क या इंटरनेट न होने पर भी सभी फीचर्स पूरी तरह काम करते हैं।

Chrome बिल्ट-इन AI लाइफसाइकिल

Prompt API कोई साधारण स्टेटलेस फंक्शन कॉल नहीं है। यह एक स्टेटफुल सेशन मॉडल पर आधारित है जिसके लिए क्षमता की जाँच, पैरामीटर ट्यूनिंग और मेमोरी मैनेजमेंट की आवश्यकता होती है।

TYPESCRIPT
// W3C Prompt API के लिए टाइप डेफिनिशन
export type AICapabilityAvailability = "readily" | "after-download" | "no";

export interface AICapabilities {
  available: AICapabilityAvailability;
  defaultTemperature?: number;
  maxTemperature?: number;
  defaultTopK?: number;
  maxTopK?: number;
}

export interface AILanguageModelCreateOptions {
  systemPrompt?: string;
  initialPrompts?: Array<{ role: "system" | "user" | "assistant"; content: string }>;
  temperature?: number;
  topK?: number;
  signal?: AbortSignal;
  monitor?: (monitor: EventTarget) => void;
}

export interface AILanguageModelSession {
  prompt(input: string, options?: { signal?: AbortSignal }): Promise<string>;
  promptStreaming(input: string, options?: { signal?: AbortSignal }): ReadableStream<string>;
  countPromptTokens(input: string): Promise<number>;
  maxTokens: number;
  tokensSoFar: number;
  tokensLeft: number;
  topK: number;
  temperature: number;
  clone(): Promise<AILanguageModelSession>;
  destroy(): void;
}

export interface AILanguageModelFactory {
  availability?(): Promise<AICapabilityAvailability>;
  capabilities?(): Promise<AICapabilities>;
  create(options?: AILanguageModelCreateOptions): Promise<AILanguageModelSession>;
}

declare global {
  interface Window {
    LanguageModel?: AILanguageModelFactory;
    ai?: {
      languageModel?: AILanguageModelFactory;
    };
  }
}

मॉडल की तीन उपलब्धता अवस्थाएँ

सेशन शुरू करने से पहले एप्लिकेशन्स को window.LanguageModel (या window.ai.languageModel) से जाँच करनी होती है। ब्राउज़र इन तीन में से एक अवस्था लौटाता है:

  • "readily": मॉडल बाइनरी लोकल डिस्क पर पहले से मौजूद है और तुरंत उपयोग के लिए तैयार है।
  • "after-download": डिवाइस हार्डवेयर शर्तों को पूरा करता है, लेकिन बाइनरी बैकग्राउंड में डाउनलोड होने की कतार में है। monitor कॉलबैक से डाउनलोड की प्रगति जाँची जा सकती है।
  • "no": डिवाइस में हार्डवेयर सपोर्ट नहीं है (अपर्याप्त VRAM/RAM या असमर्थित GPU) या फीचर फ्लैग बंद है।

क्लाइंट-साइड PII स्क्रबिंग लेयर

लोकल मॉडल का उपयोग करते समय भी सुरक्षा के सर्वोत्तम नियमों के अनुसार संवेदनशील पहचानकर्ताओं (क्रेडिट कार्ड, पहचान पत्र नंबर, ईमेल एड्रेस, टोकन) को प्रॉम्प्ट बनाने से पहले मास्क कर देना चाहिए। इससे शेयर्ड सेशन हिस्ट्री में डेटा मिक्स होने से बचता है।

यहाँ बिना किसी बाहरी डिपेंडेंसी के एक हल्का टोकनाइजेशन फिल्टर दिया गया है:

TYPESCRIPT
// lib/pii-scrubber.ts

interface ScrubRule {
  name: string;
  pattern: RegExp;
  mask: (match: string) => string;
}

const PII_RULES: ScrubRule[] = [
  {
    name: "CREDIT_CARD",
    pattern: /\b(?:\d{4}[-\s]?){3}\d{4}\b/g,
    mask: () => "[REDACTED_CARD]",
  },
  {
    name: "EMAIL",
    pattern: /[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+/g,
    mask: () => "[REDACTED_EMAIL]",
  },
  {
    name: "PHONE",
    pattern: /\b(?:\+?\d{1,3}[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g,
    mask: () => "[REDACTED_PHONE]",
  },
  {
    name: "AUTH_TOKEN",
    pattern: /\b(ey[A-Za-z0-9-_=]+\.[A-Za-z0-9-_=]+\.?[A-Za-z0-9-_.+/=]*)|(ghp_[A-Za-z0-9]{36})\b/g,
    mask: () => "[REDACTED_TOKEN]",
  },
  {
    name: "SSN",
    pattern: /\b\d{3}-\d{2}-\d{4}\b/g,
    mask: () => "[REDACTED_SSN]",
  },
];

export interface ScrubResult {
  sanitizedText: string;
  redactionCount: number;
  detectedTypes: string[];
}

export function sanitizePromptText(input: string): ScrubResult {
  let sanitizedText = input;
  let redactionCount = 0;
  const detectedTypes: string[] = [];

  for (const rule of PII_RULES) {
    const matches = sanitizedText.match(rule.pattern);
    if (matches && matches.length > 0) {
      redactionCount += matches.length;
      detectedTypes.push(rule.name);
      sanitizedText = sanitizedText.replace(rule.pattern, rule.mask);
    }
  }

  return { sanitizedText, redactionCount, detectedTypes };
}

प्रोडक्शन-ग्रेड React हुक: useBrowserAI

React कम्पोनेंट्स में मॉडल सेशन्स को मैन्युअली मैनेज करने से अनमाउंट के समय मेमोरी लीक हो सकती है। useBrowserAI हुक लाइफसाइकिल, क्षमता की जाँच, स्ट्रीमिंग टेक्स्ट एग्रीगेशन और अबॉर्ट सिग्नल को नियंत्रित करता है।

TYPESCRIPT
// hooks/useBrowserAI.ts
"use client";

import { useState, useEffect, useRef, useCallback } from "react";
import type {
  AICapabilityAvailability,
  AILanguageModelSession,
  AILanguageModelCreateOptions,
} from "@/types/chrome-ai";

interface UseBrowserAIOptions extends AILanguageModelCreateOptions {
  autoInit?: boolean;
}

interface UseBrowserAIReturn {
  availability: AICapabilityAvailability | "checking" | "unsupported";
  downloadProgress: number | null;
  isGenerating: boolean;
  error: string | null;
  generateText: (promptText: string) => Promise<string>;
  streamText: (promptText: string, onChunk: (chunk: string) => void) => Promise<string>;
  abort: () => void;
  resetSession: () => Promise<void>;
  tokensRemaining: number | null;
}

export function useBrowserAI(options: UseBrowserAIOptions = {}): UseBrowserAIReturn {
  const { systemPrompt, temperature = 0.7, topK = 3, autoInit = true } = options;

  const [availability, setAvailability] = useState<AICapabilityAvailability | "checking" | "unsupported">("checking");
  const [downloadProgress, setDownloadProgress] = useState<number | null>(null);
  const [isGenerating, setIsGenerating] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [tokensRemaining, setTokensRemaining] = useState<number | null>(null);

  const sessionRef = useRef<AILanguageModelSession | null>(null);
  const abortControllerRef = useRef<AbortController | null>(null);

  const cleanupSession = useCallback(() => {
    if (sessionRef.current) {
      try {
        sessionRef.current.destroy();
      } catch (err) {
        console.warn("Error destroying previous session:", err);
      }
      sessionRef.current = null;
    }
  }, []);

  const initSession = useCallback(async () => {
    if (typeof window === "undefined") return;

    const factory = (window as any).LanguageModel || (window as any).ai?.languageModel;
    if (!factory) {
      setAvailability("unsupported");
      return;
    }

    try {
      let availStatus: AICapabilityAvailability = "readily";
      if (typeof factory.availability === "function") {
        availStatus = await factory.availability();
      } else if (typeof factory.capabilities === "function") {
        const caps = await factory.capabilities();
        availStatus = caps.available;
      }
      
      setAvailability(availStatus);

      if (availStatus === "no") {
        return;
      }

      cleanupSession();

      const session = await factory.create({
        systemPrompt,
        temperature,
        topK,
        monitor(m: EventTarget) {
          m.addEventListener("downloadprogress", (e: Event) => {
            const customEvent = e as CustomEvent<{ loaded: number; total: number }>;
            if (customEvent.detail && customEvent.detail.total > 0) {
              const progress = Math.round((customEvent.detail.loaded / customEvent.detail.total) * 100);
              setDownloadProgress(progress);
            }
          });
        },
      });

      sessionRef.current = session;
      setTokensRemaining(session.tokensLeft ?? 4096);
      setError(null);
    } catch (err) {
      const message = err instanceof Error ? err.message : "Failed to initialize Browser AI";
      setError(message);
      cleanupSession();
    }
  }, [systemPrompt, temperature, topK, cleanupSession]);

  useEffect(() => {
    if (autoInit) {
      initSession();
    }
    return () => {
      cleanupSession();
    };
  }, [autoInit, initSession, cleanupSession]);

  const abort = useCallback(() => {
    if (abortControllerRef.current) {
      abortControllerRef.current.abort();
      abortControllerRef.current = null;
    }
    setIsGenerating(false);
  }, []);

  const streamText = useCallback(
    async (promptText: string, onChunk: (chunk: string) => void): Promise<string> => {
      if (!sessionRef.current) {
        throw new Error("Session is not initialized");
      }

      abort();
      const controller = new AbortController();
      abortControllerRef.current = controller;
      setIsGenerating(true);
      setError(null);

      let accumulatedResponse = "";

      try {
        const stream = sessionRef.current.promptStreaming(promptText, {
          signal: controller.signal,
        });

        const reader = stream.getReader();
        while (true) {
          const { done, value } = await reader.read();
          if (done) break;
          if (value) {
            accumulatedResponse = value;
            onChunk(value);
          }
        }

        if (sessionRef.current) {
          setTokensRemaining(sessionRef.current.tokensLeft);
        }

        return accumulatedResponse;
      } catch (err: unknown) {
        if (err instanceof Error && err.name === "AbortError") {
          return accumulatedResponse;
        }
        const errorMsg = err instanceof Error ? err.message : "Generation failed";
        setError(errorMsg);
        throw err;
      } finally {
        setIsGenerating(false);
        abortControllerRef.current = null;
      }
    },
    [abort]
  );

  const generateText = useCallback(
    async (promptText: string): Promise<string> => {
      if (!sessionRef.current) {
        throw new Error("Session is not initialized");
      }

      abort();
      const controller = new AbortController();
      abortControllerRef.current = controller;
      setIsGenerating(true);
      setError(null);

      try {
        const result = await sessionRef.current.prompt(promptText, {
          signal: controller.signal,
        });
        if (sessionRef.current) {
          setTokensRemaining(sessionRef.current.tokensLeft);
        }
        return result;
      } catch (err: unknown) {
        if (err instanceof Error && err.name === "AbortError") {
          return "";
        }
        const errorMsg = err instanceof Error ? err.message : "Generation failed";
        setError(errorMsg);
        throw err;
      } finally {
        setIsGenerating(false);
        abortControllerRef.current = null;
      }
    },
    [abort]
  );

  return {
    availability,
    downloadProgress,
    isGenerating,
    error,
    generateText,
    streamText,
    abort,
    resetSession: initSession,
    tokensRemaining,
  };
}

कम्पोनेंट स्पॉटलाइट: <SmartTextArea />

यह कम्पोनेंट बिना किसी रुकावट के घोस्ट-टेक्स्ट कम्प्लीशन, लाइव टोन एनालिसिस और ऑफलाइन स्टेटस बैज को जोड़ता है। जब यूजर टाइपिंग के दौरान थोड़ा रुकता है, Gemini Nano वाक्य के अगले हिस्से का अनुमान लगाता है। Tab की दबाते ही कम्प्लीशन बिना इनपुट फोकस तोड़े जुड़ जाता है।

TSX
// components/SmartTextArea.tsx
"use client";

import React, { useState, useRef, useEffect, useCallback } from "react";
import { useBrowserAI } from "@/hooks/useBrowserAI";
import { sanitizePromptText } from "@/lib/pii-scrubber";

interface SmartTextAreaProps {
  initialValue?: string;
  placeholder?: string;
  onChange?: (value: string) => void;
  debounceMs?: number;
}

type SentimentTone = "positive" | "constructive" | "neutral" | "urgent" | "analyzing";

export function SmartTextArea({
  initialValue = "",
  placeholder = "Draft your architectural decision record or engineering notes...",
  onChange,
  debounceMs = 280,
}: SmartTextAreaProps) {
  const [text, setText] = useState(initialValue);
  const [suggestion, setSuggestion] = useState("");
  const [tone, setTone] = useState<SentimentTone>("neutral");
  const [isOffline, setIsOffline] = useState(!navigator.onLine);

  const textareaRef = useRef<HTMLTextAreaElement>(null);
  const timerRef = useRef<NodeJS.Timeout | null>(null);

  // Initialize the local completion session with a constrained system prompt
  const {
    availability,
    downloadProgress,
    isGenerating,
    generateText,
    abort,
    tokensRemaining,
  } = useBrowserAI({
    systemPrompt:
      "You are an autocompletion engine for software engineers. Provide a short, direct inline continuation (1 to 8 words) for the user's text. Return ONLY the continuation words. Do not repeat the input.",
    temperature: 0.2,
    topK: 1,
  });

  // Track browser connectivity
  useEffect(() => {
    const handleOnline = () => setIsOffline(false);
    const handleOffline = () => setIsOffline(true);

    window.addEventListener("online", handleOnline);
    window.addEventListener("offline", handleOffline);
    return () => {
      window.removeEventListener("online", handleOnline);
      window.removeEventListener("offline", handleOffline);
    };
  }, []);

  // Request completion from local model
  const triggerCompletion = useCallback(
    async (currentText: string) => {
      if (availability !== "readily" || currentText.trim().length < 8) {
        setSuggestion("");
        return;
      }

      // Sanitize input to protect sensitive data locally
      const { sanitizedText } = sanitizePromptText(currentText);

      try {
        const rawPrediction = await generateText(
          `Text: "${sanitizedText}"\nContinuation:`
        );

        const cleanPrediction = rawPrediction
          .replace(/^["'\s]+|["'\s]+$/g, "")
          .trim();

        if (cleanPrediction.length > 0) {
          setSuggestion(cleanPrediction);
        } else {
          setSuggestion("");
        }
      } catch {
        setSuggestion("");
      }
    },
    [availability, generateText]
  );

  // Debounced input handler
  const handleInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
    const newText = e.target.value;
    setText(newText);
    setSuggestion("");
    abort();

    if (onChange) {
      onChange(newText);
    }

    if (timerRef.current) {
      clearTimeout(timerRef.current);
    }

    timerRef.current = setTimeout(() => {
      triggerCompletion(newText);
    }, debounceMs);
  };

  // Keyboard navigation for ghost text acceptance
  const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
    if (e.key === "Tab" && suggestion.length > 0) {
      e.preventDefault();
      const mergedText = text.endsWith(" ")
        ? text + suggestion
        : text + " " + suggestion;
      setText(mergedText);
      setSuggestion("");
      if (onChange) onChange(mergedText);
    } else if (e.key === "Escape") {
      setSuggestion("");
      abort();
    }
  };

  return (
    <div className="smart-textarea-container" style={{ position: "relative", width: "100%" }}>
      {/* Header telemetry and indicators */}
      <div
        className="telemetry-bar"
        style={{
          display: "flex",
          justifyContent: "space-between",
          alignItems: "center",
          marginBottom: "8px",
          fontSize: "12px",
          fontFamily: "monospace",
        }}
      >
        <div style={{ display: "flex", gap: "12px", alignItems: "center" }}>
          <span
            style={{
              display: "inline-flex",
              alignItems: "center",
              gap: "6px",
              color: availability === "readily" ? "#15803d" : "#b45309",
            }}
          >
            <span
              style={{
                width: "8px",
                height: "8px",
                borderRadius: "50%",
                backgroundColor: availability === "readily" ? "#22c55e" : "#f59e0b",
              }}
            />
            {availability === "readily"
              ? "Gemini Nano (Local Engine Active)"
              : availability === "after-download"
              ? `Downloading Weights (${downloadProgress || 0}%)`
              : "Chrome AI Unavailable (Fallback Mode)"}
          </span>

          {tokensRemaining !== null && (
            <span style={{ color: "#64748b" }}>
              Budget: {tokensRemaining} tokens left
            </span>
          )}
        </div>

        <div style={{ display: "flex", gap: "8px" }}>
          {isOffline && (
            <span
              style={{
                backgroundColor: "#fef3c7",
                color: "#92400e",
                padding: "2px 8px",
                borderRadius: "4px",
              }}
            >
              Offline Mode
            </span>
          )}
        </div>
      </div>

      {/* Editor overlay stack */}
      <div style={{ position: "relative", minHeight: "160px" }}>
        {/* Ghost text display layer */}
        <div
          aria-hidden="true"
          style={{
            position: "absolute",
            top: 0,
            left: 0,
            right: 0,
            bottom: 0,
            padding: "12px",
            fontFamily: "inherit",
            fontSize: "14px",
            lineHeight: "1.5",
            pointerEvents: "none",
            whiteSpace: "pre-wrap",
            wordBreak: "break-word",
            color: "transparent",
            border: "1px solid transparent",
          }}
        >
          <span>{text}</span>
          {suggestion && (
            <span style={{ color: "#94a3b8", opacity: 0.8 }}>
              {text.endsWith(" ") ? "" : " "}
              {suggestion}
            </span>
          )}
        </div>

        {/* User interactive input */}
        <textarea
          ref={textareaRef}
          value={text}
          onChange={handleInput}
          onKeyDown={handleKeyDown}
          placeholder={placeholder}
          aria-label="Smart Content Editor"
          style={{
            width: "100%",
            minHeight: "160px",
            padding: "12px",
            fontSize: "14px",
            lineHeight: "1.5",
            fontFamily: "inherit",
            backgroundColor: "transparent",
            border: "1px solid #cbd5e1",
            borderRadius: "6px",
            resize: "vertical",
            outline: "none",
            boxSizing: "border-box",
          }}
        />
      </div>

      {/* Footer controls and keyboard hints */}
      <div
        style={{
          display: "flex",
          justifyContent: "space-between",
          alignItems: "center",
          marginTop: "6px",
          fontSize: "12px",
          color: "#64748b",
        }}
      >
        <span>
          {suggestion ? "Press [Tab] to accept completion, [Esc] to dismiss" : "Type to see inline local completions"}
        </span>
        {isGenerating && <span>Generating prediction...</span>}
      </div>
    </div>
  );
}
Loading Chrome AI interactive sandbox...

परफॉर्मेंस और लेटेंसी बेंचमार्क्स

वास्तविक यूज़र एक्सपीरियंस में सुधार को मापने के लिए हमने Chrome में चल रहे Gemini Nano की तुलना एक सामान्य क्लाउड API (Next.js Edge रूट के जरिए केंद्रीय क्षेत्र में होस्टेड Gemini Flash) से की।

लेटेंसी और रिसोर्स उपयोग की तुलना

मूल्यांकन मानक Chrome Built-in AI (Gemini Nano) Next.js Edge गेटवे द्वारा क्लाउड API
पहले टोकन का समय (TTFT) 12ms - 24ms 420ms - 980ms
प्रति रिक्वेस्ट नेटवर्क डेटा 0 KB (शून्य नेटवर्क I/O) 1.4 KB - 8.2 KB
PII डेटा रिस्क शून्य जोखिम (डिवाइस मेमोरी में ही रहता है) TLS डेटा बाहर भेजना और DPA आवश्यक
ऑपरेशनल लागत $0.00 / 10 लाख रिक्वेस्ट्स 0.150.15 -0.60 / 10 लाख टोकन्स
ऑफलाइन काम करने की क्षमता पूरी तरह उपलब्ध तुरंत विफल (HTTP 503 / नेटवर्क त्रुटि)
हार्डवेयर लोड ~400MB VRAM / RAM क्लाइंट मेमोरी पर कोई लोड नहीं

प्रोडक्शन चुनौतियाँ और आर्किटेक्चरल समाधान

क्लाइंट हार्डवेयर पर मॉडल चलाने में कुछ खास चुनौतियाँ आती हैं जो सर्वर-साइड माइक्रोसर्विसेज में नहीं होतीं।

1. कॉन्टेक्स्ट विंडो का भरना और मेमोरी क्लीनअप

Gemini Nano में टोकन की एक सीमित सीमा होती है (डिवाइस के आधार पर आमतौर पर 1024 से 4096 टोकन प्रति सेशन)। यदि बिना रिसेट किए एक ही सेशन में कई बड़े प्रॉम्प्ट्स भेजे जाएं, तो session.tokensLeft शून्य हो जाता है और आगे के कॉल्स InvalidStateError फेंकते हैं।

समाधान: छोटे कार्यों के लिए session.clone() का उपयोग करके अल्पकालिक सेशन बनाएं और परिणाम मिलते ही session.destroy() चलाएं।

TYPESCRIPT
// Pattern: Ephemeral session cloning
async function runIsolatedTask(
  baseSession: AILanguageModelSession,
  taskPrompt: string
): Promise<string> {
  const ephemeralSession = await baseSession.clone();
  try {
    return await ephemeralSession.prompt(taskPrompt);
  } finally {
    ephemeralSession.destroy(); // Free underlying neural runtime resources
  }
}

2. हाइब्रिड फॉलबैक आर्किटेक्चर

हर यूज़र के पास हार्डवेयर-एक्सेलेरेटेड लोकल मॉडल वाला ब्राउज़र नहीं होगा। इसलिए प्रोडक्शन आर्किटेक्चर में प्रोग्रेसिव एन्हांसमेंट होना चाहिए:

  1. टियर 1 (लोकल): window.LanguageModel (या window.ai.languageModel) की जाँच। यदि स्थिति "readily" है, तो 20ms से कम लेटेंसी और शून्य सर्वर लोड के साथ स्थानीय स्तर पर प्रोसेस करें।
  2. लेवल 2 (Edge फॉलबैक): यदि capabilities.available === "no" हो, तो रिक्वेस्ट को क्लाउड मॉडल वाले Next.js Server Action या Edge Route पर भेजें।
  3. लेवल 3 (ग्रेसफुल मोड): इंटरनेट न होने और लोकल AI अनुपलब्ध होने पर सरल नियमों पर आ जाएं ताकि एप्लिकेशन क्रैश न हो।

निष्कर्ष

इन्फरेंस को दूरस्थ सर्वरों से सीधे ब्राउज़र में स्थानांतरित करने से इंटरएक्टिव वेब एप्लिकेशन्स की लेटेंसी पूरी तरह खत्म हो जाती है। Chrome के Prompt API को सख्त PII फिल्टरिंग, सुव्यवस्थित React हुक्स और हाइब्रिड फॉलबैक के साथ जोड़कर फ्रंटएंड डेवलपर्स तेज, सुरक्षित और भरोसेमंद एप्लिकेशन्स बना सकते हैं।

Share this article