deepali1021 commited on
Commit
b092604
·
1 Parent(s): c1fe6d9

Added files

Browse files
Chatbot.py CHANGED
@@ -1,20 +1,30 @@
1
  from dotenv import load_dotenv
2
  import streamlit as st
3
- from utils._admin_util import invoke_rag, get_ticket_category
4
  import os
 
 
5
 
6
  # Initialize categories in session state
7
  if "categories" not in st.session_state:
8
  st.session_state.categories = {
9
- "HR Support": [],
10
- "IT Support": [],
11
- "Transportation Support": [],
12
  "Other": []
13
  }
14
 
15
  def main():
16
  load_dotenv()
17
 
 
 
 
 
 
 
 
 
 
18
  # Page configuration
19
  st.set_page_config(
20
  page_title="Intelligent Customer Support Agent",
@@ -35,11 +45,6 @@ def main():
35
  - Other policies
36
  """)
37
 
38
- # Set OpenAI API key
39
- openai_api_key = os.getenv("OPENAI_API_KEY")
40
- if not openai_api_key:
41
- st.error("OpenAI API key not found! Please check your .env file.")
42
- st.stop()
43
 
44
  # Main chat interface
45
  st.title("🤖 Intelligent Customer Support Agent")
@@ -55,15 +60,15 @@ def main():
55
  if "vector_store" not in st.session_state:
56
  st.error("Please load the document data first!")
57
  st.stop()
 
58
 
59
- response = invoke_rag(st.session_state.vector_store, prompt)
60
- st.write(response)
61
-
62
  #Button to create a ticket with respective department
63
  button = st.button("Submit ticket?")
64
 
65
  if button:
66
- category = get_ticket_category(prompt)
67
  st.session_state.categories[category].append(prompt)
68
  st.success("Ticket submitted successfully!")
69
  # Display category (optional)
 
1
  from dotenv import load_dotenv
2
  import streamlit as st
 
3
  import os
4
+ from utils._graph_util import run_customer_support
5
+
6
 
7
  # Initialize categories in session state
8
  if "categories" not in st.session_state:
9
  st.session_state.categories = {
10
+ "HR": [],
11
+ "IT": [],
12
+ "Transportation": [],
13
  "Other": []
14
  }
15
 
16
  def main():
17
  load_dotenv()
18
 
19
+ # Add detailed API key verification
20
+ api_key = os.getenv("OPENAI_API_KEY")
21
+ if not api_key:
22
+ st.error("❌ OpenAI API key not found! Please ensure it's set in the environment variables.")
23
+ st.info("To set up your API key:")
24
+ st.code("1. Go to Hugging Face Space settings\n2. Add OPENAI_API_KEY in Repository Secrets")
25
+ st.stop()
26
+
27
+
28
  # Page configuration
29
  st.set_page_config(
30
  page_title="Intelligent Customer Support Agent",
 
45
  - Other policies
46
  """)
47
 
 
 
 
 
 
48
 
49
  # Main chat interface
50
  st.title("🤖 Intelligent Customer Support Agent")
 
60
  if "vector_store" not in st.session_state:
61
  st.error("Please load the document data first!")
62
  st.stop()
63
+
64
 
65
+ response = run_customer_support(prompt)
66
+ st.write(response.get("response"))
 
67
  #Button to create a ticket with respective department
68
  button = st.button("Submit ticket?")
69
 
70
  if button:
71
+ category = response.get("category")
72
  st.session_state.categories[category].append(prompt)
73
  st.success("Ticket submitted successfully!")
74
  # Display category (optional)
pages/Load_Documents.py CHANGED
@@ -4,22 +4,6 @@ from utils._admin_util import create_embeddings, create_vector_store, read_pdf_d
4
  import streamlit as st
5
  from dotenv import load_dotenv
6
 
7
- def validate_api_key(api_key):
8
- """Test if the API key is valid"""
9
- try:
10
- # Make a small test request to OpenAI
11
- client = openai.OpenAI(api_key=api_key)
12
- client.embeddings.create(input="test", model="text-embedding-ada-002")
13
- return True
14
- except openai.AuthenticationError:
15
- st.error("❌ Invalid API key")
16
- return False
17
- except openai.PermissionDeniedError:
18
- st.error("❌ Permission denied. Please check your API key's permissions")
19
- return False
20
- except Exception as e:
21
- st.error(f"❌ API key validation error: {str(e)}")
22
- return False
23
 
24
  def main():
25
  load_dotenv()
