<पी> कुछ अनुप्रयोगों में, जब कोई उपयोगकर्ता एलएलएम का अनुरोध करता है तो उससे पूछताछ करना संभव नहीं हो सकता है। किसी एपीआई को संकेत भेजने और उसकी प्रतिक्रिया की प्रतीक्षा करने की प्रक्रिया विशेष रूप से समय लेने वाली हो सकती है। अधिक जटिल कार्यों के लिए जिनमें लैंगचेन शामिल है, जैसे पीडीएफ या ऑडियो फ़ाइल से डेटा संसाधित करना और इसे एलएलएम में फीड करना, देरी उपयोगकर्ता अनुभव के लिए और भी बड़ी हिट बन जाती है। <पी> जबकि स्ट्रीमिंग है कई मामलों में एक उपयुक्त समाधान, उपयोगकर्ता द्वारा आपके एप्लिकेशन पर जाने से पहले कुछ मनमाने बिंदु पर सब कुछ करना अधिक सुविधाजनक हो सकता है। इस तरह, उपयोगकर्ता को एलएलएम द्वारा इसे उत्पन्न करने की प्रतीक्षा किए बिना, तुरंत कैश्ड प्रतिक्रिया दी जा सकती है। यह उन अनुप्रयोगों के लिए विशेष रूप से उपयोगी है जो प्रत्येक उपयोगकर्ता के लिए समान इनपुट का उपयोग करते हैं, जैसे कोई पुस्तक या समाचार सारांश। लेकिन उपयोगकर्ता से इनपुट के बिना, हम प्रतिक्रिया को कैश करने के लिए लैंगचेन को कैसे प्राप्त कर सकते हैं? <पी> हमें एक माइक्रोसर्विस की आवश्यकता होगी जिसे हम ऑन-डिमांड कॉल कर सकें, जो लैंगचेन को एक संकेत भेजने और अपस्टैश रेडिस का उपयोग करके प्रतिक्रिया को कैश करने में सक्षम हो। यह सुनिश्चित करने के लिए कि हम गलती से अपनी दर सीमा को पार न कर लें, हम अपस्टैश की रेट लिमिटिंग एसडीके का भी उपयोग करेंगे। QStash हमारे माइक्रोसर्विस को कॉल करने का सबसे बहुमुखी तरीका है - हम जब चाहें इसे कैश प्रतिक्रियाओं के लिए सेट कर सकते हैं, यहां तक कि अगर हमारे एप्लिकेशन को इसकी आवश्यकता होती है तो क्रॉन जॉब भी पास कर सकते हैं। यह एक डैशबोर्ड भी प्रदान करता है जिसका उपयोग हम अपने माइक्रोसर्विस के उपयोग की निगरानी के लिए कर सकते हैं। ऐसी स्थिति में जब हमारा समापन बिंदु अतिभारित हो जाता है या कुछ और गलत हो जाता है, QStash HTTP अनुरोध का पुनः प्रयास करेगा और सुनिश्चित करेगा कि हमारा संदेश वितरित हो गया है। <पी> हमारा माइक्रोसर्विस Hono.js का उपयोग करके बनाया जाएगा, जो कि किनारे के लिए एक हल्का और तेज़ वेब फ्रेमवर्क है। इस डेमो में, हम अपनी माइक्रोसर्विस को होस्ट करने के लिए क्लाउडफ़ेयर वर्कर का उपयोग करेंगे। हालाँकि, अपस्टैश के लिए धन्यवाद, इसे अन्य एज/सर्वर रहित रनटाइम सहित वस्तुतः कहीं भी तैनात किया जा सकता है। <पी> आप इस डेमो के लिए पूर्ण स्रोत कोड यहां पा सकते हैं। आवश्यकताएँ
- एक अपस्टैश रेडिस डेटाबेस
- QStash पर्यावरण चर
- एक OpenAI API कुंजी
आरंभ करना
प्रोजेक्ट बनाना
<पी> कई अन्य create-<package> के विपरीत एनपीएम पैकेज, create-hono आपको एक खाली निर्देशिका में रहना आवश्यक है। सबसे पहले, अपने प्रोजेक्ट के लिए एक नई खाली निर्देशिका बनाएं और उस पर नेविगेट करें: mkdir langchain-qstash
cd langchain-qstash
<पी> v1.0.0 की हालिया रिलीज़ के साथ, इस परियोजना को तैयार करने के लिए बन का उपयोग किया जाएगा। हालाँकि, create-hono का उपयोग करना भी संभव है npm के साथ , pnpm , और yarn . cloudflare-workers का चयन करना सुनिश्चित करें जब टेम्पलेट के लिए कहा गया। आप bun देख सकते हैं एक अलग विकल्प के रूप में, लेकिन यह डेमो में प्रयुक्त टेम्पलेट नहीं है। bun create hono@latest
निर्भरताएं स्थापित करना
<पी> अब, आप शेष निर्भरताएँ स्थापित करने के लिए अपनी पसंद के पैकेज मैनेजर के साथ निम्नलिखित कमांड चला सकते हैं: bun install @upstash/qstash @upstash/ratelimit @upstash/redis langchain openai
प्रोजेक्ट को कॉन्फ़िगर करना
<पी> लेखन के समय तक, create-hono इसके .gitignore में लॉकफ़ाइलें शामिल हैं डिफ़ॉल्ट रूप से. हालाँकि इसकी सख्त आवश्यकता नहीं है, आप अपना .gitignore अपडेट कर सकते हैं लॉकफ़ाइल्स को निम्नानुसार बाहर करने के लिए: node_modules
dist
.wrangler
.dev.vars
wrangler.toml
<पी> अब, हम पूर्वापेक्षाओं से पर्यावरण चर सेट कर सकते हैं। उन्हें आपके wrangler.toml में जोड़ा जा सकता है , जिसे पहले ही स्रोत नियंत्रण से बाहर रखा जाना चाहिए। [vars]
QSTASH_CURRENT_SIGNING_KEY="sig_********"
QSTASH_NEXT_SIGNING_KEY="sig_********"
UPSTASH_REDIS_REST_URL="https://********.upstash.io"
UPSTASH_REDIS_REST_TOKEN="********"
OPENAI_API_KEY="sk-********"
<पी> अंत में, अपना src/index.ts संशोधित करें पहले से पर्यावरण चर के लिए टाइपिंग जोड़ने के लिए: type Bindings = {
QSTASH_CURRENT_SIGNING_KEY: string;
QSTASH_NEXT_SIGNING_KEY: string;
UPSTASH_REDIS_REST_URL: string;
UPSTASH_REDIS_REST_TOKEN: string;
OPENAI_API_KEY: string;
};
const app = new Hono<{ Bindings: Bindings }>();
<पी> दो हस्ताक्षर कुंजी होने का लाभ इस तथ्य से मिलता है कि आप अपने पर्यावरण चर को अपडेट किए बिना उन्हें एक बार रोल कर सकते हैं, क्योंकि यदि वर्तमान कुंजी विफल हो जाती है तो QStash स्वचालित रूप से अगली हस्ताक्षर कुंजी का उपयोग करने का प्रयास करेगा। विकास
<पी> इस स्तर पर, wrangler आपके पर्यावरण चर को पढ़ने और आपके प्रोजेक्ट को तैनात करने में सक्षम होना चाहिए। ऐसा करने के लिए, आप अपने पसंदीदा पैकेज मैनेजर के साथ निम्नलिखित कमांड चला सकते हैं: bun run dev
<पी> wrangler आपके Hono.js सर्वर को चालू करेगा और आपके प्रोजेक्ट का परीक्षण करने के लिए आपको पोर्ट 8787 पर एक स्थानीय यूआरएल प्रदान करेगा। यदि आप स्थानीय स्तर पर परीक्षण करने के बजाय सीधे एज पूर्वावलोकन सत्र शुरू करना चाहते हैं, तो अपने डेव package.json को संशोधित करें स्क्रिप्ट इस प्रकार है: "dev": "wrangler dev src/index.ts --remote",
<पी> अपने क्लाउडफ़ेयर वर्कर के कॉन्फ़िगरेशन और लॉग को देखने के लिए, आपको सबसे पहले अपने वर्कर को तैनात करना होगा: bun run deploy
<पी> यह आपको क्लाउडफ़ेयर में साइन इन करने, प्रमाणीकरण के बाद पहली बार अपने प्रोजेक्ट को स्वचालित रूप से तैनात करने के चरणों के बारे में बताएगा। मिडिलवेयर बनाना
<पी> डिबगिंग उद्देश्यों के लिए, QStash से प्राप्त अनुरोधों को लॉग करना उपयोगी होगा। जैसे ही आप Begin log stream दबाएंगे, वे हमारे क्लाउडफ्लेयर वर्कर के डैशबोर्ड में "लॉग्स" टैब के अंतर्गत दिखाई देंगे। . Hono.js एक लॉगर मिडलवेयर प्रदान करता है जिसे हम अपने राउटर में जोड़ सकते हैं: import { Hono } from "hono";
import { logger } from "hono/logger";
// snip
const app = new Hono<{ Bindings: Bindings }>();
app.use("*", logger());
// snip
<पी>
QStash को क्लाउडफ्लेयर वर्कर्स से जोड़ना
<पी> इससे पहले कि हम Hono.js का उपयोग करके एक एपीआई एंडपॉइंट विकसित करना शुरू कर सकें, हमें QStash द्वारा भेजे गए संदेशों को इंटरसेप्ट करने और अमान्य हस्ताक्षर होने पर अनुरोध को त्यागने का एक तरीका चाहिए। शुक्र है, Hono.js हमारे अपने कस्टम मिडलवेयर को लागू करने का एक तरीका प्रदान करता है, जो हमेशा हैंडलर से पहले चलता है। यह इतना मजबूत है कि हम अपने मिडलवेयर को अपनी इच्छानुसार अलग-अलग फाइलों में व्यवस्थित कर सकते हैं। <पी> हमारे मिडलवेयर में, हम QStash के रिसीवर का उपयोग कर सकते हैं। आइए src/middleware/verify.ts नामक एक नई फ़ाइल बनाएं , और MiddlewareHandler के साथ टाइप किया गया फ़ंक्शन निर्यात करें : import { Receiver } from "@upstash/qstash";
import { type MiddlewareHandler } from "hono";
declare global {
interface Response {
locals: {
query: string;
};
}
}
export const verify: MiddlewareHandler = async (ctx, next) => {
const receiver = new Receiver({
currentSigningKey: ctx.env.QSTASH_CURRENT_SIGNING_KEY,
nextSigningKey: ctx.env.QSTASH_NEXT_SIGNING_KEY,
});
};
<पी> Hono.js ctx पास करता है (संदर्भ) प्रत्येक मिडलवेयर और हैंडलर पर ऑब्जेक्ट। यह सीधे तौर पर क्लाउडफ्लेयर वर्कर्स के ExecutionContext के समान नहीं है -लेकिन इसमें वही जानकारी है। होनो का ctx ऑब्जेक्ट लगभग अनुरोध ऑब्जेक्ट, पर्यावरण और निष्पादन संदर्भ के बराबर है जो डिफ़ॉल्ट क्लाउडफ़ेयर वर्कर्स को दिया गया है fetch हैंडलर, सभी एक वस्तु में संयुक्त। <पी> हम वैश्विक Response को भी संशोधित करते हैं एक कस्टम locals शामिल करने के लिए इंटरफ़ेस . एक्सप्रेस के विपरीत, होनो res.locals नहीं बनाता है डिफ़ॉल्ट रूप से ऑब्जेक्ट. हम बाद में हैंडलर को क्वेरी पास करने के लिए इसका उपयोग करेंगे। इसके बाद, हम अपने रिसीवर के निर्माण के लिए पहले से टाइप किए गए पर्यावरण चर तक पहुंचते हैं। <पी> अब हम अनुरोध के हस्ताक्षर को सत्यापित करने के लिए रिसीवर का उपयोग कर सकते हैं: // snip
const body = await ctx.req.text();
ctx.res.locals = {
query: JSON.parse(body).query,
};
const isValid = await receiver
.verify({
signature: ctx.req.headers.get("Upstash-Signature")!,
body,
})
.catch((err) => {
console.error(err);
return false;
});
if (!isValid) {
return new Response("Invalid signature", { status: 401 });
}
await next();
<पी> सबसे पहले, हम संदर्भ से अनुरोध वस्तु प्राप्त करते हैं। Hono.js आसानी से केवल वेब मानक एपीआई का उपयोग करता है, जैसे fetch , URL , Request , और Response . हालाँकि हम इसे इस डेमो में क्लाउडफ्लेयर वर्कर्स पर उपयोग कर रहे हैं, यह इसे एज/सर्वर रहित वातावरण सहित अनगिनत अन्य वातावरणों पर चलाने में सक्षम बनाता है। <पी> अनुरोध ऑब्जेक्ट से, हम अनुरोध के मुख्य भाग को टेक्स्ट के साथ-साथ Upstash-Signature के रूप में पढ़ते हैं हेडर, जिसमें एक कस्टम JWT शामिल है। हम जेडब्ल्यूटी में निहित हस्ताक्षर को सत्यापित करने के लिए रिसीवर का उपयोग हस्ताक्षर और अनुरोध के मुख्य भाग को पास करके कर सकते हैं। .catch में हैंडलर, हम त्रुटि लॉग करना सुनिश्चित करते हैं और साथ ही false लौटाते हैं यह इंगित करने के लिए कि हस्ताक्षर अमान्य है। <पी> चूँकि हम अनुरोध के मुख्य भाग का उपभोग कर रहे हैं, इसलिए बाद में हमें अपने वास्तविक हैंडलर में उस तक पहुंच नहीं मिलेगी। ऐसा इसलिए है क्योंकि बॉडी एक ReadableStream है जिसका सेवन केवल एक बार ही किया जा सकता है। इसके बजाय, क्वेरी को हैंडलर तक पहुंचाने के लिए, हम इसे locals में जोड़ सकते हैं प्रतिक्रिया पर आपत्ति. अंत में, यदि हस्ताक्षर अमान्य है, तो हम 401 अनधिकृत प्रतिक्रिया लौटाते हैं। अन्यथा, हम next() पर कॉल करते हैं अगले मिडलवेयर या हैंडलर पर जारी रखने के लिए। दर-सीमित जोड़ना
<पी> चूँकि हम इस समापन बिंदु को अपनी व्यक्तिगत OpenAI API कुंजियों से जोड़ने जा रहे हैं, इसलिए यह सुनिश्चित करना महत्वपूर्ण है कि हम गलती से अपनी दर सीमा को पार न कर लें। शुक्र है, अपस्टैश एक रेट लिमिटिंग एसडीके प्रदान करता है जिसका उपयोग हम आसानी से अपने एंडपॉइंट पर रेट-लिमिटिंग जोड़ने के लिए कर सकते हैं। यदि अनुरोधों की संख्या दी गई सीमा से अधिक हो जाती है, तो हम 429 बहुत अधिक अनुरोधों का जवाब देंगे। <पी> आइए src/middleware/ratelimit.ts में एक नया मिडलवेयर बनाकर शुरुआत करें : import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis/cloudflare";
import { type MiddlewareHandler } from "hono";
export const ratelimit: MiddlewareHandler = async (ctx, next) => {
const redis = new Redis({
url: ctx.env.UPSTASH_REDIS_REST_URL,
token: ctx.env.UPSTASH_REDIS_REST_TOKEN,
});
const ratelimit = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(10, "10 s"),
analytics: true,
});
await next();
};
<पी> यहां, हम अपने अपस्टैश रेडिस डेटाबेस को रेट लिमिटिंग एसडीके से जोड़ रहे हैं। हमें Redis बनाना होगा उदाहरण के लिए ctx.env से पर्यावरण चर का मैन्युअल रूप से उपयोग करना , क्योंकि Redis.fromEnv() क्लाउडफ़ेयर वर्कर्स का उपयोग करते समय उन्हें स्वचालित रूप से पढ़ने में विफल रहता है। जब analytics सच है, एसडीके प्रत्येक पहचानकर्ता के लिए कॉल का कैश रखने के लिए स्वचालित रूप से रेडिस को कॉल करेगा। यह @upstash/ratelimit का उपयोग करता है डिफ़ॉल्ट रूप से उपसर्ग के रूप में। <पी> एसडीके एक अल्पकालिक कैश का भी समर्थन करता है जो Map का उपयोग करता है रेडिस के बजाय, जो अत्यधिक लोड के तहत समय और संसाधनों को बचा सकता है: const cache = new Map(); // outside of the middleware handler
// snip
const ratelimit = new Ratelimit({
ephemeralCache: cache,
// snip
});
<पी> हम एक limiter भी प्रदान करते हैं एसडीके को. यह एक फ़ंक्शन है जो एसडीके को बताता है कि सीमा अनुरोधों को कैसे रेट किया जाए। हम slidingWindow का उपयोग कर रहे हैं सीमक, हर 10 सेकंड में 10 अनुरोधों को अनुमति देने के लिए कॉन्फ़िगर किया गया। <पी> एसडीके हमें प्रत्येक अनुरोध के लिए एक पहचानकर्ता प्रदान करने की सुविधा भी देता है, और यह स्वचालित रूप से एक निश्चित समय अवधि के भीतर किए गए अनुरोधों की संख्या पर नज़र रखेगा। वास्तविक दुनिया के एप्लिकेशन में, हम उपयोगकर्ता के आईपी पते को पहचानकर्ता के रूप में उपयोग कर सकते हैं, लेकिन इस डेमो के लिए, हम सभी अनुरोधों को एक रेटलिमिट के साथ सीमित करने के लिए एक निरंतर स्ट्रिंग का उपयोग करेंगे: // snip
const identifier = "openai";
const { success } = await ratelimit.limit(identifier);
if (!success) {
return new Response("Too many requests", { status: 429 });
}
await next();
// snip
<पी> यदि अनुरोध दर-सीमित है, तो हम 429 बहुत अधिक अनुरोधों का जवाब देते हैं। अन्यथा, हम next() पर कॉल करते हैं अगले मिडलवेयर या हैंडलर पर जारी रखने के लिए। इस मामले में कि हमने कई रेडिस डेटाबेस को एसडीके से जोड़ा है, उसे उनके बीच सिंक्रनाइज़ेशन करना होगा। इससे वर्सेल एज और क्लाउडफ्लेयर वर्कर्स पर लटकते वादे होंगे, जिनका हम इस तरह ध्यान रख सकते हैं: const { pending, success } = await ratelimit.limit(identifier);
ctx.event.waitUntil(pending);
<पी> ऐसा करने का तरीका आपके द्वारा उपयोग की जा रही लाइब्रेरी के आधार पर भिन्न हो सकता है। चूँकि होनो वेब स्टैंडर्ड एपीआई का उपयोग करता है, हम event.waitUntil का उपयोग कर सकते हैं वादे के समाधान की प्रतीक्षा करने की विधि। हालाँकि, इस डेमो के लिए, हम केवल एक रेडिस डेटाबेस का उपयोग करेंगे, इसलिए हमें लटकते वादों के बारे में चिंता करने की ज़रूरत नहीं है। QStash से संदेश प्राप्त करना
<पी> जब हम QStash को HTTP अनुरोध भेजते हैं, तो हम जो गंतव्य निर्दिष्ट करते हैं वह हमारे क्लाउडफ़ेयर वर्कर्स एंडपॉइंट का URL होगा। हम इस एंडपॉइंट के लिए हैंडलर बनाने के लिए Hono.js का उपयोग कर सकते हैं। आइए src/index.ts में एक नया जोड़ें , हमारे मिडलवेयर को पहले से सक्षम करते हुए: // snip
import { ratelimit } from "./middleware/ratelimit";
import { verify } from "./middleware/verify";
// snip
app.post("/api/announce", ratelimit, verify, async (ctx) => {});
// snip
<पी> इस हैंडलर में, हम दिए गए प्रॉम्प्ट पर प्रतिक्रिया उत्पन्न करने के लिए लैंगचेन का उपयोग कर सकते हैं, परिणाम को कैश करने के लिए अपस्टैश रेडिस का उपयोग कर सकते हैं: // snip
import { Redis } from "@upstash/redis/cloudflare";
import { UpstashRedisCache } from "langchain/cache/upstash_redis";
import { OpenAI } from "langchain/llms/openai";
// snip
app.post("/api/announce", ratelimit, verify, async (ctx) => {
const redis = new Redis({
url: ctx.env.UPSTASH_REDIS_REST_URL,
token: ctx.env.UPSTASH_REDIS_REST_TOKEN,
});
const cache = new UpstashRedisCache({ client: redis });
const model = new OpenAI({
cache,
openAIApiKey: ctx.env.OPENAI_API_KEY,
});
const query = ctx.res.locals.query;
const result = await model
.call(query)
.then((result) => {
console.log(result);
return result;
})
.catch((err) => console.error(err));
return new Response(result ?? "", { status: 200 });
});
// snip
<पी> यहां, हम लैंगचेन के लिए कैशिंग सेट करने के लिए आवश्यक कक्षाएं आयात करते हैं। फिर, हम OpenAI मॉडल और अपस्टैश रेडिस कैश का उपयोग करके श्रृंखला का निर्माण करते हैं, कैश में एक अपस्टैश रेडिस इंस्टेंस पास करते हैं। फिर से, हमें अपनी OpenAI API कुंजी को मॉडल में मैन्युअल रूप से पास करना होगा क्योंकि इसे क्लाउडफ़ेयर वर्कर्स पर स्वचालित रूप से नहीं पढ़ा जा सकता है। <पी> फिर, हम उस क्वेरी तक पहुंचते हैं जो पहले res.locals पर सहेजी गई थी , मॉडल को कॉल करने और परिणाम लॉग करने के लिए इसका उपयोग करें। अंत में, यदि कुछ गलत हुआ तो हम परिणाम को प्रतिक्रिया के रूप में या खाली स्ट्रिंग के रूप में लौटाते हैं। इस स्तर पर, हम एक POST अनुरोध भेजकर और प्रतिक्रिया की जाँच करके अपने समापन बिंदु का परीक्षण कर सकते हैं। सबसे पहले, deploy को फिर से चलाएँ आपके package.json में स्क्रिप्ट: bun run deploy
<पी> अपने क्लाउडफ्लेयर वर्कर्स एंडपॉइंट का यूआरएल पुनः प्राप्त करने के बाद, आप curl का उपयोग करके उस पर एक POST अनुरोध भेज सकते हैं। : curl -XPOST \
"https://qstash.upstash.io/v2/publish/https://<YOUR_API_URL>.workers.dev/api/announce" \
-H "Authorization: Bearer <YOUR_QSTASH_TOKEN>" \
-H "Content-Type: application/json" \
-d "{ \"query\": \"What's the derivative of e^x?\" }"
<पी> अपस्टैश एक QStash कंसोल प्रदान करता है जिसका उपयोग आप इन अनुरोधों को अधिक आसानी से भेजने के लिए कर सकते हैं। हमारे अनुरोध को बार-बार चलाने के लिए QStash को क्रॉन जॉब देना उतना ही सरल है। QStash अनुरोध के मुख्य भाग को हमारे समापन बिंदु तक भेजता है। हम निम्नलिखित JSON पेलोड भेज रहे हैं, जहां query मॉडल के लिए संकेत है: {
"query": "What's the derivative of e^x?"
}
<पी>
निष्कर्ष
<पी> रेट लिमिटिंग एसडीके प्रति पहचानकर्ता कॉल की संख्या को सफलतापूर्वक कैश करता है: <पी>
<पी> इसी तरह, लैंगचेन से उत्पन्न सामग्री हमारे अपस्टैश रेडिस डेटाबेस में सफलतापूर्वक कैश हो जाती है: <पी>
<पी> और अंत में, प्रतिक्रिया हमारे क्लाउडफ़ेयर वर्कर के लॉग में लॉग हो गई है: <पी>