এই ব্লগ পোস্টের জন্য, আমরা আগে যেতে pokrač করার আগে কিছু অনুমান করব, কিন্তু আপনার আদর্শভাবে থাকা উচিত:
- একটি আপস্ট্যাশ অ্যাকাউন্ট যেখানে আপনার একটি রেডিস এবং QStash ইন্সট্যান্স তৈরি আছে
- API কী অ্যাক্সেসসহ একটি OpenAI অ্যাকাউন্ট
- একটি Next.js প্রজেক্ট যেখানে আমরা স্টোরি জেনারেটর ফাংশনালিটি তৈরি করব
- আপনার প্রজেক্ট ডিপ্লয় করার জন্য একটি Vercel অ্যাকাউন্ট
ভূমিকা
আপনি কি কখনো AI ব্যবহার করে আপনার নিজের গল্প তৈরি করতে চেয়েছেন? OpenAI-এর কমপ্লিশন API এবং আপস্ট্যাশের QStash এবং রেডিসের সাথে, এখন কখনও তত সহজ ছিল না আপনার নিজের কাস্টম গল্প তৈরি করা প্রাকৃতিক ভাষা প্রক্রিয়াকরণ ব্যবহার করে। এই টিউটোরিয়ালে, আমরা এই টুলস সেট আপ এবং ব্যবহার করার প্রসেসের মধ্য দিয়ে যাব অনন্য এবং আকর্ষণীয় গল্প জেনারেট করতে।

অ্যাপের আরও ছবি দেখুন:
- Create Story Form
- Generating Story State
- Generated Story Displayed
আর্কিটেকচার
কোড দেখে আপনি সম্ভবত অ্যাপটি কীভাবে সেটআপ করা আছে সেটির একটি ভালো ধারণা পাবেন, কিন্তু একটু হাইয়ার লেভেল ওভারভিউ হিসেবে, নিচে একটি ইমেজ দেওয়া আছে যা অ্যাপ্লিকেশন ফ্লোর কিছু অংশ এবং সেগুলো কীভাবে কমিউনিকেট করে দেখাচ্ছে।