@@ -32,10 +16,6 @@ def main():
32
  st.code("1. Go to Hugging Face Space settings\n2. Add OPENAI_API_KEY in Repository Secrets")
33
  st.stop()
34
 
35
- # Validate the API key
36
- # if not validate_api_key(api_key):
37
- # st.stop()
38
-
39
 
40
  st.set_page_config(page_title="Dump PDFs to QDrant - Vector Store")
41
  st.title("Please upload your files...📁 ")
 
4
  import streamlit as st
5
  from dotenv import load_dotenv
6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
  def main():
9
  load_dotenv()
 
16
  st.code("1. Go to Hugging Face Space settings\n2. Add OPENAI_API_KEY in Repository Secrets")
17
  st.stop()
18
 
 
 
 
 
19
 
20
  st.set_page_config(page_title="Dump PDFs to QDrant - Vector Store")
21
  st.title("Please upload your files...📁 ")
pages/Pending_tickets.py CHANGED
@@ -9,18 +9,18 @@ tabs = st.tabs(tab_titles)
9
  # Add content to each tab...
10
  with tabs[0]:
11
  st.header('HR Support tickets')
12
- for ticket in st.session_state.categories["HR Support"]:
13
- st.write(str( st.session_state.categories["HR Support"].index(ticket)+1)+" : "+ticket)
14
 
15
  with tabs[1]:
16
  st.header('IT Support tickets')
17
- for ticket in st.session_state.categories['IT Support']:
18
- st.write(str(st.session_state.categories['IT Support'].index(ticket)+1)+" : "+ticket)
19
 
20
  with tabs[2]:
21
  st.header('Transportation Support tickets')
22
- for ticket in st.session_state.categories['Transportation Support']:
23
- st.write(str(st.session_state.categories['Transportation Support'].index(ticket)+1)+" : "+ticket)
24
 
25
  with tabs[3]:
26
  st.header('Other tickets')
 
9
  # Add content to each tab...
10
  with tabs[0]:
11
  st.header('HR Support tickets')
12
+ for ticket in st.session_state.categories["HR"]:
13
+ st.write(str( st.session_state.categories["HR"].index(ticket)+1)+" : "+ticket)
14
 
15
  with tabs[1]:
16
  st.header('IT Support tickets')
17
+ for ticket in st.session_state.categories['IT']:
18
+ st.write(str(st.session_state.categories['IT'].index(ticket)+1)+" : "+ticket)
19
 
20
  with tabs[2]:
21
  st.header('Transportation Support tickets')
22
+ for ticket in st.session_state.categories['Transportation']:
23
+ st.write(str(st.session_state.categories['Transportation'].index(ticket)+1)+" : "+ticket)
24
 
25
  with tabs[3]:
26
  st.header('Other tickets')
pyproject.toml CHANGED
@@ -3,17 +3,16 @@ name = "midterm-streamlit"
3
  version = "0.1.0"
4
  description = "intelligent customer support chat"
5
  readme = "README.md"
