XThomasBU
commited on
Commit
·
b409192
1
Parent(s):
c658776
saving commit - needs cleanup
Browse files- code/main.py +127 -92
- code/modules/chat/helpers.py +55 -0
- code/modules/chat/langchain/langchain_rag.py +37 -4
- code/modules/chat/langchain/utils.py +36 -0
- code/modules/chat/llm_tutor.py +18 -4
- code/modules/config/config.yml +2 -1
- code/modules/config/prompts.py +15 -18
code/main.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
import chainlit.data as cl_data
|
2 |
-
|
3 |
from modules.config.constants import (
|
4 |
LLAMA_PATH,
|
5 |
LITERAL_API_KEY_LOGGING,
|
@@ -13,10 +13,15 @@ import os
|
|
13 |
from typing import Any, Dict, no_type_check
|
14 |
import chainlit as cl
|
15 |
from modules.chat.llm_tutor import LLMTutor
|
16 |
-
from modules.chat.helpers import
|
|
|
|
|
|
|
|
|
17 |
import copy
|
18 |
from typing import Optional
|
19 |
from chainlit.types import ThreadDict
|
|
|
20 |
|
21 |
USER_TIMEOUT = 60_000
|
22 |
SYSTEM = "System 🖥️"
|
@@ -25,19 +30,30 @@ AGENT = "Agent <>"
|
|
25 |
YOU = "You 😃"
|
26 |
ERROR = "Error 🚫"
|
27 |
|
|
|
|
|
28 |
|
29 |
-
|
30 |
-
|
31 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
|
33 |
|
34 |
class Chatbot:
|
35 |
-
def __init__(self):
|
36 |
"""
|
37 |
Initialize the Chatbot class.
|
38 |
"""
|
39 |
-
self.config =
|
40 |
-
self.literal_client = cl_data._data_layer.client
|
41 |
|
42 |
def _load_config(self):
|
43 |
"""
|
@@ -51,6 +67,8 @@ class Chatbot:
|
|
51 |
"""
|
52 |
Set up the LLM with the provided settings. Update the configuration and initialize the LLM tutor.
|
53 |
"""
|
|
|
|
|
54 |
llm_settings = cl.user_session.get("llm_settings", {})
|
55 |
chat_profile, retriever_method, memory_window, llm_style = (
|
56 |
llm_settings.get("chat_model"),
|
@@ -60,7 +78,6 @@ class Chatbot:
|
|
60 |
)
|
61 |
|
62 |
chain = cl.user_session.get("chain")
|
63 |
-
print(list(chain.store.values()))
|
64 |
memory_list = cl.user_session.get(
|
65 |
"memory",
|
66 |
(
|
@@ -69,35 +86,7 @@ class Chatbot:
|
|
69 |
else []
|
70 |
),
|
71 |
)
|
72 |
-
conversation_list =
|
73 |
-
for message in memory_list:
|
74 |
-
# Convert to dictionary if possible
|
75 |
-
message_dict = message.to_dict() if hasattr(message, "to_dict") else message
|
76 |
-
|
77 |
-
# Check if the type attribute is present as a key or attribute
|
78 |
-
message_type = (
|
79 |
-
message_dict.get("type", None)
|
80 |
-
if isinstance(message_dict, dict)
|
81 |
-
else getattr(message, "type", None)
|
82 |
-
)
|
83 |
-
|
84 |
-
# Check if content is present as a key or attribute
|
85 |
-
message_content = (
|
86 |
-
message_dict.get("content", None)
|
87 |
-
if isinstance(message_dict, dict)
|
88 |
-
else getattr(message, "content", None)
|
89 |
-
)
|
90 |
-
|
91 |
-
if message_type in ["ai", "ai_message"]:
|
92 |
-
conversation_list.append(
|
93 |
-
{"type": "ai_message", "content": message_content}
|
94 |
-
)
|
95 |
-
elif message_type in ["human", "user_message"]:
|
96 |
-
conversation_list.append(
|
97 |
-
{"type": "user_message", "content": message_content}
|
98 |
-
)
|
99 |
-
else:
|
100 |
-
raise ValueError("Invalid message type")
|
101 |
print("\n\n\n")
|
102 |
print("history at setup_llm", conversation_list)
|
103 |
print("\n\n\n")
|
@@ -111,13 +100,18 @@ class Chatbot:
|
|
111 |
self.llm_tutor.update_llm(
|
112 |
old_config, self.config
|
113 |
) # update only attributes that are changed
|
114 |
-
self.chain = self.llm_tutor.qa_bot(
|
|
|
|
|
|
|
115 |
|
116 |
tags = [chat_profile, self.config["vectorstore"]["db_option"]]
|
117 |
|
118 |
cl.user_session.set("chain", self.chain)
|
119 |
cl.user_session.set("llm_tutor", self.llm_tutor)
|
120 |
|
|
|
|
|
121 |
@no_type_check
|
122 |
async def update_llm(self, new_settings: Dict[str, Any]):
|
123 |
"""
|
@@ -176,7 +170,7 @@ class Chatbot:
|
|
176 |
cl.input_widget.Select(
|
177 |
id="llm_style",
|
178 |
label="Type of Conversation (Default Normal)",
|
179 |
-
values=["Normal", "ELI5"
|
180 |
initial_index=0,
|
181 |
),
|
182 |
]
|
@@ -268,6 +262,8 @@ class Chatbot:
|
|
268 |
and display and load previous conversation if chat logging is enabled.
|
269 |
"""
|
270 |
|
|
|
|
|
271 |
await self.make_llm_settings_widgets(self.config)
|
272 |
user = cl.user_session.get("user")
|
273 |
self.user = {
|
@@ -280,10 +276,20 @@ class Chatbot:
|
|
280 |
|
281 |
cl.user_session.set("user", self.user)
|
282 |
self.llm_tutor = LLMTutor(self.config, user=self.user)
|
283 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
284 |
cl.user_session.set("llm_tutor", self.llm_tutor)
|
285 |
cl.user_session.set("chain", self.chain)
|
286 |
|
|
|
|
|
287 |
async def stream_response(self, response):
|
288 |
"""
|
289 |
Stream the response from the LLM.
|
@@ -314,6 +320,8 @@ class Chatbot:
|
|
314 |
message: The incoming chat message.
|
315 |
"""
|
316 |
|
|
|
|
|
317 |
chain = cl.user_session.get("chain")
|
318 |
|
319 |
print("\n\n\n")
|
@@ -348,64 +356,72 @@ class Chatbot:
|
|
348 |
res = chain.stream(user_query=user_query_dict, config=chain_config)
|
349 |
res = await self.stream_response(res)
|
350 |
else:
|
351 |
-
res = await chain.invoke(
|
|
|
|
|
|
|
352 |
|
353 |
answer = res.get("answer", res.get("result"))
|
354 |
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
"
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
|
|
|
|
|
|
370 |
|
371 |
answer_with_sources, source_elements, sources_dict = get_sources(
|
372 |
res, answer, stream=stream, view_sources=view_sources
|
373 |
)
|
374 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
375 |
await cl.Message(
|
376 |
-
content=answer_with_sources,
|
|
|
|
|
|
|
377 |
).send()
|
378 |
|
379 |
async def on_chat_resume(self, thread: ThreadDict):
|
380 |
steps = thread["steps"]
|
381 |
-
# conversation_pairs = []
|
382 |
-
conversation_list = []
|
383 |
-
|
384 |
-
user_message = None
|
385 |
k = self.config["llm_params"]["memory_window"]
|
386 |
-
|
387 |
-
|
388 |
-
print(steps)
|
389 |
-
|
390 |
-
for step in reversed(steps):
|
391 |
-
print(step["type"])
|
392 |
-
if step["name"] not in [SYSTEM]:
|
393 |
-
if step["type"] == "user_message":
|
394 |
-
conversation_list.append(
|
395 |
-
{"type": "user_message", "content": step["output"]}
|
396 |
-
)
|
397 |
-
elif step["type"] == "assistant_message":
|
398 |
-
if step["name"] == LLM:
|
399 |
-
conversation_list.append(
|
400 |
-
{"type": "ai_message", "content": step["output"]}
|
401 |
-
)
|
402 |
-
else:
|
403 |
-
raise ValueError("Invalid message type")
|
404 |
-
count += 1
|
405 |
-
if count >= 2 * k: # 2 * k to account for both user and assistant messages
|
406 |
-
break
|
407 |
-
|
408 |
-
conversation_list = conversation_list[::-1]
|
409 |
|
410 |
print("\n\n\n")
|
411 |
print("history at on_chat_resume", conversation_list)
|
@@ -423,11 +439,30 @@ class Chatbot:
|
|
423 |
) -> Optional[cl.User]:
|
424 |
return default_user
|
425 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
426 |
|
427 |
-
|
428 |
-
cl.set_starters(chatbot.set_starters)
|
429 |
-
cl.author_rename(chatbot.rename)
|
430 |
-
cl.on_chat_start(chatbot.start)
|
431 |
-
cl.on_chat_resume(chatbot.on_chat_resume)
|
432 |
-
cl.on_message(chatbot.main)
|
433 |
-
cl.on_settings_update(chatbot.update_llm)
|
|
|
1 |
import chainlit.data as cl_data
|
2 |
+
import asyncio
|
3 |
from modules.config.constants import (
|
4 |
LLAMA_PATH,
|
5 |
LITERAL_API_KEY_LOGGING,
|
|
|
13 |
from typing import Any, Dict, no_type_check
|
14 |
import chainlit as cl
|
15 |
from modules.chat.llm_tutor import LLMTutor
|
16 |
+
from modules.chat.helpers import (
|
17 |
+
get_sources,
|
18 |
+
get_history_chat_resume,
|
19 |
+
get_history_setup_llm,
|
20 |
+
)
|
21 |
import copy
|
22 |
from typing import Optional
|
23 |
from chainlit.types import ThreadDict
|
24 |
+
import time
|
25 |
|
26 |
USER_TIMEOUT = 60_000
|
27 |
SYSTEM = "System 🖥️"
|
|
|
30 |
YOU = "You 😃"
|
31 |
ERROR = "Error 🚫"
|
32 |
|
33 |
+
with open("modules/config/config.yml", "r") as f:
|
34 |
+
config = yaml.safe_load(f)
|
35 |
|
36 |
+
|
37 |
+
async def setup_data_layer():
|
38 |
+
"""
|
39 |
+
Set up the data layer for chat logging.
|
40 |
+
"""
|
41 |
+
if config["chat_logging"]["log_chat"]:
|
42 |
+
data_layer = CustomLiteralDataLayer(
|
43 |
+
api_key=LITERAL_API_KEY_LOGGING, server=LITERAL_API_URL
|
44 |
+
)
|
45 |
+
else:
|
46 |
+
data_layer = None
|
47 |
+
|
48 |
+
return data_layer
|
49 |
|
50 |
|
51 |
class Chatbot:
|
52 |
+
def __init__(self, config):
|
53 |
"""
|
54 |
Initialize the Chatbot class.
|
55 |
"""
|
56 |
+
self.config = config
|
|
|
57 |
|
58 |
def _load_config(self):
|
59 |
"""
|
|
|
67 |
"""
|
68 |
Set up the LLM with the provided settings. Update the configuration and initialize the LLM tutor.
|
69 |
"""
|
70 |
+
start_time = time.time()
|
71 |
+
|
72 |
llm_settings = cl.user_session.get("llm_settings", {})
|
73 |
chat_profile, retriever_method, memory_window, llm_style = (
|
74 |
llm_settings.get("chat_model"),
|
|
|
78 |
)
|
79 |
|
80 |
chain = cl.user_session.get("chain")
|
|
|
81 |
memory_list = cl.user_session.get(
|
82 |
"memory",
|
83 |
(
|
|
|
86 |
else []
|
87 |
),
|
88 |
)
|
89 |
+
conversation_list = get_history_setup_llm(memory_list)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
90 |
print("\n\n\n")
|
91 |
print("history at setup_llm", conversation_list)
|
92 |
print("\n\n\n")
|
|
|
100 |
self.llm_tutor.update_llm(
|
101 |
old_config, self.config
|
102 |
) # update only attributes that are changed
|
103 |
+
self.chain = self.llm_tutor.qa_bot(
|
104 |
+
memory=conversation_list,
|
105 |
+
callbacks=[cl.LangchainCallbackHandler()] if cl_data._data_layer else None,
|
106 |
+
)
|
107 |
|
108 |
tags = [chat_profile, self.config["vectorstore"]["db_option"]]
|
109 |
|
110 |
cl.user_session.set("chain", self.chain)
|
111 |
cl.user_session.set("llm_tutor", self.llm_tutor)
|
112 |
|
113 |
+
print("Time taken to setup LLM: ", time.time() - start_time)
|
114 |
+
|
115 |
@no_type_check
|
116 |
async def update_llm(self, new_settings: Dict[str, Any]):
|
117 |
"""
|
|
|
170 |
cl.input_widget.Select(
|
171 |
id="llm_style",
|
172 |
label="Type of Conversation (Default Normal)",
|
173 |
+
values=["Normal", "ELI5"],
|
174 |
initial_index=0,
|
175 |
),
|
176 |
]
|
|
|
262 |
and display and load previous conversation if chat logging is enabled.
|
263 |
"""
|
264 |
|
265 |
+
start_time = time.time()
|
266 |
+
|
267 |
await self.make_llm_settings_widgets(self.config)
|
268 |
user = cl.user_session.get("user")
|
269 |
self.user = {
|
|
|
276 |
|
277 |
cl.user_session.set("user", self.user)
|
278 |
self.llm_tutor = LLMTutor(self.config, user=self.user)
|
279 |
+
|
280 |
+
print(cl.LangchainCallbackHandler())
|
281 |
+
print(cl_data._data_layer)
|
282 |
+
self.chain = self.llm_tutor.qa_bot(
|
283 |
+
memory=memory,
|
284 |
+
callbacks=[cl.LangchainCallbackHandler()] if cl_data._data_layer else None,
|
285 |
+
)
|
286 |
+
self.question_generator = self.llm_tutor.question_generator
|
287 |
+
print(self.question_generator)
|
288 |
cl.user_session.set("llm_tutor", self.llm_tutor)
|
289 |
cl.user_session.set("chain", self.chain)
|
290 |
|
291 |
+
print("Time taken to start LLM: ", time.time() - start_time)
|
292 |
+
|
293 |
async def stream_response(self, response):
|
294 |
"""
|
295 |
Stream the response from the LLM.
|
|
|
320 |
message: The incoming chat message.
|
321 |
"""
|
322 |
|
323 |
+
start_time = time.time()
|
324 |
+
|
325 |
chain = cl.user_session.get("chain")
|
326 |
|
327 |
print("\n\n\n")
|
|
|
356 |
res = chain.stream(user_query=user_query_dict, config=chain_config)
|
357 |
res = await self.stream_response(res)
|
358 |
else:
|
359 |
+
res = await chain.invoke(
|
360 |
+
user_query=user_query_dict,
|
361 |
+
config=chain_config,
|
362 |
+
)
|
363 |
|
364 |
answer = res.get("answer", res.get("result"))
|
365 |
|
366 |
+
if cl_data._data_layer is not None:
|
367 |
+
with cl_data._data_layer.client.step(
|
368 |
+
type="run",
|
369 |
+
name="step_info",
|
370 |
+
thread_id=cl.context.session.thread_id,
|
371 |
+
# tags=self.tags,
|
372 |
+
) as step:
|
373 |
+
|
374 |
+
step.input = {"question": user_query_dict["input"]}
|
375 |
+
|
376 |
+
step.output = {
|
377 |
+
"chat_history": res.get("chat_history"),
|
378 |
+
"context": res.get("context"),
|
379 |
+
"answer": answer,
|
380 |
+
"rephrase_prompt": res.get("rephrase_prompt"),
|
381 |
+
"qa_prompt": res.get("qa_prompt"),
|
382 |
+
}
|
383 |
+
step.metadata = self.config
|
384 |
|
385 |
answer_with_sources, source_elements, sources_dict = get_sources(
|
386 |
res, answer, stream=stream, view_sources=view_sources
|
387 |
)
|
388 |
|
389 |
+
print("Time taken to process the message: ", time.time() - start_time)
|
390 |
+
|
391 |
+
list_of_questions = self.question_generator.generate_questions(
|
392 |
+
query=user_query_dict["input"],
|
393 |
+
response=answer,
|
394 |
+
chat_history=res.get("chat_history"),
|
395 |
+
context=res.get("context"),
|
396 |
+
)
|
397 |
+
|
398 |
+
print("\n\n\n")
|
399 |
+
print("Questions: ", list_of_questions)
|
400 |
+
print("\n\n\n")
|
401 |
+
|
402 |
+
actions = []
|
403 |
+
for question in list_of_questions:
|
404 |
+
|
405 |
+
actions.append(
|
406 |
+
cl.Action(
|
407 |
+
name="follow up question",
|
408 |
+
value="example_value",
|
409 |
+
description=question,
|
410 |
+
label=question,
|
411 |
+
)
|
412 |
+
)
|
413 |
+
|
414 |
await cl.Message(
|
415 |
+
content=answer_with_sources,
|
416 |
+
elements=source_elements,
|
417 |
+
author=LLM,
|
418 |
+
actions=actions,
|
419 |
).send()
|
420 |
|
421 |
async def on_chat_resume(self, thread: ThreadDict):
|
422 |
steps = thread["steps"]
|
|
|
|
|
|
|
|
|
423 |
k = self.config["llm_params"]["memory_window"]
|
424 |
+
conversation_list = get_history_chat_resume(steps, k, SYSTEM, LLM)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
425 |
|
426 |
print("\n\n\n")
|
427 |
print("history at on_chat_resume", conversation_list)
|
|
|
439 |
) -> Optional[cl.User]:
|
440 |
return default_user
|
441 |
|
442 |
+
async def on_action(self, action: cl.Action):
|
443 |
+
print("Action Callback")
|
444 |
+
print(action)
|
445 |
+
# main(message=action.description)\
|
446 |
+
message = await cl.Message(content=action.description, author=YOU).send()
|
447 |
+
await self.main(message)
|
448 |
+
|
449 |
+
|
450 |
+
chatbot = Chatbot(config=config)
|
451 |
+
|
452 |
+
|
453 |
+
async def start():
|
454 |
+
print("Setting up data layer...")
|
455 |
+
cl_data._data_layer = await setup_data_layer()
|
456 |
+
print("Data layer set up.")
|
457 |
+
print(cl_data._data_layer)
|
458 |
+
chatbot.literal_client = cl_data._data_layer.client if cl_data._data_layer else None
|
459 |
+
cl.set_starters(chatbot.set_starters)
|
460 |
+
cl.author_rename(chatbot.rename)
|
461 |
+
cl.on_chat_start(chatbot.start)
|
462 |
+
cl.on_chat_resume(chatbot.on_chat_resume)
|
463 |
+
cl.on_message(chatbot.main)
|
464 |
+
cl.on_settings_update(chatbot.update_llm)
|
465 |
+
cl.action_callback("follow up question")(chatbot.on_action)
|
466 |
+
|
467 |
|
468 |
+
asyncio.run(start())
|
|
|
|
|
|
|
|
|
|
|
|
code/modules/chat/helpers.py
CHANGED
@@ -116,3 +116,58 @@ def get_prompt(config, prompt_type):
|
|
116 |
return prompts["openai"]["prompt_no_history"]
|
117 |
elif prompt_type == "rephrase":
|
118 |
return prompts["openai"]["rephrase_prompt"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
116 |
return prompts["openai"]["prompt_no_history"]
|
117 |
elif prompt_type == "rephrase":
|
118 |
return prompts["openai"]["rephrase_prompt"]
|
119 |
+
|
120 |
+
|
121 |
+
def get_history_chat_resume(steps, k, SYSTEM, LLM):
|
122 |
+
conversation_list = []
|
123 |
+
count = 0
|
124 |
+
for step in reversed(steps):
|
125 |
+
print(step["type"])
|
126 |
+
if step["name"] not in [SYSTEM]:
|
127 |
+
if step["type"] == "user_message":
|
128 |
+
conversation_list.append(
|
129 |
+
{"type": "user_message", "content": step["output"]}
|
130 |
+
)
|
131 |
+
elif step["type"] == "assistant_message":
|
132 |
+
if step["name"] == LLM:
|
133 |
+
conversation_list.append(
|
134 |
+
{"type": "ai_message", "content": step["output"]}
|
135 |
+
)
|
136 |
+
else:
|
137 |
+
raise ValueError("Invalid message type")
|
138 |
+
count += 1
|
139 |
+
if count >= 2 * k: # 2 * k to account for both user and assistant messages
|
140 |
+
break
|
141 |
+
conversation_list = conversation_list[::-1]
|
142 |
+
return conversation_list
|
143 |
+
|
144 |
+
|
145 |
+
def get_history_setup_llm(memory_list):
|
146 |
+
conversation_list = []
|
147 |
+
for message in memory_list:
|
148 |
+
message_dict = message.to_dict() if hasattr(message, "to_dict") else message
|
149 |
+
|
150 |
+
# Check if the type attribute is present as a key or attribute
|
151 |
+
message_type = (
|
152 |
+
message_dict.get("type", None)
|
153 |
+
if isinstance(message_dict, dict)
|
154 |
+
else getattr(message, "type", None)
|
155 |
+
)
|
156 |
+
|
157 |
+
# Check if content is present as a key or attribute
|
158 |
+
message_content = (
|
159 |
+
message_dict.get("content", None)
|
160 |
+
if isinstance(message_dict, dict)
|
161 |
+
else getattr(message, "content", None)
|
162 |
+
)
|
163 |
+
|
164 |
+
if message_type in ["ai", "ai_message"]:
|
165 |
+
conversation_list.append({"type": "ai_message", "content": message_content})
|
166 |
+
elif message_type in ["human", "user_message"]:
|
167 |
+
conversation_list.append(
|
168 |
+
{"type": "user_message", "content": message_content}
|
169 |
+
)
|
170 |
+
else:
|
171 |
+
raise ValueError("Invalid message type")
|
172 |
+
|
173 |
+
return conversation_list
|
code/modules/chat/langchain/langchain_rag.py
CHANGED
@@ -9,11 +9,21 @@ from langchain.memory import (
|
|
9 |
ConversationSummaryBufferMemory,
|
10 |
)
|
11 |
|
|
|
|
|
|
|
12 |
|
13 |
class Langchain_RAG_V1(BaseRAG):
|
14 |
|
15 |
def __init__(
|
16 |
-
self,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
):
|
18 |
"""
|
19 |
Initialize the Langchain_RAG class.
|
@@ -77,9 +87,29 @@ class Langchain_RAG_V1(BaseRAG):
|
|
77 |
return res
|
78 |
|
79 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
class Langchain_RAG_V2(BaseRAG):
|
81 |
def __init__(
|
82 |
-
self,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
83 |
):
|
84 |
"""
|
85 |
Initialize the Langchain_RAG class.
|
@@ -171,6 +201,9 @@ class Langchain_RAG_V2(BaseRAG):
|
|
171 |
],
|
172 |
)
|
173 |
|
|
|
|
|
|
|
174 |
def get_session_history(
|
175 |
self, user_id: str, conversation_id: str, memory_window: int
|
176 |
) -> BaseChatMessageHistory:
|
@@ -192,7 +225,7 @@ class Langchain_RAG_V2(BaseRAG):
|
|
192 |
) # add previous messages to the store. Note: the store is in-memory.
|
193 |
return self.store[(user_id, conversation_id)]
|
194 |
|
195 |
-
async def invoke(self, user_query, config):
|
196 |
"""
|
197 |
Invoke the chain.
|
198 |
|
@@ -202,7 +235,7 @@ class Langchain_RAG_V2(BaseRAG):
|
|
202 |
Returns:
|
203 |
dict: The output variables.
|
204 |
"""
|
205 |
-
res = await self.rag_chain.ainvoke(user_query, config)
|
206 |
res["rephrase_prompt"] = self.rephrase_prompt
|
207 |
res["qa_prompt"] = self.qa_prompt
|
208 |
return res
|
|
|
9 |
ConversationSummaryBufferMemory,
|
10 |
)
|
11 |
|
12 |
+
import chainlit as cl
|
13 |
+
from langchain_community.chat_models import ChatOpenAI
|
14 |
+
|
15 |
|
16 |
class Langchain_RAG_V1(BaseRAG):
|
17 |
|
18 |
def __init__(
|
19 |
+
self,
|
20 |
+
llm,
|
21 |
+
memory,
|
22 |
+
retriever,
|
23 |
+
qa_prompt: str,
|
24 |
+
rephrase_prompt: str,
|
25 |
+
config: dict,
|
26 |
+
callbacks=None,
|
27 |
):
|
28 |
"""
|
29 |
Initialize the Langchain_RAG class.
|
|
|
87 |
return res
|
88 |
|
89 |
|
90 |
+
class QuestionGenerator:
|
91 |
+
"""
|
92 |
+
Generate a question from the LLMs response and users input and past conversations.
|
93 |
+
"""
|
94 |
+
|
95 |
+
def __init__(self):
|
96 |
+
pass
|
97 |
+
|
98 |
+
def generate_questions(self, query, response, chat_history, context):
|
99 |
+
questions = return_questions(query, response, chat_history, context)
|
100 |
+
return questions
|
101 |
+
|
102 |
+
|
103 |
class Langchain_RAG_V2(BaseRAG):
|
104 |
def __init__(
|
105 |
+
self,
|
106 |
+
llm,
|
107 |
+
memory,
|
108 |
+
retriever,
|
109 |
+
qa_prompt: str,
|
110 |
+
rephrase_prompt: str,
|
111 |
+
config: dict,
|
112 |
+
callbacks=None,
|
113 |
):
|
114 |
"""
|
115 |
Initialize the Langchain_RAG class.
|
|
|
201 |
],
|
202 |
)
|
203 |
|
204 |
+
if callbacks is not None:
|
205 |
+
self.rag_chain = self.rag_chain.with_config(callbacks=callbacks)
|
206 |
+
|
207 |
def get_session_history(
|
208 |
self, user_id: str, conversation_id: str, memory_window: int
|
209 |
) -> BaseChatMessageHistory:
|
|
|
225 |
) # add previous messages to the store. Note: the store is in-memory.
|
226 |
return self.store[(user_id, conversation_id)]
|
227 |
|
228 |
+
async def invoke(self, user_query, config, **kwargs):
|
229 |
"""
|
230 |
Invoke the chain.
|
231 |
|
|
|
235 |
Returns:
|
236 |
dict: The output variables.
|
237 |
"""
|
238 |
+
res = await self.rag_chain.ainvoke(user_query, config, **kwargs)
|
239 |
res["rephrase_prompt"] = self.rephrase_prompt
|
240 |
res["qa_prompt"] = self.qa_prompt
|
241 |
return res
|
code/modules/chat/langchain/utils.py
CHANGED
@@ -311,3 +311,39 @@ def create_retrieval_chain(
|
|
311 |
).with_config(run_name="retrieval_chain")
|
312 |
|
313 |
return retrieval_chain
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
311 |
).with_config(run_name="retrieval_chain")
|
312 |
|
313 |
return retrieval_chain
|
314 |
+
|
315 |
+
|
316 |
+
def return_questions(query, response, chat_history_str, context):
|
317 |
+
|
318 |
+
system = (
|
319 |
+
"You are someone that suggests a question based on the student's input and chat history. "
|
320 |
+
"Generate a question that is relevant to the student's input and chat history. "
|
321 |
+
"Incorporate relevant details from the chat history to make the question clearer and more specific. "
|
322 |
+
"Chat history: \n{chat_history_str}\n"
|
323 |
+
"Use the context to generate a question that is relevant to the student's input and chat history: Context: {context}"
|
324 |
+
"Generate 3 short and concise questions from the students voice based on the following input and response: "
|
325 |
+
"The 3 short and concise questions should be sperated by dots. Example: 'What is the capital of France?...What is the population of France?...What is the currency of France?'"
|
326 |
+
"User Query: {query}"
|
327 |
+
"AI Response: {response}"
|
328 |
+
"The 3 short and concise questions seperated by dots (...) are:"
|
329 |
+
)
|
330 |
+
|
331 |
+
prompt = ChatPromptTemplate.from_messages(
|
332 |
+
[
|
333 |
+
("system", system),
|
334 |
+
("human", "{chat_history_str}, {context}, {query}, {response}"),
|
335 |
+
]
|
336 |
+
)
|
337 |
+
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
|
338 |
+
question_generator = prompt | llm | StrOutputParser()
|
339 |
+
new_questions = question_generator.invoke(
|
340 |
+
{
|
341 |
+
"chat_history_str": chat_history_str,
|
342 |
+
"context": context,
|
343 |
+
"query": query,
|
344 |
+
"response": response,
|
345 |
+
}
|
346 |
+
)
|
347 |
+
|
348 |
+
list_of_questions = new_questions.split("...")
|
349 |
+
return list_of_questions
|
code/modules/chat/llm_tutor.py
CHANGED
@@ -2,7 +2,11 @@ from modules.chat.helpers import get_prompt
|
|
2 |
from modules.chat.chat_model_loader import ChatModelLoader
|
3 |
from modules.vectorstore.store_manager import VectorStoreManager
|
4 |
from modules.retriever.retriever import Retriever
|
5 |
-
from modules.chat.langchain.langchain_rag import
|
|
|
|
|
|
|
|
|
6 |
|
7 |
|
8 |
class LLMTutor:
|
@@ -86,7 +90,9 @@ class LLMTutor:
|
|
86 |
compare_dicts(old_config, new_config)
|
87 |
return changes
|
88 |
|
89 |
-
def retrieval_qa_chain(
|
|
|
|
|
90 |
"""
|
91 |
Create a Retrieval QA Chain.
|
92 |
|
@@ -110,7 +116,10 @@ class LLMTutor:
|
|
110 |
qa_prompt=qa_prompt,
|
111 |
rephrase_prompt=rephrase_prompt,
|
112 |
config=self.config,
|
|
|
113 |
)
|
|
|
|
|
114 |
else:
|
115 |
raise ValueError(
|
116 |
f"Invalid LLM Architecture: {self.config['llm_params']['llm_arch']}"
|
@@ -128,7 +137,7 @@ class LLMTutor:
|
|
128 |
llm = chat_model_loader.load_chat_model()
|
129 |
return llm
|
130 |
|
131 |
-
def qa_bot(self, memory=None):
|
132 |
"""
|
133 |
Create a QA bot instance.
|
134 |
|
@@ -147,7 +156,12 @@ class LLMTutor:
|
|
147 |
)
|
148 |
|
149 |
qa = self.retrieval_qa_chain(
|
150 |
-
self.llm,
|
|
|
|
|
|
|
|
|
|
|
151 |
)
|
152 |
|
153 |
return qa
|
|
|
2 |
from modules.chat.chat_model_loader import ChatModelLoader
|
3 |
from modules.vectorstore.store_manager import VectorStoreManager
|
4 |
from modules.retriever.retriever import Retriever
|
5 |
+
from modules.chat.langchain.langchain_rag import (
|
6 |
+
Langchain_RAG_V1,
|
7 |
+
Langchain_RAG_V2,
|
8 |
+
QuestionGenerator,
|
9 |
+
)
|
10 |
|
11 |
|
12 |
class LLMTutor:
|
|
|
90 |
compare_dicts(old_config, new_config)
|
91 |
return changes
|
92 |
|
93 |
+
def retrieval_qa_chain(
|
94 |
+
self, llm, qa_prompt, rephrase_prompt, db, memory=None, callbacks=None
|
95 |
+
):
|
96 |
"""
|
97 |
Create a Retrieval QA Chain.
|
98 |
|
|
|
116 |
qa_prompt=qa_prompt,
|
117 |
rephrase_prompt=rephrase_prompt,
|
118 |
config=self.config,
|
119 |
+
callbacks=callbacks,
|
120 |
)
|
121 |
+
|
122 |
+
self.question_generator = QuestionGenerator()
|
123 |
else:
|
124 |
raise ValueError(
|
125 |
f"Invalid LLM Architecture: {self.config['llm_params']['llm_arch']}"
|
|
|
137 |
llm = chat_model_loader.load_chat_model()
|
138 |
return llm
|
139 |
|
140 |
+
def qa_bot(self, memory=None, callbacks=None):
|
141 |
"""
|
142 |
Create a QA bot instance.
|
143 |
|
|
|
156 |
)
|
157 |
|
158 |
qa = self.retrieval_qa_chain(
|
159 |
+
self.llm,
|
160 |
+
self.qa_prompt,
|
161 |
+
self.rephrase_prompt,
|
162 |
+
self.vector_db,
|
163 |
+
memory,
|
164 |
+
callbacks=callbacks,
|
165 |
)
|
166 |
|
167 |
return qa
|
code/modules/config/config.yml
CHANGED
@@ -28,7 +28,7 @@ llm_params:
|
|
28 |
llm_arch: 'langchain' # [langchain, langgraph_agentic]
|
29 |
use_history: True # bool
|
30 |
memory_window: 3 # int
|
31 |
-
llm_style: 'Normal' # str [Normal, ELI5
|
32 |
llm_loader: 'gpt-4o-mini' # str [local_llm, gpt-3.5-turbo-1106, gpt-4, gpt-4o-mini]
|
33 |
openai_params:
|
34 |
temperature: 0.7 # float
|
@@ -39,6 +39,7 @@ llm_params:
|
|
39 |
chat_logging:
|
40 |
log_chat: False # bool
|
41 |
platform: 'literalai'
|
|
|
42 |
|
43 |
splitter_options:
|
44 |
use_splitter: True # bool
|
|
|
28 |
llm_arch: 'langchain' # [langchain, langgraph_agentic]
|
29 |
use_history: True # bool
|
30 |
memory_window: 3 # int
|
31 |
+
llm_style: 'Normal' # str [Normal, ELI5]
|
32 |
llm_loader: 'gpt-4o-mini' # str [local_llm, gpt-3.5-turbo-1106, gpt-4, gpt-4o-mini]
|
33 |
openai_params:
|
34 |
temperature: 0.7 # float
|
|
|
39 |
chat_logging:
|
40 |
log_chat: False # bool
|
41 |
platform: 'literalai'
|
42 |
+
callbacks: True # bool
|
43 |
|
44 |
splitter_options:
|
45 |
use_splitter: True # bool
|
code/modules/config/prompts.py
CHANGED
@@ -24,31 +24,28 @@ prompts = {
|
|
24 |
"AI Tutor:"
|
25 |
),
|
26 |
"eli5": (
|
27 |
-
"You are an AI Tutor for the course DS598, taught by Prof. Thomas Gardos. Answer the user's question
|
28 |
-
"If you don't know the answer, do your best without making things up. Keep the conversation
|
29 |
-
"Use chat history and context as guides but avoid repeating past responses. Provide links from the source_file metadata. Use the source context that is most relevant.
|
30 |
-
"Speak in a friendly and engaging manner, like talking to
|
|
|
31 |
"Chat History:\n{chat_history}\n\n"
|
32 |
"Context:\n{context}\n\n"
|
33 |
-
"Answer the student's question below in a friendly, simple, and engaging manner. Use the context and history only if relevant, otherwise, engage in a free-flowing conversation
|
34 |
-
"Give a
|
35 |
-
"
|
36 |
"AI Tutor:"
|
37 |
),
|
38 |
"socratic": (
|
39 |
-
"You are an AI Tutor for the course DS598, taught by Prof. Thomas Gardos.
|
40 |
-
"
|
41 |
-
"
|
42 |
-
"
|
43 |
-
"
|
44 |
-
"3. Provide additional explanations or context if necessary to move the conversation forward."
|
45 |
-
"4. End with an open-ended question that invites further exploration."
|
46 |
-
"Based on the chat history determine which guideline to follow., and answer accordingly\n\n"
|
47 |
-
"If the student is stuck, offer gentle hints or break down the concept into simpler parts. Maintain a friendly, engaging tone throughout the conversation.\n\n"
|
48 |
-
"Use chat history and context as guides, but avoid repeating past responses. Provide links from the source_file metadata when appropriate. Use the most relevant source context.\n\n"
|
49 |
"Chat History:\n{chat_history}\n\n"
|
50 |
"Context:\n{context}\n\n"
|
51 |
-
"
|
|
|
52 |
"Student: {input}\n"
|
53 |
"AI Tutor:"
|
54 |
),
|
|
|
24 |
"AI Tutor:"
|
25 |
),
|
26 |
"eli5": (
|
27 |
+
"You are an AI Tutor for the course DS598, taught by Prof. Thomas Gardos. Answer the user's question in the simplest way possible, like explaining to someone new to the topic. Use the provided context to help clarify your explanation."
|
28 |
+
"If you don't know the answer, do your best without making things up. Keep the conversation straightforward and easy to follow."
|
29 |
+
"Use chat history and context as guides but avoid repeating past responses. Provide links from the source_file metadata when relevant. Use the source context that is most relevant."
|
30 |
+
"Speak in a friendly and engaging manner, like talking to someone who is curious and eager to learn. Avoid using complex terms and jargon."
|
31 |
+
"Use examples wherever possible to aid in understanding."
|
32 |
"Chat History:\n{chat_history}\n\n"
|
33 |
"Context:\n{context}\n\n"
|
34 |
+
"Answer the student's question below in a friendly, simple, and engaging manner. Use the context and history only if relevant, otherwise, engage in a free-flowing conversation."
|
35 |
+
"Give a clear and detailed explanation with examples to make it easier to understand."
|
36 |
+
"Student: {input}\n"
|
37 |
"AI Tutor:"
|
38 |
),
|
39 |
"socratic": (
|
40 |
+
"You are an AI Tutor for the course DS598, taught by Prof. Thomas Gardos. Engage the student in a Socratic dialogue to help them discover answers on their own. Use the provided context to guide your questioning."
|
41 |
+
"If you don't know the answer, do your best without making things up. Keep the conversation engaging and inquisitive."
|
42 |
+
"Use chat history and context as guides but avoid repeating past responses. Provide links from the source_file metadata when relevant. Use the source context that is most relevant."
|
43 |
+
"Speak in a friendly and engaging manner, encouraging critical thinking and self-discovery."
|
44 |
+
"Use questions to lead the student to explore the topic and uncover answers."
|
|
|
|
|
|
|
|
|
|
|
45 |
"Chat History:\n{chat_history}\n\n"
|
46 |
"Context:\n{context}\n\n"
|
47 |
+
"Answer the student's question below by guiding them through a series of questions and insights that lead to deeper understanding. Use the context and history only if relevant, otherwise, engage in a free-flowing conversation."
|
48 |
+
"Foster an inquisitive mindset and help the student discover answers through dialogue."
|
49 |
"Student: {input}\n"
|
50 |
"AI Tutor:"
|
51 |
),
|