Prompt API की मदद से सेशन को छोटा करना

पब्लिश होने की तारीख: 23 जून, 2026

हर LanguageModel सेशन के लिए, कॉन्टेक्स्ट विंडो की समयसीमा तय होती है. जैसे-जैसे बातचीत बढ़ती है, मॉडल अपने कॉन्टेक्स्ट में मैसेज का पूरा इतिहास इकट्ठा करता जाता है. इसमें, उपयोगकर्ता के हर प्रॉम्प्ट और Assistant के हर जवाब शामिल होते हैं. जब विंडो भर जाती है, तो ब्राउज़र में मौजूद, ओवरफ़्लो को अपने-आप मैनेज करने की सुविधा काम करने लगती है. यह नए प्रॉम्प्ट के लिए जगह बनाने के लिए, सबसे पुराने मैसेज के जोड़े को हटा देती है. इसमें एक बार में एक प्रॉम्प्ट और जवाब का जोड़ा हटाया जाता है. अगर आने वाला प्रॉम्प्ट इतना बड़ा है कि बातचीत का पूरा इतिहास हटाने के बाद भी वह उसमें फ़िट नहीं होता, तो QuotaExceededError के साथ कॉल पूरी तरह से फ़ेल हो जाती है.

सेशन को कंप्रेस करना एक बेहतर विकल्प है. इसके लिए, बातचीत के इतिहास की खास जानकारी Summarizer API की मदद से दें. इसके बाद, इन खास जानकारियों को initialPrompts के तौर पर इस्तेमाल करके, नया सेशन शुरू करें. रनटाइम के दौरान, ओवरफ़्लो को मैनेज करने के लिए, ब्राउज़र कभी भी initialPrompts को नहीं हटाता. इसलिए, कंप्रेस की गई खास जानकारी, मॉडल के कॉन्टेक्स्ट में हमेशा बनी रहती है. हालांकि, यह तब तक बनी रहती है, जब तक create() को कॉल करने पर, खास जानकारी कॉन्टेक्स्ट विंडो में फ़िट हो जाती है. नए सेशन में, बातचीत का वही थ्रेड जारी रहता है. हालांकि, इसमें ओरिजनल टोकन की लागत का एक छोटा हिस्सा ही लगता है.

सेशन को कंप्रेस करने की सुविधा की मदद से, LanguageModel की लंबी बातचीत को कॉन्टेक्स्ट विंडो में बनाए रखा जा सकता है. साथ ही, बातचीत की निरंतरता भी बनी रहती है. इसके लिए, ये ज़रूरी चरण पूरे करने होते हैं:

  1. contextWindow के मुकाबले contextUsage की निगरानी करें और इसे उपयोगकर्ता को दिखाएं.
  2. contextoverflow इवेंट को शुरुआती चेतावनी के तौर पर सुनें.
  3. Language Detector API की मदद से, हर मैसेज की भाषा का पता लगाएं. इसके बाद, भाषा की जानकारी देने वाले Summarizer API इंस्टेंस की मदद से, उसकी खास जानकारी दें.
  4. पुराने सेशन को खत्म करें और initialPrompts के साथ नया सेशन शुरू करें.
  5. गड़बड़ी ठीक करने के लिए, fullHistory की एक कॉपी रखें.

कॉन्टेक्स्ट के इस्तेमाल को ट्रैक करना

Prompt API, सेशन के कॉन्टेक्स्ट के पूरी तरह से भर जाने की निगरानी करने के लिए, दो एट्रिब्यूट दिखाता है:

  • session.contextUsage: फ़िलहाल, इस्तेमाल किए गए टोकन की संख्या.
  • session.contextWindow: सेशन की कुल टोकन क्षमता.

इसे <progress> एलिमेंट में दिखाएं, ताकि उपयोगकर्ताओं को एक नज़र में पता चल जाए कि सेशन अपनी सीमा के कितने करीब है. value और max को सीधे टोकन की संख्या पर सेट करें. ब्राउज़र, बार को अपने-आप स्केल करता है:

