Skip to content

Added RAG-based AI chatbot to the site (Work In Progress) #467

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"@mdx-js/react": "^3.0.0",
"clsx": "^2.0.0",
"lint": "^0.8.19",
"lucide-react": "^0.451.0",
"prism-react-renderer": "^2.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
222 changes: 222 additions & 0 deletions src/components/ChatBot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
import React, {useState, useCallback, useRef, useEffect} from "react";
import {MessageCircle, X} from "lucide-react";

function textFormat(text) {
let block = 0;
let code = ``;
let lang = "";
let heading = 2;
let output = ``;

for (let i = 0; i < text.length; i++) {
if (text.substring(i, i + 3) === "```") {
i += 2;
if (block === 0) {
block = 1;
lang = "";
while (i < text.length && text.charAt(i) !== "\n") {
lang += text.charAt(i++);
}
} else {
output += code;
code = ``;
lang = "";
block = 0;
}
} else if (block === 0) {
if (text.substring(i, i + 3) === "###") {
i += 2;
heading = 3;
output += `\n\n`;
} else if (text.substring(i, i + 2) === "##") {
i += 1;
heading = 2;
output += `\n\n`;
} else if (text.substring(i, i + 1) == "#") {
heading = 1;
output += `\n\n`;
} else if (text.substring(i, i + 1) == "]") {
output += ": ";
} else if (text.substring(i, i + 1) == "[") {
output += "";
} else if (
text.substring(i, i + 1) == "(" ||
text.substring(i, i + 1) == ")"
) {
output += `"`;
} else if (text.substring(i, i + 1) == "<") {
i++;
while (
i < text.length &&
text.substring(i, i + 2) != "/>" &&
text.substring(i, i + 1) != ">"
) {
i++;
}
} else if (text.substring(i, i + 2) === "**") {
i += 1;
} else if (text.substring(i, i + 2) === "* ") {
i += 1;
output += `\n• `;
} else if (text.charAt(i) === "\n") {
output += `\n`;
} else {
output += text.charAt(i);
}
} else {
code += text.charAt(i);
}
}

return output;
}

export default function ChatBot() {
const [messages, setMessages] = useState([]);
const [input, setInput] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [isOpen, setIsOpen] = useState(false);
const messagesEndRef = useRef(null);

const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({behavior: "smooth"});
};

useEffect(() => {
scrollToBottom();
}, [messages]);

const sendMessage = useCallback(async () => {
if (!input.trim() || isLoading) return;

const userMessage = {text: input, sender: "user"};
setMessages((prevMessages) => [...prevMessages, userMessage]);
setIsLoading(true);

try {
const apiRes = await fetch(
"https://keploy-api.abhishekkushwaha.me/chat",
{
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({question: input}),
}
);

if (!apiRes.ok) {
throw new Error(`HTTP error! status: ${apiRes.status}`);
}

const {answer} = await apiRes.json();

setMessages((prevMessages) => [
...prevMessages,
{text: answer, sender: "bot"},
]);
} catch (error) {
console.error("Error fetching response from Keploy API", error);
setMessages((prevMessages) => [
...prevMessages,
{
text: "Sorry, there was an error processing your request.",
sender: "bot",
},
]);
} finally {
setIsLoading(false);
setInput("");
}
}, [input, isLoading]);

const handleKeyPress = useCallback(
(e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
},
[sendMessage]
);

const toggleChat = useCallback(() => {
setIsOpen((prev) => !prev);
}, []);

return (
<div className="fixed bottom-14 right-4 z-50 sm:right-8 md:right-16">
{!isOpen && (
<button
onClick={toggleChat}
className="rounded-full bg-[#FF914D] p-4 text-white shadow-lg transition-all duration-300 ease-in-out hover:scale-110 hover:bg-[#FF914D] hover:shadow-2xl"
>
<MessageCircle size={24} />
</button>
)}
{isOpen && (
<div className="flex h-[500px] w-80 flex-col rounded-lg bg-white shadow-xl sm:w-96">
<div className="flex items-center justify-between border-b p-4">
<h3 className="text-lg font-semibold text-black">Chat with us</h3>
<button
onClick={toggleChat}
className="text-gray-500 hover:text-gray-700"
>
<X size={24} />
</button>
</div>
<div className="flex h-full flex-grow flex-col bg-gray-100">
<div className="flex-1 overflow-y-auto p-4">
{messages.map((msg, index) => (
<div
key={index}
className={`mb-2 ${
msg.sender === "user" ? "text-right" : "text-left"
}`}
>
<span
className={`inline-block rounded-lg px-4 py-2 ${
msg.sender === "user"
? "bg-[#FF914D] text-white"
: "bg-gray-300 text-black"
}`}
>
{textFormat(msg.text)}
</span>
</div>
))}
{isLoading && (
<div className="text-center text-gray-500">
Bot is typing...
</div>
)}
<div ref={messagesEndRef} />
</div>

<div className="flex border-t bg-white p-4">
<div className="flex items-center space-x-4">
<input
type="text"
className="flex-1 rounded-lg border bg-white px-4 py-2 text-black focus:border-[#FF914D] focus:outline-none"
placeholder="Type your message"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={handleKeyPress}
disabled={isLoading}
/>
<button
className="rounded-lg bg-[#FF914D] px-4 py-2 text-white disabled:opacity-80"
onClick={sendMessage}
disabled={isLoading || !input.trim()}
>
{isLoading ? "Sent" : "Send"}
</button>
</div>
</div>
</div>
</div>
)}
</div>
);
}
3 changes: 3 additions & 0 deletions src/pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import {Community, KeployCloud, Resources, QuickStart} from "../components";
import {GSoC} from "../components/GSoC";
import {Intro} from "../components";
import ChatBot from "../components/ChatBot";

export default function Home() {
const context = useDocusaurusContext();
const {siteConfig = {}} = context;
Expand All @@ -15,6 +17,7 @@ export default function Home() {
description={`${siteConfig.tagline}`}
>
<main className="mx-auto max-w-screen-lg p-6 md:p-10">
<ChatBot />
<QuickStart />
{/* <Hacktoberfest /> */}
{/*<GitTogether />*/}
Expand Down
15 changes: 8 additions & 7 deletions static/scripts/chat.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// hubspot.js
const script = document.createElement("script");
script.type = "text/javascript";
script.id = "hs-script-loader";
script.async = true;
script.defer = true;
script.src = "//js-na1.hs-scripts.com/43912677.js";
document.head.appendChild(script);

// const script = document.createElement("script");
// script.type = "text/javascript";
// script.id = "hs-script-loader";
// script.async = true;
// script.defer = true;
// script.src = "//js-na1.hs-scripts.com/43912677.js";
// document.head.appendChild(script);
Loading