OpenAI और Python के साथ एक कस्टम चैटबॉट लागू करना
- बिल्डिंग ब्लॉक्स
- सहायक लाइब्रेरी
- CLI चैट ऐप
- स्ट्रीमिंग डॉग्स
- स्ट्रीमिंग OpenAI
- असिस्टेंट के साथ चैट वेब ऐप
- अतिरिक्त
- संदर्भ
बिल्डिंग ब्लॉक्स
Python जनरेटर
जनरेटर ऐसे फ़ंक्शन होते हैं जो आपको एक ऐसा फ़ंक्शन घोषित करने की अनुमति देते हैं जो इटरेटर (iterator) की तरह व्यवहार करता है, यानी इसे 'for' लूप में इस्तेमाल किया जा सकता है। वे आपको पूरे डेटा सेट को मेमोरी में स्टोर किए बिना डेटा पर इटरेट करने की अनुमति देते हैं, जो बड़े डेटा के साथ काम करते समय या जब आप अनंत अनुक्रम (infinite sequences) बनाना चाहते हैं, तब बहुत उपयोगी हो सकता है।
जब आप एक सामान्य Python फ़ंक्शन को कॉल करते हैं, तो वह पूरा रन होता है और एक परिणाम देता है। हालाँकि, एक जनरेटर फ़ंक्शन को कॉल करने से एक जनरेटर ऑब्जेक्ट बनता है, लेकिन फ़ंक्शन का कोई भी कोड तुरंत रन नहीं होता है। इसके बजाय, जब आप जनरेटर पर इटरेट करते हैं (उदाहरण के लिए, 'for' लूप का उपयोग करके), तो फ़ंक्शन तब तक रन होता है जब तक कि वह 'yield' स्टेटमेंट तक नहीं पहुँच जाता, जो यील्ड किए गए मान को लौटाता है और फ़ंक्शन की स्थिति को रोक देता है। इसके बाद फ़ंक्शन को जनरेटर के बाहर से 'yield' स्टेटमेंट के ठीक बाद फिर से शुरू किया जा सकता है।
यहाँ एक सरल जनरेटर फ़ंक्शन और इसे उपयोग करने का तरीका दिया गया है:
def simple_generator():
yield 1
yield 2
yield 3
# जनरेटर ऑब्जेक्ट्स पर इटरेट किया जा सकता है
for value in simple_generator():
print(value)जब आप इस कोड को रन करेंगे, तो यह प्रिंट करेगा:
``` 1 2 3 ```
AsyncIO
Asyncio एक Python लाइब्रेरी है जो कोरूटीन (coroutines) का उपयोग करके सिंगल-थ्रेडेड समवर्ती (concurrent) कोड लिखने के लिए एक फ्रेमवर्क प्रदान करती है, जो सॉकेट्स और अन्य संसाधनों पर I/O एक्सेस को मल्टीप्लेक्स करती है। यह आपको ऐसा कोड लिखने की अनुमति देता है जो उच्च-स्तरीय एसिंक्रोनस I/O ऑपरेशन करता है, बिना थ्रेड प्रबंधन की चिंता किए।
Asyncio का उपयोग करने के लिए, आप 'async' कीवर्ड का उपयोग करके कोरूटीन को परिभाषित करते हैं। एक कोरूटीन एक विशेष फ़ंक्शन है जो किसी एसिंक्रोनस ऑपरेशन से परिणाम की प्रतीक्षा करते समय अपनी स्थिति खोए बिना कॉलर को नियंत्रण दे सकता है। यह 'await' कीवर्ड के माध्यम से किया जाता है।
यहाँ asyncio का उपयोग करने के कुछ उदाहरण दिए गए हैं:
import asyncio
# एक कोरूटीन को परिभाषित करें
async def say_after(delay, what):
await asyncio.sleep(delay)
print(what)
# मुख्य कोरूटीन जो अन्य कोरूटीन की प्रतीक्षा करता है
async def main():
print(f"started at {time.strftime('%X')}")
# दो कोरूटीन के पूरा होने की प्रतीक्षा करें
await say_after(1, 'hello')
await say_after(2, 'world')
print(f"finished at {time.strftime('%X')}")
# मुख्य कोरूटीन को रन करना
asyncio.run(main())यह कोड 1 सेकंड की देरी के बाद "hello" और फिर 2 सेकंड की देरी के बाद "world" प्रिंट करेगा। ध्यान दें कि 'asyncio.run()' वह फ़ंक्शन है जो मुख्य कोरूटीन को रन करता है, और 'await' के साथ इवेंट लूप और कोरूटीन के बीच नियंत्रण पास किया जाता है।
संक्षेप में, जनरेटर आपको आवश्यकतानुसार एक-एक करके मान यील्ड करने की अनुमति देते हैं, जिससे मेमोरी की बचत होती है, जबकि asyncio आपको ऐसा कोड लिखने की अनुमति देता है जो एसिंक्रोनस ऑपरेशंस को कुशल और पठनीय तरीके से संभालता है, जिससे मल्टी-थ्रेडिंग का उपयोग किए बिना बहुत सारे नेटवर्क या I/O-बाउंड कार्यों को एक साथ करना संभव हो जाता है।
ASGI बनाम WSGI
विवरण
ASGI (Asynchronous Server Gateway Interface) और WSGI (Web Server Gateway Interface) दोनों ही वेब सर्वर के लिए Python वेब एप्लिकेशन के साथ संचार करने के विनिर्देश (specifications) हैं, लेकिन उन्हें अलग-अलग प्रोग्रामिंग प्रतिमानों (paradigms) को ध्यान में रखकर डिज़ाइन किया गया है।
### WSGI
WSGI एक सिंक्रोनस मानक है जिसे PEP 333 में परिभाषित किया गया है जो एक वेब सर्वर को Python वेब एप्लिकेशन के साथ संचार करने की अनुमति देता है। यह एक सरल और पारंपरिक मॉडल है जहाँ एप्लिकेशन को एक एनवायरनमेंट (environ) डिक्शनरी के साथ कॉल किया जाता है जिसमें अनुरोध की जानकारी होती है और प्रतिक्रिया शुरू करने के लिए एक start_response फ़ंक्शन होता है। चूँकि WSGI सिंक्रोनस है, यह प्रति प्रक्रिया एक समय में केवल एक अनुरोध को संभाल सकता है, जो इसे सीमित समानांतर प्रसंस्करण आवश्यकताओं वाले एप्लिकेशन के लिए उपयुक्त बनाता है।
### ASGI
दूसरी ओर, ASGI एक एसिंक्रोनस मानक है जिसे 'asyncio' लाइब्रेरी जैसी एसिंक्रोनस Python सुविधाओं का समर्थन करने के लिए डिज़ाइन किया गया है। ASGI एप्लिकेशन एक ही प्रक्रिया के भीतर एक साथ कई अनुरोधों को संभाल सकते हैं, जो उन्हें उन एप्लिकेशन के लिए अधिक स्केलेबल और प्रदर्शनकारी बनाता है जो कई समवर्ती कनेक्शनों का प्रबंधन करते हैं या जिनमें I/O ऑपरेशंस के लिए बहुत अधिक प्रतीक्षा समय होता है।
### तुलना
यहाँ एक संक्षिप्त तुलना दी गई है:
- समवर्तीता (Concurrency): ASGI कई समवर्ती कनेक्शनों को संभाल सकता है, जो इसे WebSockets, लॉन्ग-पोल HTTP और अन्य लंबे समय तक चलने वाले कनेक्शनों के लिए उपयुक्त बनाता है।
- संगतता (Compatibility): WSGI एक पुराना मानक है और लगभग सभी Python वेब फ्रेमवर्क द्वारा समर्थित है, जबकि ASGI का समर्थन बढ़ रहा है लेकिन यह अभी उतना सर्वव्यापी नहीं है।
- जटिलता (Complexity): कोड की एसिंक्रोनस प्रकृति के कारण ASGI एप्लिकेशन अधिक जटिल हो सकते हैं।
- प्रदर्शन (Performance): ASGI एप्लिकेशन संसाधनों के साथ अधिक कुशल हो सकते हैं, विशेष रूप से I/O-बाउंड ऑपरेशंस के साथ और HTTP/2 या सर्वर सेंट इवेंट्स (SSE) का लाभ उठाते समय।
### HTTP/2 और SSE के साथ ASGI
HTTP/2, HTTP नेटवर्क प्रोटोकॉल का एक बड़ा संशोधन है जो एक ही TCP कनेक्शन पर कई समवर्ती अनुरोधों की अनुमति देता है, जिसे मल्टीप्लेक्सिंग कहा जाता है। यह ASGI की एसिंक्रोनस क्षमताओं के लिए एक अच्छा मेल है, जो ASGI को कई थ्रेड्स या प्रक्रियाओं को बनाने के ओवरहेड के बिना इन कई अनुरोधों को एक साथ प्रबंधित करने की अनुमति देता है।
सर्वर सेंट इवेंट्स (SSE) एक मानक है जो एक सर्वर को HTTP कनेक्शन पर क्लाइंट को रीयल-टाइम अपडेट पुश करने की अनुमति देता है। SSE के लिए सर्वर को कनेक्शन खुला रखने और नए अपडेट उपलब्ध होने पर इवेंट भेजने की आवश्यकता होती है।
ASGI, SSE के लिए उपयुक्त है क्योंकि:
- एसिंक्रोनस हैंडलिंग: ASGI SSE में उपयोग किए जाने वाले खुले कनेक्शनों को थ्रेड्स को ब्लॉक किए बिना कुशलतापूर्वक प्रबंधित कर सकता है, क्योंकि सर्वर केवल तभी क्लाइंट को डेटा भेजता है जब कोई इवेंट उपलब्ध होता है।
- स्केलेबिलिटी: एक ही प्रक्रिया में कई कनेक्शनों को संभालने की अपनी क्षमता के साथ, ASGI सर्वर एक साथ कई क्लाइंट्स का समर्थन कर सकते हैं, और आवश्यकतानुसार प्रत्येक क्लाइंट को अपडेट भेज सकते हैं।
- HTTP/2 के साथ संगतता: HTTP/2 की मल्टीप्लेक्सिंग क्षमताओं को ASGI के साथ जोड़ने से एक ही कनेक्शन पर कई SSE स्ट्रीम को संभालना आसान हो जाता है।
इस प्रकार, जब HTTP/2 के साथ उपयोग किया जाता है, तो ASGI SSE के साथ एप्लिकेशन बनाने के लिए एक उत्कृष्ट विकल्प बन जाता है क्योंकि यह क्लाइंट्स के साथ रीयल-टाइम, सर्वर-इनिशिएटेड संचार को संभालने का एक प्रदर्शनकारी और स्केलेबल तरीका प्रदान करता है।
चित्रण
sequenceDiagram
participant Client
participant Server
Client->>Server: GET /stream
Note over Client: Header: Accept: text/event-stream
Note over Server: Establishes stream connection
loop Streaming data
Server-->>Client: data: {"event": "message", "data": "JSON payload"}
Note over Server: Header: Content-Type: text/event-stream
end
Note over Client: Processes each received eventसर्वर सेंट इवेंट्स (SSE)
विवरण
सर्वर सेंट इवेंट्स (SSE) एक ऐसी तकनीक है जो एक सर्वर को एक ही स्थायी HTTP कनेक्शन पर वेब पेज पर रीयल-टाइम अपडेट भेजने की अनुमति देती है। यह उन एप्लिकेशन को बनाने के लिए उपयोग की जाने वाली तकनीक है जिन्हें रीयल-टाइम में क्लाइंट को अपडेट करने की आवश्यकता होती है, जैसे कि न्यूज़ फ़ीड, सोशल मीडिया अपडेट, या लाइव स्कोर।
यहाँ वेब के नए उपयोगकर्ताओं के लिए एक संक्षिप्त व्याख्या दी गई है:
सामान्य तौर पर, जब आप किसी वेबपेज पर जाते हैं, तो आपका ब्राउज़र सर्वर को अनुरोध करता है, सर्वर अनुरोधित पेज वापस भेजता है, और बस इतना ही। कनेक्शन तब तक बंद रहता है जब तक आप कोई अन्य अनुरोध नहीं करते (जैसे किसी लिंक पर क्लिक करना या पेज को रिफ्रेश करना)। हालाँकि, कभी-कभी आप सर्वर से लगातार अपडेट प्राप्त करना चाहते हैं बिना हर बार उनके लिए पूछने (पोलिंग) के। SSE इसी के लिए है।
SSE के साथ, सर्वर प्रारंभिक प्रतिक्रिया भेजने के बाद कनेक्शन को खुला रखता है और फिर डेटा उपलब्ध होने पर उसे भेज सकता है। यह रीयल-टाइम में अपडेट देने के लिए बेहद उपयोगी है।
चित्रण
नीचे सर्वर से क्लाइंट तक SSE प्रवाह को दिखाने वाले आरेख का टेक्स्ट-आधारित प्रतिनिधित्व दिया गया है, जिसमें विषय और डेटा के साथ पेलोड आकार शामिल हैं:
sequenceDiagram
participant Client
participant Server
Note over Client,Server: Connection Initialization
Client->>Server: GET /events
Note over Server: Set "Content-Type: text/event-stream"
Server-->>Client: HTTP 200 OK
Note over Client,Server: Streaming Events
loop Every Time an Event Is Sent
Note over Server: Server prepares an event with a specific topic and data
Server->>Client: event: user-update\n\n
Server->>Client: data: {"userId": 1, "status": "active"}\n\n
Client->>Server: Event received, handling user-update
Server->>Client: event: message\n\n
Server->>Client: data: {"chatId": 42, "text": "Hello there!"}\n\n
Client->>Server: Event received, handling message
end
Note over Client,Server: Connection Closed (either by client or server)
Server--xClient: Connection closed
Client--xServer: Connection closedआप इस टेक्स्ट को Mermaid लाइव एडिटर में प्लग कर सकते हैं या विज़ुअल आरेख रेंडर करने के लिए इसे GitHub MarkDown फ़ाइल में शामिल कर सकते हैं। यदि आप इसे मैन्युअल रूप से कोड कर रहे हैं, तो आप क्लाइंट और सर्वर को अभिनेताओं के रूप में दर्शाने के लिए आयत बनाएंगे, स्ट्रीमिंग कनेक्शन का प्रतिनिधित्व करने के लिए एक रेखा बनाएंगे, और फिर पेलोड को लेबल किए गए आकृतियों (जैसे बुलबुले या आयत) के रूप में बनाएंगे जिनमें इवेंट का प्रकार और JSON डेटा संरचनाएं शामिल हों जिन्हें आप सर्वर और क्लाइंट के बीच पास होने की उम्मीद करते हैं। इवेंट्स सर्वर से क्लाइंट तक दिशात्मक तीर होने चाहिए, जो SSE की एकतरफा प्रकृति को प्रदर्शित करते हैं।
कोड
यहाँ org-mode स्वरूपित कोड के साथ एक उदाहरण दिया गया है:
क्लाइंट-साइड कोड (HTML + JavaScript)
निम्नलिखित उदाहरण दिखाता है कि आप एक सरल वेब पेज कैसे लागू कर सकते हैं जो JavaScript का उपयोग करके SSE को सुनता है।
<html>
<head>
<title>Server Sent Events Exampletitle>
head>
<body>
<h1>Real-time Updatesh1>
<div id="updates">div>
<script>
// SSE एंडपॉइंट से कनेक्ट करने वाला एक नया EventSource इंस्टेंस बनाएं
const eventSource = new EventSource('/events');
eventSource.onmessage = function(event) {
// यह फ़ंक्शन तब कॉल किया जाता है जब कोई संदेश प्राप्त होता है
const messageData = event.data;
// नए डेटा को 'updates' div में जोड़ें
const updatesElement = document.getElementById('updates');
updatesElement.innerHTML += messageData + '
';
};
eventSource.onerror = function(error) {
// होने वाली किसी भी त्रुटि को संभालें
console.log('EventSource failed: ', error);
};
// जब आप इवेंट्स सुनना समाप्त कर लें
// eventSource.close();
script>
body>
html>सर्वर-साइड कोड (Node.js का उपयोग करके)
सर्वर साइड पर, आपके पास एक एंडपॉइंट होगा जो SSE का उपयोग करके क्लाइंट को अपडेट स्ट्रीम करता है। निम्नलिखित उदाहरण Express फ्रेमवर्क के साथ Node.js का उपयोग करता है।
const express = require('express');
const app = express();
app.get('/events', (req, res) => {
// SSE सेट अप करने के लिए हेडर
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
const sendEvent = (data) => {
res.write(`data: ${JSON.stringify(data)}\n\n`); // क्लाइंट को डेटा भेजें
};
// हर सेकंड एक अपडेट भेजें
const intervalId = setInterval(() => {
const message = { text: 'Hello World', timestamp: new Date() };
sendEvent(message);
}, 1000);
// क्लाइंट के डिस्कनेक्ट होने पर कनेक्शन बंद करें
req.on('close', () => {
clearInterval(intervalId);
});
});
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});इस उदाहरण को चलाने के लिए, आपको अपने सिस्टम पर Node.js सेट अप करना होगा, Express (`npm install express`) इंस्टॉल करना होगा, और सर्वर-साइड कोड को एक फ़ाइल (जैसे `server.js`) में सहेजना होगा। फिर, आप बस `node server.js` के साथ अपना सर्वर चला सकते हैं, और इवेंट्स प्राप्त करना शुरू करने के लिए अपने वेब ब्राउज़र को क्लाइंट-साइड HTML पेज पर इंगित कर सकते हैं।
सहायक लाइब्रेरी
poetry init # अपनी वर्तमान निर्देशिका में एक नया poetry प्रोजेक्ट प्रारंभ करेंOpenAI
OpenAI एक AI अनुसंधान और परिनियोजन कंपनी है। उनका मिशन यह सुनिश्चित करना है कि आर्टिफिशियल जनरल इंटेलिजेंस (AGI) पूरी मानवता को लाभान्वित करे।
GPT-4 OpenAI का सबसे उन्नत सिस्टम है, जो सुरक्षित और अधिक उपयोगी प्रतिक्रियाएं उत्पन्न करता है। GPT-4 अपने व्यापक सामान्य ज्ञान और समस्या-समाधान क्षमताओं के कारण अधिक सटीकता के साथ कठिन समस्याओं को हल कर सकता है।
DALL·E 3 हमारे पिछले सिस्टम की तुलना में काफी अधिक बारीकियों और विवरणों को समझता है, जिससे आप अपने विचारों को असाधारण रूप से सटीक छवियों में आसानी से अनुवादित कर सकते हैं।
poetry add openai # openai जोड़ेंFastAPI
FastAPI मानक Python टाइप हिंट्स के आधार पर Python 3.8+ के साथ API बनाने के लिए एक आधुनिक, तेज़ (उच्च-प्रदर्शन), वेब फ्रेमवर्क है।
poetry add "fastapi[all]" "uvicorn[standard]" # fastapi और uvicorn जोड़ेंJinja2
Jinja एक तेज़, अभिव्यंजक, विस्तार योग्य टेम्प्लेटिंग इंजन है। टेम्प्लेट में विशेष प्लेसहोल्डर Python सिंटैक्स के समान कोड लिखने की अनुमति देते हैं। फिर अंतिम दस्तावेज़ को रेंडर करने के लिए टेम्प्लेट को डेटा पास किया जाता है।
इंस्टालेशन
poetry add jinja2 # jinja2 जोड़ेंकोड
templates/doggo.jinja2
<html>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
<script src="https://unpkg.com/htmx.org@1.9.10">script>
<script src="https://unpkg.com/htmx.org@1.9.10/dist/ext/sse.js">script>
<title>Formulario | Page Doggotitle>
head>
<body>
<h1>Dog Breeds as Server Sent Eventsh1>
<hr>
<div id="doggo-sse-listener" hx-ext="sse" sse-connect="/dogstream" sse-swap="Terminate,DogBreedNoMass,DogBreed">
div>
<b>
<div id="DogBreedNoMass">div>
<br>
<div id="DogBreed">div>
b>
body>
html>templates/openai.jinja2
<html>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
<script src="https://unpkg.com/htmx.org@1.9.10">script>
<script src="https://unpkg.com/htmx.org@1.9.10/dist/ext/sse.js">script>
<title>Page | Raw OpenAI Responsetitle>
head>
<body>
<h1>OpenAI Response as Server Sent Eventsh1>
<hr>
<div hx-ext="sse" sse-connect="/openaistream">
<article class="message is-info">
<div class="message-header">
<p sse-swap="ResponseNoMass">Infop>
<button class="delete" aria-label="delete">button>
div>
article>
<div class="message-body" sse-swap="Response" hx-swap="innerHTML">
div>
div>
body>
html>HTMX
htmx आपको सीधे HTML में AJAX, CSS ट्रांज़िशन, WebSockets और सर्वर सेंट इवेंट्स तक पहुंच प्रदान करता है, एट्रिब्यूट्स का उपयोग करके, ताकि आप हाइपरटेक्स्ट की सादगी और शक्ति के साथ आधुनिक यूजर इंटरफेस बना सकें।
कोड
templates/layout.jinja2
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.tailwindcss.com">script>
<script src="https://unpkg.com/htmx.org@1.9.10">script>
<script src="https://unpkg.com/htmx.org@1.9.10/dist/ext/sse.js">script>
{% block head %}{% endblock %}
head>
<body hx-boost="true">
{% block body %}{% endblock %}
{% block scripts %}{% endblock %}
body>
html>templates/partials/sse.jinja2
{% macro sse_stream(sse_config) %}
<div id="{{ sse_config.listener }}" hx-ext="sse" sse-connect="{{ sse_config.path }}" sse-swap="{{ sse_config.topics | join(',') }}">
div>
{% endmacro %}templates/partials/streaming_chunk.jinja2
event: {{ event }}
data: <div {% for name, value in attrs.items() %} {{ name }}="{{ value }}" {% endfor %}>{{ chunk }}div>templates/partials/ai_message.jinja2
{% macro ai_msg(message) %}
<div class="flex gap-3 my-4 text-gray-600 text-sm flex-1">
<span class="relative flex shrink-0 overflow-hidden rounded-full w-8 h-8">
<div class="rounded-full bg-gray-100 border p-1">
<svg stroke="none" fill="black" stroke-width="1.5"
viewBox="0 0 24 24" aria-hidden="true" height="20" width="20"
xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round"
d="M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09zM18.259 8.715L18 9.75l-.259-1.035a3.375 3.375 0 00-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 002.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 002.456 2.456L21.75 6l-1.035.259a3.375 3.375 0 00-2.456 2.456zM16.894 20.567L16.5 21.75l-.394-1.183a2.25 2.25 0 00-1.423-1.423L13.5 18.75l1.183-.394a2.25 2.25 0 001.423-1.423l.394-1.183.394 1.183a2.25 2.25 0 001.423 1.423l1.183.394-1.183.394a2.25 2.25 0 00-1.423 1.423z">
path>
svg>
div>
span>
<p class="leading-relaxed">
<span class="block font-bold text-gray-700">AI span>
{{ message }}
<span id="Response">span>
p>
div>
{% endmacro %}templates/partials/user_message.jinja2
{% macro user_msg(message) %}
<div class="flex gap-3 my-4 text-gray-600 text-sm flex-1"><span
class="relative flex shrink-0 overflow-hidden rounded-full w-8 h-8">
<div class="rounded-full bg-gray-100 border p-1"><svg stroke="none" fill="black" stroke-width="0"
viewBox="0 0 16 16" height="20" width="20" xmlns="http://www.w3.org/2000/svg">
<path
d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6Zm2-3a2 2 0 1 1-4 0 2 2 0 0 1 4 0Zm4 8c0 1-1 1-1 1H3s-1 0-1-1 1-4 6-4 6 3 6 4Zm-1-.004c-.001-.246-.154-.986-.832-1.664C11.516 10.68 10.289 10 8 10c-2.29 0-3.516.68-4.168 1.332-.678.678-.83 1.418-.832 1.664h10Z">
path>
svg>div>
span>
<p class="leading-relaxed">
<span class="block font-bold text-gray-700">You span>
{{ message }}
p>
div>
{% endmacro %}templates/index.jinja2
{% extends "layout.jinja2" %}
{% from "partials/sse.jinja2" import sse_stream %}
{% from "partials/ai_message.jinja2" import ai_msg %}
{% from "partials/user_message.jinja2" import user_msg %}
{% block body %}
<button class="fixed bottom-4 right-4 inline-flex items-center justify-center text-sm font-medium disabled:pointer-events-none disabled:opacity-50 border rounded-full w-16 h-16 bg-black hover:bg-gray-700 m-0 cursor-pointer border-gray-200 bg-none p-0 normal-case leading-5 hover:text-gray-900"
type="button" aria-haspopup="dialog" aria-expanded="false" data-state="closed">
<svg xmlns=" http://www.w3.org/2000/svg" width="30" height="40" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="text-white block border-gray-200 align-middle">
<path d="m3 21 1.9-5.7a8.5 8.5 0 1 1 3.8 3.8z" class="border-gray-200">
path>
svg>
button>
<div class="md:container md:mx-auto" style="box-shadow: 0 0 #0000, 0 0 #0000, 0 1px 2px 0 rgb(0 0 0 / 0.05);"
class="fixed bottom-[calc(4rem+1.5rem)] right-40 mr-4 bg-white p-6 rounded-lg border border-[#e5e7eb] w-[3/4] h-[634px]">
<div class="flex flex-col space-y-1.5 pb-6">
<h2 class="font-semibold text-lg tracking-tight">Custom Chatboth2>
<p class="text-sm text-[#6b7280] leading-3">Powered by Your OpenAI Keyp>
div>
<div class="pr-4 h-[474px]" style="min-width: 100%; display: table;">
{% if sse_config %}
{{ sse_stream(sse_config) }}
{% endif %}
{% for message in messages %}
{% if message.sender == 'ai' %}
{{ ai_msg(message.content) }}
{% endif %}
{% if message.sender == 'user' %}
{{ user_msg(message.content) }}
{% endif %}
{% endfor %}
div>
<div class="flex items-center pt-0">
<form
action="{{ url_for('openai', req_id=req_id) }}" method="POST"
class="flex items-center justify-center w-full space-x-2"
>
<input
class="flex h-10 w-full rounded-md border border-[#e5e7eb] px-3 py-2 text-sm placeholder-[#6b7280] focus:outline-none focus:ring-2 focus:ring-[#9ca3af] disabled:cursor-not-allowed disabled:opacity-50 text-[#030712] focus-visible:ring-offset-2"
type="text" name="user_prompt"
placeholder="Message ChatGPT..." value="">
<input
type="submit"
class="inline-flex items-center justify-center rounded-md text-sm font-medium text-[#f9fafb] disabled:pointer-events-none disabled:opacity-50 bg-black hover:bg-[#111827E6] h-10 px-4 py-2"
value="Send message">
form>
div>
div>
{% endblock %}Markdown
Markdown एक हल्का मार्कअप भाषा है जिसका उपयोग आप सादे टेक्स्ट दस्तावेज़ों में फ़ॉर्मेटिंग तत्व जोड़ने के लिए कर सकते हैं। 2004 में जॉन ग्रुबर द्वारा बनाया गया, Markdown अब दुनिया की सबसे लोकप्रिय मार्कअप भाषाओं में से एक है।
sequenceDiagram
participant M as Markdown File
participant P as Markdown Processor
participant H as HTML File
participant B as Web Browser
M->>P: Process Markdown
P->>H: Generate HTML
H->>B: Render in Browserयह अनुक्रम आरेख Markdown फ़ाइल से प्रसंस्करण के माध्यम से रेंडरिंग तक के सरल प्रवाह को इस प्रकार दर्शाता है:
- "Markdown फ़ाइल" इनपुट के रूप में कार्य करती है।
- "Markdown प्रोसेसर" Markdown को HTML में परिवर्तित करता है।
- परिणामी "HTML फ़ाइल" तब तैयार हो जाती है।
- अंत में, "वेब ब्राउज़र" उपयोगकर्ता को आउटपुट प्रदर्शित करने के लिए HTML फ़ाइल को रेंडर करता है।
TailwindCSS
TailwindCSS यूटिलिटी-फर्स्ट CSS फ्रेमवर्क है जो flex, pt-4, text-center और rotate-90 जैसी क्लास से भरा है, जिन्हें आपके मार्कअप में सीधे किसी भी डिज़ाइन को बनाने के लिए संयोजित किया जा सकता है।
SQLAlchemy
कोड
[tool.poetry]
name = "custom-chatbot"
version = "0.1.0"
description = ""
authors = ["ChiefKemist"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.12"
Jinja2 = "^3.1.2"
fastapi = "^0.109.0"
python-multipart = "^0.0.6"
uvicorn = {extras = ["standard"], version = "^0.25.0"}
httpx = "^0.26.0"
openai = "^1.7.2"
sqlalchemy = "^2.0.25"
markdown = "^3.5.2"
pygments = "^2.17.2"
semantic-router = "^0.0.18"
rich = "^13.7.0"
[tool.poetry.group.dev.dependencies]
pytest = "^7.4.4"
black = "^23.12.1"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"CLI चैट ऐप
चैट संदर्भ
चैट इतिहास दृढ़ता (Persistence)
कोड
import logging
import sys
import hashlib
import openai
from sqlalchemy import create_engine, Column, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from datetime import datetime
from getpass import getpass
from rich.console import Console
logging.basicConfig(
stream=sys.stdout, level=logging.DEBUG,
format='%(asctime)s %(levelname)s [%(module)s] %(message)s',
)
log = logging.getLogger(__name__)
console = Console()
# SQLAlchemy सेटअप
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
username = Column(String, unique=True)
password_hash = Column(String)
class Message(Base):
__tablename__ = 'messages'
id = Column(Integer, primary_key=True)
chat_room = Column(String)
sender = Column(String)
message = Column(String)
timestamp = Column(DateTime, default=datetime.now)
# SQLite डेटाबेस कनेक्शन
engine = create_engine('sqlite:///chat.db')
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
def hash_password(password):
return hashlib.sha256(password.encode()).hexdigest()
def register_user():
username = input("Enter new username: ")
if session.query(User).filter_by(username=username).first():
console.log(
"Username already exists. Please try a different username.",
style="bold red"
)
return None
password = getpass("Enter new password: ")
hashed_password = hash_password(password)
new_user = User(username=username, password_hash=hashed_password)
session.add(new_user)
session.commit()
return username
def login_user():
username = input("Enter username: ")
password = getpass("Enter password: ")
hashed_password = hash_password(password)
user = session.query(User).filter_by(username=username, password_hash=hashed_password).first()
if user:
return username
else:
console.log("Invalid username or password.", style="bold red")
return None
def save_message(chat_room, sender, message):
new_message = Message(chat_room=chat_room, sender=sender, message=message)
session.add(new_message)
session.commit()
def get_chat_history(chat_room):
messages = session.query(Message).filter_by(chat_room=chat_room).order_by(Message.timestamp).all()
return [f"{message.sender}: {message.message}" for message in messages]
def get_gpt4_response(prompt, chat_history):
# openai.api_key = 'your-api-key' # अपनी वास्तविक OpenAI API कुंजी के साथ बदलें
combined_prompt = "\n".join(chat_history[-50:]) + f"\n{prompt}" # इतिहास को अंतिम 50 संदेशों तक सीमित करें
response = openai.OpenAI().chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": combined_prompt}
]
)
return response.choices[0].message.content
def run_chat(username):
chat_room = username
console.log(
f"Welcome to your personal GPT-4 Chat CLI, {username}. Type 'quit' to exit.",
style="bold blue"
)
while True:
user_input = input("You: ")
if user_input.lower() == 'quit':
break
chat_context = get_chat_history(chat_room)
gpt_response = get_gpt4_response(user_input, chat_context)
save_message(chat_room, "You", user_input)
save_message(chat_room, "GPT-4", gpt_response)
console.log(f"GPT-4: {gpt_response}", style="bold green")
console.log("Chat ended.", style="bold blue")
def main():
log.info("Welcome to the Chat Application")
choice = input("Do you want to [L]ogin or [R]egister? (L/R): ").lower()
username = None
while not username:
if choice == 'r':
username = register_user()
elif choice == 'l':
username = login_user()
else:
choice = input("Please enter 'L' to login or 'R' to register: ").lower()
if username:
run_chat(username)
if __name__ == "__main__":
main()स्ट्रीमिंग डॉग्स
विवरण
Python के asyncio और जनरेटर, साथ ही Htmx का लाभ उठाते हुए ब्राउज़र में डॉग ब्रीड्स को स्ट्रीम करें। डॉग ब्रीड्स dog.ceo द्वारा प्रदान की जाती हैं।
कोड
#begin_src bash
curl https://dog.ceo/api/breeds/list/all
#+end_src
async_doggo.py
#!/usr/bin/env python3
import asyncio
import logging
import sys
import typing
import httpx
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse, StreamingResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from jinja2 import Environment, FileSystemLoader, select_autoescape
logging.basicConfig(
stream=sys.stdout, level=logging.DEBUG,
format='%(asctime)s %(levelname)s [%(module)s] %(message)s',
)
log = logging.getLogger(__name__)
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
def app_context(request: Request) -> typing.Dict[str, typing.Any]:
return {'app': request.app}
templates = Jinja2Templates(
directory="templates",
context_processors=[app_context],
)
@app.get("/", response_class=HTMLResponse)
async def index(request: Request):
return templates.TemplateResponse(
request=request, name="doggo.jinja2",
)
def render_sse_html_chunk(event, chunk, attrs=None):
if attrs is None:
attrs = {}
tmpl = Environment(
loader=FileSystemLoader('templates/partials'),
autoescape=select_autoescape(['html'])
).select_template(['streaming_chunk.jinja2'])
html_chunk = tmpl.render(**dict(event=event, chunk=chunk, attrs=attrs))
return html_chunk
async def gen_dog_breeds():
async with httpx.AsyncClient() as client:
breeds = (await client.get('https://dog.ceo/api/breeds/list/all')).json()
for breed in breeds['message'].keys():
log.info(f"Yielding {breed}")
yield breed
@app.get("/dogstream", response_class=StreamingResponse)
async def dogstream(request: Request):
async def dogbreeds_iter():
async for breed in gen_dog_breeds():
await asyncio.sleep(0.2)
breed_status_chunk = render_sse_html_chunk(
'DogBreedNoMass',
'More doggo senior :-)',
{
'id': 'DogBreedNoMass',
'hx-swap-oob': 'true',
},
)
yield f'{breed_status_chunk}\n\n'.encode('utf-8')
await asyncio.sleep(0.2)
chunk = render_sse_html_chunk(
'DogBreed',
breed,
{
'id': 'DogBreed',
'hx-swap-oob': 'true',
},
)
yield f'{chunk}\n\n'.encode('utf-8')
breed_status_chunk = render_sse_html_chunk(
'DogBreedNoMass',
'No more doggo senior :-(',
{
'id': 'DogBreedNoMass',
'hx-swap-oob': 'true',
},
)
yield f'{breed_status_chunk}\n\n'.encode('utf-8')
return StreamingResponse(
dogbreeds_iter(),
media_type='text/event-stream',
)
if __name__ == '__main__':
import uvicorn
uvicorn.run('async_doggo:app', host='0.0.0.0', port=6543, reload=True)स्ट्रीमिंग OpenAI
रॉ / सादा
बेहतर दिखने वाला
रिच टेक्स्ट
async_chat.py
#!/usr/bin/env python3
import asyncio
import functools
import concurrent.futures
import logging
import re
import sys
import queue
import typing
import uuid
from io import StringIO
from time import sleep
import httpx
import markdown
from pygments import highlight
from pygments.lexers import get_lexer_by_name
from pygments.formatters.html import HtmlFormatter
from fastapi import FastAPI, Request, Form
from fastapi.responses import HTMLResponse, StreamingResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from jinja2 import Environment, FileSystemLoader, select_autoescape
logging.basicConfig(
stream=sys.stdout, level=logging.DEBUG,
format='%(asctime)s %(levelname)s [%(module)s] %(message)s',
)
log = logging.getLogger(__name__)
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
def app_context(request: Request) -> typing.Dict[str, typing.Any]:
return {'app': request.app}
templates = Jinja2Templates(
directory="templates",
context_processors=[app_context],
)
@app.get("/", response_class=HTMLResponse)
async def index(request: Request):
req_id = str(uuid.uuid4())
messages = [
{'sender': 'ai', 'content': 'Hi, how can I help you today?'},
{'sender': 'user', 'content': 'faslskadalksjioqjeqlkj'},
{'sender': 'ai',
'content': 'Sorry, I couldn\'t find any information in the documentation about that. Expect answer to be less accurateI could not find the answer to this in the verified sources.'},
]
return templates.TemplateResponse(
request=request, name="index.jinja2",
context=dict(messages=messages, req_id=req_id)
)
reqs = {}
@app.post("/openai/{req_id}", response_class=HTMLResponse)
async def openai(request: Request, req_id: str, user_prompt: typing.Annotated[str, Form()]):
log.debug(f'User prompt: {user_prompt}')
reqs[req_id] = user_prompt
messages = [
{'sender': 'user', 'content': user_prompt},
{'sender': 'ai', 'content': ''},
]
sse_config = dict(
listener='openai',
path=f'/openaistream/{req_id}',
topics=[
'Response',
'ResponseNoMass',
'Terminate',
]
)
new_req_id = str(uuid.uuid4())
return templates.TemplateResponse(
request=request, name="index.jinja2",
context=dict(
messages=messages, req_id=new_req_id, sse_config=sse_config
)
)
def render_sse_html_chunk(event, chunk, attrs=None):
if attrs is None:
attrs = {}
tmpl = Environment(
loader=FileSystemLoader('templates/partials'),
autoescape=select_autoescape(['html'])
).select_template(['streaming_chunk.jinja2'])
html_chunk = tmpl.render(**dict(event=event, chunk=chunk, attrs=attrs))
return html_chunk
def markdown_to_html_with_highlighting(source_markdown):
md = markdown.Markdown(extensions=['fenced_code', 'codehilite'])
html = md.convert(source_markdown)
css = HtmlFormatter().get_style_defs('.codehilite')
return f"{html.replace('\n', '
')}"
def markdown_to_html_with_inline_highlighting(source_markdown):
formatter = HtmlFormatter(style='default', cssclass='', noclasses=True)
def inline_highlight(match):
language = match.group('lang')
code = match.group('code')
lexer = get_lexer_by_name(language, stripall=True)
highlighted_code = highlight(code.replace('
', '\n'), lexer, formatter)
return highlighted_code.replace('\n', '
')
highlighted_markdown = re.sub(
r'```(?P\w+)\s*(?P.*?)```',
inline_highlight,
source_markdown,
flags=re.DOTALL
)
html = markdown.markdown(highlighted_markdown)
return html
async def run_openai(req_id: str):
from time import perf_counter
from openai import AsyncOpenAI
user_prompt = reqs[req_id]
log.debug(f'User prompt: {user_prompt}')
messages = [
{'role': 'system', 'content': 'please response in markdown only.'},
{'role': 'user', 'content': user_prompt},
]
chunks = await AsyncOpenAI().chat.completions.create(
model='gpt-4',
messages=messages,
stream=True,
)
last = None
result_chunks = []
result_concat = StringIO()
async for chunk in chunks:
now = perf_counter()
if last is not None:
t = now - last
else:
t = 0
text = chunk.choices[0].delta.content
if text is not None:
result_chunks.append((t, text))
result_concat.write(f"{text}")
mdText = markdown_to_html_with_inline_highlighting(
result_concat.getvalue().replace('\n', "
")
)
yield mdText
else:
log.debug('No text adding space')
last = now
yield None # All Done
text = ''.join(text for _, text in result_chunks)
@app.get("/openaistream/{req_id}", response_class=StreamingResponse)
async def openaistream(request: Request, req_id: str):
log.info(f"Request ID: {req_id}")
async def openai_iter():
async for resp in run_openai(req_id):
if resp is None:
chunk = render_sse_html_chunk(
'Terminate',
'',
{
'id': 'openai',
'hx-swap-oob': 'true',
},
)
yield f'{chunk}\n\n'.encode('utf-8')
break
chunk = render_sse_html_chunk(
'Response',
resp,
{
'id': 'Response',
'hx-swap-oob': 'true',
},
)
yield f'{chunk}\n\n'.encode('utf-8')
chunk = render_sse_html_chunk(
'Terminate',
'',
{
'id': 'openai',
'hx-swap-oob': 'true',
},
)
yield f'{chunk}\n\n'.encode('utf-8')
return StreamingResponse(
openai_iter(),
media_type='text/event-stream',
)
if __name__ == '__main__':
import uvicorn
uvicorn.run('async_chat:app', host='0.0.0.0', port=6543, reload=True)असिस्टेंट के साथ चैट वेब ऐप
असिस्टेंट API
असिस्टेंट विवरण को बनाए रखें
असिस्टेंट डेटा
संदर्भ और दृढ़ता के साथ मल्टी-थ्रेडेड चैट
सिमेंटिक राउटर
अतिरिक्त
परिनियोजन (Deployment)
FastAPI
FastUI
संदर्भ
- Hypermedia Systems ऑनलाइन पुस्तक पढ़ें।
- Htmx Patterns हाइपरमीडिया सिस्टम के अध्याय 5 में पढ़ें।
- Tricks of The Htmx Masters हाइपरमीडिया सिस्टम के अध्याय 8 में पढ़ें।
- ///_hyperscript आधुनिक वेब फ्रंटएंड के लिए एक आसान और सुलभ भाषा है।
- H/ Hyperview सर्वर-संचालित मोबाइल ऐप विकसित करने के लिए एक नया हाइपरमीडिया प्रारूप और React Native क्लाइंट है।