<progress id="token-bar" value="0" max="1"></progress>
<label for="token-bar" id="token-label">Context: — / — tokens</label>
function updateTokenDisplay(session) {
  const usage = session.contextUsage;
  const total = session.contextWindow;

  tokenBar.value = usage;
  tokenBar.max = total;
  tokenLabel.textContent =
    `${Math.round(usage)} / ${Math.round(total)} tokens ` +
    `(${Math.round((usage / total) * 100)}%)`;
}

हर प्रॉम्प्ट के जवाब के बाद, updateTokenDisplay() को कॉल करें, ताकि बार अप-टू-डेट रहे.

कॉन्टेक्स्ट के ओवरफ़्लो होने की जानकारी पाना

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

session.addEventListener('contextoverflow', () => {
  showWarning('⚠ Context window nearly full. Consider compacting the session.');
});

हटाने के इस तरीके की दो ज़रूरी प्रॉपर्टी हैं:

  • रनटाइम के दौरान, initialPrompts को नहीं हटाया जाता. ब्राउज़र, आने वाले प्रॉम्प्ट के लिए जगह बनाने के लिए, उन्हें नहीं हटाता. हालांकि, अगर initialPrompts का कुल साइज़, कॉन्टेक्स्ट विंडो में फ़िट होने के लिए बहुत ज़्यादा है, तो create() QuotaExceededError के साथ अस्वीकार कर देता है. इसलिए, पक्का करें कि कंप्रेस की गई खास जानकारी इतनी छोटी हो कि बातचीत जारी रखी जा सके.LanguageModel.create()
  • हटाने की एक सीमा होती है. अगर आने वाला प्रॉम्प्ट इतना बड़ा है कि पिछली पूरी बातचीत हटाने के बाद भी वह उसमें फ़िट नहीं होता, तो prompt() या promptStreaming() कॉल, QuotaExceededError के साथ फ़ेल हो जाती है और कुछ भी नहीं हटाया जाता.

Prompt API के दस्तावेज़ में, कॉन्टेक्स्ट के ओवरफ़्लो को मैनेज करने के बारे में ज़्यादा पढ़ें.

contextoverflow इवेंट का इस्तेमाल करके, उपयोगकर्ता को चेतावनी दें, 'भेजें' बटन को बंद करें या ब्राउज़र के चुपचाप बातचीत के इतिहास को हटाने से पहले, कंप्रेस करने की प्रोसेस को अपने-आप ट्रिगर करें.

सेशन को कंप्रेस करना

कंप्रेस करने की प्रोसेस में तीन चरण होते हैं:

  1. Summarizer API की मदद से, बातचीत के इतिहास में मौजूद हर मैसेज की खास जानकारी दें.
  2. पुराने सेशन को खत्म करें.
  3. खास जानकारियों को initialPrompts के तौर पर इस्तेमाल करके, नया सेशन शुरू करें.

इतिहास की खास जानकारी देना

The Summarizer API, चैट के अलग-अलग मैसेज को कंप्रेस करने के लिए सबसे सही है. हर मैसेज के लिए, सबसे पहले Language Detector API की मदद से उसकी भाषा का पता लगाएं, ताकि खास जानकारी देने वाले टूल को सही तरीके से कॉन्फ़िगर किया जा सके:

async function detectLanguage(text, threshold = 0.7) {
  const detector = await LanguageDetector.create();
  const results = await detector.detect(text);
  if (results.length > 0 && results[0].confidence >= threshold) {
    return results[0].detectedLanguage;
  }
  return null; // confidence too low — caller falls back to navigator.language
}

कॉन्फ़िडेंस थ्रेशोल्ड 0.7 सेट करने से, अनिश्चित पहचान के आधार पर कार्रवाई नहीं की जाती. जब कॉन्फ़िडेंस थ्रेशोल्ड से कम होता है, तो navigator.language पर वापस जाएं.