প্রজেক্ট সেটআপ
প্রথমে আমরা একটি Next.js প্রজেক্ট তৈরি করব। এটি টাইপস্ক্রিপ্টসহ একটি নতুন Next.js প্রজেক্ট তৈরি করার জন্য নিচের কমান্ড চালাতে পারেন। Next.js সেটআপ করার ধাপগুলো এখানে পাবেন।
এই টিউটোরিয়ালের জন্য, আমাদের Tailwind CSS (forms এবং typography ও) ইনস্টল করা আছে, কিন্তু এটি সম্পূর্ণ অপশনাল এবং শুধু ফ্রন্টএন্ড ফর্ম স্টাইলিং-এর জন্য।
এরপর আমরা আপস্ট্যাশের QStash এবং রেডিস লাইব্রেরি ইনস্টল করব নিচের কমান্ডগুলো দিয়ে:
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=
আপনি আপনার QStash এবং রেডিস টোকেন আপস্ট্যাশ কনসোলে পাবেন, OpenAI API কী এখানে, এবং আপনার সাইট URL আপনার Vercel ড্যাশবোর্ডে একবার আপনার একটি প্রজেক্ট তৈরি হয়ে যাওয়া এবং একটি বেসিক Next.js প্রজেক্ট ডিপ্লয় হয়ে যাওয়ার পর।
ফ্রন্টএন্ড সেটআপ
এরপর আমরা স্টোরি প্রম্পট ইনপুট করার জন্য পেজ এবং ফর্ম তৈরি করব। আপনার প্রম্পটের জন্য একটি টেক্সট ফিল্ড এবং একটি সাবমিট বাটন দরকার।
স্টোরি সৃষ্টি
File: 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 (
<>
StoryTime
StoryTime
{story.length > 0 && (
{story.map((paragraph: string, index: number) => (
{paragraph}
))}
)}
{story.length == 0 && (
)}
>
);
}
এই ফাইলটি একটি React কম্পোনেন্ট সংজ্ঞায়িত করে যা একটি ফর্ম প্রদর্শন করে যা ব্যবহারকারীদের একটি থিম, ক্যারেক্টার, এবং একটি গল্পের জন্য নৈতিক শিক্ষা ইনপুট করতে দেয়। ফর্ম সাবমিট হলে, এটি ইনপুট করা থিম, ক্যারেক্টার এবং নৈতিক শিক্ষার মানগুলো বডি হিসেবে /api/create এন্ডপয়েন্টে একটি POST রিকোয়েস্ট পাঠায়।
কম্পোনেন্টটি তারপর একটি পোলিং স্টেটে প্রবেশ করে যেখানে এটি প্রতি সেকেন্ডে /api/poll এন্ডপয়েন্টে একটি GET রিকোয়েস্ট পাঠায় পূর্বের স্টোরি ক্রিয়েশন রিকোয়েস্টে পাওয়া মেসেজ আইডেন্টিফায়ারের সাথে, যা আমাদের স্টোরি ক্রিয়েশন রিকোয়েস্টকে আমরা যে স্টোরি পোল করছি সেটির সাথে ট্র্যাক করতে দেয় যাতে চেক করা যায় কখন এটি OpenAI দ্বারা জেনারেট হওয়া ফিনিশ হয়েছে।
যখন /api/poll এন্ডপয়েন্ট থেকে রেসপন্সে একটি choices প্রপার্টি থাকে, আমরা জানি পোলিং রিকোয়েস্টটি একটি সফলভাবে জেনারেট করা স্টোরি ফেরত দিয়েছে, তাই কম্পোনেন্ট পোলিং বন্ধ করে এবং স্টোরি টেক্সটটি প্যারাগ্রাফে ভাগ করে প্রতিটি প্যারাগ্রাফ আলাদা করে রেন্ডার করে।
ইন্টারভাল হুক
File: 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 সেটআপ
প্রথমে আমরা কলব্যাক, পোল এবং ক্রিয়েট ফাইলগুলো তৈরি করব, সেইসাথে রেডিস এবং QStash লাইব্রেরি ব্যবহার।
স্টোরি সৃষ্টি
File: 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 কিনা, এবং যদি না হয়, ৪০০ স্ট্যাটাস কোড (ক্লায়েন্ট এরর ইন্ডিকেট করে) সহ একটি রেসপন্স পাঠাই। তারপর আমরা রিকোয়েস্টের বডি থেকে theme, character, এবং moral ফিল্ডগুলো ডিস্ট্রাকচার করি।
এরপর আমরা qstashClient অবজেক্টে publishJSON মেথড কল করি, যা OpenAI API-এ একটি POST রিকোয়েস্ট পাঠায় একটি JSON বডি সহ যেটে একটি প্রম্পট থাকে চাইল্ড্রেনস স্টোরি জেনারেট করতে theme, character, এবং moral-এর মানগুলো ভিত্তি করে। এটি কয়েকটি হেডার সেট করে, যাতে একটি অথরাইজেশন হেডার থাকে QSTASH_TOKEN এনভায়রনমেন্ট ভেরিয়েবলে সংরক্ষিত একটি টোকেনসহ, এবং একটি ফরওয়ার্ডেড অথরাইজেশন হেডার OPENAI_API_KEY পাস থ্রু করার জন্য যা OpenAI API রিকোয়েস্টের সাথে ব্যবহার হবে।
তারপর আমরা রিকোয়েস্টের মেসেজ আইডি ফেরত দিই যদি publishJSON কল সফল হয়, যা পোলিং-এ ব্যবহার হবে চেক করার জন্য কখন রিকোয়েস্টটি ফিনিশ হয়েছে। যদি কোনো এরর হয়, এটি ৫০০ স্ট্যাটাস কোড (ইন্টারন্যাল সার্ভার এরর ইন্ডিকেট করে) এবং সংশ্লিষ্ট এরর মেসেজসহ একটি রেসপন্স পাঠায়।
কলব্যাক
File: 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 });
}
}
প্রথমে, আমরা ইনকামিং রিকোয়েস্টের বডি ডিকোড করার চেষ্টা করি, যা একটি base64-এনকোডেড স্ট্রিং হবে, এবং যদি সফল হয়, এটি ডিকোড করা স্ট্রিংটি রেডিসে সংরক্ষণ করে একই কী আন্ডারে যা রিটার্ন করা হয়েছিল আমরা QStash-এ প্রাথমিক রিকোয়েস্ট পাঠালে।
শেষে, আমরা ২০০ স্ট্যাটাস কোড (সাকসেস ইন্ডিকেট করে) এবং ডিকোড করা স্ট্রিংসহ একটি রেসপন্স ফেরত দেই। যদি কোনো এরর হয়, আমরা ৫০০ স্ট্যাটাস কোড (ইন্টারন্যাল সার্ভার এরর ইন্ডিকেট করে) এবং এরর মেসেজসহ একটি রেসপন্স ফেরত দেব।
পোলিং
File: 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 আন্ডারে রেডিসে সংরক্ষিত ডেটা রিট্রিভ করার চেষ্টা করি এবং যদি কোনো ডেটা না পাওয়া যায়, এটি ৪০৪ স্ট্যাটাস কোড (রিকোয়েস্ট করা রিসোর্স পাওয়া যায়নি ইন্ডিকেট করে) এবং একটি মেসেজ পাঠায়।
যদি প্রদত্ত কীতে принадлежащий ডেটা পাওয়া যায়, এটি ২০০ স্ট্যাটাস কোড (সাকসেস ইন্ডিকেট করে) এবং পাওয়া ডেটাসহ একটি রেসপন্স পাঠায়। যদি কোনো এরর হয়, আমরা ৫০০ স্ট্যাটাস কোড (ইন্টারন্যাল সার্ভার এরর ইন্ডিকেট করে) এবং সংশ্লিষ্ট এরর মেসেজসহ একটি রেসপন্স ফেরত দেব।
লিবস
এরপর, আমরা QStash এবং রেডিস ক্লায়েন্ট তৈরি করার জন্য দুটি ফাইল তৈরি করব, যা স্টোরি জেনারেশন প্রসেসে ব্যবহৃত হয়। উভয় ফাইলই একটি অবজেক্ট এক্সপোর্ট করে যা সংশ্লিষ্ট এক্সটার্নাল সার্ভিসের সাথে ইন্টারঅ্যাক্ট করতে ব্যবহৃত হয়।
File: 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 রিকোয়েস্ট পাঠাতে ব্যবহার করা যেতে পারে।
File: 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;
রেডিস ক্লায়েন্টটি UPSTASH_REDIS_REST_URL এবং UPSTASH_REDIS_REST_TOKEN এনভায়রনমেন্ট ভেরিয়েবলে সংরক্ষিত URL এবং টোকেন দিয়ে ইনিশিয়ালাইজ করা হয়। এই অবজেক্টটি আপস্ট্যাশ রেডিস REST API-এর মাধ্যমে একটি রেডিস ডেটাবেসে ডেটা সংরক্ষণ এবং রিট্রিভ করতে ব্যবহার করা যেতে পারে।
উপসংহার
OpenAI-এর কমপ্লিশন API, সেইসাথে আপস্ট্যাশের QStash এবং রেডিসের সাথে, প্রাকৃতিক ভাষা প্রক্রিয়াকরণ ব্যবহার করে কাস্টম স্টোরি জেনারেট করা সহজ। এই টিউটোরিয়াল ফলো করে, আপনি এখন আপনার নিজের সিস্টেম সেট আপ করতে পারবেন এই টুলস ব্যবহার করে স্টোরি জেনারেট করার জন্য, এবং আপনার নিজের পরিবর্তন এবং উন্নতির ওপর এটি বিল্ড করতে পারবেন।
আপনি সম্পূর্ণ সোর্স কোড এখানে দেখতে পারেন।
আরও উন্নতি
নিচে কিছু আইডিয়া দেওয়া আছে আপনি কী করবেন পরের ধাপে, এই স্টোরি জেনারেটরকে একটি শুরুর পয়েন্ট হিসেবে ব্যবহার করে:
- ফ্রন্টএন্ড স্টাইলিং আপডেট করুন আরও ভিজ্যুয়ালি আকর্ষণীয় এবং রঙিন করার জন্য
- OpenAI-এ Dall-E ইমেজ জেনারেশন যোগ করুন স্টোরিগুলোর সাথে একটি প্রদত্ত প্রম্পটের ভিত্তিতে
- আউটপুটকে একটি বুক প্রিন্টিং সার্ভিসের সাথে হুক করুন API-এর মাধ্যমে, যাতে ব্যবহারকারীরা ফিজিক্যাল বুক অর্ডার করতে পারে
অনেক সম্ভাবনা এবং ডিরেকশন আছে আপনি এটি নিতে পারেন, তাই মজা করুন এবং প্রসেসটি উপভোগ করুন। আপনি এখন পর্যন্ত কिये কাজটিও অন্য প্রজেক্টের বেস হিসেবে ব্যবহার করতে পারেন যা OpenAI, QStash এবং রেডিস ব্যবহার করতে পারে।