File size: 4,390 Bytes
48dd198 7f0439e 48dd198 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
import React, {useState} from 'react';
import './App.css';
import {fetchEventSource} from "@microsoft/fetch-event-source";
interface Message {
message: string;
isUser: boolean;
sources?: string[];
}
function App() {
const [inputValue, setInputValue] = useState("")
const [messages, setMessages] = useState<Message[]>([]);
const setPartialMessage = (chunk: string, sources: string[] = []) => {
setMessages(prevMessages => {
let lastMessage = prevMessages[prevMessages.length - 1];
if (prevMessages.length === 0 || !lastMessage.isUser) {
return [...prevMessages.slice(0, -1), {
message: lastMessage.message + chunk,
isUser: false,
sources: lastMessage.sources ? [...lastMessage.sources, ...sources] : sources
}];
}
return [...prevMessages, {message: chunk, isUser: false, sources}];
})
}
function handleReceiveMessage(data: string) {
let parsedData = JSON.parse(data);
if (parsedData.answer) {
setPartialMessage(parsedData.answer.content)
}
if (parsedData.docs) {
setPartialMessage("", parsedData.docs.map((doc: any) => doc.metadata.source))
}
}
const handleSendMessage = async (message: string) => {
setInputValue("")
setMessages(prevMessages => [...prevMessages, {message, isUser: true}]);
await fetchEventSource(`${process.env.REACT_APP_BACKEND_URL}/rag/stream`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
input: {
question: message,
}
}),
onmessage(event) {
if (event.event === "data") {
handleReceiveMessage(event.data);
}
},
})
}
const handleKeyPress = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (event.key === "Enter" && !event.shiftKey) {
handleSendMessage(inputValue.trim())
}
}
function formatSource(source: string) {
return source.split("/").pop() || "";
}
return (
<div className="min-h-screen bg-gray-900 flex flex-col">
<header className="bg-gray-800 text-white text-center p-4">
Epic v. Apple Legal Assistant
</header>
<main className="flex-grow container mx-auto p-4 flex-col">
<div className="flex-grow bg-gray-700 shadow overflow-hidden sm:rounded-lg">
<div className="border-b border-gray-600 p-4">
{messages.map((msg, index) => (
<div key={index}
className={`p-3 my-3 rounded-lg text-white ml-auto ${msg.isUser ? "bg-gray-800" : "bg-gray-900"}`}>
{msg.message}
{/* Source */}
{!msg.isUser && (
<div className={"text-xs"}>
<hr className="border-b mt-5 mb-5"></hr>
{msg.sources?.map((source, index) => (
<div>
<a
target="_blank"
download
href={`${"http://localhost:8000"}/rag/static/${encodeURI(formatSource(source))}`}
rel="noreferrer"
>{formatSource(source)}</a>
</div>
))}
</div>
)}
</div>
))}
</div>
<div className="p-4 bg-gray-800">
<textarea
className="form-textarea w-full p-2 border rounded text-white bg-gray-900 border-gray-600 resize-none h-auto"
placeholder="Enter your message here..."
onKeyUp={handleKeyPress}
onChange={(e) => setInputValue(e.target.value)}
value={inputValue}
></textarea>
<button
className="mt-2 bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"
onClick={() => handleSendMessage(inputValue.trim())}
>
Send
</button>
</div>
</div>
</main>
<footer className="bg-gray-800 text-white text-center p-4 text-xs">
*AI Agents can make mistakes. Consider checking important information.
<br/>
All training data derived from public records
<br/>
<br/>
© 2024 Focused Labs
</footer>
</div>
);
}
export default App;
|