কম্পিউটার টিউটোরিয়াল

OpenAI, Upstash, এবং Next.js এর সাথে AI-চালিত স্টোরি জেনারেটর তৈরি করুন

এই ব্লগ পোস্টের জন্য, আমরা চালিয়ে যাওয়ার আগে কয়েকটি অনুমান করব, তবে আপনার আদর্শভাবে থাকা উচিত:

  • একটি Upstash অ্যাকাউন্ট যেখানে আপনি একটি Redis এবং QStash উদাহরণ তৈরি করেছেন
  • আপনার API কী অ্যাক্সেস সহ একটি OpenAI অ্যাকাউন্ট
  • একটি Next.js প্রকল্প যেখানে আমরা স্টোরি জেনারেটর কার্যকারিতা তৈরি করব
  • আপনার প্রোজেক্ট স্থাপন করার জন্য একটি Vercel অ্যাকাউন্ট

পরিচয়

আপনি কি কখনো এআই ব্যবহার করে নিজের গল্প তৈরি করতে চেয়েছেন? 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 কী, এবং আপনার Vercel ড্যাশবোর্ডে আপনার সাইটের URL একবার আপনি একটি প্রজেক্ট তৈরি হয়ে গেলে এবং একটি বেসিক 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>
 </>
 );
}

এই ফাইলটি একটি প্রতিক্রিয়া উপাদান সংজ্ঞায়িত করে যা একটি ফর্ম প্রদর্শন করে যা ব্যবহারকারীদের একটি গল্পের জন্য একটি থিম, চরিত্র এবং নৈতিক ইনপুট করতে দেয়৷ ফর্ম জমা দেওয়া হলে, এটি একটি POST পাঠায় /api/create কে অনুরোধ করুন ইনপুট করা থিম, চরিত্র এবং বডি হিসাবে নৈতিক মূল্যবোধ সহ শেষ পয়েন্ট।

কম্পোনেন্টটি তারপর একটি পোলিং স্টেটে প্রবেশ করে যেখানে এটি একটি GET পাঠায় /api/poll কে অনুরোধ করুন পূর্ববর্তী গল্প তৈরির অনুরোধে প্রাপ্ত বার্তা শনাক্তকারীর সাথে প্রতি সেকেন্ডে এন্ডপয়েন্ট, যা আমাদের গল্প তৈরির অনুরোধ ট্র্যাক করার অনুমতি দেয় যে গল্পের জন্য আমরা পোলিং করছি তা কখন OpenAI দ্বারা তৈরি করা শেষ হয়েছে তা পরীক্ষা করতে।

যখন /api/poll থেকে প্রতিক্রিয়া এন্ডপয়েন্টে একটি পছন্দের সম্পত্তি রয়েছে, আমরা জানি যে পোলিং অনুরোধটি একটি সফলভাবে তৈরি করা গল্প ফিরিয়ে দিয়েছে, তাই উপাদানটি পোলিং বন্ধ করে দেয় এবং গল্পের পাঠ্যটিকে অনুচ্ছেদে বিভক্ত করে এবং প্রতিটি অনুচ্ছেদকে আলাদাভাবে রেন্ডার করে প্রদর্শন করে৷

ইন্টারভাল হুক

ফাইল: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 ব্যবধান এবং কলব্যাক ফাংশন পরিচালনা করার জন্য হুক যা প্রতিক্রিয়া উপাদান লাইফসাইকেলের সাথে নির্বিঘ্নে কাজ করে, সেইসাথে একটি প্রতিক্রিয়া উপাদানের মধ্যে ব্যবধান এবং কলব্যাক পরিচালনা করার একটি সুবিধাজনক উপায় প্রদান করে, কর্মক্ষমতা অপ্টিমাইজ করে এবং কোডবেসটিকে একটু বেশি রক্ষণাবেক্ষণযোগ্য করে তোলে। আপনি এখানে এবং এখানে এই হুক সম্পর্কে আরও তথ্য পেতে পারেন৷

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 এর স্ট্যাটাস কোড সহ একটি প্রতিক্রিয়া পাঠায় (একটি ক্লায়েন্ট ত্রুটি নির্দেশ করে) যদি এটি না হয়। তারপরে আমরা অনুরোধের মূল অংশ থেকে থিম, চরিত্র এবং নৈতিক ক্ষেত্রগুলিকে ধ্বংস করতে এগিয়ে যাই।

পরবর্তীতে আমরা publishJSON কল করি qstashClient এ পদ্ধতি বস্তু, যা একটি POST পাঠায় থিম, চরিত্র এবং নৈতিক মূল্যবোধের উপর ভিত্তি করে একটি শিশুদের গল্প তৈরি করার জন্য একটি JSON বডি সহ OpenAI API-এর কাছে অনুরোধ। এটি QSTASH_TOKEN-এ সংরক্ষিত একটি টোকেন সহ একটি অনুমোদন শিরোনাম সহ একাধিক শিরোনামও সেট করে। এনভায়রনমেন্ট ভেরিয়েবল, এবং OPENAI_API_KEY এর মধ্য দিয়ে যাওয়ার জন্য একটি ফরোয়ার্ড করা অনুমোদন হেডার যা OpenAI API অনুরোধের সাথে ব্যবহার করা হবে।

