Computer >> कंप्यूटर ट्यूटोरियल >  >> प्रोग्रामिंग >> Redis

OpenAI, Upstash, और Next.js के साथ AI-संचालित स्टोरी जेनरेटर बनाएं

इस ब्लॉग पोस्ट के लिए, हम जारी रखने से पहले कुछ धारणाएं बनाएंगे, लेकिन आपके पास आदर्श रूप से होना चाहिए:

  • एक Upstash अकाउंट जहां आपने एक Redis और QStash इंस्टेंस बनाया है
  • अपने API कुंजी तक पहुंच के साथ एक OpenAI अकाउंट
  • एक Next.js प्रोजेक्ट जहां हम स्टोरी जेनरेटर कार्यक्षमता बनाएंगे
  • अपने प्रोजेक्ट को डेप्लॉय करने के लिए एक Vercel अकाउंट

परिचय

क्या आप कभी AI का उपयोग करके अपनी खुद की कहानियां जनरेट करना चाहते थे? OpenAI के कम्पलीशन API और Upstash के QStash और Redis के साथ, अब प्राकृतिक भाषा प्रसंस्करण का उपयोग करके अपनी खुद की कस्टम कहानियां बनाना पहले से कहीं ज्यादा आसान है। इस ट्यूटोरियल में, हम इन उपकरणों को सेटअप और उपयोग करने की प्रक्रिया के माध्यम से चलेंगे ताकि अद्वितीय और आकर्षक कहानियां जनरेट की जा सकें।

OpenAI, Upstash, और Next.js के साथ AI-संचालित स्टोरी जेनरेटर बनाएं

ऐप की अधिक इमेज देखें:

  • क्रिएट स्टोरी फॉर्म
  • जनरेटिंग स्टोरी स्टेट
  • जनरेटेड स्टोरी प्रदर्शित

आर्किटेक्चर

कोड को देखकर आपको ऐप कैसे सेटअप है इसकी अच्छी समझ होने की संभावना है, लेकिन थोड़े उच्च स्तरीय अवलोकन के रूप में, नीचे दी गई छवि एप्लिकेशन फ्लो के कुछ हिस्सों और वे कैसे संचार करते हैं दिखा रही है।

OpenAI, Upstash, और Next.js के साथ AI-संचालित स्टोरी जेनरेटर बनाएं

प्रोजेक्ट सेटअप

सबसे पहले हम एक Next.js प्रोजेक्ट बनाना चाहेंगे। यह TypeScript के साथ एक नया Next.js प्रोजेक्ट बनाने के लिए निम्न चलाकर किया जा सकता है। आप Next.js सेटअप करने के चरण यहां पा सकते हैं।

इस ट्यूटोरियल के लिए, हमारे पास Tailwind CSS (फॉर्म और टाइपोग्राफी भी) इंस्टॉल है, लेकिन यह पूरी तरह से वैकल्पिक है और केवल फ्रंटएंड फॉर्म स्टाइलिंग के लिए है।

अब हम Upstash के QStash और Redis लाइब्रेरी को निम्न के माध्यम से इंस्टॉल करना चाहेंगे:

npm install @upstash/qstash
npm install @upstash/redis

अब आप एक .env.local फाइल बनाना चाहेंगे और इसे निम्न कुंजियों (और संबंधित स्थानों से मान) के साथ भरेंगे।

SITE_URL=https://your-project-url.vercel.app
OPENAI_API_KEY=
QSTASH_TOKEN=
UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=

आप Upstash कंसोल में अपने QStash और Redis टोकन पा सकते हैं, OpenAI API कुंजी यहां, और आपकी साइट URL आपके Vercel डैशबोर्ड में एक बार जब आपके पास एक प्रोजेक्ट बन जाता है और एक बेसिक Next.js प्रोजेक्ट डेप्लॉय हो जाता है।

फ्रंटएंड सेटअप

अब हम स्टोरी प्रॉम्प्ट इनपुट करने के लिए पेज और फॉर्म बनाएंगे। आपको प्रॉम्प्ट के लिए एक टेक्स्ट फील्ड और एक सबमिट बटन की आवश्यकता होगी।

स्टोरी निर्माण

फाइल: pages/index.tsx

