File size: 9,065 Bytes
04ab479
3619850
 
05db081
 
 
04ab479
852a59a
 
3619850
 
 
734bdd7
 
 
 
 
 
 
b032864
 
 
 
86a6fb4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3619850
 
 
 
 
 
 
 
 
 
 
 
70ee470
 
86a6fb4
70ee470
 
4b36a0a
3619850
 
 
 
 
 
734bdd7
 
 
 
 
b032864
 
734bdd7
 
 
 
b032864
3619850
 
 
 
 
 
 
 
 
d5f21dc
 
3619850
d5f21dc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3619850
 
 
 
 
 
 
 
 
 
 
 
04ab479
3619850
04ab479
3619850
 
 
 
 
 
 
 
 
 
 
 
 
734bdd7
3619850
 
 
b032864
 
 
 
 
 
734bdd7
 
 
b032864
734bdd7
 
b032864
 
 
 
 
734bdd7
 
b032864
 
 
3619850
 
 
 
734bdd7
 
3619850
 
 
 
b032864
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
734bdd7
 
 
 
 
 
b032864
734bdd7
 
b032864
 
 
3619850
 
 
b032864
3619850
 
 
 
 
 
 
 
 
04ab479
3619850
 
 
04ab479
3619850
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
import streamlit as st
import os
import re, ast
import sqlite3
import streamlit as st


#from langchain.chat_models import ChatOpenAI
from langchain_openai import ChatOpenAI
from langchain_community.utilities import SQLDatabase
from langchain_core.messages import HumanMessage

# ---- Session State Initialization ----
if "conversation_stage" not in st.session_state:
    st.session_state.conversation_stage = "IDLE"

if "pending_order_id" not in st.session_state:
    st.session_state.pending_order_id = None

if "pending_action" not in st.session_state:
    st.session_state.pending_action = None



DB_PATH = "customer_orders.db"

def initialize_database():
    conn = sqlite3.connect(DB_PATH)
    cursor = conn.cursor()

    cursor.execute("""
    CREATE TABLE IF NOT EXISTS orders (
        order_id INTEGER PRIMARY KEY,
        cust_id TEXT,
        order_time TEXT,
        order_status TEXT,
        payment_status TEXT,
        item_in_order TEXT,
        preparing_eta TEXT,
        prepared_time TEXT,
        delivery_eta TEXT,
        delivery_time TEXT
    )
    """)

    cursor.execute("SELECT COUNT(*) FROM orders")
    count = cursor.fetchone()[0]

    # Populate only once
    if count == 0:
        cursor.executemany("""
        INSERT INTO orders VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        """, [
            (12486, "C1011", "12:00", "preparing food", "COD", "Burger, Fries", "12:15", None, None, None),
            (12487, "C1012", "12:05", "cancelled", "cancelled", "Pizza", None, None, None, None),
            (12488, "C1013", "12:10", "delivered", "completed", "Sandwich, Soda", "12:25", "12:25", "12:55", "13:00"),
            (12489, "C1014", "12:15", "picked up", "COD", "Salad", "12:30", "12:30", "12:45", None),
            (12490, "C1015", "12:20", "delivered", "completed", "Pasta", "12:35", "12:35", "13:05", "13:10"),
			(12491,"C1016","12:25","preparing food","COD","Burger","12:40",None,None,None),
(12492,"C1017","12:30","delivered","completed","Sushi, Salad","12:45","12:45","13:15","13:15"),
(12493,"C1018","12:35","picked up","COD","Steak","12:50","12:50","01:10",None),
(12494,"C1019","12:40","canceled","canceled","Pizza, Garlic Bread",None,None,None,None),
(12495,"C1020","12:45","preparing food","COD","Wrap, Juice","13:00",None,None,None),
(12496,"C1021","12:50","preparing food","COD","Taco, Nachos","13:05",None,None,None),
(12497,"C1022","12:55","delivered","completed","Burrito","13:10","13:10","13:15","13:15"),
(12498,"C1023","12:56","picked up","COD","Pancakes, Coffee","13:11","13:11","13:40",None),
(12499,"C1024","12:57","canceled","canceled","Waffle",None,None,None,None),
(12500,"C1025","12:58","delivered","completed","Omelette, Toast","13:13","13:13","13:15","13:15"),
(12501,"C1026","12:59","preparing food","COD","Burger, Fries, Soda","13:14",None,None,None),
(12502,"C1027","12:00","picked up","COD","Salad, Soup","13:15","13:15","13:45",None),
(12503,"C1028","12:40","delivered","completed","Steak, Pizza","13:15","13:15","13:15","13:15"),
(12504,"C1029","12:35","canceled","canceled","Hotdog",None,None,None,None),
(12505,"C1030","12:50","delivered","completed","Pasta, Garlic Bread","13:10","13:10","13:15","13:15")
        ])
        conn.commit()

    conn.close()

initialize_database()


# ---------------- PAGE CONFIG ----------------
st.set_page_config(
    page_title="FoodHub AI Customer Support",
    page_icon="πŸ”",
    layout="centered"
)