তারপরে আমরা অনুরোধের মেসেজ আইডি ফেরত দিই যদি publishJSON কল সফল হয়েছে, যা অনুরোধ শেষ হলে পরীক্ষা করার জন্য পোলিং এর জন্য ব্যবহার করা হবে। যদি একটি ত্রুটি ঘটে, এটি 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-এনকোডেড স্ট্রিং, এবং সফল হলে, এটি ডিকোড করা স্ট্রিংটিকে একই কী-এর অধীনে রেডিস-এ সঞ্চয় করে যা আমরা 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 এর অধীনে। এবং যদি কোন ডেটা পাওয়া না যায়, তাহলে এটি 404-এর একটি স্ট্যাটাস কোড সহ একটি প্রতিক্রিয়া পাঠায় (অনুরোধ করা সংস্থানটি খুঁজে পাওয়া যায়নি) এবং এটি বলে একটি বার্তা পাঠায়৷

যদি প্রদত্ত কী-এর অন্তর্গত ডেটা পাওয়া যায়, তবে এটি 200 এর স্ট্যাটাস কোড সহ একটি প্রতিক্রিয়া পাঠায় (সফলতা নির্দেশ করে), এবং এর সাথে পাওয়া ডেটা। কোনো ত্রুটি দেখা দিলে, আমরা 500 এর স্ট্যাটাস কোড (একটি অভ্যন্তরীণ সার্ভারের ত্রুটি নির্দেশ করে) এবং প্রাসঙ্গিক ত্রুটি বার্তা সহ একটি প্রতিক্রিয়া ফেরত দিই৷

Libs

পরবর্তীতে, আমরা 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 ক্লায়েন্ট একটি URL এবং UPSTASH_REDIS_REST_URL-এ সংরক্ষিত টোকেন দিয়ে শুরু করা হয় এবং UPSTASH_REDIS_REST_TOKEN পরিবেশ ভেরিয়েবল, যথাক্রমে। এই বস্তুটি Upstash Redis REST API-এর মাধ্যমে একটি Redis ডাটাবেসে ডেটা সংরক্ষণ এবং পুনরুদ্ধার করতে ব্যবহার করা যেতে পারে।

উপসংহার

OpenAI এর সমাপ্তি API, সেইসাথে Upstash এর QStash এবং Redis এর সাথে, প্রাকৃতিক ভাষা প্রক্রিয়াকরণ ব্যবহার করে কাস্টম গল্প তৈরি করা সহজ। এই টিউটোরিয়ালটি অনুসরণ করে, আপনি এখন এই সরঞ্জামগুলি ব্যবহার করে গল্প তৈরি করার জন্য আপনার নিজস্ব সিস্টেম সেট আপ করতে সক্ষম হবেন এবং এতে আপনার নিজের পরিবর্তন এবং উন্নতি করতে পারবেন৷

আপনি এখানে সোর্স কোডটি সম্পূর্ণরূপে দেখতে পারেন।

আরো উন্নতি

শুরুতে এই স্টোরি জেনারেটর ব্যবহার করে আপনি পরবর্তীতে কী করতে পারেন সে সম্পর্কে নীচে কয়েকটি ধারণা দেওয়া হল:

  • অনেক বেশি দৃষ্টিকটু এবং রঙিন হতে ফ্রন্টএন্ড স্টাইলিং আপডেট করুন
  • প্রদত্ত প্রম্পটের উপর ভিত্তি করে গল্পগুলিতে OpenAI ব্যবহার করে Dall-E ইমেজ জেনারেশন যোগ করুন
  • একটি API এর মাধ্যমে একটি বই মুদ্রণ পরিষেবাতে আউটপুটকে হুক করুন, যাতে ব্যবহারকারীরা প্রকৃত বই অর্ডার করতে পারে

আপনি এটি নিতে পারেন এমন অনেক সম্ভাবনা এবং দিকনির্দেশ রয়েছে, তাই মজা করুন এবং প্রক্রিয়াটি উপভোগ করুন। এমনকি আপনি অন্য প্রকল্পগুলির জন্য একটি ভিত্তি হিসাবে কাজটি ব্যবহার করতে পারেন যা OpenAI, QStash এবং Redis ব্যবহার করতে পারে৷


  1. একটি অনির্দেশিত গ্রাফে C++ এ প্রদত্ত আকারের একটি স্বাধীন সেট রয়েছে কিনা তা খুঁজুন

  2. কিভাবে HTML এ টেবিল বর্ডার তৈরি করবেন?

  3. এক্সিকিউশন প্ল্যান - এমএস এসকিউএল সার্ভারে এক্সিকিউশন প্ল্যান

  4. অ্যান্ড্রয়েড কনকারেন্টলিঙ্কডডেক-এ clear() কীভাবে ব্যবহার করবেন?