import { RefObject, useRef, useState } from "react";
import Head from "next/head";
import useInterval from "../hooks/useInterval";
export default function Home() {
  const [generating, setGenerating] = useState<boolean>(false);
  const [messageId, setMessageId] = useState<string | null>(null);
  const [story, setStory] = useState<string[]>([]);
  const themeRef: RefObject<HTMLInputElement> = useRef(null);
  const characterRef: RefObject<HTMLInputElement> = useRef(null);
  const moralRef: RefObject<HTMLInputElement> = useRef(null);
  useInterval(
    async () => {
      await fetch(`/api/poll?id=${messageId}`)
        .then((res: any) => res.json())
        .then((data: any) => {
          if (!data.choices) {
            return;
          }
          setGenerating(false);
          setMessageId(null);
          setStory(data.choices[0].text.split("\n\n"));
        })
        .catch((err: any) => console.error(err));
    },
    messageId ? 1000 : null,
  );
  async function generateStory(event: any) {
    event.preventDefault();
    setGenerating(true);
    await fetch("/api/create", {
      method: "POST",
      body: JSON.stringify({
        theme: themeRef.current?.value,
        character: characterRef.current?.value,
        moral: moralRef.current?.value,
      }),
      headers: {
        "Content-Type": "application/json",
      },
    })
      .then((res: any) => res.json())
      .then((data: any) => setMessageId(data.id))
      .catch((err: any) => console.error(err));
  }
  return (
    <>
      <Head>
        <title>StoryTime</title>
        <meta name="description" content="A simple Next.js application which allows you to create stories using AI." />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main>
        <div className="my-16 flex flex-col items-center justify-center md:my-32">
          <h1 className="text-5xl font-black">StoryTime</h1>
          {story.length > 0 && (
            <div className="mx-auto mt-10 max-w-3xl">
              <div className="prose lg:prose-xl w-full">
                {story.map((paragraph: string, index: number) => (
                  <p key={index}>{paragraph}</p>
                ))}
              </div>
              <div className="text-center">
                <button type="button" onClick={() => setStory([])} className="mt-6 inline-flex items-center rounded-full border border-transparent bg-gray-900 px-6 py-2.5 text-sm font-medium text-white shadow-sm hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-600 focus:ring-offset-2">
                  Start Over
                </button>
              </div>
            </div>
          )}
          {story.length == 0 && (
            <form onSubmit={generateStory} className="mt-10 flex w-full max-w-lg flex-col items-center">
              <div className="w-full space-y-4">
                <div>
                  <label htmlFor="theme" className="text-sm font-semibold">
                    My story is about
                  </label>
                  <input name="theme" id="theme" type="text" className="mt-0.5 block w-full rounded-md border-gray-300 shadow-sm focus:border-gray-500 focus:ring-gray-500" placeholder="two friends going on an adventure" ref={themeRef} required />
                </div>
                <div>
                  <label htmlFor="character" className="text-sm font-semibold">
                    My main character is
                  </label>
                  <input name="character" id="character" type="text" className="mt-0.5 block w-full rounded-md border-gray-300 shadow-sm focus:border-gray-500 focus:ring-gray-500" placeholder="a dog named Spot" ref={characterRef} required />
                </div>
                <div>
                  <label htmlFor="moral" className="text-sm font-semibold">
                    The moral of my story is
                  </label>
                  <input name="moral" id="moral" type="text" className="mt-0.5 block w-full rounded-md border-gray-300 shadow-sm focus:border-gray-500 focus:ring-gray-500" placeholder="to always be kind" ref={moralRef} required />
                </div>
              </div>
              <button type="submit" disabled={generating} className="mt-6 inline-flex items-center rounded-full border border-transparent bg-gray-900 px-6 py-2.5 text-sm font-medium text-white shadow-sm hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-600 focus:ring-offset-2 disabled:opacity-50">
                {generating ? "Generating..." : "Generate"}
              </button>
            </form>
          )}
        </div>
      </main>
    </>
  );
}

यह फाइल एक React कंपोनेंट को परिभाषित करती है जो एक फॉर्म प्रदर्शित करता है जो यूजर्स को स्टोरी के लिए थीम, कैरेक्टर, और नैतिक इनपुट करने की अनुमति देता है। जब फॉर्म सबमिट किया जाता है, तो यह इनपुट किए गए थीम, कैरेक्टर, और नैतिक मूल्यों को बॉडी के रूप में /api/create एंडपॉइंट पर एक POST अनुरोध भेजता है।