6
- requires-python = ">=3.12"
7
  dependencies = [
8
- "pydantic==2.10.1",
9
  "langchain-core==0.3.31",
10
- "langchain==0.3.15",
11
- "langchain-community==0.3.15",
12
- "langchain-openai==0.3.1",
13
- "langchain-qdrant==0.2.0",
14
- "qdrant-client==1.13.2",
15
  "tiktoken>=0.8.0",
16
- "pymupdf==1.25.2",
17
  "langgraph>=0.2.67",
18
  "langsmith>=0.3.1",
19
  "openai>=1.58.1",
 
3
  version = "0.1.0"
4
  description = "intelligent customer support chat"
5
  readme = "README.md"
6
+ requires-python = ">=3.13"
7
  dependencies = [
 
8
  "langchain-core==0.3.31",
9
+ "langchain>=0.3.15",
10
+ "langchain-community>=0.3.15",
11
+ "langchain-openai>=0.3.2",
12
+ "langchain-qdrant>=0.2.0",
13
+ "qdrant-client>=1.13.2",
14
  "tiktoken>=0.8.0",
15
+ "pymupdf>=1.25.2",
16
  "langgraph>=0.2.67",
17
  "langsmith>=0.3.1",
18
  "openai>=1.58.1",
requirements.txt CHANGED
@@ -15,4 +15,5 @@ pymupdf
15
  langchain-core>=0.1.0
16
  qdrant-client>=1.7.0
17
  langchain-qdrant>=0.1.0
18
- httpx>=0.27.2
 
 
15
  langchain-core>=0.1.0
16
  qdrant-client>=1.7.0
17
  langchain-qdrant>=0.1.0
18
+ httpx>=0.27.2
19
+ langgraph>=0.2.67
utils/_admin_util.py CHANGED
@@ -11,31 +11,27 @@ from langchain_core.output_parsers import StrOutputParser
11
  from langchain_openai import ChatOpenAI
12
  from langchain_core.prompts import ChatPromptTemplate
13
  import streamlit as st
 
 
 
14
 
15
 
16
 
17
  HUMAN_TEMPLATE = """
18
- #CONTEXT:
19
- {context}
20
 
21
- QUERY:
 
 
 
 
22
  {query}
23
 
24
- Use the provide context to answer the provided user query. Only use the provided context to answer the query. If you do not know the answer, or it's not contained in the provided context response with "I don't know"
 
25
  """
26
 
27
- # Define the system prompt for categorization
28
- CATEGORY_PROMPT = """You are a ticket categorization system. Categorize the following query into exactly one of these categories:
29
- - HR Support: For queries about employment, benefits, leaves, workplace policies, etc.
30
- - IT Support: For queries about software, hardware, network, system access, etc.
31
- - Transportation Support: For queries about company transport, parking, vehicle maintenance, etc.
32
- - Other: For queries that do not fit into the above categories.
33
- Respond with ONLY the category name, nothing else.
34
-
35
- Query: {query}
36
- """
37
-
38
  def check_api_key():
 
39
  """Verify that the API key is set and valid"""
40
  api_key = os.getenv("OPENAI_API_KEY")
41
  if not api_key:
@@ -66,10 +62,8 @@ def tiktoken_len(text):
66
  def split_data(text):
67
  try:
68
  text_splitter = RecursiveCharacterTextSplitter(
69
- chunk_size=500, # Increased for better context
70
- chunk_overlap=50, # Added overlap for better continuity
71
- length_function=tiktoken_len,
72
- separators=["\n\n", "\n", " ", ""]
73
  )
74
  chunks = text_splitter.split_text(text)
75
  if not chunks:
@@ -85,6 +79,7 @@ def create_embeddings():
85
  api_key = check_api_key()
86
  embedding_model = OpenAIEmbeddings(
87
  model="text-embedding-3-small",
 
88
  )
89
  return embedding_model
90
  except Exception as e:
@@ -123,21 +118,24 @@ def create_vector_store(embedding_model, chunks):
123
  raise Exception(f"Error in vector store creation: {str(e)}")
124
 
125
  # create RAG
126
- def create_rag(vector_store):
127
  try:
128
  api_key = check_api_key()
129
  openai_chat_model = ChatOpenAI(
130
- model="gpt-3.5-turbo",
131
- openai_api_key=api_key,
132
- temperature=0.7
133
  )
134
 
135
  chat_prompt = ChatPromptTemplate.from_messages([
136
  ("system", "You are a helpful assistant that answers questions based on the provided context."),
137
  ("human", HUMAN_TEMPLATE)
138
  ])
 
 
 
 
139
 
140
- retriever = vector_store.as_retriever(search_kwargs={"k": 3})
141
 
142
  simple_rag = (
143
  {"context": retriever, "query": RunnablePassthrough()}
@@ -151,37 +149,14 @@ def create_rag(vector_store):
151
  raise Exception(f"Error creating RAG chain: {str(e)}")
152
 
153
  # Invoke RAG
154
- def invoke_rag(vector_store, query):
155
  try:
156
- rag_chain = create_rag(vector_store)
157
  response = rag_chain.invoke(query)
158
  return response
159
  except Exception as e:
160
  raise Exception(f"Error invoking RAG chain: {str(e)}")
161
 
162
 
163
- def get_ticket_category(query):
164
- try:
165
- api_key = check_api_key()
166
- client = ChatOpenAI(
167
- model="gpt-3.5-turbo",
168
- openai_api_key=api_key,
169
- temperature=0
170
- )
171
-
172
- prompt = ChatPromptTemplate.from_messages([
173
- ("system", CATEGORY_PROMPT)
174
- ])
175
-
176
- chain = prompt | client | StrOutputParser()
177
- category = chain.invoke({"query": query})
178
-
179
- category = category.strip()
180
- valid_categories = ["HR Support", "IT Support", "Transportation Support", "Other"]
181
-
182
- return category if category in valid_categories else "Other"
183
- except Exception as e:
184
- st.error(f"Error in category classification: {str(e)}")
185
- return "Other" # Fallback category
186
 
187
-
 
11
  from langchain_openai import ChatOpenAI
12
  from langchain_core.prompts import ChatPromptTemplate
13
  import streamlit as st
14
+ from langchain.prompts import ChatPromptTemplate
15
+ from dotenv import load_dotenv
16
+
17
 
18
 
19
 
20
  HUMAN_TEMPLATE = """
 
 
21
 
22
+ You are a helpful assistant who answers questions based on provided context.
23
+ You must only use the provided context, and cannot use your own knowledge.
24
+ If you do not know the answer, or it's not contained in the provided context response with "I don't know"
25
+
26
+ #Question:
27
  {query}
28
 
29
+ #CONTEXT:
30
+ {context}
31
  """
32
 
 
 
 
 
 
 
 
 
 
 
 
33
  def check_api_key():
34
+ load_dotenv()
35
  """Verify that the API key is set and valid"""
36
  api_key = os.getenv("OPENAI_API_KEY")
37
  if not api_key:
 
62
  def split_data(text):
63
  try:
64
  text_splitter = RecursiveCharacterTextSplitter(
65
+ chunk_size=1000, # Increased for better context
66
+ chunk_overlap=200, # Added overlap for better continuity
 
 
67
  )
68
  chunks = text_splitter.split_text(text)
69
  if not chunks:
 
79
  api_key = check_api_key()
80
  embedding_model = OpenAIEmbeddings(
81
  model="text-embedding-3-small",
82
+ openai_api_key=api_key
83
  )
84
  return embedding_model
85
  except Exception as e:
 
118
  raise Exception(f"Error in vector store creation: {str(e)}")
119
 
120
  # create RAG
121
+ def create_rag():
122
  try:
123
  api_key = check_api_key()
124
  openai_chat_model = ChatOpenAI(
125
+ model="gpt-4o-mini",
126
+ openai_api_key=api_key
 
127
  )
128
 
129
  chat_prompt = ChatPromptTemplate.from_messages([
130
  ("system", "You are a helpful assistant that answers questions based on the provided context."),
131
  ("human", HUMAN_TEMPLATE)
132
  ])
133
+ if 'vector_store' in st.session_state:
134
+ vector_store = st.session_state.vector_store
135
+ else:
136
+ raise ValueError("Vector store not found in session state")
137
 
138
+ retriever = vector_store.as_retriever(search_kwargs={"k": 5})
139
 
140
  simple_rag = (
141
  {"context": retriever, "query": RunnablePassthrough()}
 
149
  raise Exception(f"Error creating RAG chain: {str(e)}")
150
 
151
  # Invoke RAG
152
+ def invoke_rag(query):
153
  try:
154
+ rag_chain = create_rag()
155
  response = rag_chain.invoke(query)
156
  return response
157
  except Exception as e:
158
  raise Exception(f"Error invoking RAG chain: {str(e)}")
159
 
160
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
 
162
+
utils/_graph_util.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from turtle import st
2
+ from typing import TypedDict, Dict
3
+ from langgraph.graph import StateGraph, END
4
+ from langchain_core.prompts import ChatPromptTemplate
5
+ from langchain_core.runnables.graph import MermaidDrawMethod
6
+ from IPython.display import display , Image
7
+ from langchain_openai import ChatOpenAI
8
+ import os
9
+ from dotenv import load_dotenv
10
+
11
+ from utils._admin_util import create_rag
12
+
13
+ class State(TypedDict):
14
+ query: str
15
+ category: str
16
+ sentiment: str
17
+ response: str
18
+
19
+ def check_api_key():
20
+ load_dotenv()
21
+ """Verify that the API key is set and valid"""
22
+ api_key = os.getenv("OPENAI_API_KEY")
23
+ print("api_key", api_key)
24
+ if not api_key:
25
+ raise ValueError("OpenAI API key not found in environment variables")
26
+ return api_key
27
+
28
+ api_key = check_api_key()
29
+
30
+ llm = ChatOpenAI(
31
+ model="gpt-3.5-turbo",
32
+ openai_api_key=api_key,
33
+ temperature=0.7
34
+ )
35
+
36
+ def rag(state: State)->State:
37
+ rag_chain = create_rag()
38
+ # Extract just the query string from the state
39
+ query = state["query"]
40
+ print("query", query)
41
+ response = rag_chain.invoke(query) # Pass the string directly, not a dict
42
+ print("response", response)
43
+ return {"response": response}
44
+
45
+ def categorize(state: State) -> State:
46
+ "HR, IT, Transportation"
47
+ prompt = ChatPromptTemplate.from_template(
48
+ "Categorize the following query into one of these categories: "
49
+ "HR, IT, Transportation, Other. Query: {query}"
50
+ )
51
+ chain = prompt | llm
52
+ category = chain.invoke({"query": state["query"]}).content
53
+ return {"category": category}
54
+
55
+ def analyze_sentiment(state: State) -> State:
56
+ prompt = ChatPromptTemplate.from_template(
57
+ "Analyze the sentiment of the following customer query"
58
+ "Response with either 'Position', 'Neutral' , or 'Negative'. Query: {query}"
59
+ )
60
+ chain = prompt | llm
61
+ sentiment = chain.invoke({"query": state["query"]}).content
62
+ return {"sentiment": sentiment}
63
+
64
+
65
+ def handle_hr(state: State)->State:
66
+ prompt = ChatPromptTemplate.from_template(
67
+ "Provide a HR support response to the following query : {query}"
68
+ )
69
+ chain = prompt | llm
70
+ response = chain.invoke({"query": state["query"]}).content
71
+ return {"response": response}
72
+
73
+ def handle_it(state: State)->State:
74
+ prompt = ChatPromptTemplate.from_template(
75
+ "Provide a IT support response to the following query : {query}"
76
+ )
77
+ chain = prompt | llm
78
+ response = chain.invoke({"query": state["query"]}).content
79
+ return {"response": response}
80
+
81
+ def handle_transportation(state: State)->State:
82
+ prompt = ChatPromptTemplate.from_template(
83
+ "Provide a transportation support response to the following query : {query}"
84
+ )
85
+ chain = prompt | llm
86
+ response = chain.invoke({"query": state["query"]}).content
87
+ return {"response": response}
88
+
89
+ def handle_general(state: State)->State:
90
+ prompt = ChatPromptTemplate.from_template(
91
+ "Provide a general support response to the following query : {query}"
92
+ )
93
+ chain = prompt | llm
94
+ response = chain.invoke({"query": state["query"]}).content
95
+ return {"response": response}
96
+
97
+ def escalate(state: State)->State:
98
+ return {"response": "This query has been escalate to a human agent due to its negative sentiment"}
99
+
100
+ def route_query(state: State)->State:
101
+ if state["sentiment"] == "Negative":
102
+ return "escalate"
103
+ elif state["category"] == "HR":
104
+ return "handle_hr"
105
+ elif state["category"] == "IT":
106
+ return "handle_it"
107
+ elif state["category"] == "Transportation":
108
+ return "handle_transportation"
109
+ else:
110
+ return "handle_general"
111
+
112
+ def rout_to_agent(state: State)->State:
113
+ if "i don't know" in state["response"].lower():
114
+ print(state["response"])
115
+ print("return analyze_sentiment")
116
+ return "analyze_sentiment"
117
+ else:
118
+ return "END"
119
+
120
+
121
+ def run_customer_support(query: str)->Dict[str, str]:
122
+ workflow = StateGraph(State)
123
+ workflow.add_node("categorize", categorize)
124
+ workflow.add_node("rag", rag)
125
+ workflow.add_node("analyze_sentiment", analyze_sentiment)
126
+ workflow.add_node("handle_hr", handle_hr)
127
+ workflow.add_node("handle_it", handle_it)
128
+ workflow.add_node("handle_transportation", handle_transportation)
129
+ workflow.add_node("escalate", escalate)
130
+
131
+ workflow.add_edge("categorize", "rag")
132
+ workflow.add_conditional_edges("rag", rout_to_agent, {"analyze_sentiment": "analyze_sentiment", "END": END})
133
+ workflow.add_conditional_edges(
134
+ "analyze_sentiment",
135
+ route_query,
136
+ {
137
+ "handle_hr" : "handle_hr",
138
+ "handle_it" : "handle_it",
139
+ "handle_transportation" : "handle_transportation",
140
+ "escalate": "escalate"
141
+ }
142
+ )
143
+
144
+ workflow.add_edge("handle_hr", END)
145
+ workflow.add_edge("handle_it", END)
146
+ workflow.add_edge("handle_transportation", END)
147
+ workflow.add_edge("escalate", END)
148
+
149
+ workflow.set_entry_point("categorize")
150
+
151
+ app = workflow.compile()
152
+ results = app.invoke({"query": query})
153
+ return {
154
+ "category": results.get('category', ''), # Returns empty string if key missing
155
+ "sentiment": results.get('sentiment', ''),
156
+ "response": results['response']
157
+ }
uv.lock CHANGED
The diff for this file is too large to render. See raw diff