# ---------------- API KEY ----------------
#os.environ["OPENAI_API_KEY"] = "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"
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")

# ---------------- LOAD DB & LLM ----------------
#db = SQLDatabase.from_uri("sqlite:///./customer_orders.db")

DB_PATH = "customer_orders.db"
db = SQLDatabase.from_uri(f"sqlite:///{DB_PATH}")



llm = ChatOpenAI(
    model="gpt-4o-mini",
    temperature=0.2
)

# ---------------- handel Cancel ----------------
def detect_intent(text: str):
    text = text.lower()
    if "cancel" in text:
        return "CANCEL"
    if "add" in text or "update" in text or "extra" in text:
        return "ADD_ITEM"
    return "STATUS"




# ---------------- GUARDRAILS ----------------
def is_malicious_or_invalid(query: str) -> bool:
    blocked_keywords = [
        "hacker", "hack", "all orders",
        "database", "dump", "steal"
    ]
    return any(word in query.lower() for word in blocked_keywords)


import re

def extract_order_id(text: str):
    """
    Extract order ID from flexible user phrases.
    Supports:
    - order_id 1023
    - order id 1023
    - order 1023
    - my order is 1023
    """
    text = text.lower()

    patterns = [
        r"order[_\s]?id\s*(\d+)",
        r"order\s*(\d+)",
        r"order\s*is\s*(\d+)"
    ]

    for pattern in patterns:
        match = re.search(pattern, text)
        if match:
            return match.group(1)

    return None



# ---------------- TOOLS ----------------
def order_query_tool(order_id: str):
    query = f"SELECT * FROM orders WHERE order_id = {order_id}"
    result = db.run(query)

    if not result:
        return None

    if isinstance(result, str):
        result = ast.literal_eval(result)

    return result[0]


def answer_tool(order_data):
    prompt = f"""
    You are a FoodHub customer support assistant.
    Generate a polite and professional response.
    Order Data: {order_data}
    """
    return llm.invoke(prompt).content


# ---------------- CHAT AGENT ----------------
def chatagent(user_query: str):

    # 🚫 Guardrail
    if is_malicious_or_invalid(user_query):
        return "🚫 Unauthorized request. I can only help with your own order."

    # ---------------------------------
    # Handle follow-up confirmation
    # ---------------------------------
    if st.session_state.conversation_stage == "WAITING_FOR_CONFIRMATION":
        order_id = st.session_state.pending_order_id
        action = st.session_state.pending_action

        st.session_state.conversation_stage = "IDLE"
        st.session_state.pending_order_id = None
        st.session_state.pending_action = None

        return (
            f"βœ… Thank you for confirming.\n\n"
            f"Your request regarding **Order #{order_id}** has been "
            f"forwarded to our **human support team**.\n\n"
            f"πŸ“ž A support executive will contact you shortly to assist "
            f"with item updates and payment."
        )

    # ---------------------------------
    # Normal flow
    # ---------------------------------
    order_id = extract_order_id(user_query)
    if not order_id:
        return "ℹ️ Please provide your order ID to proceed."

    intent = detect_intent(user_query)

    order_data = order_query_tool(order_id)
    if not order_data:
        return f"❌ No order found with ID {order_id}."

    order_status = order_data[3].lower()  # order_status column

    # ---------------------------------
    # ADD ITEM FLOW
    # ---------------------------------
    if intent == "ADD_ITEM":
        if "preparing" in order_status:
            st.session_state.conversation_stage = "WAITING_FOR_CONFIRMATION"
            st.session_state.pending_order_id = order_id
            st.session_state.pending_action = "ADD_ITEM"

            return (
                f"πŸ“ Your order **#{order_id}** is currently being prepared.\n\n"
                f"βž• Adding items at this stage may require **additional payment**.\n\n"
                f"❓ Please confirm if you want to proceed. "
                f"Reply with **Yes** to connect with a human support agent."
            )
        else:
            return (
                f"⚠️ Item modification is not possible because "
                f"your order **#{order_id}** is already **{order_status}**."
            )

    # ---------------------------------
    # CANCEL FLOW (already implemented)
    # ---------------------------------
    if intent == "CANCEL":
        st.session_state.conversation_stage = "WAITING_FOR_REASON"
        st.session_state.pending_order_id = order_id

        return (
            f"⚠️ You have requested to cancel **Order #{order_id}**.\n\n"
            f"❓ Please tell us the reason for cancellation."
        )

    # ---------------------------------
    # STATUS FLOW
    # ---------------------------------
    return answer_tool(order_data)



# ---------------- UI ----------------
st.title("πŸ” FoodHub AI Customer Support Chatbot")

st.markdown(
    """
Ask questions about your **order status, delivery ETA, or cancellation**.
**Example queries:**
- Where is my order with order_id 1023?
- I want to cancel my order with order_id 1023
"""
)

user_query = st.text_input("πŸ’¬ Enter your question here")

if st.button("Ask"):
    if user_query.strip():
        with st.spinner("Fetching your order details..."):
            response = chatagent(user_query)
        st.success(response)
    else:
        st.warning("Please enter a question.")