कंपोनेंट तब एक पोलिंग स्थिति में प्रवेश करता है जहां यह /api/poll एंडपॉइंट पर हर सेकंड एक GET अनुरोध भेजता है, साथ ही पिछले स्टोरी निर्माण अनुरोध में प्राप्त संदेश पहचानकर्ता के साथ, जो हमें स्टोरी निर्माण अनुरोध को उस स्टोरी से ट्रैक करने की अनुमति देता है जिसे हम पोल कर रहे हैं यह जांचने के लिए कि यह OpenAI द्वारा कब समाप्त हो गया है।

जब /api/poll एंडपॉइंट से प्रतिक्रिया में choices प्रॉपर्टी होती है, तो हम जानते हैं कि पोलिंग अनुरोध ने एक सफलतापूर्वक जनरेटेड स्टोरी लौटाई है, इसलिए कंपोनेंट पोलिंग बंद कर देता है और इसे पैराग्राफ में विभाजित करके और प्रत्येक पैराग्राफ को अलग से रेंडर करके स्टोरी टेक्स्ट प्रदर्शित करता है।

इंटरवल हुक

फाइल: hooks/useInterval.ts

import { useEffect, useRef } from "react";
function useInterval(callback: () => void, delay: number | null) {
  const savedCallback = useRef(callback);
  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);
  useEffect(() => {
    if (!delay && delay !== 0) {
      return;
    }
    const id = setInterval(() => savedCallback.current(), delay);
    return () => clearInterval(id);
  }, [delay]);
}
export default useInterval;

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

API सेटअप

सबसे पहले हम कॉलबैक, पोल और क्रिएट फाइल बनाएंगे, साथ ही Redis और QStash लाइब्रेरी उपयोग।

स्टोरी निर्माण

फाइल: pages/api/create.ts

import type { NextApiRequest, NextApiResponse } from "next";
import qstashClient from "../../lib/qstash";
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse,
) {
  if (req.method !== "POST") {
    return res.status(400).json({ message: `Invalid request method: ${req.method}.`, });
  }
  const { theme, character, moral }: any = req.body;
  qstashClient
    .publishJSON({
      url: "https://api.openai.com/v1/completions",
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.QSTASH_TOKEN}`,
        "Content-Type": "application/json",
        "Upstash-Callback": `${process.env.SITE_URL}/api/callback`,
        "Upstash-Forward-Authorization": `Bearer ${process.env.OPENAI_API_KEY}`,
      },
      body: {
        model: "text-davinci-003",
        prompt: `Write a children's story about ${theme}, which has a main character who is ${character} with the moral of the story being ${moral}.`,
        max_tokens: 500,
        temperature: 0.75,
      },
    })
    .then((data: any) => {
      return res.status(202).json({ id: data.messageId });
    })
    .catch((error: any) => {
      return res.status(500).json({ message: error.message });
    });
}

हम पहले जांचते हैं कि अनुरोध मेथड POST है, और यदि नहीं तो 400 स्टेटस कोड (क्लाइंट त्रुटि दर्शाता है) के साथ प्रतिक्रिया भेजते हैं। फिर हम अनुरोध के बॉडी से थीम, कैरेक्टर, और नैतिक फ़ील्ड को डिस्ट्रक्चर करते हैं।

अब हम qstashClient ऑब्जेक्ट पर publishJSON मेथड को कॉल करते हैं, जो OpenAI API पर एक JSON बॉडी के साथ एक POST अनुरोध भेजता है जिसमें थीम, कैरेक्टर, और नैतिक के मूल्यों के आधार पर बच्चों की कहानी जनरेट करने के लिए एक प्रॉम्प्ट होता है। यह कई हेडर भी सेट करता है, जिसमें QSTASH_TOKEN एनवायरनमेंट वेरिएबल में संग्रहीत टोकन के साथ एक ऑथराइज़ेशन हेडर, और OPENAI_API_KEY को पास करने के लिए एक फॉरवर्डेड ऑथराइज़ेशन हेडर शामिल है जिसका उपयोग OpenAI API अनुरोध के साथ किया जाएगा।

