Skip to content

Commit

Permalink
trying to get initial input. but need to figure out async data fetchi…
Browse files Browse the repository at this point in the history
…ng in component rendering.
  • Loading branch information
dokmy committed Jan 8, 2024
1 parent 4495643 commit e8b28c5
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 60 deletions.
21 changes: 20 additions & 1 deletion src/app/(root)/(routes)/results/[searchId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,30 @@ const resultsPage = async ({ params: { searchId } }: Props) => {
},
});

const chatInitiated = async (searchResult: SearchResult) => {
const dbMessages = await prismadb.message.findMany({
where: {
searchResultId: searchResult.id,
},
});

if (dbMessages.length === 0) {
return false;
} else {
return true;
}
};

return (
<div className="flex overflow-x-auto h-screen">
{searchResults.map((result) => (
<div key={result.id} className="flex-none w-1/3 p-3 border-r">
<ChatComponent key={result.id} data={result} />
<ChatComponent
key={result.id}
data={result}
query={search_metadata[0].query}
chatInitiated={() => chatInitiated(result)}
/>
</div>
))}
</div>
Expand Down
95 changes: 64 additions & 31 deletions src/app/(root)/(routes)/results/components/chat-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import ResultCard from "@/components/result-card";
import ChatMessages from "@/components/chat-messages";
import axios from "axios";
import { useEffect, useState } from "react";
import { useChat } from "ai/react";

interface ChatComponentProps {
data: {
Expand All @@ -17,46 +18,78 @@ interface ChatComponentProps {
searchId: string;
userId: string;
};
query: string;
}

const ChatComponent: React.FC<ChatComponentProps> = ({ data }) => {
const [messages, setMessages] = useState(null);
const [error, setError] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const ChatComponent: React.FC<ChatComponentProps> = ({ data, query }) => {
// const [dbMessages, setDbMessages] = useState([]);
// const [chatArgs, setChatArgs] = useState({});

useEffect(() => {
const fetchMessages = async () => {
try {
const response = await axios.post("/api/get-messages", {
searchResultId: data.id,
});
setMessages(response.data);
if (response.data.length === 0) {
console.log("Messages is empty!");
}
} catch (err: any) {
setError(err);
} finally {
setIsLoading(false);
}
};
// useEffect(() => {
// const fetchDbMessages = async () => {
// try {
// const response = await axios.post(`/api/get-messages`, {
// searchResultId: data.id,
// });
// setDbMessages(response.data);
// } catch (error) {
// console.error("Error fetching messages:", error);
// }
// };

if (data.id) {
fetchMessages();
}
}, [data.id]);
// fetchDbMessages();
// }, [data.id]);

// call api to get messages
// if messages is empty, then define const of initialInput
// then, call up useChat and define messages to be the messages retrieved or empty array. Also add initialInput if message is empty
// in the JSX, pass the messages to chat-messages
// the form to handle handlesubmit, the input to handle onchange and value
// in the api/chat, onStart of streaming, insert the input to db, oncompletion, insert the response to db
// useEffect(() => {
// if (dbMessages.length === 0) {
// console.log("no messages. Adding initial input.");
// setChatArgs({
// initialInput:
// "Please first summarise this case for me and then explain why this case is relevant to my situation as follow: " +
// query,
// });
// console.log(chatArgs);
// } else {
// console.log("Have messages. Adding initial messages.");
// const simplifiedMessages = dbMessages.map(({ role, content }) => ({
// role,
// content,
// }));
// setChatArgs({ initialMessages: simplifiedMessages });
// console.log(chatArgs);
// }
// }, [dbMessages, query]);

// console.log(chatArgs);
const { messages, input, handleInputChange, handleSubmit } = useChat({
body: { filter: data.caseNeutralCit },
initialInput:
"Please first summarise this case for me and then explain why this case is relevant to my siutation as follow: " +
query,
});

return (
<div>
<ResultCard data={data} />
<ChatMessages key={data.id} searchResultId={data.id} />
<ChatMessages key={data.id} messages={messages} />
<div>
<form
onSubmit={handleSubmit}
className="mt-1 mb-1 relative p-3 border-t"
>
<div className="flex-row space-x-2">
<input
className="resize-none overflow-auto max-h-24 border rounded w-full py-2 pl-3 pr-20 text-gray-200 leading-tight bg-black border-gray-700 duration-200 h-24"
value={input}
onChange={handleInputChange}
></input>

<span className="absolute inset-y-0 right-5 flex items-center pr-3 pointer-events-none text-gray-400">
<div className="h-3 w-3"></div>
</span>
</div>
</form>
</div>
</div>
);
};
Expand Down
14 changes: 3 additions & 11 deletions src/app/api/chat/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,11 @@ export async function POST(req: Request) {
try {

const { messages, filter} = await req.json()
// console.log("POSTMAN is summoned. Here are my messages and filter: \n")
// console.log(messages + "\n")
// console.log(filter + "\n")


// Get the last message
const lastMessage = messages[messages.length - 1]

// Get the context from the last message
const context = await getContext(lastMessage.content, '', filter)


let prompt = [
let prompt = [
{
role: 'system',
content: `Here are the context information:
Expand All @@ -46,14 +38,14 @@ export async function POST(req: Request) {
stream: true,
messages: [...prompt, ...messages.filter((message: Message) => message.role === 'user')]
})
// Convert the response into a friendly text-stream


console.log(response.statusText)


const stream = OpenAIStream(response)

// Respond with the stream

return new StreamingTextResponse(stream)
} catch (e) {
throw (e)
Expand Down
2 changes: 1 addition & 1 deletion src/app/api/get-messages/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import prismadb from "../../../lib/prismadb";
import { NextResponse } from "next/server";

export const POST = async (req: Request) => {
console.log("API IS SUMMONED")
console.log("GET MESSAGE API IS SUMMONED")
const { searchResultId } = await req.json();
const _messages = await prismadb.message.findMany({
where: {
Expand Down
66 changes: 58 additions & 8 deletions src/app/components/chat-messages.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,61 @@
import React from "react";
import { Message } from "ai";
import { useState } from "react";
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
import DoneIcon from "@mui/icons-material/Done";

interface ChatMessagesProps {
searchResultId: string;
}
export default function Messages({ messages }: { messages: Message[] }) {
const [copiedIndices, setCopiedIndices] = useState<{
[key: number]: boolean;
}>({});

const ChatMessages: React.FC<ChatMessagesProps> = ({ searchResultId }) => {
return <div>ChatMessages</div>;
};
const handleIconClick = async (content: string, index: number) => {
try {
await navigator.clipboard.writeText(content);
setCopiedIndices((prev) => ({ ...prev, [index]: true }));
setTimeout(() => {
setCopiedIndices((prev) => ({ ...prev, [index]: false }));
}, 1000);
} catch (err) {
console.error("Failed to copy: ", err);
}
};

export default ChatMessages;
return (
<div className="flex flex-col bg-gray-700 leading-7">
{messages.map((msg, index) => (
<div
key={index}
className={`group ${
msg.role === "assistant" ? "bg-[#18181A]" : "bg-[#18181A]"
} px-3 py-3 shadow-md hover:shadow-lg transition-shadow duration-200 flex slide-in-bottom border-b message-glow`}
>
<div
className={`pr-3 border-r border-gray-500 flex items-center ${
msg.role === "assistant" ? "bg-[#18181A]" : "bg-[#18181A]"
}`}
>
{msg.role === "assistant" ? "🤖" : "🧑‍💻"}
</div>
<div className="ml-4 flex flex-col items-center text-gray-200 pr-7">
{msg.content.split("\n").map((line, i) => (
<span key={i}>
{line}
<br></br>
</span>
))}
</div>
<div
onClick={() => handleIconClick(msg.content, index)}
className="cursor-pointer"
>
{copiedIndices[index] ? (
<DoneIcon className="text-white text-lg" />
) : (
<ContentCopyIcon className="text-white text-lg" />
)}
</div>
</div>
))}
</div>
);
}
10 changes: 5 additions & 5 deletions src/app/utils/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { filter } from "cheerio/lib/api/traversing";
// chunk: string,
// }

interface raw_case_num_filter {
raw_case_num: string
interface neutral_cit_filter {
neutral_cit: string
}

// The function `getContext` is used to retrieve the context of a given message
Expand All @@ -20,11 +20,11 @@ export const getContext = async (message: string, namespace: string, filter:stri
const embedding = await getEmbeddings(message);

console.log("context.tx is called. Here is my filter: " + filter + "\n")
const raw_case_num_filter = {"raw_case_num": filter}
console.log(raw_case_num_filter)
const neutral_cit_filter = {"neutral_cit": filter}
console.log(neutral_cit_filter)

// Retrieve the matches for the embeddings from the specified namespace
const matches = await getMatchesFromEmbeddings(embedding, 5, namespace, raw_case_num_filter);
const matches = await getMatchesFromEmbeddings(embedding, 5, namespace, neutral_cit_filter);

// Filter out the matches that have a score lower than the minimum score
const qualifyingDocs = matches.filter(m => m.score && m.score > minScore);
Expand Down
6 changes: 3 additions & 3 deletions src/app/utils/pinecone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import { Pinecone, type ScoredPineconeRecord } from "@pinecone-database/pinecone
// hash: string
// }

interface raw_case_num_filter {
raw_case_num: string
interface neutral_cit_filter {
neutral_cit: string
}

interface case_prefix_filter {
case_prefix: { "$in": string[]}
}

type metadata_filter = raw_case_num_filter | case_prefix_filter
type metadata_filter = neutral_cit_filter | case_prefix_filter

// The function `getMatchesFromEmbeddings` is used to retrieve matches for the given embeddings
const getMatchesFromEmbeddings = async (embeddings: number[], topK: number, namespace: string, filter?:metadata_filter): Promise<any[]> => {
Expand Down

0 comments on commit e8b28c5

Please sign in to comment.