इसके बाद, पता लगाई गई भाषा के लिए कॉन्फ़िगर किया गया खास जानकारी देने वाला टूल बनाएं. छोटे और कम-लेटेंसी वाले मॉडल के वैरिएंट को चुनने के लिए, preference: 'speed' को प्राथमिकता दें. अगर तेज़ मॉडल, पता लगाई गई भाषा के साथ काम नहीं करता है, तो preference: 'auto' पर वापस जाएं:

const summarizers = {}; // cache, keyed by `${format}:${lang}`

async function getSummarizer(format, lang) {
  const key = `${format}:${lang}`;
  if (summarizers[key]) return summarizers[key];

  const baseOptions = {
    type: 'tldr',
    format, // 'markdown' or 'plain-text'
    length: 'short',
    expectedInputLanguages: [lang],
    expectedContextLanguages: [lang],
    outputLanguage: lang,
  };

  let options = { ...baseOptions, preference: 'speed' };
  let avail = await Summarizer.availability(options);

  if (avail === 'unavailable') {
    options = { ...baseOptions, preference: 'auto' };
    avail = await Summarizer.availability(options);
  }

  if (avail === 'unavailable') {
    throw new Error('Summarizer API unavailable on this device.');
  }

  summarizers[key] = await Summarizer.create(options);
  return summarizers[key];
}

format+lang के हर जोड़े के लिए, खास जानकारी देने वाले टूल को कैश मेमोरी में सेव करने से, एक ही भाषा वाले लगातार मैसेज के लिए, create() को बार-बार कॉल करने की ज़रूरत नहीं पड़ती.

format आर्ग्युमेंट, मैसेज के कॉन्टेंट से ही लिया जाता है. सादे टेक्स्ट के लिए 'markdown' तय करने से, अनचाही फ़ॉर्मैटिंग हो सकती है. वहीं, Markdown के लिए 'plain-text' तय करने से, कोड फ़ेंस और ज़ोर देने के लिए इस्तेमाल किए गए टैग हट जाते हैं. इन दोनों के बीच अंतर करने के लिए, एक छोटा रेगुलर एक्सप्रेशन इस्तेमाल किया जाता है:

function looksLikeMarkdown(text) {
  return /(?:^#{1,6} |^[-*+] |\d+\. |\*\*|__|\[.+?\]\(|^> |^```)/m.test(text);
}

भाषा और फ़ॉर्मैट तय करने के बाद, हर मैसेज की खास जानकारी दें और context स्ट्रिंग पास करें, ताकि मॉडल को यह समझ में आ जाए कि वह चैट के टर्न को कंप्रेस कर रहा है, न कि किसी अलग दस्तावेज़ को:

const compacted = [];

for (const msg of history) {
  const lang = (await detectLanguage(msg.content)) ?? navigator.language;
  const format = looksLikeMarkdown(msg.content) ? 'markdown' : 'plain-text';
  const summarizer = await getSummarizer(format, lang);

  const summary = await summarizer.summarize(msg.content.trim(), {
    context:
      `This is a ${msg.role} turn from a chat conversation. ` +
      `Preserve its key meaning as concisely as possible.`,
  });

  // Only use the summary if it's actually shorter.
  compacted.push({
    role: msg.role,
    content:
      summary.trim().length < msg.content.length ? summary.trim() : msg.content,
  });
}

पुराने सेशन को खत्म करना

पुराने सेशन के संसाधनों को रिलीज़ करें. इसके बाद, नया सेशन बनाएं:

session.destroy();
session = null;

कंप्रेस किए गए इतिहास के साथ नया सेशन बनाना

बातचीत के कॉन्टेक्स्ट के साथ नया सेशन शुरू करने के लिए, कंप्रेस किए गए मैसेज को initialPrompts के तौर पर पास करें:

// Collect every language the detector was confident about.
const sessionLangs =
  confidentLangs.size > 0 ? [...confidentLangs] : [navigator.language];

session = await LanguageModel.create({
  expectedInputs: [{ type: 'text', languages: sessionLangs }],
  expectedOutputs: [{ type: 'text', languages: sessionLangs }],
  initialPrompts: compacted,
});

// Re-register the overflow handler on the new session.
session.addEventListener('contextoverflow', () => {
  /* ... */
});

नया सेशन, कम contextUsage के साथ शुरू होता है. बातचीत वहीं से जारी रहती है जहां से वह खत्म हुई थी. मॉडल के पास, खास जानकारी पहले से मौजूद होती है. इसलिए, वह पहले के विषयों के बारे में पूछे गए सवालों के जवाब दे सकता है.

गड़बड़ियां ठीक करना

अगर पुराने सेशन को खत्म करने के बाद, खास जानकारी देने या सेशन बनाने में गड़बड़ी होती है, तो उपयोगकर्ता चैट नहीं कर पाएगा. fullHistory का एक अलग कलेक्शन बनाएं. इसे कंपैक्शन से कभी भी ओवरराइट न करें और इसे रिकवरी फ़ॉलबैक के तौर पर इस्तेमाल करें:

const history = []; // current session's view, replaced on each compaction
const fullHistory = []; // every original message, never overwritten

// In the catch block:
if (!session) {
  session = await LanguageModel.create({
    initialPrompts: fullHistory.map(({ role, content }) => ({ role, content })),
  });
  session.addEventListener('contextoverflow', () => {
    /* ... */
  });
}

fullHistory से ठीक करने पर, कॉन्टेक्स्ट फिर से अपनी क्षमता के करीब पहुंच सकता है. हालांकि, उपयोगकर्ता कम से कम काम करने की स्थिति में वापस आ जाता है और तुरंत कंप्रेस करने की एक और कोशिश कर सकता है.

ज़रूरी नहीं: कुछ कॉन्टेंट को कंप्रेस होने से रोकना

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

// Splits text into alternating prose and code-fence segments.
// Returns [{ type: 'prose'|'code', content: string }, …]
function splitByCodeFences(text) {
  const parts = [];
  const re = /^```[^\n]*\n[\s\S]*?^```[ \t]*$/gm;
  let lastIndex = 0;
  let match;
  while ((match = re.exec(text)) !== null) {
    if (match.index > lastIndex) {
      parts.push({
        type: 'prose',
        content: text.slice(lastIndex, match.index),
      });
    }
    parts.push({ type: 'code', content: match[0] });
    lastIndex = match.index + match[0].length;
  }
  if (lastIndex < text.length) {
    parts.push({ type: 'prose', content: text.slice(lastIndex) });
  }
  return parts;
}

डेमो आज़माएं

सेशन को कंप्रेस करने के डेमो की मदद से, Prompt API के साथ चैट की जा सकती है और सेशन को कभी भी कंप्रेस किया जा सकता है. टोकन बार, कॉन्टेक्स्ट के इस्तेमाल को रीयल टाइम में दिखाता है. साथ ही, कॉन्टेक्स्ट भरने पर इसका रंग बदल जाता है. कंप्रेस करने की हर प्रोसेस के बाद, लॉग एंट्री में कंप्रेस करने से पहले और बाद में टोकन की संख्या रिकॉर्ड की जाती है, ताकि कंप्रेस करने से होने वाली कमी को सीधे तौर पर देखा जा सके.

पेज के सबसे नीचे मौजूद, डीबग करें: बातचीत का JSON सेक्शन में, बातचीत के पूरे और कंप्रेस किए गए JSON को देखा जा सकता है. इस सेक्शन को छोटा या बड़ा किया जा सकता है.

सोर्स कोड, GitHub पर मौजूद है.