Ubuntu TechHive
implementing-a-custom-chatbot-with-openai-and-python.md
OpenAI और Python के साथ एक कस्टम चैटबॉट लागू करना
article.विवरण

OpenAI और Python के साथ एक कस्टम चैटबॉट लागू करना

reading.प्रगति 22 मिनट पढ़ें

OpenAI और Python के साथ एक कस्टम चैटबॉट लागू करने का विवरण

OpenAI और Python के साथ एक कस्टम चैटबॉट लागू करना

बिल्डिंग ब्लॉक्स

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 फ़ाइल से प्रसंस्करण के माध्यम से रेंडरिंग तक के सरल प्रवाह को इस प्रकार दर्शाता है:

  1. "Markdown फ़ाइल" इनपुट के रूप में कार्य करती है।
  2. "Markdown प्रोसेसर" Markdown को HTML में परिवर्तित करता है।
  3. परिणामी "HTML फ़ाइल" तब तैयार हो जाती है।
  4. अंत में, "वेब ब्राउज़र" उपयोगकर्ता को आउटपुट प्रदर्शित करने के लिए 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 क्लाइंट है।