Spaces:
Sleeping
Sleeping
Commit
·
3c3c588
1
Parent(s):
a831891
customer service - completed
Browse files- .gitignore +1 -0
- src/langgraphagenticai/graph/graph_builder.py +6 -0
- src/langgraphagenticai/node/customer_support_chatbot.py +80 -0
- src/langgraphagenticai/tools/customer_support_tools.py +184 -0
- src/langgraphagenticai/ui/streamlitui/display_result.py +24 -1
- src/langgraphagenticai/ui/streamlitui/loadui.py +21 -3
- src/langgraphagenticai/ui/{configfile.ini → uiconfigfile.ini} +0 -0
- src/langgraphagenticai/ui/{configfile.py → uiconfigfile.py} +1 -1
- src/langgraphagenticai/vectorstores/vectore_store.py +122 -0
.gitignore
CHANGED
|
@@ -1 +1,2 @@
|
|
| 1 |
*.pyc
|
|
|
|
|
|
| 1 |
*.pyc
|
| 2 |
+
/.chroma_db
|
src/langgraphagenticai/graph/graph_builder.py
CHANGED
|
@@ -3,6 +3,7 @@ from langgraph.prebuilt import tools_condition,ToolNode
|
|
| 3 |
from langchain_core.prompts import ChatPromptTemplate
|
| 4 |
import datetime
|
| 5 |
#module import
|
|
|
|
| 6 |
from src.langgraphagenticai.tools.customtool import book_appointment, cancel_appointment, get_next_available_appointment
|
| 7 |
from src.langgraphagenticai.tools.search_tool import create_tool_node, get_tools
|
| 8 |
from src.langgraphagenticai.node.chatbot_with_tool_node import ChatbotWithToolNode
|
|
@@ -109,6 +110,9 @@ class GraphBuilder:
|
|
| 109 |
# Set Entry Point and build the graph
|
| 110 |
self.graph_builder.set_entry_point("agent")
|
| 111 |
|
|
|
|
|
|
|
|
|
|
| 112 |
|
| 113 |
def setup_graph(self, usecase: str):
|
| 114 |
"""
|
|
@@ -120,6 +124,8 @@ class GraphBuilder:
|
|
| 120 |
self.chatbot_with_tool_build_graph()
|
| 121 |
elif usecase == "Appointment Receptionist":
|
| 122 |
self.appointment_receptionist_bot_build_graph()
|
|
|
|
|
|
|
| 123 |
else:
|
| 124 |
raise ValueError("Invalid use case selected.")
|
| 125 |
return self.graph_builder.compile()
|
|
|
|
| 3 |
from langchain_core.prompts import ChatPromptTemplate
|
| 4 |
import datetime
|
| 5 |
#module import
|
| 6 |
+
from src.langgraphagenticai.node.customer_support_chatbot import Customer_Support_Bot
|
| 7 |
from src.langgraphagenticai.tools.customtool import book_appointment, cancel_appointment, get_next_available_appointment
|
| 8 |
from src.langgraphagenticai.tools.search_tool import create_tool_node, get_tools
|
| 9 |
from src.langgraphagenticai.node.chatbot_with_tool_node import ChatbotWithToolNode
|
|
|
|
| 110 |
# Set Entry Point and build the graph
|
| 111 |
self.graph_builder.set_entry_point("agent")
|
| 112 |
|
| 113 |
+
def customer_support_build_graph(self):
|
| 114 |
+
obj_cs_bot = Customer_Support_Bot(llm=self.llm)
|
| 115 |
+
self.graph_builder = obj_cs_bot.chat_bot()
|
| 116 |
|
| 117 |
def setup_graph(self, usecase: str):
|
| 118 |
"""
|
|
|
|
| 124 |
self.chatbot_with_tool_build_graph()
|
| 125 |
elif usecase == "Appointment Receptionist":
|
| 126 |
self.appointment_receptionist_bot_build_graph()
|
| 127 |
+
elif usecase =="Customer Support":
|
| 128 |
+
self.customer_support_build_graph()
|
| 129 |
else:
|
| 130 |
raise ValueError("Invalid use case selected.")
|
| 131 |
return self.graph_builder.compile()
|
src/langgraphagenticai/node/customer_support_chatbot.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from langgraph.graph import StateGraph, MessagesState
|
| 2 |
+
from langchain_core.prompts import ChatPromptTemplate
|
| 3 |
+
from langgraph.prebuilt import ToolNode
|
| 4 |
+
from src.langgraphagenticai.tools.customer_support_tools import query_knowledge_base, search_for_product_reccommendations, data_protection_check, create_new_customer, place_order, retrieve_existing_customer_orders
|
| 5 |
+
|
| 6 |
+
import os
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class Customer_Support_Bot:
|
| 10 |
+
def __init__(self,llm):
|
| 11 |
+
self.llm = llm
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def chat_bot(self):
|
| 15 |
+
|
| 16 |
+
prompt = """#Purpose
|
| 17 |
+
|
| 18 |
+
You are a customer service chatbot for a flower shop company. You can help the customer achieve the goals listed below.
|
| 19 |
+
|
| 20 |
+
#Goals
|
| 21 |
+
|
| 22 |
+
1. Answer questions the user might have relating to serivces offered
|
| 23 |
+
2. Recommend products to the user based on their preferences
|
| 24 |
+
3. Help the customer check on an existing order, or place a new order
|
| 25 |
+
4. To place and manage orders, you will need a customer profile (with an associated id). If the customer already has a profile, perform a data protection check to retrieve their details. If not, create them a profile.
|
| 26 |
+
|
| 27 |
+
#Tone
|
| 28 |
+
|
| 29 |
+
Helpful and friendly. Use gen-z emojis to keep things lighthearted. You MUST always include a funny flower related pun in every response."""
|
| 30 |
+
|
| 31 |
+
chat_template = ChatPromptTemplate.from_messages(
|
| 32 |
+
[
|
| 33 |
+
('system', prompt),
|
| 34 |
+
('placeholder', "{messages}")
|
| 35 |
+
]
|
| 36 |
+
)
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
tools = [query_knowledge_base, search_for_product_reccommendations, data_protection_check, create_new_customer, place_order, retrieve_existing_customer_orders]
|
| 41 |
+
|
| 42 |
+
llm = self.llm
|
| 43 |
+
|
| 44 |
+
llm_with_prompt = chat_template | llm.bind_tools(tools)
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def call_agent(message_state: MessagesState):
|
| 48 |
+
|
| 49 |
+
response = llm_with_prompt.invoke(message_state)
|
| 50 |
+
|
| 51 |
+
return {
|
| 52 |
+
'messages': [response]
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
def is_there_tool_calls(state: MessagesState):
|
| 56 |
+
last_message = state['messages'][-1]
|
| 57 |
+
if last_message.tool_calls:
|
| 58 |
+
return 'tool_node'
|
| 59 |
+
else:
|
| 60 |
+
return '__end__'
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
graph = StateGraph(MessagesState)
|
| 64 |
+
|
| 65 |
+
tool_node = ToolNode(tools)
|
| 66 |
+
|
| 67 |
+
graph.add_node('agent', call_agent)
|
| 68 |
+
graph.add_node('tool_node', tool_node)
|
| 69 |
+
|
| 70 |
+
graph.add_conditional_edges(
|
| 71 |
+
"agent",
|
| 72 |
+
is_there_tool_calls
|
| 73 |
+
)
|
| 74 |
+
graph.add_edge('tool_node', 'agent')
|
| 75 |
+
|
| 76 |
+
graph.set_entry_point('agent')
|
| 77 |
+
|
| 78 |
+
# app = graph.compile()
|
| 79 |
+
return graph
|
| 80 |
+
|
src/langgraphagenticai/tools/customer_support_tools.py
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from langchain_core.tools import tool
|
| 2 |
+
from typing import List, Dict
|
| 3 |
+
import json
|
| 4 |
+
|
| 5 |
+
from src.langgraphagenticai.vectorstores.vectore_store import FlowerShopVectorStore
|
| 6 |
+
|
| 7 |
+
vector_store = FlowerShopVectorStore()
|
| 8 |
+
INVENTORY_FILE_PATH = './data/inventory.json'
|
| 9 |
+
|
| 10 |
+
customers_database = [
|
| 11 |
+
{"name": "John Doe", "postcode": "SW1A 1AA", "dob": "1990-01-01", "customer_id": "CUST001", "first_line_address": "123 Main St", "phone_number": "07712345678", "email": "john.doe@example.com"},
|
| 12 |
+
{"name": "Jane Smith", "postcode": "E1 6AN", "dob": "1985-05-15", "customer_id": "CUST002", "first_line_address": "456 High St", "phone_number": "07723456789", "email": "jane.smith@example.com"},
|
| 13 |
+
]
|
| 14 |
+
|
| 15 |
+
orders_database = [
|
| 16 |
+
{"order_id": "ORD001", "customer_id": "CUST001", "status": "Processing", "items": ["Red Roses Bouquet"], "quantity": [1]},
|
| 17 |
+
{"order_id": "ORD002", "customer_id": "CUST002", "status": "Shipped", "items": ["Mixed Tulips", "Vase"], "quantity": [3, 1]},
|
| 18 |
+
]
|
| 19 |
+
|
| 20 |
+
with open(INVENTORY_FILE_PATH, 'r') as f:
|
| 21 |
+
inventory_database = json.load(f)
|
| 22 |
+
|
| 23 |
+
data_protection_checks = []
|
| 24 |
+
|
| 25 |
+
@tool
|
| 26 |
+
def data_protection_check(name: str, postcode: str, year_of_birth: int, month_of_birth: int, day_of_birth: int) -> Dict:
|
| 27 |
+
"""
|
| 28 |
+
Perform a data protection check against a customer to retrieve customer details.
|
| 29 |
+
|
| 30 |
+
Args:
|
| 31 |
+
name (str): Customer first and last name
|
| 32 |
+
postcode (str): Customer registered address
|
| 33 |
+
year_of_birth (int): The year the customer was born
|
| 34 |
+
month_of_birth (int): The month the customer was born
|
| 35 |
+
day_of_birth (int): The day the customer was born
|
| 36 |
+
|
| 37 |
+
Returns:
|
| 38 |
+
Dict: Customer details (name, postcode, dob, customer_id, first_line_address, email)
|
| 39 |
+
"""
|
| 40 |
+
data_protection_checks.append(
|
| 41 |
+
{
|
| 42 |
+
'name': name,
|
| 43 |
+
'postcode': postcode,
|
| 44 |
+
'year_of_birth': year_of_birth,
|
| 45 |
+
'month_of_birth': month_of_birth,
|
| 46 |
+
'day_of_birth': day_of_birth
|
| 47 |
+
}
|
| 48 |
+
)
|
| 49 |
+
for customer in customers_database:
|
| 50 |
+
if (customer['name'].lower() == name.lower() and
|
| 51 |
+
customer['postcode'].lower() == postcode.lower() and
|
| 52 |
+
int(customer['dob'][0:4]) == year_of_birth and
|
| 53 |
+
int(customer["dob"][5:7]) == month_of_birth and
|
| 54 |
+
int(customer["dob"][8:10]) == day_of_birth):
|
| 55 |
+
return f"DPA check passed - Retrieved customer details:\n{customer}"
|
| 56 |
+
|
| 57 |
+
return "DPA check failed, no customer with these details found"
|
| 58 |
+
|
| 59 |
+
@tool
|
| 60 |
+
def create_new_customer(first_name: str, surname: str, year_of_birth: int, month_of_birth: int, day_of_birth: int, postcode: str, first_line_of_address: str, phone_number: str, email: str) -> str:
|
| 61 |
+
"""
|
| 62 |
+
Creates a customer profile, so that they can place orders.
|
| 63 |
+
|
| 64 |
+
Args:
|
| 65 |
+
first_name (str): Customers first name
|
| 66 |
+
surname (str): Customers surname
|
| 67 |
+
year_of_birth (int): Year customer was born
|
| 68 |
+
month_of_birth (int): Month customer was born
|
| 69 |
+
day_of_birth (int): Day customer was born
|
| 70 |
+
postcode (str): Customer's postcode
|
| 71 |
+
first_line_address (str): Customer's first line of address
|
| 72 |
+
phone_number (str): Customer's phone number
|
| 73 |
+
email (str): Customer's email address
|
| 74 |
+
|
| 75 |
+
Returns:
|
| 76 |
+
str: Confirmation that the profile has been created or any issues with the inputs
|
| 77 |
+
"""
|
| 78 |
+
if len(phone_number) != 11:
|
| 79 |
+
return "Phone number must be 11 digits"
|
| 80 |
+
customer_id = len(customers_database) + 1
|
| 81 |
+
customers_database.append({
|
| 82 |
+
'name': first_name + ' ' + surname,
|
| 83 |
+
'dob': f'{year_of_birth}-{month_of_birth:02}-{day_of_birth:02}',
|
| 84 |
+
'postcode': postcode,
|
| 85 |
+
'first_line_address': first_line_of_address,
|
| 86 |
+
'phone_number': phone_number,
|
| 87 |
+
'email': email,
|
| 88 |
+
'customer_id': f'CUST{customer_id}'
|
| 89 |
+
})
|
| 90 |
+
return f"Customer registered, with customer_id {f'CUST{customer_id}'}"
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
@tool
|
| 94 |
+
def query_knowledge_base(query: str) -> List[Dict[str, str]]:
|
| 95 |
+
"""
|
| 96 |
+
Looks up information in a knowledge base to help with answering customer questions and getting information on business processes.
|
| 97 |
+
|
| 98 |
+
Args:
|
| 99 |
+
query (str): Question to ask the knowledge base
|
| 100 |
+
|
| 101 |
+
Return:
|
| 102 |
+
List[Dict[str, str]]: Potentially relevant question and answer pairs from the knowledge base
|
| 103 |
+
"""
|
| 104 |
+
return vector_store.query_faqs(query=query)
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
@tool
|
| 109 |
+
def search_for_product_reccommendations(description: str):
|
| 110 |
+
"""
|
| 111 |
+
Looks up information in a knowledge base to help with product recommendation for customers. For example:
|
| 112 |
+
|
| 113 |
+
"Boquets suitable for birthdays, maybe with red flowers"
|
| 114 |
+
"A large boquet for a wedding"
|
| 115 |
+
"A cheap boquet with wildflowers"
|
| 116 |
+
|
| 117 |
+
Args:
|
| 118 |
+
query (str): Description of product features
|
| 119 |
+
|
| 120 |
+
Return:
|
| 121 |
+
List[Dict[str, str]]: Potentially relevant products
|
| 122 |
+
"""
|
| 123 |
+
return vector_store.query_inventories(query=description)
|
| 124 |
+
|
| 125 |
+
@tool
|
| 126 |
+
def retrieve_existing_customer_orders(customer_id: str) -> List[Dict]:
|
| 127 |
+
"""
|
| 128 |
+
Retrieves the orders associated with the customer, including their status, items and ids
|
| 129 |
+
|
| 130 |
+
Args:
|
| 131 |
+
customer_id (str): Customer unique id associated with the order
|
| 132 |
+
|
| 133 |
+
Returns:
|
| 134 |
+
List[Dict]: All the orders associated with the customer_id passed in
|
| 135 |
+
"""
|
| 136 |
+
customer_orders = [order for order in orders_database if order['customer_id'] == customer_id]
|
| 137 |
+
if not customer_orders:
|
| 138 |
+
return f"No orders associated with this customer id: {customer_id}"
|
| 139 |
+
return customer_orders
|
| 140 |
+
|
| 141 |
+
@tool
|
| 142 |
+
def place_order(items: Dict[str, int], customer_id: str) -> str:
|
| 143 |
+
"""
|
| 144 |
+
Places an order for the requested items, and for the required quantities.
|
| 145 |
+
|
| 146 |
+
Args:
|
| 147 |
+
items (Dict[str, int]): Dictionary of items to order, with item id as the key and the quantity of that item as the value.
|
| 148 |
+
customer_id (str): The customer to place the order for
|
| 149 |
+
|
| 150 |
+
Returns:
|
| 151 |
+
str: Message indicating that the order has been placed, or, it hasnt been placed due to an issue
|
| 152 |
+
"""
|
| 153 |
+
# Check that the item ids are valid
|
| 154 |
+
# Check that the quantities of items are valid
|
| 155 |
+
availability_messages = []
|
| 156 |
+
valid_item_ids = [
|
| 157 |
+
item['id'] for item in inventory_database
|
| 158 |
+
]
|
| 159 |
+
for item_id, quantity in items.items():
|
| 160 |
+
if item_id not in valid_item_ids:
|
| 161 |
+
availability_messages.append(f'Item with id {item_id} is not found in the inventory')
|
| 162 |
+
else:
|
| 163 |
+
inventory_item = [item for item in inventory_database if item['id'] == item_id][0]
|
| 164 |
+
if quantity > inventory_item['quantity']:
|
| 165 |
+
availability_messages.append(f'There is insufficient quantity in the inventory for this item {inventory_item["name"]}\nAvailable: {inventory_item["quantity"]}\nRequested: {quantity}')
|
| 166 |
+
if availability_messages:
|
| 167 |
+
return "Order cannot be placed due to the following issues: \n" + '\n'.join(availability_messages)
|
| 168 |
+
|
| 169 |
+
# Place the order (in pretend database)
|
| 170 |
+
order_id = len(orders_database) + 1
|
| 171 |
+
orders_database.append(
|
| 172 |
+
{
|
| 173 |
+
'order_id': order_id,
|
| 174 |
+
'customer_id': customer_id,
|
| 175 |
+
'status': 'Waiting for payment',
|
| 176 |
+
'items': list(items.keys()),
|
| 177 |
+
'quantity': list(items.values())
|
| 178 |
+
}
|
| 179 |
+
)
|
| 180 |
+
# Update the inventory
|
| 181 |
+
for item_id, quantity in items.items():
|
| 182 |
+
inventory_item = [item for item in inventory_database if item['id'] == item_id][0]
|
| 183 |
+
inventory_item['quantity'] -= quantity
|
| 184 |
+
return f"Order with id {order_id} has been placed successfully"
|
src/langgraphagenticai/ui/streamlitui/display_result.py
CHANGED
|
@@ -2,6 +2,7 @@ import streamlit as st
|
|
| 2 |
from langchain_core.messages import HumanMessage,AIMessage,ToolMessage
|
| 3 |
|
| 4 |
from src.langgraphagenticai.tools.customtool import APPOINTMENTS
|
|
|
|
| 5 |
|
| 6 |
class DisplayResultStreamlit:
|
| 7 |
def __init__(self,usecase,graph,user_message):
|
|
@@ -58,10 +59,32 @@ class DisplayResultStreamlit:
|
|
| 58 |
else:
|
| 59 |
with st.chat_message("assistant"):
|
| 60 |
st.write(message.content)
|
| 61 |
-
|
| 62 |
with col2:
|
| 63 |
st.header("Appointments")
|
| 64 |
st.write(APPOINTMENTS)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
# display graph
|
| 66 |
if graph:
|
| 67 |
st.write('state graph - workflow')
|
|
|
|
| 2 |
from langchain_core.messages import HumanMessage,AIMessage,ToolMessage
|
| 3 |
|
| 4 |
from src.langgraphagenticai.tools.customtool import APPOINTMENTS
|
| 5 |
+
from src.langgraphagenticai.tools.customer_support_tools import customers_database, data_protection_checks
|
| 6 |
|
| 7 |
class DisplayResultStreamlit:
|
| 8 |
def __init__(self,usecase,graph,user_message):
|
|
|
|
| 59 |
else:
|
| 60 |
with st.chat_message("assistant"):
|
| 61 |
st.write(message.content)
|
|
|
|
| 62 |
with col2:
|
| 63 |
st.header("Appointments")
|
| 64 |
st.write(APPOINTMENTS)
|
| 65 |
+
elif usecase == "Customer Support":
|
| 66 |
+
|
| 67 |
+
main_col, right_col = st.columns([2, 1])
|
| 68 |
+
response = graph.invoke({
|
| 69 |
+
'messages': user_message
|
| 70 |
+
})
|
| 71 |
+
with main_col:
|
| 72 |
+
st.session_state.message_history = response['messages']
|
| 73 |
+
for i in range(1, len(st.session_state.message_history) + 1):
|
| 74 |
+
this_message = st.session_state.message_history[-i]
|
| 75 |
+
if isinstance(this_message, AIMessage):
|
| 76 |
+
message_box = st.chat_message('assistant')
|
| 77 |
+
else:
|
| 78 |
+
message_box = st.chat_message('user')
|
| 79 |
+
if this_message.content:
|
| 80 |
+
message_box.markdown(this_message.content)
|
| 81 |
+
# 3. State variables
|
| 82 |
+
with right_col:
|
| 83 |
+
st.title('customers database')
|
| 84 |
+
st.write(customers_database)
|
| 85 |
+
st.title('data protection checks')
|
| 86 |
+
st.write(data_protection_checks)
|
| 87 |
+
|
| 88 |
# display graph
|
| 89 |
if graph:
|
| 90 |
st.write('state graph - workflow')
|
src/langgraphagenticai/ui/streamlitui/loadui.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
| 1 |
import streamlit as st
|
| 2 |
import os
|
| 3 |
|
| 4 |
-
from src.langgraphagenticai.ui.
|
|
|
|
| 5 |
|
| 6 |
|
| 7 |
class LoadStreamlitUI:
|
|
@@ -46,7 +47,24 @@ class LoadStreamlitUI:
|
|
| 46 |
st.subheader("Appointments")
|
| 47 |
|
| 48 |
elif self.user_controls['selected_usecase']=="Customer Support":
|
| 49 |
-
|
| 50 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
|
| 52 |
return self.user_controls
|
|
|
|
| 1 |
import streamlit as st
|
| 2 |
import os
|
| 3 |
|
| 4 |
+
from src.langgraphagenticai.ui.uiconfigfile import Config
|
| 5 |
+
from langchain_core.messages import AIMessage, HumanMessage
|
| 6 |
|
| 7 |
|
| 8 |
class LoadStreamlitUI:
|
|
|
|
| 47 |
st.subheader("Appointments")
|
| 48 |
|
| 49 |
elif self.user_controls['selected_usecase']=="Customer Support":
|
| 50 |
+
st.subheader('Flower Shop Chatbot' + '💐')
|
| 51 |
+
greeting="Hi 🙋🏻♀️, I am the flower shop chatbot. How can I help?"
|
| 52 |
+
|
| 53 |
+
if 'message_history' not in st.session_state:
|
| 54 |
+
st.session_state.message_history = [AIMessage(content=greeting)]
|
| 55 |
+
|
| 56 |
+
with st.chat_message("assistant"):
|
| 57 |
+
st.write(greeting)
|
| 58 |
+
|
| 59 |
+
# 1. Buttons for chat - Clear Button
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
with st.sidebar:
|
| 64 |
+
if st.button('Clear Chat'):
|
| 65 |
+
st.session_state.message_history = []
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
|
| 69 |
|
| 70 |
return self.user_controls
|
src/langgraphagenticai/ui/{configfile.ini → uiconfigfile.ini}
RENAMED
|
File without changes
|
src/langgraphagenticai/ui/{configfile.py → uiconfigfile.py}
RENAMED
|
@@ -2,7 +2,7 @@ from configparser import ConfigParser
|
|
| 2 |
|
| 3 |
|
| 4 |
class Config:
|
| 5 |
-
def __init__(self, config_file="./src/langgraphagenticai/ui/
|
| 6 |
self.config = ConfigParser()
|
| 7 |
self.config.read(config_file)
|
| 8 |
|
|
|
|
| 2 |
|
| 3 |
|
| 4 |
class Config:
|
| 5 |
+
def __init__(self, config_file="./src/langgraphagenticai/ui/uiconfigfile.ini"):
|
| 6 |
self.config = ConfigParser()
|
| 7 |
self.config.read(config_file)
|
| 8 |
|
src/langgraphagenticai/vectorstores/vectore_store.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from chromadb import PersistentClient, EmbeddingFunction, Embeddings
|
| 2 |
+
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
|
| 3 |
+
from typing import List
|
| 4 |
+
import json
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
MODEL_NAME = 'dunzhang/stella_en_1.5B_v5'
|
| 8 |
+
DB_PATH = './.chroma_db'
|
| 9 |
+
FAQ_FILE_PATH= './data/FAQ.json'
|
| 10 |
+
INVENTORY_FILE_PATH = './data/inventory.json'
|
| 11 |
+
|
| 12 |
+
class Product:
|
| 13 |
+
def __init__(self, name: str, id: str, description: str, type: str, price: float, quantity: int):
|
| 14 |
+
self.name = name
|
| 15 |
+
self.id = id
|
| 16 |
+
self.description = description
|
| 17 |
+
self.type = type
|
| 18 |
+
self.price = price
|
| 19 |
+
self.quantity = quantity
|
| 20 |
+
|
| 21 |
+
class QuestionAnswerPairs:
|
| 22 |
+
def __init__(self, question: str, answer: str):
|
| 23 |
+
self.question = question
|
| 24 |
+
self.answer = answer
|
| 25 |
+
|
| 26 |
+
class CustomEmbeddingClass(EmbeddingFunction):
|
| 27 |
+
def __init__(self, model_name):
|
| 28 |
+
self.embedding_model = HuggingFaceEmbedding(model_name=MODEL_NAME)
|
| 29 |
+
|
| 30 |
+
def __call__(self, input_texts: List[str]) -> Embeddings:
|
| 31 |
+
return [self.embedding_model.get_text_embedding(text) for text in input_texts]
|
| 32 |
+
|
| 33 |
+
class FAQCollection:
|
| 34 |
+
def __init__(self):
|
| 35 |
+
self.documents = []
|
| 36 |
+
self.ids = []
|
| 37 |
+
self.metadatas = []
|
| 38 |
+
|
| 39 |
+
def add(self, documents, ids, metadatas):
|
| 40 |
+
self.documents.extend(documents)
|
| 41 |
+
self.ids.extend(ids)
|
| 42 |
+
self.metadatas.extend(metadatas)
|
| 43 |
+
|
| 44 |
+
def display(self):
|
| 45 |
+
for doc, id_, meta in zip(self.documents, self.ids, self.metadatas):
|
| 46 |
+
print(f"ID: {id_}, Document: {doc}, Metadata: {meta}")
|
| 47 |
+
|
| 48 |
+
# Define the InventoryCollection class
|
| 49 |
+
class InventoryCollection:
|
| 50 |
+
def __init__(self):
|
| 51 |
+
self.documents = []
|
| 52 |
+
self.ids = []
|
| 53 |
+
self.metadatas = []
|
| 54 |
+
|
| 55 |
+
def add(self, documents, ids, metadatas):
|
| 56 |
+
self.documents.extend(documents)
|
| 57 |
+
self.ids.extend(ids)
|
| 58 |
+
self.metadatas.extend(metadatas)
|
| 59 |
+
|
| 60 |
+
def display(self):
|
| 61 |
+
for doc, id_, meta in zip(self.documents, self.ids, self.metadatas):
|
| 62 |
+
print(f"ID: {id_}, Document: {doc}, Metadata: {meta}")
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
class FlowerShopVectorStore:
|
| 68 |
+
def __init__(self):
|
| 69 |
+
db = PersistentClient(path=DB_PATH)
|
| 70 |
+
|
| 71 |
+
custom_embedding_function = CustomEmbeddingClass(MODEL_NAME)
|
| 72 |
+
|
| 73 |
+
self.faq_collection = db.get_or_create_collection(name='FAQ', embedding_function=custom_embedding_function)
|
| 74 |
+
self.inventory_collection = db.get_or_create_collection(name='Inventory', embedding_function=custom_embedding_function)
|
| 75 |
+
|
| 76 |
+
if self.faq_collection.count() == 0:
|
| 77 |
+
try :
|
| 78 |
+
self._load_faq_collection(FAQ_FILE_PATH)
|
| 79 |
+
except Exception as e:
|
| 80 |
+
raise ValueError(e)
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
if self.inventory_collection.count() == 0:
|
| 84 |
+
self._load_inventory_collection(INVENTORY_FILE_PATH)
|
| 85 |
+
|
| 86 |
+
def _load_faq_collection(self, faq_file_path: str):
|
| 87 |
+
try:
|
| 88 |
+
|
| 89 |
+
with open(faq_file_path, 'r') as f:
|
| 90 |
+
faqs = json.load(f)
|
| 91 |
+
|
| 92 |
+
# Create an instance of FAQCollection
|
| 93 |
+
obj_faq_collection = FAQCollection()
|
| 94 |
+
|
| 95 |
+
obj_faq_collection.add(
|
| 96 |
+
documents=[faq['question'] for faq in faqs] + [faq['answer'] for faq in faqs],
|
| 97 |
+
ids=[str(i) for i in range(0, 2*len(faqs))],
|
| 98 |
+
metadatas = faqs + faqs
|
| 99 |
+
)
|
| 100 |
+
self.faq_collection = obj_faq_collection
|
| 101 |
+
except Exception as ex:
|
| 102 |
+
raise ValueError(ex)
|
| 103 |
+
|
| 104 |
+
def _load_inventory_collection(self, inventory_file_path: str):
|
| 105 |
+
with open(inventory_file_path, 'r') as f:
|
| 106 |
+
inventories = json.load(f)
|
| 107 |
+
|
| 108 |
+
# Create an instance of InventoryCollection
|
| 109 |
+
obj_inventory_collection = InventoryCollection()
|
| 110 |
+
|
| 111 |
+
obj_inventory_collection.add(
|
| 112 |
+
documents=[inventory['description'] for inventory in inventories],
|
| 113 |
+
ids=[str(i) for i in range(0, len(inventories))],
|
| 114 |
+
metadatas = inventories
|
| 115 |
+
)
|
| 116 |
+
self.inventory_collection = obj_inventory_collection
|
| 117 |
+
|
| 118 |
+
def query_faqs(self, query: str):
|
| 119 |
+
return self.faq_collection.query(query_texts=[query], n_results=5)
|
| 120 |
+
|
| 121 |
+
def query_inventories(self, query: str):
|
| 122 |
+
return self.inventory_collection.query(query_texts=[query], n_results=5)
|