यदि publishJSON कॉल सफल होती है तो हम अनुरोध की संदेश ID वापस करते हैं, जिसका उपयोग पोलिंग के लिए यह जांचने के लिए किया जाएगा कि अनुरोध कब समाप्त हो गया है। यदि कोई त्रुटि होती है, तो यह 500 स्टेटस कोड (आंतरिक सर्वर त्रुटि दर्शाता है) और प्रासंगिक त्रुटि संदेश के साथ प्रतिक्रिया भेजता है।

कॉलबैक

फाइल: pages/api/callback.ts

import type { NextApiRequest, NextApiResponse } from "next";
import redis from "../../lib/redis";
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse,
) {
  const { body }: any = req;
  try {
    const decoded = Buffer.from(body.body, "base64").toString("utf-8");
    await redis.set(body.sourceMessageId, decoded);
    return res.status(200).send(decoded);
  } catch (error) {
    return res.status(500).json({ error });
  }
}

सबसे पहले, हम इनकमिंग अनुरोध के बॉडी को डिकोड करने का प्रयास करते हैं, जो एक बेस64-एन्कोडेड स्ट्रिंग होगी, और यदि सफल होता है, तो यह डिकोडेड स्ट्रिंग को Redis में उसी कुंजी के तहत संग्रहीत करता है जो तब वापस की गई थी जब हमने QStash को प्रारंभिक अनुरोध भेजा था।

अंत में, हम 200 स्टेटस कोड (सफलता दर्शाता है) के साथ-साथ डिकोडेड स्ट्रिंग के साथ प्रतिक्रिया वापस भेजते हैं। यदि कोई त्रुटि होती है, तो हम 500 स्टेटस कोड (आंतरिक सर्वर त्रुटि दर्शाता है) और त्रुटि संदेश के साथ प्रतिक्रिया वापस करेंगे।

पोलिंग

फाइल: pages/api/poll.ts

import type { NextApiRequest, NextApiResponse } from "next";
import redis from "../../lib/redis";
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse,
) {
  const { id }: any = req.query;
  try {
    const data = await redis.get(id);
    if (!data) {
      return res
        .status(404)
        .json({ message: "Data for supplied ID not found" });
    }
    return res.status(200).json(data);
  } catch (error: any) {
    return res.status(500).json({ message: error.message });
  }
}

सबसे पहले, हम अनुरोध के क्वेरी ऑब्जेक्ट से id को डिस्ट्रक्चर करते हैं। हम फिर डिस्ट्रक्चर किए गए id के तहत Redis में संग्रहीत डेटा को पुनर्प्राप्त करने का प्रयास करते हैं और यदि कोई डेटा नहीं मिलता है, तो यह 404 स्टेटस कोड (दर्शाता है कि अनुरोधित संसाधन नहीं मिल सका) और एक संदेश बताते हुए प्रतिक्रिया भेजता है।

यदि दिए गए कुंजी से संबंधित डेटा मिलता है, तो यह 200 स्टेटस कोड (सफलता दर्शाता है) के साथ प्रतिक्रिया भेजता है, और मिले डेटा के साथ। यदि कोई त्रुटि होती है, तो हम 500 स्टेटस कोड (आंतरिक सर्वर त्रुटि दर्शाता है) और प्रासंगिक त्रुटि संदेश के साथ प्रतिक्रिया वापस करते हैं।

लाइब्रेरी

अब, हम QStash और Redis क्लाइंट बनाने के लिए दो फाइल बनाएंगे, जो स्टोरी जनरेशन प्रक्रिया के भीतर उपयोग किए जाते हैं। दोनों फाइलें एक ऑब्जेक्ट निर्यात करती हैं जिसका उपयोग संबंधित बाहरी सेवा के साथ इंटरैक्ट करने के लिए किया जाता है।

फाइल: lib/qstash.ts

import { Client } from "@upstash/qstash";
const qstashClient = new Client({ token: process.env.QSTASH_TOKEN as string });
export default qstashClient;

QStash क्लाइंट को QSTASH_TOKEN एनवायरनमेंट वेरिएबल में संग्रहीत टोकन के साथ इनिशियलाइज़ किया जाता है। इस ऑब्जेक्ट का उपयोग Upstash QStash सेवा पर HTTP अनुरोध भेजने के लिए किया जा सकता है।

फाइल: lib/redis.ts

