Step 1: Setting up the Development Environment
Before diving into the project, make sure you have Node.js installed on your machine. You can download it from nodejs.org.
Create a new directory for your project and initialize it with the following commands:
mkdir personal-assistant
cd personal-assistant
npm init -yTitle: Building Your Personal Assistant Website with OpenAI, NodeJS, and ReactJS
In the fast-paced digital era, having a personal assistant to streamline tasks and enhance productivity is a luxury. What if you could create your own personal assistant website tailored to your needs? In this blog, we will guide you through the process of building a personalized virtual assistant using OpenAI, NodeJS, and ReactJS.
Step 1: Setting up the Development Environment
Before diving into the project, make sure you have Node.js installed on your machine. You can download it from nodejs.org.
Create a new directory for your project and initialize it with the following commands:
mkdir personal-assistant
cd personal-assistant
npm init -yStep 2: Installing Dependencies
Next, install the necessary packages. We’ll be using Express for the server and React for the frontend. OpenAI’s API will be utilized for natural language processing.
npm install express react react-dom axiosStep 3: Creating the Express Server
Create a file named server.js and set up a basic Express server.
import express from "express";
import { openai } from "./Assistant.js";
import { createAssistantIfNeeded } from "./Assistant.js";
import cors from "cors";
// Setup Express
const app = express();
app.use(express.json()); // Middleware to parse JSON bodies
app.use(cors());
const assistant = await createAssistantIfNeeded();
// Assistant can be created via API or UI
const assistantId = assistant.id;
let pollingInterval;
// Set up a Thread
async function createThread() {
console.log("Creating a new thread...");
const thread = await openai.beta.threads.create();
console.log(thread);
return thread;
}
async function addMessage(threadId, message) {
console.log("Adding a new message to thread: " + threadId);
const response = await openai.beta.threads.messages.create(threadId, {
role: "user",
content: message,
});
return response;
}
async function runAssistant(threadId) {
console.log("Running assistant for thread: " + threadId);
const response = await openai.beta.threads.runs.create(threadId, {
assistant_id: assistantId,
});
console.log(response);
return response;
}
async function checkingStatus(res, threadId, runId) {
const runObject = await openai.beta.threads.runs.retrieve(threadId, runId);
const status = runObject.status;
console.log(runObject);
console.log("Current status: " + status);
if (status == "completed") {
clearInterval(pollingInterval);
const messagesList = await openai.beta.threads.messages.list(threadId);
let messages = [];
messagesList.body.data.forEach((message) => {
messages.push(message.content);
});
res.json({ messages });
}
}
//=========================================================
//============== ROUTE SERVER =============================
//=========================================================
// Open a new thread
app.get("/thread", (req, res) => {
createThread().then((thread) => {
res.json({ threadId: thread.id });
});
});
app.post("/message", (req, res) => {
const { message, threadId } = req.body;
addMessage(threadId, message).then((message) => {
// res.json({ messageId: message.id });
// Run the assistant
runAssistant(threadId).then((run) => {
const runId = run.id;
// Check the status
pollingInterval = setInterval(() => {
checkingStatus(res, threadId, runId);
}, 5000);
});
});
});
// Start the server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});Step 4: Setting Up React
Initialize the React app in a client folder.
npx create-react-app clientStep 5: Creating OpenAI Account and API Key
Visit the OpenAI website (openai.com) to create an account. Once registered, obtain your API key from the API section.
Step 6: Implementing OpenAI API in NodeJS
In your Assistant.js file, integrate the OpenAI API.
import OpenAI from "openai";
import dotenv from "dotenv";
import fs from "fs";
dotenv.config();
export const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
// Helper function to upload a file
async function uploadFile(filePath) {
try {
const file = await openai.files.create({
file: fs.createReadStream(filePath),
purpose: "assistants",
});
return file.id; // Return the uploaded file's ID
} catch (error) {
console.error("Error uploading file:", filePath, error);
throw error; // Rethrow to handle it in the caller
}
}
export async function createAssistantIfNeeded() {
try {
// Check if the assistant already exists
const existingAssistants = await openai.beta.assistants.list();
const existingAssistant = existingAssistants.data.find(
(assistant) => assistant.name === "LegalGuide"
);
if (existingAssistant) {
return existingAssistant; // Return the existing assistant if found
}
// Upload files separately
const fileIds = await Promise.all([
uploadFile("test1.txt"),
uploadFile("test2.txt"),
]);
// If not found, create a new assistant with both file IDs
const assistant = await openai.beta.assistants.create({
name: "LegalGuide",
instructions:
"LegalGuide AI is your intelligent legal companion, designed to assist you in navigating the complex world of laws and regulations effortlessly.",
model: "gpt-3.5-turbo",
tools: [{ type: "code_interpreter" }],
file_ids: fileIds, // Use the uploaded file IDs
});
console.log("New assistant created:", assistant);
return assistant;
} catch (error) {
console.error("Error creating assistant:", error);
}
}
createAssistantIfNeeded();Step 7: Implementing OpenAI API in ReactJS
In your Chat.js file, integrate the OpenAI API from the server.js file.
import React, { useState, useEffect, useRef } from "react";
import axios from "axios";
import "./Chat.css";
const Chat = () => {
const [input, setInput] = useState("");
const [messages, setMessages] = useState([]);
const [threadId, setThreadId] = useState(null); // Store thread ID
const [isLoading, setIsLoading] = useState(false); // Loading state
const messagesEndRef = useRef(null);
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
};
useEffect(scrollToBottom, [messages]);
// Function to create a new thread and store its ID
const createThread = async () => {
try {
const apiUrl = "http://localhost:3000/thread"; // Ensure this matches your API endpoint for creating a thread
const { data } = await axios.get(apiUrl);
setThreadId(data.threadId); // Assuming the API returns an object with a threadId property
} catch (error) {
console.error("Error creating thread:", error);
}
};
// Call createThread when the component mounts
useEffect(() => {
createThread();
}, []);
const sendMessage = async () => {
if (!input.trim() || !threadId) return; // Prevent sending empty messages or if thread ID is not set
setIsLoading(true); // Start loading
const userMessage = input;
setMessages((prevMessages) => [
...prevMessages,
{ role: "user", content: userMessage },
]);
setInput(""); // Clear input after sending
try {
const apiUrl = `http://localhost:3000/message`; // Ensure this matches your API endpoint for sending messages
const requestBody = {
message: input,
threadId, // Use the stored thread ID
};
const { data } = await axios.post(apiUrl, requestBody);
console.log(data);
// Extracting the bot's response from the nested structure
const botResponse = data.messages[0][0].text.value;
// Assuming 'data' contains the bot's response in a manner you expect
setMessages((prevMessages) => [
...prevMessages,
{ role: "bot", content: botResponse },
]);
} catch (error) {
console.error("Error sending message:", error);
// Optionally handle the error visually in the chat
setMessages((prevMessages) => [
...prevMessages,
{
role: "bot",
content: "Sorry, there was an error processing your message.",
},
]);
} finally {
setIsLoading(false); // End loading regardless of outcome
}
};
return (
<div className="chat-container">
<div className="messages-container">
{messages.map((message, index) => (
<p key={index} className={`message ${message.role}`}>
{message.content}
</p>
))}
{/* Loading indicator */}
{isLoading && <p className="loading">Sending...</p>}
<div ref={messagesEndRef} />
</div>
<div className="input-container">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Type a message..."
onKeyPress={(e) => e.key === "Enter" && sendMessage()} // Allows sending message with Enter key
disabled={isLoading} // Disable input while loading
/>
<button onClick={sendMessage} disabled={isLoading}>
Send
</button>
</div>
</div>
);
};
export default Chat;Step 8: Use Chat.js in App.js File
In your App.js file, integrate the Chat from the Chat.js file.
import "./App.css";
// App.js
import React from "react";
import Chat from "./chat.js";
function App() {
return (
<div className="App">
<h1>ChatGPT React App</h1>
<Chat />
</div>
);
}
export default App;Step 9: Run the Application
Start both the server and the React app by running the following command in separate terminal windows for both frontend and backend:
npm startVisit http://localhost:3000 in your browser to interact with your personalized virtual assistant.
Congratulations! You’ve successfully created a personal assistant website using OpenAI, NodeJS, and ReactJS. Feel free to customize and expand your virtual assistant’s capabilities based on your preferences and needs.
