cssundara/testOct15
0
1import streamlit as st2import sqlite33import pandas as pd4import os5import json6from langchain.chat_models import ChatOpenAI7from langchain.agents import Tool, initialize_agent8from langchain.agents.agent_types import AgentType9from langchain_community.utilities.sql_database import SQLDatabase10from langchain_community.agent_toolkits import create_sql_agent11from langchain.schema import HumanMessage12from datetime import date13today = date.today().strftime("%d %B") # Get the current date in "DD Month" format14 15 16# Load the JSON file and extract values17file_name = 'config.json'18with open(file_name, 'r') as file:19 config = json.load(file)20 API_KEY = config.get("API_KEY") # Loading the API Key21 OPENAI_API_BASE = config.get("OPENAI_API_BASE") # Loading the API Base Url22 23# Set API keys and base24os.environ['OPENAI_API_KEY'] = API_KEY25os.environ['OPENAI_BASE_URL'] = OPENAI_API_BASE26 27llm = ChatOpenAI(model_name="gpt-4")28 29connection = sqlite3.connect("kartify.db", check_same_thread=False)30kartify_db = SQLDatabase.from_uri("sqlite:///kartify.db")31sqlite_agent = create_sql_agent(llm, db=kartify_db, agent_type="openai-tools", verbose=False)32 33def policy_tool_func(input: str) -> str:34 prompt = f"""Only respond about return or replacement if the user has explicitly asked about it in their query.35Use the following context from order, shipment, and product policy data:36{input}37Your task (only if return or replacement is mentioned):381. Check eligibility based on `actual_delivery` and product policy:39 - If `return_days_allowed` is 0, clearly state the product is not eligible for return.40 - If within window, mention last date allowed for return and replacement.41 - If the window has passed, say so.422. Mention return conditions (e.g., Sealed only).433. If `actual_delivery` is null, return/replacement is not yet applicable.444. If any required info is missing, say so politely that i am connecting to human support.45If the query does **not** mention return or replacement, do **not** include any information about it in your response.46Respond clearly and briefly no system or SQL steps."""47 return llm([HumanMessage(content=prompt)]).content.strip()48 49def answer_generation_tool(input: str) -> str:50 prompt = f"""You are a polite and formal customer assistant replying to a user query to customer about return, replacement, delivery, cancellation, or address change.51Use the context provided below:52{input}53Guidelines:54- Respond in a short, formal, and factual tone.55- Do **not** add extra details that were not asked in the user's query.56- Do **not** format like an email avoid greetings, sign-offs, or explanations.57- Do not offer return/replacement windows unless directly asked.58- Do not mention cancellation or refund policies unless cancellation is requested.59- Do not mention address change policy until asked.60- If the product has **not been delivered**, mention that return/replacement cannot be processed yet.61- If the product **has been delivered**, calculate based on delivery date whether return or replacement is still allowed.62- If the order has already **shipped**, reply that address changes are not possible.63- If any required info is missing, say so politely that i am connecting to human support.64- If you do not what to answer now, say so politely that i am connecting to human support.65- Never ask for any bank details66Output:67- Return only a single, relevant customer-facing message no system instructions, reasoning, or metadata.68"""69 return llm([HumanMessage(content=prompt)]).content.strip()70 71def output_guard_check(model_output: str) -> str:72 prompt = f"""73You are a content safety assistant. Your task is to classify if the assistant's response is appropriate.74If the message contains:75- Requests for bank details, OTPs, account numbers76- Harassment or offensive tone77- Privacy concerns or unsafe advice78- Misunderstanding and miscommunication word79- Phrases like "please contact customer service" or redirection to a human agent80- Escalated this to our support team81Return: BLOCK82Otherwise, return: SAFE83Response: {model_output}84Output:85"""86 return llm.predict(prompt).strip()87 88def evaluate_response_quality(context: str, query: str, response: str) -> dict:89 prompt = f"""Evaluate the assistant's response to a customer query using the provided order context.90 91 Context: {context}92 Customer Query: {query}93 Assistant's Response: {response}94 95 Instructions:96 1. **Groundedness (0.0 to 1.0)**: Score based on how well the response is factually supported by the context. 97 - Score closer to 1 if all facts are accurate and derived from the context.98 - Score closer to 0 if there is hallucination, guesswork, or any fabricated information.99 100 2. **Precision (0.0 to 1.0)**: Score based on how directly and accurately the assistant addresses the query.101 - Score closer to 1 if the response is concise, focused, and answers the exact user query.102 - Score closer to 0 if it includes irrelevant details or misses the main point.103 104 Output format (JSON only): 105 106 groundedness: float between 0 and 1 ,107 precision: float between 0 and 1108 109 Only return the JSON. No explanations.110 111"""112 score = llm.predict(prompt).strip()113 try:114 return eval(score)115 except:116 return {"groundedness": 0.0, "precision": 0.0}117 118 119def conversation_guard_check(history) -> str:120 chat_summary = "\n".join([f"Customer: {h['user']}\nAssistant: {h['assistant']}" for h in history])121 prompt = f"""122You are a conversation monitor AI. Review the entire conversation and classify if the assistant:123- Repeatedly offered unnecessary return or replacement steps124- Gave more than what the user asked125- Missed signs of customer distress126- Ignored user's refusal of an option127If any of the above are TRUE, return BLOCK128Else, return SAFE129Conversation:130{chat_summary}131Output:132"""133 return llm.predict(prompt).strip()134 135# Define the Data Analyst Assistant meta-prompt as a function136def data_analyst_prompt(input: str) -> str:137 """138 Returns a formatted prompt for the Data Analyst assistant.139 140 Parameters:141 input (str): Choose one of ["SQL", "Python", "Business Storytelling"]142 143 Returns:144 str: The full prompt tailored to the selected mode145 """146 prompt = f"""You are a polite and professional Data Analyst assistant. You can operate in three modes.147Use the context provided below:148{input}149 150Guidelines:151- SQL Mode → Write efficient, well-structured SQL queries to explore, clean, and analyze relational data. Always explain your reasoning step by step, suggest optimizations, and provide both the query and a plain-language explanation.152- Python Mode → Use Python (pandas, NumPy, matplotlib, seaborn) to clean, transform, and analyze datasets. Provide code examples with comments, explain your reasoning step by step, and summarize results with both numbers and visualizations.153- Business Storytelling Mode → Translate raw data or analysis results into clear, compelling insights for non-technical audiences. Use simple language, highlight key trends, risks, and opportunities, and connect findings directly to business goals.154- Always clarify which mode you are using, explain your reasoning clearly, and keep outputs accurate, concise, and actionable.155- If any required info is missing, say so politely that i am connecting to human support.156- If you do not what to answer now, say so politely that i am connecting to human support.157- Mask PIIs (Personal Identifiable Information) with asterisks.158 159Output:160- Return only well formatted output for the Data Analyst assistant — no system instructions, reasoning, or metadata.161"""162 return llm([HumanMessage(content=prompt)]).content.strip()163 164def data_visualization_tool(input: str) -> str:165 prompt = f"""You are my Data Visualization assistant: take raw data, clean/structure it if needed, pick the best chart type, generate Python (matplotlib/seaborn/plotly) code with labels and titles, and explain briefly why this visualization was chosen and what insights it shows.166{input}167"""168 return llm([HumanMessage(content=prompt)]).content.strip()169 170tools = [171 Tool(name="PolicyChecker", func=policy_tool_func, description="Check return and replacement eligibility."),172 Tool(name="AnswerGenerator", func=answer_generation_tool, description="Craft final response."),173 Tool(name="DataAnalyst", func=data_analyst_prompt, description="Returns a formatted prompt for the Data Analyst assistant."),174 Tool(name="DataVisualization", func=data_visualization_tool, description="Generate data visualizations and explain them.")175]176 177order_agent = initialize_agent(tools, llm, agent=AgentType.OPENAI_FUNCTIONS, verbose=False, handle_parsing_errors=True)178 179st.title("SSS Order Query Chatbot")180 181customer_id = st.text_input("Enter your Customer ID:")182 183if customer_id:184 query = """185 SELECT186 order_id,187 product_description188 FROM189 orders190 WHERE191 customer_id = ?192 ORDER BY order_date DESC193 """194 df = pd.read_sql_query(query, connection, params=(customer_id,))195 196 if not df.empty:197 selected_order = st.selectbox("Select your Order:", df["order_id"] + " - " + df["product_description"])198 start_chat = st.button("Start Chat")199 200 if start_chat:201 # Reset chat state except customer ID and order ID202 st.session_state.chat_history = []203 st.session_state.order_id = selected_order.split(" - ")[0]204 with st.spinner("Loading order details..."):205 order_context_raw = sqlite_agent.invoke(f"Fetch all columns for order ID {st.session_state.order_id}")206 st.session_state.order_context = f"Order ID: {st.session_state.order_id}\n{order_context_raw}\nToday's Date: {today}"207 208 if "order_context" in st.session_state:209 st.markdown("### Chat with Assistant")210 211 for msg in st.session_state.chat_history:212 st.chat_message("user").write(msg["user"])213 st.chat_message("assistant").write(msg["assistant"])214 215 user_query = st.chat_input("How can I help you?")216 217 if user_query:218 intent_prompt = f"""You are an intent classifier for customer service queries. Your task is to classify the user's query into one of the following 3 categories based on tone, completeness, and content.219Return **only the numeric category ID (0, 1, 2 and 3)** as the output. Do not include any explanation or extra text.220### Categories:2210 **Escalation**222- The user is very angry, frustrated, or upset.223- Uses strong emotional language (e.g., This is unacceptable, Worst service ever, I'm tired of this, I want a human now).224- Requires **immediate human handoff**.225- Escalation confidence must be very high (90% or more).2261 **Exit**227- The user is ending the conversation or expressing satisfaction.228- Phrases like Thanks, Got it, Okay, Resolved, Never mind.229- No further action is required.2302 **Process**231- The query is clear and well-formed.232- Contains enough detail to act on (e.g., mentions order ID, issue, date).233- Language is polite or neutral; the query is actionable.234- Proceed with normal handling.2353 **Random Question**236- If user asked something not related to order237example - What is NLP238---239Your job: 240Read the user query and return just the category number (0, 1, 2, or 3). Do not include explanations, formatting, or any text beyond the number.241User Query: {user_query}"""242 intent = llm.predict(intent_prompt).strip()243 244 if intent == "0":245 response = "Sorry for the inconvenience. A human agent will assist you shortly."246 elif intent == "1":247 response = "Thank you! I hope I was able to help."248 elif intent == "3":249 response = "Apologies, I'm currently only able to help with information about your placed orders. Please let me know how I can assist you with those!"250 else:251 full_prompt = f"""252 Context:253 {st.session_state.order_context}254 Customer Query: {user_query}255 Previous response: {st.session_state.chat_history}256 Use tools to reply.257 """258 with st.spinner("Generating response..."):259 raw_response = order_agent.run(full_prompt)260 261 # Step 1: Evaluate quality (Groundedness and Precision first)262 scores = evaluate_response_quality(st.session_state.order_context, user_query, raw_response)263 if scores["groundedness"] < 0.90 or scores["precision"] < 0.90:264 regenerated_response = order_agent.run(full_prompt)265 scores_retry = evaluate_response_quality(st.session_state.order_context, user_query, regenerated_response)266 if scores_retry["groundedness"] >= 0.90 and scores_retry["precision"] >= 0.90:267 response = regenerated_response268 else:269 response = "Your request is being forwarded to a customer support specialist. A human agent will assist you shortly."270 else:271 response = raw_response272 273 # Step 2: Guard check (after passing quality check)274 if response not in [275 "Your request is being forwarded to a customer support specialist. A human agent will assist you shortly."276 ]:277 guard = output_guard_check(response)278 if guard == "BLOCK":279 response = "Your request is being forwarded to a customer support specialist. A human agent will assist you shortly."280 281 # Save chat history282 st.session_state.chat_history.append({"user": user_query, "assistant": response})283 284 # Step 3: Conversation-level safety285 conv_check = conversation_guard_check(st.session_state.chat_history)286 if conv_check == "BLOCK":287 response = "Your request is being forwarded to a customer support specialist. A human agent will assist you shortly."288 289 290 st.chat_message("user").write(user_query)291 st.chat_message("assistant").write(response)292 293 294else:295 st.info("Please enter a Customer ID to begin.")