import { Redis } from "@upstash/redis";
const redis = new Redis({
  url: process.env.UPSTASH_REDIS_REST_URL as string,
  token: process.env.UPSTASH_REDIS_REST_TOKEN as string,
});
export default redis;

Redis क्लाइंट को UPSTASH_REDIS_REST_URL और UPSTASH_REDIS_REST_TOKEN एनवायरनमेंट वेरिएबल में संग्रहीत URL और टोकन के साथ इनिशियलाइज़ किया जाता है। इस ऑब्जेक्ट का उपयोग Upstash Redis REST API के माध्यम से Redis डेटाबेस में डेटा संग्रहीत और पुनर्प्राप्त करने के लिए किया जा सकता है।

निष्कर्ष

OpenAI के कम्पलीशन API, साथ ही Upstash के QStash और Redis के साथ, प्राकृतिक भाषा प्रसंस्करण का उपयोग करके कस्टम कहानियां जनरेट करना आसान है। इस ट्यूटोरियल का पालन करके, अब आप इन उपकरणों का उपयोग करके कहानियां जनरेट करने के लिए अपना खुद का सिस्टम सेटअप करने में सक्षम होना चाहिए, और इस पर अपने खुद के परिवर्तन और सुधार कर सकते हैं।

आप यहां पूरी तरह से सोर्स कोड देख सकते हैं।

आगे के सुधार

नीचे कुछ विचार हैं कि आप आगे क्या कर सकते हैं, इस स्टोरी जेनरेटर को शुरुआत के रूप में उपयोग करके:

  • फ्रंटएंड स्टाइलिंग को और अधिक दृश्यमान आकर्षक और रंगीन बनाने के लिए अपडेट करें
  • दिए गए प्रॉम्प्ट के आधार पर कहानियों में Dall-E इमेज जनरेशन को OpenAI का उपयोग करके जोड़ें
  • API के माध्यम से बुक प्रिंटिंग सेवा से आउटपुट को हुक करें, ताकि यूजर्स भौतिक किताबें ऑर्डर कर सकें

इतनी सारी संभावनाएं और दिशाएं हैं जिन्हें आप इसे ले जा सकते हैं, इसलिए मज़े करें और प्रक्रिया का आनंद लें। आप अब तक के काम को अन्य प्रोजेक्ट के लिए आधार के रूप में भी उपयोग कर सकते हैं जो OpenAI, QStash और Redis का उपयोग कर सकते हैं।

  1. 7 शक्तिशाली Ruby Gems जिनके बारे में आपने शायद नहीं सुना होगा 7 शक्तिशाली Ruby Gems जिनके बारे में आपने शायद नहीं सुना होगा

    आपके Rails प्रोजेक्ट्स में उपयोग करने के लिए सबसे अच्छे Ruby gems क्या हैं? यही आप इस आर्टिकल में खोजेंगे! मैं आपको 7 gems देने जा रहा हूँ, लेकिन वही पुराने gems नहीं जिन्हें आपने लाखों बार देखा है, मैं आपके साथ कुछ ऐसे gems साझा करने जा रहा हूँ जो बहुत उपयोगी हैं, लेकिन कम ज्ञात हैं। लेकिन इससे पहल

  1. CSS से फुल-स्क्रीन वीडियो बैकग्राउंड कैसे बनाएं? CSS से फुल-स्क्रीन वीडियो बैकग्राउंड कैसे बनाएं?

    फुल-स्क्रीन वीडियो बैकग्राउंड के लिए position: fixed, min-width: 100%, min-height: 100% उपयोग करें। कंटेंट ओवरले के लिए rgba पारदर्शिता, और प्ले/पॉज़ बटन जावास्क्रिप्ट से नियंत्रित होता है। उदाहरण <!DOCTYPE html> <html> <head> <meta name=viewport content=width=device-width,

  1. HTML DOM inputEncoding गुण HTML DOM inputEncoding गुण

    HTML DOM inputEncoding गुण पार्सेबल HTML दस्तावेज़ के लिए कैरेक्टर एन्कोडिंग लौटाता है और इसे ओवरराइट किया जा सकता है। सिंटैक्स निम्नलिखित सिंटैक्स है − HTML दस्तावेज़ के कैरेक्टर एन्कोडिंग को लौटाना। document.inputEncoding उदाहरण आइए InputEncoding गुण का एक उदाहरण देखें − <!DOCTYPE html> <