Search working!!!

This commit is contained in:
2024-07-30 14:59:10 +08:00
parent 4627da2efb
commit a18b12c725
4 changed files with 112 additions and 29 deletions

View File

@@ -1,5 +1,6 @@
import { import {
Button, Button,
Card,
Input, Input,
Kbd, Kbd,
Modal, Modal,
@@ -9,15 +10,19 @@ import {
useDisclosure, useDisclosure,
} from "@nextui-org/react"; } from "@nextui-org/react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { MagnifyingGlassIcon } from "../icons"; import { ArrowTopRightOnSquare, MagnifyingGlassIcon } from "../icons";
import config from "../config"; import config from "../config";
import instance from "../security/http"; import instance from "../security/http";
import EcoconnectFullLogo from "./EcoconnectFullLogo"; import EcoconnectFullLogo from "./EcoconnectFullLogo";
import { useNavigate } from "react-router-dom";
export default function EcoconnectSearch() { export default function EcoconnectSearch() {
const [searchQuery, setSearchQuery] = useState(""); const [searchQuery, setSearchQuery] = useState("");
const [aiResponse, setAiResponse] = useState(""); const [aiResponseRoute, setAiResponseRoute] = useState("");
const [aiResponseRouteDescription, setAiResponseRouteDescription] =
useState("");
const [isQueryLoading, setIsQueryLoading] = useState(false); const [isQueryLoading, setIsQueryLoading] = useState(false);
const navigate = useNavigate();
const { const {
isOpen: isAiDialogOpen, isOpen: isAiDialogOpen,
@@ -28,7 +33,7 @@ export default function EcoconnectSearch() {
const dialogOpenChange = () => { const dialogOpenChange = () => {
onAiDialogOpenChange(); onAiDialogOpenChange();
setSearchQuery(""); setSearchQuery("");
setAiResponse(""); setAiResponseRoute("");
}; };
const handleKeyPress = (event: KeyboardEvent) => { const handleKeyPress = (event: KeyboardEvent) => {
@@ -42,18 +47,35 @@ export default function EcoconnectSearch() {
if (searchQuery.length <= 0) return; if (searchQuery.length <= 0) return;
setIsQueryLoading(true); setIsQueryLoading(true);
instance instance
.get( .get(`${config.serverAddress}/connections/nls/${searchQuery}`)
`${config.serverAddress}/connections/openai-chat-completion/${searchQuery}`
)
.then((response) => { .then((response) => {
console.log(response.data.response); const rawResponse = response.data.response;
setAiResponse(response.data.response); const parsedResponse = JSON.parse(rawResponse);
const resolvedRoute = parsedResponse.route;
setAiResponseRoute(resolvedRoute);
setAiResponseRouteDescription(getRouteDescription(resolvedRoute));
}) })
.finally(() => { .finally(() => {
setIsQueryLoading(false); setIsQueryLoading(false);
}); });
}; };
const routeDescriptions: { [key: string]: string } = {
"/": "Go home",
"/springboard": "Go to the Dashboard",
"/manage-account": "Management your account",
"/events": "Browse events",
"/karang-guni-schedules": "Browse available Karang Guni",
"/home-bill-contest": "Take part in a contest",
"/home-bill-contest/new-submission": "Submit your bill",
"/community-posts": "Browse community posts",
"/community-posts/create": "Create a post",
};
const getRouteDescription = (route: string) => {
return routeDescriptions[route] || "Unknown";
};
useEffect(() => { useEffect(() => {
window.addEventListener("keydown", handleKeyPress); window.addEventListener("keydown", handleKeyPress);
@@ -93,7 +115,7 @@ export default function EcoconnectSearch() {
placement="top" placement="top"
> >
<ModalContent> <ModalContent>
{() => { {(onClose) => {
return ( return (
<> <>
<ModalBody> <ModalBody>
@@ -121,10 +143,30 @@ export default function EcoconnectSearch() {
</Button> </Button>
} }
/> />
{aiResponse.length > 0 && ( {aiResponseRoute.length > 0 && (
<p className="bg-neutral-50 p-4 border-2 rounded-xl"> <Card className="p-4">
{aiResponse} <div className="flex flex-row justify-between *:my-auto">
</p> <div className="flex flex-col">
<p className="text-xl font-bold">
{aiResponseRouteDescription}
</p>
<p className="text-sm opacity-50">
https://ecoconnect.gov{aiResponseRoute}
</p>
</div>
<Button
isIconOnly
variant="light"
className="text-red-500"
onPress={() => {
onClose();
navigate(aiResponseRoute);
}}
>
<ArrowTopRightOnSquare />
</Button>
</div>
</Card>
)} )}
</div> </div>
</ModalBody> </ModalBody>

View File

@@ -520,3 +520,22 @@ export const LifebuoyIcon = () => {
</svg> </svg>
); );
}; };
export const ArrowTopRightOnSquare = () => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="size-6"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25"
/>
</svg>
);
};

View File

@@ -1,11 +1,11 @@
const OpenAI = require("openai"); const OpenAI = require("openai");
const { getApiKey } = require("./apiKey"); const { getApiKey } = require("./apiKey");
async function openAiChatCompletion(query) { async function openAiChatCompletion(query, systemPrompt) {
const openai = new OpenAI({ apiKey: await getApiKey("openai_api_key") }); const openai = new OpenAI({ apiKey: await getApiKey("openai_api_key") });
const completion = await openai.chat.completions.create({ const completion = await openai.chat.completions.create({
messages: [ messages: [
{ role: "system", content: "You are a helpful assistant." }, { role: "system", content: systemPrompt },
{ role: "user", content: query }, { role: "user", content: query },
], ],
model: "gpt-4o-mini", model: "gpt-4o-mini",

View File

@@ -3,20 +3,42 @@ const { openAiChatCompletion } = require("../connections/openai");
const { validateToken } = require("../middlewares/auth"); const { validateToken } = require("../middlewares/auth");
const router = express.Router(); const router = express.Router();
router.get( const nlsPrompt = `
"/openai-chat-completion/:query", You are an AI designed to help navigate a website by interpreting natural language inputs and providing the correct site route. Below are routes and a brief description of each:
validateToken,
async (req, res) => { "/" : home
let data = req.params.query; "/springboard" : user dashboard
console.log(data); "/manage-account" : user account management
try { "/events" : events
let chatResponse = await openAiChatCompletion(data); "/karang-guni-schedules" : browse slots
res.json({ response: chatResponse }); "/home-bill-contest" : participate in contest & earn vouchers
} catch (error) { "/home-bill-contest/new-submission" : submit bill
console.error("Error with AI:", error); "/community-posts" : show posts
res.status(500).json({ message: "Internal Server Error" }); "/community-posts/create" : create post
}
based on input, provide appropriate route in following format:
{
"route": "<appropriate route>",
}
If none matches user query, return empty route.
`;
async function naturalLanguageSearch(userQuery) {
return await openAiChatCompletion(userQuery, nlsPrompt);
}
router.get("/nls/:query", validateToken, async (req, res) => {
let data = req.params.query;
console.log(data);
try {
let chatResponse = await naturalLanguageSearch(data);
res.json({ response: chatResponse });
} catch (error) {
console.error("Error with AI:", error);
res.status(500).json({ message: "Internal Server Error" });
} }
); });
module.exports = router; module.exports = router;