Spaces:
Runtime error
Runtime error
Upload 5 files
Browse files- index.html +69 -0
- script.js +204 -0
- server.py +55 -0
- style.css +219 -0
- worker_huggingFace.py +83 -0
index.html
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html>
|
3 |
+
<head>
|
4 |
+
<link
|
5 |
+
rel="stylesheet"
|
6 |
+
href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
|
7 |
+
integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z"
|
8 |
+
crossorigin="anonymous"
|
9 |
+
/>
|
10 |
+
<link
|
11 |
+
rel="stylesheet"
|
12 |
+
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
|
13 |
+
/>
|
14 |
+
<link rel="stylesheet" href="../static/style.css"/>
|
15 |
+
<link
|
16 |
+
rel="stylesheet"
|
17 |
+
href="{{ url_for('static', filename='style.css') }}"
|
18 |
+
/>
|
19 |
+
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
|
20 |
+
<script src="../static/script.js"></script>
|
21 |
+
</head>
|
22 |
+
<body>
|
23 |
+
<div class="container d-flex flex-column vh-100">
|
24 |
+
<div class="row">
|
25 |
+
<div class="col-12" style="margin-top: 2rem">
|
26 |
+
<h1 class="text-center mb-3">Personal Data Assistant</h1>
|
27 |
+
</div>
|
28 |
+
<div class="col-12 text-center">
|
29 |
+
<div class="custom-control custom-switch mb-3">
|
30 |
+
<input type="checkbox" class="custom-control-input" id="light-dark-mode-switch" />
|
31 |
+
<label class="custom-control-label" for="light-dark-mode-switch">Toggle light/dark mode</label>
|
32 |
+
</div>
|
33 |
+
</div>
|
34 |
+
</div>
|
35 |
+
<div class="row flex-grow-1 overflow-auto">
|
36 |
+
<div class="col-12 col-md-8 mx-auto d-flex flex-column">
|
37 |
+
<div id="chat-window" class="p-3">
|
38 |
+
<div id="message-list"></div>
|
39 |
+
<div class="loading-animation my-loading">
|
40 |
+
<div class="loading-dots my-loading">
|
41 |
+
<div class="dot"></div>
|
42 |
+
<div class="dot"></div>
|
43 |
+
<div class="dot"></div>
|
44 |
+
</div>
|
45 |
+
</div>
|
46 |
+
<div class="loading-animation">
|
47 |
+
<div class="loading-dots">
|
48 |
+
<div class="dot"></div>
|
49 |
+
<div class="dot"></div>
|
50 |
+
<div class="dot"></div>
|
51 |
+
</div>
|
52 |
+
</div>
|
53 |
+
</div>
|
54 |
+
<div class="input-group mt-1">
|
55 |
+
<input type="text" id="message-input" class="form-control" placeholder="Type your message here..." />
|
56 |
+
<div class="input-group-append">
|
57 |
+
<button id="send-button" class="btn btn-primary send">
|
58 |
+
<i class='fa fa-paper-plane'></i>
|
59 |
+
</button>
|
60 |
+
<button id="reset-button" class="btn btn-primary reset">
|
61 |
+
<i class="fa fa-refresh"></i>
|
62 |
+
</button>
|
63 |
+
</div>
|
64 |
+
</div>
|
65 |
+
</div>
|
66 |
+
</div>
|
67 |
+
</div>
|
68 |
+
</body>
|
69 |
+
</html>
|
script.js
ADDED
@@ -0,0 +1,204 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
let lightMode = true;
|
2 |
+
let recorder = null;
|
3 |
+
let recording = false;
|
4 |
+
const responses = [];
|
5 |
+
const botRepeatButtonIDToIndexMap = {};
|
6 |
+
const userRepeatButtonIDToRecordingMap = {};
|
7 |
+
const baseUrl = window.location.origin
|
8 |
+
|
9 |
+
async function showBotLoadingAnimation() {
|
10 |
+
await sleep(200);
|
11 |
+
$(".loading-animation")[1].style.display = "inline-block";
|
12 |
+
document.getElementById('send-button').disabled = true;
|
13 |
+
}
|
14 |
+
|
15 |
+
function hideBotLoadingAnimation() {
|
16 |
+
$(".loading-animation")[1].style.display = "none";
|
17 |
+
if(!isFirstMessage){
|
18 |
+
document.getElementById('send-button').disabled = false;
|
19 |
+
}
|
20 |
+
}
|
21 |
+
|
22 |
+
async function showUserLoadingAnimation() {
|
23 |
+
await sleep(100);
|
24 |
+
$(".loading-animation")[0].style.display = "flex";
|
25 |
+
}
|
26 |
+
|
27 |
+
function hideUserLoadingAnimation() {
|
28 |
+
$(".loading-animation")[0].style.display = "none";
|
29 |
+
}
|
30 |
+
|
31 |
+
|
32 |
+
const processUserMessage = async (userMessage) => {
|
33 |
+
let response = await fetch(baseUrl + "/process-message", {
|
34 |
+
method: "POST",
|
35 |
+
headers: { Accept: "application/json", "Content-Type": "application/json" },
|
36 |
+
body: JSON.stringify({ userMessage: userMessage }),
|
37 |
+
});
|
38 |
+
response = await response.json();
|
39 |
+
console.log(response);
|
40 |
+
return response;
|
41 |
+
};
|
42 |
+
|
43 |
+
const cleanTextInput = (value) => {
|
44 |
+
return value
|
45 |
+
.trim() // remove starting and ending spaces
|
46 |
+
.replace(/[\n\t]/g, "") // remove newlines and tabs
|
47 |
+
.replace(/<[^>]*>/g, "") // remove HTML tags
|
48 |
+
.replace(/[<>&;]/g, ""); // sanitize inputs
|
49 |
+
};
|
50 |
+
|
51 |
+
const sleep = (time) => new Promise((resolve) => setTimeout(resolve, time));
|
52 |
+
|
53 |
+
const scrollToBottom = () => {
|
54 |
+
// Scroll the chat window to the bottom
|
55 |
+
$("#chat-window").animate({
|
56 |
+
scrollTop: $("#chat-window")[0].scrollHeight,
|
57 |
+
});
|
58 |
+
};
|
59 |
+
|
60 |
+
const populateUserMessage = (userMessage, userRecording) => {
|
61 |
+
// Clear the input field
|
62 |
+
$("#message-input").val("");
|
63 |
+
|
64 |
+
// Append the user's message to the message list
|
65 |
+
$("#message-list").append(
|
66 |
+
`<div class='message-line my-text'><div class='message-box my-text${
|
67 |
+
!lightMode ? " dark" : ""
|
68 |
+
}'><div class='me'>${userMessage}</div></div></div>`
|
69 |
+
);
|
70 |
+
|
71 |
+
scrollToBottom();
|
72 |
+
};
|
73 |
+
|
74 |
+
let isFirstMessage = true;
|
75 |
+
|
76 |
+
const populateBotResponse = async (userMessage) => {
|
77 |
+
await showBotLoadingAnimation();
|
78 |
+
|
79 |
+
let response;
|
80 |
+
let uploadButtonHtml = '';
|
81 |
+
|
82 |
+
if (isFirstMessage) {
|
83 |
+
response = { botResponse: "Hello there! I'm your friendly data assistant, ready to answer any questions regarding your data. Could you please upload a PDF file for me to analyze?"};
|
84 |
+
uploadButtonHtml = `
|
85 |
+
<input type="file" id="file-upload" accept=".pdf" hidden>
|
86 |
+
<button id="upload-button" class="btn btn-primary btn-sm">Upload File</button>
|
87 |
+
`;
|
88 |
+
|
89 |
+
} else {
|
90 |
+
response = await processUserMessage(userMessage);
|
91 |
+
}
|
92 |
+
|
93 |
+
renderBotResponse(response, uploadButtonHtml)
|
94 |
+
|
95 |
+
// Event listener for file upload
|
96 |
+
if (isFirstMessage) {
|
97 |
+
$("#upload-button").on("click", function () {
|
98 |
+
$("#file-upload").click();
|
99 |
+
});
|
100 |
+
|
101 |
+
$("#file-upload").on("change", async function () {
|
102 |
+
const file = this.files[0];
|
103 |
+
|
104 |
+
await showBotLoadingAnimation();
|
105 |
+
|
106 |
+
// Create a new FormData instance
|
107 |
+
const formData = new FormData();
|
108 |
+
|
109 |
+
// Append the file to the FormData instance
|
110 |
+
formData.append('file', file);
|
111 |
+
|
112 |
+
// Now send this data to /process-document endpoint
|
113 |
+
let response = await fetch(baseUrl + "/process-document", {
|
114 |
+
method: "POST",
|
115 |
+
headers: { Accept: "application/json" }, // "Content-Type" should not be explicitly set here, the browser will automatically set it to "multipart/form-data"
|
116 |
+
body: formData, // send the FormData instance as the body
|
117 |
+
});
|
118 |
+
|
119 |
+
if (response.status !== 400) {
|
120 |
+
document.querySelector('#upload-button').disabled = true;
|
121 |
+
}
|
122 |
+
|
123 |
+
response = await response.json();
|
124 |
+
console.log('/process-document', response)
|
125 |
+
renderBotResponse(response, '')
|
126 |
+
});
|
127 |
+
|
128 |
+
|
129 |
+
isFirstMessage = false; // after the first message, set this to false
|
130 |
+
}
|
131 |
+
};
|
132 |
+
|
133 |
+
const renderBotResponse = (response, uploadButtonHtml) => {
|
134 |
+
responses.push(response);
|
135 |
+
|
136 |
+
hideBotLoadingAnimation();
|
137 |
+
|
138 |
+
$("#message-list").append(
|
139 |
+
`<div class='message-line'><div class='message-box${!lightMode ? " dark" : ""}'>${response.botResponse.trim()}<br>${uploadButtonHtml}</div></div>`
|
140 |
+
);
|
141 |
+
|
142 |
+
scrollToBottom();
|
143 |
+
}
|
144 |
+
|
145 |
+
populateBotResponse()
|
146 |
+
|
147 |
+
|
148 |
+
$(document).ready(function () {
|
149 |
+
|
150 |
+
//start the chat with send button disabled
|
151 |
+
document.getElementById('send-button').disabled = true;
|
152 |
+
|
153 |
+
// Listen for the "Enter" key being pressed in the input field
|
154 |
+
$("#message-input").keyup(function (event) {
|
155 |
+
let inputVal = cleanTextInput($("#message-input").val());
|
156 |
+
|
157 |
+
if (event.keyCode === 13 && inputVal != "") {
|
158 |
+
const message = inputVal;
|
159 |
+
|
160 |
+
populateUserMessage(message, null);
|
161 |
+
populateBotResponse(message);
|
162 |
+
}
|
163 |
+
|
164 |
+
inputVal = $("#message-input").val();
|
165 |
+
});
|
166 |
+
|
167 |
+
// When the user clicks the "Send" button
|
168 |
+
$("#send-button").click(async function () {
|
169 |
+
// Get the message the user typed in
|
170 |
+
const message = cleanTextInput($("#message-input").val());
|
171 |
+
|
172 |
+
populateUserMessage(message, null);
|
173 |
+
populateBotResponse(message);
|
174 |
+
|
175 |
+
});
|
176 |
+
|
177 |
+
//reset chat
|
178 |
+
// When the user clicks the "Reset" button
|
179 |
+
$("#reset-button").click(async function () {
|
180 |
+
// Clear the message list
|
181 |
+
$("#message-list").empty();
|
182 |
+
|
183 |
+
// Reset the responses array
|
184 |
+
responses.length = 0;
|
185 |
+
|
186 |
+
// Reset isFirstMessage flag
|
187 |
+
isFirstMessage = true;
|
188 |
+
|
189 |
+
document.querySelector('#upload-button').disabled = false;
|
190 |
+
|
191 |
+
// Start over
|
192 |
+
populateBotResponse();
|
193 |
+
});
|
194 |
+
|
195 |
+
|
196 |
+
// handle the event of switching light-dark mode
|
197 |
+
$("#light-dark-mode-switch").change(function () {
|
198 |
+
$("body").toggleClass("dark-mode");
|
199 |
+
$(".message-box").toggleClass("dark");
|
200 |
+
$(".loading-dots").toggleClass("dark");
|
201 |
+
$(".dot").toggleClass("dark-dot");
|
202 |
+
lightMode = !lightMode;
|
203 |
+
});
|
204 |
+
});
|
server.py
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import logging
|
2 |
+
import os
|
3 |
+
from flask import Flask, render_template, request, jsonify
|
4 |
+
from flask_cors import CORS
|
5 |
+
import worker # Import the worker module
|
6 |
+
|
7 |
+
# Initialize Flask app and CORS
|
8 |
+
app = Flask(__name__)
|
9 |
+
cors = CORS(app, resources={r"/*": {"origins": "*"}})
|
10 |
+
app.logger.setLevel(logging.ERROR)
|
11 |
+
|
12 |
+
# Define the route for the index page
|
13 |
+
@app.route('/', methods=['GET'])
|
14 |
+
def index():
|
15 |
+
return render_template('index.html') # Render the index.html template
|
16 |
+
|
17 |
+
# Define the route for processing messages
|
18 |
+
@app.route('/process-message', methods=['POST'])
|
19 |
+
def process_message_route():
|
20 |
+
user_message = request.json['userMessage'] # Extract the user's message from the request
|
21 |
+
print('user_message', user_message)
|
22 |
+
|
23 |
+
bot_response = worker.process_prompt(user_message) # Process the user's message using the worker module
|
24 |
+
|
25 |
+
# Return the bot's response as JSON
|
26 |
+
return jsonify({
|
27 |
+
"botResponse": bot_response
|
28 |
+
}), 200
|
29 |
+
|
30 |
+
# Define the route for processing documents
|
31 |
+
@app.route('/process-document', methods=['POST'])
|
32 |
+
def process_document_route():
|
33 |
+
# Check if a file was uploaded
|
34 |
+
if 'file' not in request.files:
|
35 |
+
return jsonify({
|
36 |
+
"botResponse": "It seems like the file was not uploaded correctly, can you try "
|
37 |
+
"again. If the problem persists, try using a different file"
|
38 |
+
}), 400
|
39 |
+
|
40 |
+
file = request.files['file'] # Extract the uploaded file from the request
|
41 |
+
|
42 |
+
file_path = file.filename # Define the path where the file will be saved
|
43 |
+
file.save(file_path) # Save the file
|
44 |
+
|
45 |
+
worker.process_document(file_path) # Process the document using the worker module
|
46 |
+
|
47 |
+
# Return a success message as JSON
|
48 |
+
return jsonify({
|
49 |
+
"botResponse": "Thank you for providing your PDF document. I have analyzed it, so now you can ask me any "
|
50 |
+
"questions regarding it!"
|
51 |
+
}), 200
|
52 |
+
|
53 |
+
# Run the Flask app
|
54 |
+
if __name__ == "__main__":
|
55 |
+
app.run(debug=True, port=8000, host='0.0.0.0')
|
style.css
ADDED
@@ -0,0 +1,219 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* Light mode */
|
2 |
+
body {
|
3 |
+
background-color: #d6ddec;
|
4 |
+
color: #121717;
|
5 |
+
}
|
6 |
+
|
7 |
+
/* Dark mode */
|
8 |
+
.dark-mode {
|
9 |
+
background-color: #121717;
|
10 |
+
color: #c9c9c9;
|
11 |
+
}
|
12 |
+
|
13 |
+
#chat-window {
|
14 |
+
position: relative;
|
15 |
+
height: 70vh;
|
16 |
+
overflow: auto;
|
17 |
+
}
|
18 |
+
|
19 |
+
.message-line {
|
20 |
+
padding-bottom: 8px;
|
21 |
+
width: 70%;
|
22 |
+
word-break: break-word;
|
23 |
+
display: flex;
|
24 |
+
}
|
25 |
+
|
26 |
+
.message-box {
|
27 |
+
padding: 12px;
|
28 |
+
border-radius: 15px;
|
29 |
+
display: inline-block;
|
30 |
+
position: relative;
|
31 |
+
background-color: #efefef;
|
32 |
+
min-width: 26px;
|
33 |
+
border-top-left-radius: 0;
|
34 |
+
word-wrap: break-word;
|
35 |
+
flex-grow: 0;
|
36 |
+
}
|
37 |
+
|
38 |
+
.message-line.my-text {
|
39 |
+
flex-direction: row-reverse;
|
40 |
+
width: 100%;
|
41 |
+
}
|
42 |
+
|
43 |
+
.message-box.my-text {
|
44 |
+
transform: rotateY(180deg);
|
45 |
+
background-color: #e7f9d8;
|
46 |
+
margin-left:150px;
|
47 |
+
}
|
48 |
+
|
49 |
+
.me {
|
50 |
+
transform: scale(-1, 1);
|
51 |
+
}
|
52 |
+
|
53 |
+
.message-box.my-text.dark {
|
54 |
+
background-color: #0f4c9e;
|
55 |
+
}
|
56 |
+
|
57 |
+
.message-box.dark {
|
58 |
+
background-color: #263443;
|
59 |
+
}
|
60 |
+
|
61 |
+
/* Input container */
|
62 |
+
#message-input {
|
63 |
+
bottom: 0;
|
64 |
+
width: 100%;
|
65 |
+
flex-grow: 1;
|
66 |
+
font-size: 16px;
|
67 |
+
box-sizing: border-box;
|
68 |
+
border: none;
|
69 |
+
padding: 10px 0 10px 12px;
|
70 |
+
border-radius: 40px 0 0 40px;
|
71 |
+
background-color: transparent;
|
72 |
+
height: auto;
|
73 |
+
}
|
74 |
+
|
75 |
+
#send-button {
|
76 |
+
border: none;
|
77 |
+
font: inherit;
|
78 |
+
background-color: transparent;
|
79 |
+
margin: 0;
|
80 |
+
appearance: none;
|
81 |
+
padding: 10px 12px;
|
82 |
+
cursor: pointer;
|
83 |
+
font-size: 24px;
|
84 |
+
display: flex;
|
85 |
+
}
|
86 |
+
|
87 |
+
#reset-button {
|
88 |
+
border: none;
|
89 |
+
font: inherit;
|
90 |
+
background-color: transparent;
|
91 |
+
margin: 0;
|
92 |
+
appearance: none;
|
93 |
+
padding: 10px 12px;
|
94 |
+
cursor: pointer;
|
95 |
+
font-size: 24px;
|
96 |
+
display: flex;
|
97 |
+
}
|
98 |
+
|
99 |
+
#repeat-button {
|
100 |
+
border: none;
|
101 |
+
background-color: transparent;
|
102 |
+
margin: 0 0 2px 0;
|
103 |
+
padding: 0 10px;
|
104 |
+
font-size: 24px;
|
105 |
+
}
|
106 |
+
|
107 |
+
.input-group {
|
108 |
+
position: relative;
|
109 |
+
display: flex;
|
110 |
+
flex-wrap: nowrap;
|
111 |
+
align-items: stretch;
|
112 |
+
width: 100%;
|
113 |
+
border-radius: 40px;
|
114 |
+
border: 1px solid #2d2d2d;
|
115 |
+
}
|
116 |
+
|
117 |
+
#upload-button {
|
118 |
+
color: white;
|
119 |
+
background-color: #007bff;
|
120 |
+
border: none;
|
121 |
+
padding: 5px 10px;
|
122 |
+
border-radius: 5px;
|
123 |
+
font-size: 14px;
|
124 |
+
cursor: pointer;
|
125 |
+
transition: background-color 0.3s ease;
|
126 |
+
margin-top: 12px;
|
127 |
+
}
|
128 |
+
|
129 |
+
#upload-button:hover {
|
130 |
+
background-color: #0056b3;
|
131 |
+
}
|
132 |
+
|
133 |
+
.send i {
|
134 |
+
display: block;
|
135 |
+
width: 25px;
|
136 |
+
color: #125ee5;
|
137 |
+
}
|
138 |
+
|
139 |
+
.reset i {
|
140 |
+
display: block;
|
141 |
+
width: 25px;
|
142 |
+
color: #125ee5;
|
143 |
+
}
|
144 |
+
|
145 |
+
|
146 |
+
.loading-animation {
|
147 |
+
padding-bottom: 8px;
|
148 |
+
word-break: break-word;
|
149 |
+
display: none;
|
150 |
+
width: 70%;
|
151 |
+
}
|
152 |
+
|
153 |
+
.loading-animation.my-loading {
|
154 |
+
flex-direction: row-reverse;
|
155 |
+
width: 100%;
|
156 |
+
}
|
157 |
+
|
158 |
+
.loading-dots {
|
159 |
+
padding: 12px;
|
160 |
+
border-radius: 15px;
|
161 |
+
position: relative;
|
162 |
+
background-color: #efefef;
|
163 |
+
min-width: 26px;
|
164 |
+
border-top-left-radius: 0;
|
165 |
+
display: flex;
|
166 |
+
justify-content: center;
|
167 |
+
align-items: baseline;
|
168 |
+
width: fit-content;
|
169 |
+
}
|
170 |
+
|
171 |
+
.loading-dots.my-loading {
|
172 |
+
transform: rotateY(180deg);
|
173 |
+
background-color: #e7f9d8;
|
174 |
+
float: right;
|
175 |
+
}
|
176 |
+
|
177 |
+
.dot {
|
178 |
+
width: 7px;
|
179 |
+
height: 7px;
|
180 |
+
margin: 0 2px;
|
181 |
+
border-radius: 50%;
|
182 |
+
animation: bounce 1.5s ease-in-out infinite;
|
183 |
+
background: #5a5a5a;
|
184 |
+
}
|
185 |
+
|
186 |
+
.loading-dots.my-loading.dark {
|
187 |
+
background-color: #0e4d9e;
|
188 |
+
}
|
189 |
+
|
190 |
+
.dark {
|
191 |
+
background-color: #263443;
|
192 |
+
}
|
193 |
+
|
194 |
+
.dark-dot {
|
195 |
+
background: #c9c9c9;
|
196 |
+
}
|
197 |
+
|
198 |
+
.dot:nth-of-type(1) {
|
199 |
+
margin-left: 5px;
|
200 |
+
animation-delay: 0s;
|
201 |
+
}
|
202 |
+
|
203 |
+
.dot:nth-of-type(2) {
|
204 |
+
animation-delay: 0.2s;
|
205 |
+
}
|
206 |
+
|
207 |
+
.dot:nth-of-type(3) {
|
208 |
+
animation-delay: 0.4s;
|
209 |
+
}
|
210 |
+
|
211 |
+
@keyframes bounce {
|
212 |
+
0%,
|
213 |
+
40% {
|
214 |
+
transform: translateY(0);
|
215 |
+
}
|
216 |
+
20% {
|
217 |
+
transform: translateY(-10px);
|
218 |
+
}
|
219 |
+
}
|
worker_huggingFace.py
ADDED
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import torch
|
3 |
+
from langchain import PromptTemplate
|
4 |
+
from langchain.chains import RetrievalQA
|
5 |
+
from langchain.embeddings import HuggingFaceInstructEmbeddings
|
6 |
+
from langchain.document_loaders import PyPDFLoader
|
7 |
+
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
8 |
+
from langchain.vectorstores import Chroma
|
9 |
+
from langchain.llms import HuggingFaceHub
|
10 |
+
|
11 |
+
# Check for GPU availability and set the appropriate device for computation.
|
12 |
+
DEVICE = "cuda:0" if torch.cuda.is_available() else "cpu"
|
13 |
+
|
14 |
+
# Global variables
|
15 |
+
conversation_retrieval_chain = None
|
16 |
+
chat_history = []
|
17 |
+
llm_hub = None
|
18 |
+
embeddings = None
|
19 |
+
|
20 |
+
# Function to initialize the language model and its embeddings
|
21 |
+
def init_llm():
|
22 |
+
global llm_hub, embeddings
|
23 |
+
# Set up the environment variable for HuggingFace and initialize the desired model.
|
24 |
+
os.environ["HUGGINGFACEHUB_API_TOKEN"] = "YOUR API KEY"
|
25 |
+
|
26 |
+
# repo name for the model
|
27 |
+
model_id = "tiiuae/falcon-7b-instruct"
|
28 |
+
# load the model into the HuggingFaceHub
|
29 |
+
llm_hub = HuggingFaceHub(repo_id=model_id, model_kwargs={"temperature": 0.1, "max_new_tokens": 600, "max_length": 600})
|
30 |
+
|
31 |
+
#Initialize embeddings using a pre-trained model to represent the text data.
|
32 |
+
embeddings = HuggingFaceInstructEmbeddings(
|
33 |
+
model_name="sentence-transformers/all-MiniLM-L6-v2", model_kwargs={"device": DEVICE}
|
34 |
+
)
|
35 |
+
|
36 |
+
|
37 |
+
# Function to process a PDF document
|
38 |
+
def process_document(document_path):
|
39 |
+
global conversation_retrieval_chain
|
40 |
+
|
41 |
+
# Load the document
|
42 |
+
loader = PyPDFLoader(document_path)
|
43 |
+
documents = loader.load()
|
44 |
+
|
45 |
+
# Split the document into chunks
|
46 |
+
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1024, chunk_overlap=64)
|
47 |
+
texts = text_splitter.split_documents(documents)
|
48 |
+
|
49 |
+
# Create an embeddings database using Chroma from the split text chunks.
|
50 |
+
db = Chroma.from_documents(texts, embedding=embeddings)
|
51 |
+
|
52 |
+
|
53 |
+
# --> Build the QA chain, which utilizes the LLM and retriever for answering questions.
|
54 |
+
# By default, the vectorstore retriever uses similarity search.
|
55 |
+
# If the underlying vectorstore support maximum marginal relevance search, you can specify that as the search type (search_type="mmr").
|
56 |
+
# You can also specify search kwargs like k to use when doing retrieval. k represent how many search results send to llm
|
57 |
+
conversation_retrieval_chain = RetrievalQA.from_chain_type(
|
58 |
+
llm=llm_hub,
|
59 |
+
chain_type="stuff",
|
60 |
+
retriever=db.as_retriever(search_type="mmr", search_kwargs={'k': 6, 'lambda_mult': 0.25}),
|
61 |
+
return_source_documents=False,
|
62 |
+
input_key = "question"
|
63 |
+
# chain_type_kwargs={"prompt": prompt} # if you are using prompt template, you need to uncomment this part
|
64 |
+
)
|
65 |
+
|
66 |
+
|
67 |
+
# Function to process a user prompt
|
68 |
+
def process_prompt(prompt):
|
69 |
+
global conversation_retrieval_chain
|
70 |
+
global chat_history
|
71 |
+
|
72 |
+
# Query the model
|
73 |
+
output = conversation_retrieval_chain({"question": prompt, "chat_history": chat_history})
|
74 |
+
answer = output["result"]
|
75 |
+
|
76 |
+
# Update the chat history
|
77 |
+
chat_history.append((prompt, answer))
|
78 |
+
|
79 |
+
# Return the model's response
|
80 |
+
return answer
|
81 |
+
|
82 |
+
# Initialize the language model
|
83 |
+
init_llm()
|