Spaces:
Runtime error
Runtime error
pr2
#1
by
rohan13
- opened
- app.py +3 -20
- main.py +14 -19
- open_ai.index +2 -2
- open_ai.pkl +2 -2
- requirements.txt +9 -10
- static/chatbot.js +9 -97
- static/style.css +9 -153
- templates/index.html +0 -7
- utils.py +20 -219
app.py
CHANGED
@@ -1,21 +1,13 @@
|
|
1 |
from flask import Flask, render_template
|
2 |
-
from flask_executor import Executor
|
3 |
from flask_socketio import SocketIO, emit
|
4 |
from flask_cors import cross_origin, CORS
|
5 |
from main import run
|
6 |
-
from gevent import monkey
|
7 |
|
8 |
-
monkey.patch_all(ssl=False)
|
9 |
app = Flask(__name__)
|
10 |
app.config['SECRET_KEY'] = 'secret!'
|
11 |
-
socketio = SocketIO(app, cors_allowed_origins="*", async_mode='gevent'
|
12 |
cors = CORS(app)
|
13 |
|
14 |
-
executor = Executor(app)
|
15 |
-
|
16 |
-
executor.init_app(app)
|
17 |
-
app.config['EXECUTOR_MAX_WORKERS'] = 10
|
18 |
-
|
19 |
@app.route('/')
|
20 |
def index():
|
21 |
return render_template('index.html')
|
@@ -25,18 +17,9 @@ def index():
|
|
25 |
def handle_message(data):
|
26 |
question = data['question']
|
27 |
print("question: " + question)
|
|
|
|
|
28 |
|
29 |
-
if executor.futures:
|
30 |
-
emit('response', {'response': 'Server is busy, please try again later'})
|
31 |
-
return
|
32 |
-
|
33 |
-
try:
|
34 |
-
future = executor.submit(run, question)
|
35 |
-
response = future.result()
|
36 |
-
emit('response', {'response': response})
|
37 |
-
except Exception as e:
|
38 |
-
traceback.print_exc()
|
39 |
-
emit('response', {'response': 'Server is busy. Please try again later.'})
|
40 |
|
41 |
if __name__ == '__main__':
|
42 |
socketio.run(app, host="0.0.0.0", port=7860)
|
|
|
1 |
from flask import Flask, render_template
|
|
|
2 |
from flask_socketio import SocketIO, emit
|
3 |
from flask_cors import cross_origin, CORS
|
4 |
from main import run
|
|
|
5 |
|
|
|
6 |
app = Flask(__name__)
|
7 |
app.config['SECRET_KEY'] = 'secret!'
|
8 |
+
socketio = SocketIO(app, cors_allowed_origins="*", async_mode='gevent')
|
9 |
cors = CORS(app)
|
10 |
|
|
|
|
|
|
|
|
|
|
|
11 |
@app.route('/')
|
12 |
def index():
|
13 |
return render_template('index.html')
|
|
|
17 |
def handle_message(data):
|
18 |
question = data['question']
|
19 |
print("question: " + question)
|
20 |
+
response = run(question)
|
21 |
+
emit('response', {'response': response})
|
22 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
|
24 |
if __name__ == '__main__':
|
25 |
socketio.run(app, host="0.0.0.0", port=7860)
|
main.py
CHANGED
@@ -1,27 +1,22 @@
|
|
1 |
-
from
|
2 |
-
from
|
3 |
-
|
4 |
|
5 |
-
|
6 |
-
|
7 |
-
index = get_search_index()
|
8 |
|
9 |
-
|
|
|
10 |
|
11 |
-
|
12 |
|
13 |
-
|
14 |
|
15 |
-
|
16 |
|
17 |
-
|
18 |
|
19 |
-
|
20 |
-
result = agent_chain.run(question)
|
21 |
-
print(result)
|
22 |
-
except ValueError as ve:
|
23 |
-
if "Could not parse LLM output:" in ve.args[0] and question.lower().startswith(tuple(question_starters)) and not question.lower().endswith('?'):
|
24 |
-
question = question + '?'
|
25 |
-
result = agent_chain.run(question)
|
26 |
|
27 |
-
|
|
|
|
1 |
+
from langchain.chains.qa_with_sources.loading import load_qa_with_sources_chain
|
2 |
+
from langchain.embeddings.openai import OpenAIEmbeddings
|
3 |
+
from langchain.llms import OpenAI
|
4 |
|
5 |
+
from utils import generate_answer
|
6 |
+
from utils import get_search_index
|
|
|
7 |
|
8 |
+
open_ai_pkl = "open_ai.pkl"
|
9 |
+
open_ai_index = "open_ai.index"
|
10 |
|
11 |
+
gpt_3_5 = OpenAI(model_name='gpt-3.5-turbo',temperature=0)
|
12 |
|
13 |
+
open_ai_embeddings = OpenAIEmbeddings()
|
14 |
|
15 |
+
def run(question):
|
16 |
|
17 |
+
gpt_3_5_index = get_search_index(open_ai_pkl, open_ai_index, open_ai_embeddings)
|
18 |
|
19 |
+
gpt_3_5_chain = load_qa_with_sources_chain(gpt_3_5, chain_type="stuff", verbose=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
|
21 |
+
answer = generate_answer(gpt_3_5_chain, gpt_3_5_index, question)
|
22 |
+
return answer
|
open_ai.index
CHANGED
@@ -1,3 +1,3 @@
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:
|
3 |
-
size
|
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:1d25013b3bf7b7195d01ec1cc9ac1527638d8db68d94556b3dcc69b7dd8ff704
|
3 |
+
size 3016749
|
open_ai.pkl
CHANGED
@@ -1,3 +1,3 @@
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:
|
3 |
-
size
|
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:9c903018b7b3ad6f89b802f0e36fc92c88fb793c4f6e2499687b8823050a4df0
|
3 |
+
size 3373815
|
requirements.txt
CHANGED
@@ -1,11 +1,10 @@
|
|
1 |
faiss-cpu==1.7.3
|
2 |
-
langchain==0.0.
|
3 |
-
beautifulsoup4
|
4 |
-
PyPDF2
|
5 |
-
openai
|
6 |
-
flask
|
7 |
-
flask-socketio
|
8 |
-
flask-cors
|
9 |
-
|
10 |
-
gevent
|
11 |
-
gevent-websocket==0.10.1
|
|
|
1 |
faiss-cpu==1.7.3
|
2 |
+
langchain==0.0.117
|
3 |
+
beautifulsoup4
|
4 |
+
PyPDF2
|
5 |
+
openai
|
6 |
+
flask
|
7 |
+
flask-socketio
|
8 |
+
flask-cors
|
9 |
+
gevent
|
10 |
+
gevent-websocket
|
|
static/chatbot.js
CHANGED
@@ -1,39 +1,15 @@
|
|
1 |
$(document).ready(function() {
|
2 |
// Initialize variables
|
3 |
-
var $
|
4 |
-
var $chatHeader = $('.chat-header');
|
5 |
-
var $chatBody = $('.chat-body');
|
6 |
-
var $chatInput = $('.chat-input');
|
7 |
var $input = $('.chat-input input');
|
8 |
var $submit = $('.chat_submit');
|
9 |
var session_id = '';
|
10 |
-
$
|
11 |
$(this).addClass('chat-message');
|
12 |
});
|
13 |
-
const buttonLabels = ["What is Makerlab?", "What is 3D printing?",
|
14 |
-
"Who are the founders of Makerlab?", "What are the 3D printing prices at Makerlab?",
|
15 |
-
"How can I host a birthday at Makerlab?", "Can I book an appointment at Makerlab?",
|
16 |
-
"Tell me about softwares used to create 3D printing designs", "Hi, I am bob. Tell me when Makerlab was founded.",
|
17 |
-
"Can I get my custom designs 3D printed at Makerlab?", "Can I host a private event at Makerlab?",
|
18 |
-
"Does Makerlab host any workshop?", "When is Makerlab open?", "How can I contact the Makerlab Team?"];
|
19 |
-
|
20 |
|
21 |
// Initialize SocketIO connection
|
22 |
var socket = io.connect('https://' + document.domain + ':' + location.port);
|
23 |
-
const container = document.getElementById("button-container");
|
24 |
-
|
25 |
-
|
26 |
-
for (let i = 0; i < buttonLabels.length; i++) {
|
27 |
-
|
28 |
-
const button = document.createElement("button");
|
29 |
-
button.innerHTML = buttonLabels[i];
|
30 |
-
button.setAttribute("class", "queries");
|
31 |
-
button.setAttribute("id", `button-${i}`);
|
32 |
-
button.style.margin = "5px";
|
33 |
-
container.appendChild(button);
|
34 |
-
}
|
35 |
-
scrollButtons();
|
36 |
-
|
37 |
|
38 |
// Function to send message to Flask-SocketIO app
|
39 |
function sendMessage(message) {
|
@@ -42,14 +18,9 @@ $(document).ready(function() {
|
|
42 |
}
|
43 |
|
44 |
// Function to display message
|
45 |
-
function displayMessage(message, isUser
|
46 |
var $message = $('<div>').addClass('chat-message round');
|
47 |
-
|
48 |
-
$messageText = $('<p>').html(message);
|
49 |
-
} else {
|
50 |
-
$messageText = $('<p>').html(message.replace(/(https?:\/\/[^\s,]+)/g, '<a href="$1" target="_blank">$1</a>').replace(/(SOURCES:)/, '<br>$1'));
|
51 |
-
}
|
52 |
-
// var $messageText = $('<p>').html(message.replace(/(https?:\/\/[^\s,]+)/g, '<a href="$1">$1</a>'));
|
53 |
|
54 |
$message.append($messageText);
|
55 |
if (isUser) {
|
@@ -57,10 +28,10 @@ $(document).ready(function() {
|
|
57 |
} else {
|
58 |
$message.addClass('bot')
|
59 |
}
|
60 |
-
if ($
|
61 |
-
$
|
62 |
-
if ($
|
63 |
-
$
|
64 |
}
|
65 |
} else {
|
66 |
$('.chat-container').append($message);
|
@@ -97,64 +68,5 @@ $(document).ready(function() {
|
|
97 |
});
|
98 |
|
99 |
// Initial message
|
100 |
-
displayMessage('
|
101 |
-
displayMessage('This bot is powered by Gpt 3.5 and thus may get things wrong. You can click on any of the questions on the left and they will get copied to this window. You can also type in a question here. If you find the bot useful or have feedback on improving it, drop us a line at <a href="https://makerlab.illinois.edu/contact" target="_blank">Makerlab - Contact</a>', false, true);
|
102 |
-
|
103 |
-
// Function to minimize the widget
|
104 |
-
function minimizeWidget() {
|
105 |
-
$chatContainer.addClass('minimized');
|
106 |
-
$chatHeader.hide();
|
107 |
-
$chatBody.hide()
|
108 |
-
$chatInput.hide();
|
109 |
-
$chatContainer.append('<div class="chat-bot-icon"><i class="fa fa-android"></i></div>');
|
110 |
-
}
|
111 |
-
|
112 |
-
// Function to maximize the widget
|
113 |
-
function maximizeWidget() {
|
114 |
-
$chatContainer.removeClass('minimized');
|
115 |
-
$chatBody.show()
|
116 |
-
$chatHeader.show();
|
117 |
-
$chatInput.show();
|
118 |
-
$('.chat-bot-icon').remove();
|
119 |
-
}
|
120 |
-
|
121 |
-
// Minimize the widget on click of close button
|
122 |
-
$chatHeader.find('.chat-close').click(function() {
|
123 |
-
minimizeWidget();
|
124 |
-
});
|
125 |
-
|
126 |
-
// Maximize the widget on click of chat-bot-icon
|
127 |
-
$chatContainer.on('click', '.chat-bot-icon', function() {
|
128 |
-
maximizeWidget();
|
129 |
-
});
|
130 |
-
|
131 |
-
// Add event listener to each button
|
132 |
-
$('.queries').click(function() {
|
133 |
-
// Set the value of the input field to the text content of the clicked button
|
134 |
-
$('input[type="text"]').val($(this).text());
|
135 |
-
});
|
136 |
-
|
137 |
-
function scrollButtons() {
|
138 |
-
var container = document.getElementById("button-container");
|
139 |
-
var buttons = container.querySelectorAll(".queries");
|
140 |
-
var current = 0;
|
141 |
-
|
142 |
-
var scrollInterval = setInterval(function() {
|
143 |
-
buttons[current].scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" });
|
144 |
-
current = (current + 1) % buttons.length;
|
145 |
-
}, 1000);
|
146 |
-
|
147 |
-
container.addEventListener("mouseenter", function() {
|
148 |
-
clearInterval(scrollInterval);
|
149 |
-
});
|
150 |
-
|
151 |
-
container.addEventListener("mouseleave", function() {
|
152 |
-
scrollInterval = setInterval(function() {
|
153 |
-
buttons[current].scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" });
|
154 |
-
current = (current + 1) % buttons.length;
|
155 |
-
}, 1000);
|
156 |
-
});
|
157 |
-
}
|
158 |
-
|
159 |
-
|
160 |
});
|
|
|
1 |
$(document).ready(function() {
|
2 |
// Initialize variables
|
3 |
+
var $messages = $('.chat-messages');
|
|
|
|
|
|
|
4 |
var $input = $('.chat-input input');
|
5 |
var $submit = $('.chat_submit');
|
6 |
var session_id = '';
|
7 |
+
$messages.children().each(function() {
|
8 |
$(this).addClass('chat-message');
|
9 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
|
11 |
// Initialize SocketIO connection
|
12 |
var socket = io.connect('https://' + document.domain + ':' + location.port);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
|
14 |
// Function to send message to Flask-SocketIO app
|
15 |
function sendMessage(message) {
|
|
|
18 |
}
|
19 |
|
20 |
// Function to display message
|
21 |
+
function displayMessage(message, isUser) {
|
22 |
var $message = $('<div>').addClass('chat-message round');
|
23 |
+
var $messageText = $('<p>').html(message.replace(/(https?:\/\/[^\s]+)/g, '<a href="$1">$1</a>'));
|
|
|
|
|
|
|
|
|
|
|
24 |
|
25 |
$message.append($messageText);
|
26 |
if (isUser) {
|
|
|
28 |
} else {
|
29 |
$message.addClass('bot')
|
30 |
}
|
31 |
+
if ($messages) {
|
32 |
+
$messages.append($message);
|
33 |
+
if ($messages[0]) {
|
34 |
+
$messages.animate({scrollTop: $messages[0].scrollHeight}, 300);
|
35 |
}
|
36 |
} else {
|
37 |
$('.chat-container').append($message);
|
|
|
68 |
});
|
69 |
|
70 |
// Initial message
|
71 |
+
displayMessage('Ask me anything');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
72 |
});
|
static/style.css
CHANGED
@@ -7,7 +7,7 @@
|
|
7 |
border-radius: 10px;
|
8 |
box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.2);
|
9 |
max-width: 400px;
|
10 |
-
min-width:
|
11 |
}
|
12 |
|
13 |
.round {
|
@@ -22,7 +22,7 @@
|
|
22 |
align-items: center;
|
23 |
justify-content: space-between;
|
24 |
padding: 10px;
|
25 |
-
background-color:
|
26 |
color: #fff;
|
27 |
border-top-left-radius: 10px;
|
28 |
border-top-right-radius: 10px;
|
@@ -37,7 +37,7 @@
|
|
37 |
}
|
38 |
|
39 |
.chat-body {
|
40 |
-
height:
|
41 |
overflow-y: scroll;
|
42 |
padding: 10px;
|
43 |
word-wrap: break-word;
|
@@ -57,7 +57,6 @@
|
|
57 |
position: relative;
|
58 |
word-wrap: break-word;
|
59 |
border-radius: 10px;
|
60 |
-
color: #fff;
|
61 |
}
|
62 |
|
63 |
.chat-message.user {
|
@@ -66,11 +65,12 @@
|
|
66 |
justify-content: flex-end;
|
67 |
text-align: right;
|
68 |
align-items: center;
|
69 |
-
background-color: rgba(
|
70 |
border-top-right-radius: 0px;
|
71 |
border-bottom-right-radius: 0px;
|
72 |
border-bottom-left-radius: 10px;
|
73 |
word-wrap: break-word;
|
|
|
74 |
}
|
75 |
|
76 |
|
@@ -80,7 +80,7 @@
|
|
80 |
justify-content: flex-start;
|
81 |
text-align: left;
|
82 |
align-items: center;
|
83 |
-
background-color: rgba(
|
84 |
border-top-left-radius: 0px;
|
85 |
border-bottom-right-radius: 10px;
|
86 |
border-bottom-left-radius: 0px;
|
@@ -107,7 +107,7 @@
|
|
107 |
height: 0;
|
108 |
border-top: 15px solid transparent;
|
109 |
border-bottom: 15px solid transparent;
|
110 |
-
border-left:
|
111 |
border-top-right-radius: 10px;
|
112 |
}
|
113 |
|
@@ -120,7 +120,7 @@
|
|
120 |
height: 0;
|
121 |
border-top: 15px solid transparent;
|
122 |
border-bottom: 15px solid transparent;
|
123 |
-
border-right: 15px solid #
|
124 |
border-top-left-radius: 10px;
|
125 |
}
|
126 |
|
@@ -141,7 +141,7 @@
|
|
141 |
}
|
142 |
|
143 |
.chat-input button {
|
144 |
-
background-color: #
|
145 |
color: #fff;
|
146 |
border: none;
|
147 |
border-radius: 5px;
|
@@ -150,147 +150,3 @@
|
|
150 |
cursor: pointer;
|
151 |
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.1);
|
152 |
}
|
153 |
-
|
154 |
-
/* CSS for chat-container when minimized */
|
155 |
-
.chat-container.minimized {
|
156 |
-
min-width: 70px;
|
157 |
-
height: 70px;
|
158 |
-
border-radius: 50%;
|
159 |
-
position: fixed;
|
160 |
-
bottom: 10px;
|
161 |
-
right: 10px;
|
162 |
-
z-index: 9999;
|
163 |
-
background-color: #fff;
|
164 |
-
box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.3);
|
165 |
-
cursor: pointer;
|
166 |
-
}
|
167 |
-
|
168 |
-
/* CSS for chat-bot-icon */
|
169 |
-
.chat-bot-icon {
|
170 |
-
font-size: 30px;
|
171 |
-
color: #00008BBF;
|
172 |
-
position: absolute;
|
173 |
-
top: 50%;
|
174 |
-
left: 50%;
|
175 |
-
transform: translate(-50%, -50%);
|
176 |
-
}
|
177 |
-
|
178 |
-
/* CSS for chat-header when not minimized */
|
179 |
-
.chat-header {
|
180 |
-
display: flex;
|
181 |
-
justify-content: space-between;
|
182 |
-
align-items: center;
|
183 |
-
background-color: #6c7ae0;
|
184 |
-
color: #fff;
|
185 |
-
padding: 10px;
|
186 |
-
border-top-left-radius: 5px;
|
187 |
-
border-top-right-radius: 5px;
|
188 |
-
}
|
189 |
-
|
190 |
-
/* CSS for chat-container when not minimized */
|
191 |
-
.chat-container:not(.minimized) {
|
192 |
-
border-radius: 5px;
|
193 |
-
position: fixed;
|
194 |
-
bottom: 10px;
|
195 |
-
right: 10px;
|
196 |
-
z-index: 9999;
|
197 |
-
background-color: #fff;
|
198 |
-
box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.3);
|
199 |
-
}
|
200 |
-
|
201 |
-
/* CSS for chat-bot-icon when chat-container is minimized */
|
202 |
-
.chat-container.minimized .chat-bot-icon {
|
203 |
-
display: block;
|
204 |
-
}
|
205 |
-
|
206 |
-
/* CSS for chat-bot-icon when chat-container is not minimized */
|
207 |
-
.chat-container:not(.minimized) .chat-bot-icon {
|
208 |
-
display: none;
|
209 |
-
}
|
210 |
-
|
211 |
-
.queries {
|
212 |
-
|
213 |
-
padding: 8px 12px;
|
214 |
-
font-size: 16px;
|
215 |
-
font-weight: bold;
|
216 |
-
text-align: center;
|
217 |
-
text-decoration: none;
|
218 |
-
border: 0.5px solid #a5a0a0;
|
219 |
-
border-radius: 20px;
|
220 |
-
color: #000;
|
221 |
-
background-color: #343a404a;
|
222 |
-
cursor: pointer;
|
223 |
-
margin: 5px;
|
224 |
-
}
|
225 |
-
|
226 |
-
.queries:hover {
|
227 |
-
background-color: #343a40ad;
|
228 |
-
}
|
229 |
-
|
230 |
-
.queries:active {
|
231 |
-
background-color: #0053a4;
|
232 |
-
}
|
233 |
-
|
234 |
-
#button-container {
|
235 |
-
display: flex;
|
236 |
-
position: relative;
|
237 |
-
left: 2%;
|
238 |
-
top: 40%;
|
239 |
-
flex-direction: column;
|
240 |
-
justify-content: inherit;
|
241 |
-
align-items: center;
|
242 |
-
width: auto;
|
243 |
-
overflow-y: scroll;
|
244 |
-
max-height: 350px;
|
245 |
-
padding-top: 110%;
|
246 |
-
margin-top: 2%;
|
247 |
-
|
248 |
-
}
|
249 |
-
|
250 |
-
#button-container button {
|
251 |
-
margin-bottom: 10px;
|
252 |
-
}
|
253 |
-
|
254 |
-
.query-heading {
|
255 |
-
display: flex;
|
256 |
-
position: relative;
|
257 |
-
width: auto%;
|
258 |
-
background-color: #fff;
|
259 |
-
padding: 10px;
|
260 |
-
z-index: 1;
|
261 |
-
justify-content: inherit;
|
262 |
-
width: 100%;
|
263 |
-
border-bottom: 1px solid #2f4f4f5e;
|
264 |
-
}
|
265 |
-
|
266 |
-
.sample-query {
|
267 |
-
display: flex;
|
268 |
-
position: absolute;
|
269 |
-
left: 30%;
|
270 |
-
top: 10%;
|
271 |
-
flex-direction: column;
|
272 |
-
justify-content: flex-start;
|
273 |
-
align-items: center;
|
274 |
-
width: auto;
|
275 |
-
padding: 10px;
|
276 |
-
border: 1px solid #2f4f4f5e;
|
277 |
-
justify-content: center;
|
278 |
-
border-radius: 10px;
|
279 |
-
max-width: 30%;
|
280 |
-
}
|
281 |
-
|
282 |
-
::-webkit-scrollbar {
|
283 |
-
width: 8px;
|
284 |
-
}
|
285 |
-
|
286 |
-
::-webkit-scrollbar-track {
|
287 |
-
background-color: #f4f4f4;
|
288 |
-
}
|
289 |
-
|
290 |
-
::-webkit-scrollbar-thumb {
|
291 |
-
background-color: #a3bfe9a6;
|
292 |
-
border-radius: 20px;
|
293 |
-
}
|
294 |
-
|
295 |
-
|
296 |
-
|
|
|
7 |
border-radius: 10px;
|
8 |
box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.2);
|
9 |
max-width: 400px;
|
10 |
+
min-width: 300px;
|
11 |
}
|
12 |
|
13 |
.round {
|
|
|
22 |
align-items: center;
|
23 |
justify-content: space-between;
|
24 |
padding: 10px;
|
25 |
+
background-color: rgb(113, 239, 234);
|
26 |
color: #fff;
|
27 |
border-top-left-radius: 10px;
|
28 |
border-top-right-radius: 10px;
|
|
|
37 |
}
|
38 |
|
39 |
.chat-body {
|
40 |
+
height: 300px;
|
41 |
overflow-y: scroll;
|
42 |
padding: 10px;
|
43 |
word-wrap: break-word;
|
|
|
57 |
position: relative;
|
58 |
word-wrap: break-word;
|
59 |
border-radius: 10px;
|
|
|
60 |
}
|
61 |
|
62 |
.chat-message.user {
|
|
|
65 |
justify-content: flex-end;
|
66 |
text-align: right;
|
67 |
align-items: center;
|
68 |
+
background-color: rgba(113, 239, 234, 0.75);
|
69 |
border-top-right-radius: 0px;
|
70 |
border-bottom-right-radius: 0px;
|
71 |
border-bottom-left-radius: 10px;
|
72 |
word-wrap: break-word;
|
73 |
+
color: #000;
|
74 |
}
|
75 |
|
76 |
|
|
|
80 |
justify-content: flex-start;
|
81 |
text-align: left;
|
82 |
align-items: center;
|
83 |
+
background-color: rgba(113, 239, 234, 0.75);
|
84 |
border-top-left-radius: 0px;
|
85 |
border-bottom-right-radius: 10px;
|
86 |
border-bottom-left-radius: 0px;
|
|
|
107 |
height: 0;
|
108 |
border-top: 15px solid transparent;
|
109 |
border-bottom: 15px solid transparent;
|
110 |
+
border-left: 15px solid #71EFEABF;
|
111 |
border-top-right-radius: 10px;
|
112 |
}
|
113 |
|
|
|
120 |
height: 0;
|
121 |
border-top: 15px solid transparent;
|
122 |
border-bottom: 15px solid transparent;
|
123 |
+
border-right: 15px solid #71EFEABF;
|
124 |
border-top-left-radius: 10px;
|
125 |
}
|
126 |
|
|
|
141 |
}
|
142 |
|
143 |
.chat-input button {
|
144 |
+
background-color: #FFA500;
|
145 |
color: #fff;
|
146 |
border: none;
|
147 |
border-radius: 5px;
|
|
|
150 |
cursor: pointer;
|
151 |
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.1);
|
152 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
templates/index.html
CHANGED
@@ -9,18 +9,11 @@
|
|
9 |
<link rel="stylesheet" href="static/style.css">
|
10 |
</head>
|
11 |
<body>
|
12 |
-
<div class = "sample-query">
|
13 |
-
<div class="query-heading"><h4>Sample Queries</h4></div>
|
14 |
-
<div id="button-container">
|
15 |
-
</div></div>
|
16 |
<div class="chat-container">
|
17 |
<div class="chat-header">
|
18 |
<h4>Makerlab Q&A Bot</h4>
|
19 |
<i class="fa fa-close chat-close"></i>
|
20 |
</div>
|
21 |
-
<div class="chat-bot-icon">
|
22 |
-
<i class="fa fa-android"></i> <!-- Replace with your bot icon -->
|
23 |
-
</div>
|
24 |
<div class="chat-body chat-messages round"></div>
|
25 |
<div class="chat-input">
|
26 |
<input type="text" placeholder="Type your message">
|
|
|
9 |
<link rel="stylesheet" href="static/style.css">
|
10 |
</head>
|
11 |
<body>
|
|
|
|
|
|
|
|
|
12 |
<div class="chat-container">
|
13 |
<div class="chat-header">
|
14 |
<h4>Makerlab Q&A Bot</h4>
|
15 |
<i class="fa fa-close chat-close"></i>
|
16 |
</div>
|
|
|
|
|
|
|
17 |
<div class="chat-body chat-messages round"></div>
|
18 |
<div class="chat-input">
|
19 |
<input type="text" placeholder="Type your message">
|
utils.py
CHANGED
@@ -1,111 +1,36 @@
|
|
1 |
import os
|
2 |
import pickle
|
3 |
-
import re
|
4 |
import time
|
5 |
-
from typing import List, Union
|
6 |
from urllib.parse import urlparse, urljoin
|
7 |
|
8 |
import faiss
|
9 |
import requests
|
10 |
from PyPDF2 import PdfReader
|
11 |
from bs4 import BeautifulSoup
|
12 |
-
from langchain import OpenAI, LLMChain
|
13 |
-
from langchain.agents import ConversationalAgent
|
14 |
-
from langchain.agents import Tool, AgentExecutor, LLMSingleActionAgent, AgentOutputParser
|
15 |
-
from langchain.prompts import BaseChatPromptTemplate
|
16 |
-
from langchain.chains import ConversationalRetrievalChain
|
17 |
from langchain.docstore.document import Document
|
18 |
-
from langchain.embeddings import OpenAIEmbeddings
|
19 |
-
from langchain.memory import ConversationBufferWindowMemory
|
20 |
-
from langchain.schema import AgentAction, AgentFinish, HumanMessage
|
21 |
from langchain.text_splitter import CharacterTextSplitter
|
22 |
from langchain.vectorstores.faiss import FAISS
|
23 |
|
24 |
book_url = 'https://g.co/kgs/2VFC7u'
|
25 |
book_file = "Book.pdf"
|
26 |
url = 'https://makerlab.illinois.edu/'
|
|
|
27 |
|
28 |
-
pickle_file = "open_ai.pkl"
|
29 |
-
index_file = "open_ai.index"
|
30 |
-
|
31 |
-
gpt_3_5 = OpenAI(model_name='gpt-3.5-turbo',temperature=0)
|
32 |
-
|
33 |
-
embeddings = OpenAIEmbeddings()
|
34 |
-
|
35 |
-
chat_history = []
|
36 |
-
|
37 |
-
memory = ConversationBufferWindowMemory(memory_key="chat_history")
|
38 |
-
|
39 |
-
gpt_3_5_index = None
|
40 |
-
|
41 |
-
class CustomOutputParser(AgentOutputParser):
|
42 |
-
|
43 |
-
def parse(self, llm_output: str) -> Union[AgentAction, AgentFinish]:
|
44 |
-
# Check if agent replied without using tools
|
45 |
-
if "AI:" in llm_output:
|
46 |
-
return AgentFinish(return_values={"output": llm_output.split("AI:")[-1].strip()},
|
47 |
-
log=llm_output)
|
48 |
-
# Check if agent should finish
|
49 |
-
if "Final Answer:" in llm_output:
|
50 |
-
return AgentFinish(
|
51 |
-
# Return values is generally always a dictionary with a single `output` key
|
52 |
-
# It is not recommended to try anything else at the moment :)
|
53 |
-
return_values={"output": llm_output.split("Final Answer:")[-1].strip()},
|
54 |
-
log=llm_output,
|
55 |
-
)
|
56 |
-
# Parse out the action and action input
|
57 |
-
regex = r"Action: (.*?)[\n]*Action Input:[\s]*(.*)"
|
58 |
-
match = re.search(regex, llm_output, re.DOTALL)
|
59 |
-
if not match:
|
60 |
-
raise ValueError(f"Could not parse LLM output: `{llm_output}`")
|
61 |
-
action = match.group(1).strip()
|
62 |
-
action_input = match.group(2)
|
63 |
-
# Return the action and action input
|
64 |
-
return AgentAction(tool=action, tool_input=action_input.strip(" ").strip('"'), log=llm_output)
|
65 |
-
|
66 |
-
# Set up a prompt template
|
67 |
-
class CustomPromptTemplate(BaseChatPromptTemplate):
|
68 |
-
# The template to use
|
69 |
-
template: str
|
70 |
-
# The list of tools available
|
71 |
-
tools: List[Tool]
|
72 |
-
|
73 |
-
def format_messages(self, **kwargs) -> str:
|
74 |
-
# Get the intermediate steps (AgentAction, Observation tuples)
|
75 |
-
# Format them in a particular way
|
76 |
-
intermediate_steps = kwargs.pop("intermediate_steps")
|
77 |
-
thoughts = ""
|
78 |
-
for action, observation in intermediate_steps:
|
79 |
-
thoughts += action.log
|
80 |
-
thoughts += f"\nObservation: {observation}\nThought: "
|
81 |
-
# Set the agent_scratchpad variable to that value
|
82 |
-
kwargs["agent_scratchpad"] = thoughts
|
83 |
-
# Create a tools variable from the list of tools provided
|
84 |
-
kwargs["tools"] = "\n".join([f"{tool.name}: {tool.description}" for tool in self.tools])
|
85 |
-
# Create a list of tool names for the tools provided
|
86 |
-
kwargs["tool_names"] = ", ".join([tool.name for tool in self.tools])
|
87 |
-
formatted = self.template.format(**kwargs)
|
88 |
-
return [HumanMessage(content=formatted)]
|
89 |
-
|
90 |
-
def get_search_index():
|
91 |
-
global gpt_3_5_index
|
92 |
if os.path.isfile(pickle_file) and os.path.isfile(index_file) and os.path.getsize(pickle_file) > 0:
|
93 |
# Load index from pickle file
|
94 |
with open(pickle_file, "rb") as f:
|
95 |
search_index = pickle.load(f)
|
96 |
else:
|
97 |
-
|
98 |
|
99 |
-
|
100 |
|
|
|
|
|
|
|
|
|
|
|
101 |
|
102 |
-
def create_index():
|
103 |
-
source_chunks = create_chunk_documents()
|
104 |
-
search_index = search_index_from_docs(source_chunks)
|
105 |
-
faiss.write_index(search_index.index, index_file)
|
106 |
-
# Save index to pickle file
|
107 |
-
with open(pickle_file, "wb") as f:
|
108 |
-
pickle.dump(search_index, f)
|
109 |
return search_index
|
110 |
|
111 |
|
@@ -193,143 +118,19 @@ def get_document_data(book_file, book_url):
|
|
193 |
# print("document list" + str(len(document_list)))
|
194 |
return document_list
|
195 |
|
196 |
-
def search_index_from_docs(source_chunks):
|
197 |
# Create index from chunk documents
|
198 |
# print("Size of chunk" + str(len(source_chunks)))
|
199 |
search_index = FAISS.from_texts([doc.page_content for doc in source_chunks], embeddings, metadatas=[doc.metadata for doc in source_chunks])
|
200 |
return search_index
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
res.append(f"Human:{human}\nAI:{ai}")
|
213 |
-
return "\n".join(res)
|
214 |
-
|
215 |
-
|
216 |
-
def generate_answer(question) -> str:
|
217 |
-
global chat_history, gpt_3_5_index
|
218 |
-
gpt_3_5_chain = get_qa_chain(gpt_3_5_index)
|
219 |
-
result = gpt_3_5_chain(
|
220 |
-
{"question": question, "chat_history": chat_history,"vectordbkwargs": {"search_distance": 0.8}})
|
221 |
-
print("REsult: " + str(result))
|
222 |
-
chat_history = [(question, result["answer"])]
|
223 |
-
sources = []
|
224 |
-
|
225 |
-
for document in result['source_documents']:
|
226 |
-
source = document.metadata['source']
|
227 |
-
sources.append(source)
|
228 |
-
|
229 |
-
source = ',\n'.join(set(sources))
|
230 |
-
return result['answer'] + '\nSOURCES: ' + source
|
231 |
-
|
232 |
-
|
233 |
-
def get_agent_chain(prompt, tools):
|
234 |
-
global gpt_3_5
|
235 |
-
# output_parser = CustomOutputParser()
|
236 |
-
llm_chain = LLMChain(llm=gpt_3_5, prompt=prompt)
|
237 |
-
agent = ConversationalAgent(llm_chain=llm_chain, tools=tools, verbose=True)
|
238 |
-
agent_chain = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True, memory=memory,
|
239 |
-
intermediate_steps=True)
|
240 |
-
return agent_chain
|
241 |
-
|
242 |
-
|
243 |
-
def get_prompt_and_tools():
|
244 |
-
tools = get_tools()
|
245 |
-
|
246 |
-
prefix = """Have a conversation with a human, answering the following questions as best you can.
|
247 |
-
Always try to use Vectorstore first.
|
248 |
-
Your name is Makerlab Bot because you are a personal assistant of Makerlab. You have access to the following tools:"""
|
249 |
-
suffix = """Begin! If you use any tool, ALWAYS return a "SOURCES" part in your answer"
|
250 |
-
|
251 |
-
{chat_history}
|
252 |
-
Question: {input}
|
253 |
-
{agent_scratchpad}
|
254 |
-
SOURCES:"""
|
255 |
-
prompt = ConversationalAgent.create_prompt(
|
256 |
-
tools,
|
257 |
-
prefix=prefix,
|
258 |
-
suffix=suffix,
|
259 |
-
input_variables=["input", "chat_history", "agent_scratchpad"]
|
260 |
-
)
|
261 |
-
# print("Template: " + prompt.template)
|
262 |
-
return prompt, tools
|
263 |
-
|
264 |
-
|
265 |
-
def get_tools():
|
266 |
-
tools = [
|
267 |
-
Tool(
|
268 |
-
name="Vectorstore",
|
269 |
-
func=generate_answer,
|
270 |
-
description="useful for when you need to answer questions about the Makerlab or 3D Printing.",
|
271 |
-
return_direct=True
|
272 |
-
)]
|
273 |
-
return tools
|
274 |
-
|
275 |
-
def get_custom_agent(prompt, tools):
|
276 |
-
|
277 |
-
llm_chain = LLMChain(llm=gpt_3_5, prompt=prompt)
|
278 |
-
|
279 |
-
output_parser = CustomOutputParser()
|
280 |
-
tool_names = [tool.name for tool in tools]
|
281 |
-
agent = LLMSingleActionAgent(
|
282 |
-
llm_chain=llm_chain,
|
283 |
-
output_parser=output_parser,
|
284 |
-
stop=["\nObservation:"],
|
285 |
-
allowed_tools=tool_names
|
286 |
-
)
|
287 |
-
agent_executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True, memory=memory,
|
288 |
-
intermediate_steps=True)
|
289 |
-
return agent_executor
|
290 |
-
|
291 |
-
def get_prompt_and_tools_for_custom_agent():
|
292 |
-
template = """
|
293 |
-
Have a conversation with a human, answering the following questions as best you can.
|
294 |
-
Always try to use Vectorstore first.
|
295 |
-
Your name is Makerlab Bot because you are a personal assistant of Makerlab. You have access to the following tools:
|
296 |
-
|
297 |
-
{tools}
|
298 |
-
|
299 |
-
To answer for the new input, use the following format:
|
300 |
-
|
301 |
-
New Input: the input question you must answer
|
302 |
-
Thought: Do I need to use a tool? Yes
|
303 |
-
Action: the action to take, should be one of [{tool_names}]
|
304 |
-
Action Input: the input to the action
|
305 |
-
Observation: the result of the action
|
306 |
-
... (this Thought/Action/Action Input/Observation can repeat N times)
|
307 |
-
Thought: I now know the final answer
|
308 |
-
Final Answer: the final answer to the original input question. SOURCES: the sources referred to find the final answer
|
309 |
-
|
310 |
-
|
311 |
-
When you have a response to say to the Human and DO NOT need to use a tool:
|
312 |
-
1. DO NOT return "SOURCES" if you did not use any tool.
|
313 |
-
2. You MUST use this format:
|
314 |
-
```
|
315 |
-
Thought: Do I need to use a tool? No
|
316 |
-
AI: [your response here]
|
317 |
-
```
|
318 |
-
|
319 |
-
Begin! Remember to speak as a personal assistant when giving your final answer.
|
320 |
-
ALWAYS return a "SOURCES" part in your answer, if you used any tool.
|
321 |
-
|
322 |
-
Previous conversation history:
|
323 |
-
{chat_history}
|
324 |
-
New input: {input}
|
325 |
-
{agent_scratchpad}
|
326 |
-
SOURCES:"""
|
327 |
-
tools = get_tools()
|
328 |
-
prompt = CustomPromptTemplate(
|
329 |
-
template=template,
|
330 |
-
tools=tools,
|
331 |
-
# This omits the `agent_scratchpad`, `tools`, and `tool_names` variables because those are generated dynamically
|
332 |
-
# This includes the `intermediate_steps` variable because that is needed
|
333 |
-
input_variables=["input", "intermediate_steps", "chat_history"]
|
334 |
-
)
|
335 |
-
return prompt, tools
|
|
|
1 |
import os
|
2 |
import pickle
|
|
|
3 |
import time
|
|
|
4 |
from urllib.parse import urlparse, urljoin
|
5 |
|
6 |
import faiss
|
7 |
import requests
|
8 |
from PyPDF2 import PdfReader
|
9 |
from bs4 import BeautifulSoup
|
|
|
|
|
|
|
|
|
|
|
10 |
from langchain.docstore.document import Document
|
|
|
|
|
|
|
11 |
from langchain.text_splitter import CharacterTextSplitter
|
12 |
from langchain.vectorstores.faiss import FAISS
|
13 |
|
14 |
book_url = 'https://g.co/kgs/2VFC7u'
|
15 |
book_file = "Book.pdf"
|
16 |
url = 'https://makerlab.illinois.edu/'
|
17 |
+
def get_search_index(pickle_file, index_file, embeddings):
|
18 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
if os.path.isfile(pickle_file) and os.path.isfile(index_file) and os.path.getsize(pickle_file) > 0:
|
20 |
# Load index from pickle file
|
21 |
with open(pickle_file, "rb") as f:
|
22 |
search_index = pickle.load(f)
|
23 |
else:
|
24 |
+
source_chunks = create_chunk_documents()
|
25 |
|
26 |
+
search_index = search_index_from_docs(source_chunks, embeddings=embeddings)
|
27 |
|
28 |
+
faiss.write_index(search_index.index, index_file)
|
29 |
+
|
30 |
+
# Save index to pickle file
|
31 |
+
with open(pickle_file, "wb") as f:
|
32 |
+
pickle.dump(search_index, f)
|
33 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
return search_index
|
35 |
|
36 |
|
|
|
118 |
# print("document list" + str(len(document_list)))
|
119 |
return document_list
|
120 |
|
121 |
+
def search_index_from_docs(source_chunks, embeddings):
|
122 |
# Create index from chunk documents
|
123 |
# print("Size of chunk" + str(len(source_chunks)))
|
124 |
search_index = FAISS.from_texts([doc.page_content for doc in source_chunks], embeddings, metadatas=[doc.metadata for doc in source_chunks])
|
125 |
return search_index
|
126 |
+
def generate_answer(chain, index, question):
|
127 |
+
#Get answer
|
128 |
+
answer = chain(
|
129 |
+
{
|
130 |
+
"input_documents": index.similarity_search(question, k=4),
|
131 |
+
"question": question,
|
132 |
+
},
|
133 |
+
return_only_outputs=True,
|
134 |
+
)["output_text"]
|
135 |
+
|
136 |
+
return answer
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|