aliMohammad16 commited on
Commit
1fac276
Β·
verified Β·
1 Parent(s): f74f783

Rename app.py to app_streamlit.py

Browse files
Files changed (2) hide show
  1. app.py +0 -89
  2. app_streamlit.py +583 -0
app.py DELETED
@@ -1,89 +0,0 @@
1
- import torch
2
- from transformers import AutoTokenizer, pipeline
3
- from sentence_transformers import SentenceTransformer
4
- import faiss
5
- import numpy as np
6
- import gradio as gr
7
- from typing import List
8
-
9
- # Configuration
10
- class Config:
11
- model_name = "TinyLlama/TinyLlama-1.1B-Chat-v1.0"
12
- embedding_model = "all-MiniLM-L6-v2"
13
- vector_dim = 384 # Sentence Transformer embedding dimension
14
- top_k = 3 # Retrieve top 3 relevant chunks
15
- chunk_size = 256 # Text chunk size
16
-
17
- # Vector Database
18
- class VectorDB:
19
- def __init__(self):
20
- self.index = faiss.IndexFlatL2(Config.vector_dim)
21
- self.texts = []
22
- self.embedding_model = SentenceTransformer(Config.embedding_model)
23
-
24
- def add_text(self, text: str):
25
- embedding = self.embedding_model.encode([text])[0]
26
- embedding = np.array([embedding], dtype=np.float32)
27
- faiss.normalize_L2(embedding)
28
- self.index.add(embedding)
29
- self.texts.append(text)
30
-
31
- def search(self, query: str) -> List[str]:
32
- if self.index.ntotal == 0:
33
- return []
34
- query_embedding = self.embedding_model.encode([query])[0]
35
- query_embedding = np.array([query_embedding], dtype=np.float32)
36
- faiss.normalize_L2(query_embedding)
37
- D, I = self.index.search(query_embedding, min(Config.top_k, self.index.ntotal))
38
- return [self.texts[i] for i in I[0] if i < len(self.texts)]
39
-
40
- # Load Model
41
- class TinyChatModel:
42
- def __init__(self):
43
- self.tokenizer = AutoTokenizer.from_pretrained(Config.model_name)
44
- self.pipe = pipeline("text-generation", model=Config.model_name, torch_dtype=torch.bfloat16, device_map="auto")
45
-
46
- def generate_response(self, message: str, context: str = "") -> str:
47
- messages = [{"role": "user", "content": message}]
48
- if context:
49
- messages.insert(0, {"role": "system", "content": f"Context:\n{context}"})
50
- prompt = self.tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
51
- outputs = self.pipe(prompt, max_new_tokens=256, do_sample=True, temperature=0.7, top_k=50, top_p=0.95)
52
- return outputs[0]["generated_text"].split("<|assistant|>")[-1].strip()
53
-
54
- # Initialize
55
- vector_db = VectorDB()
56
- chat_model = TinyChatModel()
57
-
58
- # Function to handle context addition and chat
59
- def chat_function(user_input: str, context: str = ""):
60
- if context:
61
- vector_db.add_text(context)
62
-
63
- # Search relevant context
64
- context_text = "\n".join(vector_db.search(user_input))
65
- response = chat_model.generate_response(user_input, context_text)
66
- vector_db.add_text(f"User: {user_input}\nAssistant: {response}")
67
-
68
- return response
69
-
70
- # Gradio Interface
71
- def gradio_interface(user_input: str, context: str = ""):
72
- response = chat_function(user_input, context)
73
- return response
74
-
75
- # Create Gradio UI
76
- with gr.Blocks() as demo:
77
- gr.Markdown("# TinyChat: A Conversational AI")
78
- with gr.Row():
79
- with gr.Column():
80
- user_input = gr.Textbox(label="User Input", placeholder="Ask anything...")
81
- context_input = gr.Textbox(label="Optional Context", placeholder="Paste context here (optional)", lines=3)
82
- submit_button = gr.Button("Send")
83
- output = gr.Textbox(label="Response", placeholder="Assistant's reply will appear here...")
84
-
85
- submit_button.click(fn=gradio_interface, inputs=[user_input, context_input], outputs=output)
86
-
87
- # Run the Gradio app
88
- if __name__ == "__main__":
89
- demo.launch(share=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app_streamlit.py ADDED
@@ -0,0 +1,583 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import json
4
+ from openai import OpenAI
5
+ import os
6
+ import uuid
7
+ import time
8
+
9
+ # Sample data - you'll need to create data.py or embed this data
10
+ restaurants_data = [
11
+ {"id": "r001", "name": "Spice Garden", "locality": "Downtown", "cuisine": "Indian", "price_range": "800-1200"},
12
+ {"id": "r002", "name": "Pizza Palace", "locality": "Mall Road", "cuisine": "Italian", "price_range": "400-800"},
13
+ {"id": "r003", "name": "Dragon House", "locality": "City Center", "cuisine": "Chinese", "price_range": "600-1000"},
14
+ {"id": "r004", "name": "Burger Junction", "locality": "Food Street", "cuisine": "American", "price_range": "300-600"},
15
+ {"id": "r005", "name": "Sushi Bar", "locality": "Downtown", "cuisine": "Japanese", "price_range": "1000-1500"},
16
+ ]
17
+
18
+ reservation_data = [
19
+ {"reservation_id": 31005202500001, "restaurant_id": "r001", "user_name": "John Doe", "party_size": 4, "date": "2025-06-15", "time": "19:00", "special_requests": "", "status": "Confirmed"},
20
+ ]
21
+
22
+ # Streamlit UI setup
23
+ st.set_page_config(page_title="foodieSpot", layout="centered")
24
+
25
+ class BookingState:
26
+ def __init__(self):
27
+ self.data = {
28
+ "reservation_id": None,
29
+ "state": None,
30
+ "cuisine_preference": None,
31
+ "location": None,
32
+ "date": None,
33
+ "time": None,
34
+ "party_size": None,
35
+ "special_requests": None,
36
+ "restaurant_id": None,
37
+ "user_name": None
38
+ }
39
+
40
+ def update(self, **kwargs):
41
+ for key, value in kwargs.items():
42
+ if key in self.data:
43
+ self.data[key] = value
44
+ else:
45
+ raise KeyError(f"Invalid key: '{key}' not in booking data.")
46
+ return self.check_state()
47
+
48
+ def check_state(self):
49
+ return {k: v for k, v in self.data.items() if v is not None}
50
+
51
+ def is_complete(self):
52
+ required = [
53
+ "cuisine_preference", "location", "date", "time", "party_size",
54
+ "restaurant_id", "user_name"
55
+ ]
56
+ return all(self.data.get(k) is not None for k in required)
57
+
58
+ def reset(self):
59
+ for key in self.data:
60
+ self.data[key] = None
61
+
62
+ def to_dict(self):
63
+ return self.data.copy()
64
+
65
+ class ReservationManager:
66
+ def __init__(self, restaurants_df, reservation_df):
67
+ self.restaurants_df = restaurants_df
68
+ self.reservations_df = reservation_df
69
+ self.reservation_counter = 31005202500001
70
+
71
+ def _generate_reservation_id(self):
72
+ self.reservation_counter += 1
73
+ return self.reservation_counter
74
+
75
+ def is_valid_booking(self, booking_state):
76
+ required = ["restaurant_id", "user_name", "party_size", "date", "time"]
77
+ return all(booking_state.data.get(k) for k in required)
78
+
79
+ def add_reservation(self, booking_state):
80
+ if not self.is_valid_booking(booking_state):
81
+ missing = [
82
+ k for k in
83
+ ["restaurant_id", "user_name", "party_size", "date", "time"]
84
+ if booking_state.data.get(k) is None
85
+ ]
86
+ return {
87
+ "success": False,
88
+ "message": "Reservation could not be created. Missing fields.",
89
+ "missing_fields": missing
90
+ }
91
+
92
+ reservation_id = self._generate_reservation_id()
93
+
94
+ reservation = {
95
+ "reservation_id": reservation_id,
96
+ "restaurant_id": booking_state.data["restaurant_id"],
97
+ "user_name": booking_state.data["user_name"],
98
+ "party_size": booking_state.data["party_size"],
99
+ "date": booking_state.data["date"],
100
+ "time": booking_state.data["time"],
101
+ "special_requests": booking_state.data.get("special_requests", ""),
102
+ "status": "Confirmed"
103
+ }
104
+
105
+ # Add to DataFrame
106
+ new_row = pd.DataFrame([reservation])
107
+ self.reservations_df = pd.concat([self.reservations_df, new_row], ignore_index=True)
108
+
109
+ return {
110
+ "success": True,
111
+ "message": "Reservation confirmed!",
112
+ "reservation_details": reservation
113
+ }
114
+
115
+ def get_all_reservations(self):
116
+ return self.reservations_df.to_dict(orient="records")
117
+
118
+ class RestaurantQueryEngine:
119
+ def __init__(self, df):
120
+ self.df = df
121
+
122
+ def get_options(self, column_name):
123
+ if column_name in self.df.columns:
124
+ return sorted(self.df[column_name].dropna().unique().tolist())
125
+ return []
126
+
127
+ def filter_by(self, column_name, value):
128
+ result = self.df.copy()
129
+ if column_name in result.columns and value is not None:
130
+ result = result[result[column_name] == value]
131
+ return result[["id", "name", "locality", "cuisine", "price_range"]].to_dict(orient="records")
132
+
133
+ # Initialize OpenAI client
134
+ @st.cache_resource
135
+ def get_openai_client():
136
+ api_key = os.environ.get('OPENAI_API_KEY')
137
+ if not api_key:
138
+ st.error("❌ OPENAI_API_KEY environment variable is required")
139
+ st.info("Please set your OpenAI API key in the Hugging Face Spaces settings")
140
+ st.stop()
141
+ return OpenAI(api_key=api_key)
142
+
143
+ # Initialize session state
144
+ if "messages" not in st.session_state:
145
+ st.session_state.messages = []
146
+
147
+ if "session_id" not in st.session_state:
148
+ st.session_state.session_id = str(uuid.uuid4())
149
+
150
+ if "page" not in st.session_state:
151
+ st.session_state.page = "chat"
152
+
153
+ if "booking_state" not in st.session_state:
154
+ st.session_state.booking_state = BookingState()
155
+
156
+ if "reservation_manager" not in st.session_state:
157
+ restaurants_df = pd.DataFrame(restaurants_data)
158
+ reservations_df = pd.DataFrame(reservation_data)
159
+ st.session_state.reservation_manager = ReservationManager(restaurants_df, reservations_df)
160
+
161
+ if "query_engine" not in st.session_state:
162
+ restaurants_df = pd.DataFrame(restaurants_data)
163
+ st.session_state.query_engine = RestaurantQueryEngine(restaurants_df)
164
+
165
+ if "conversation_history" not in st.session_state:
166
+ st.session_state.conversation_history = []
167
+
168
+ # Tools definition
169
+ tools = [{
170
+ "type": "function",
171
+ "function": {
172
+ "name": "get_column_options",
173
+ "description": "Get unique available values for a column like cuisine, locality, or price_range.",
174
+ "parameters": {
175
+ "type": "object",
176
+ "properties": {
177
+ "column_name": {
178
+ "type": "string",
179
+ "description": "The column to get unique values from. Common options: 'cuisine', 'locality', 'price_range'."
180
+ }
181
+ },
182
+ "required": ["column_name"]
183
+ }
184
+ }
185
+ }, {
186
+ "type": "function",
187
+ "function": {
188
+ "name": "filter_restaurants",
189
+ "description": "Filter the list of restaurants based on a specific attribute like cuisine, location, or price range.",
190
+ "parameters": {
191
+ "type": "object",
192
+ "properties": {
193
+ "column_name": {
194
+ "type": "string",
195
+ "description": "The column to filter by. Common values: 'cuisine', 'locality', 'price_range'."
196
+ },
197
+ "value": {
198
+ "type": "string",
199
+ "description": "The value to match in the specified column."
200
+ }
201
+ },
202
+ "required": ["column_name", "value"]
203
+ }
204
+ }
205
+ }, {
206
+ "type": "function",
207
+ "function": {
208
+ "name": "update_booking_state",
209
+ "description": "Update the booking information with user's reservation details.",
210
+ "parameters": {
211
+ "type": "object",
212
+ "properties": {
213
+ "cuisine_preference": {"type": "string"},
214
+ "location": {"type": "string"},
215
+ "date": {"type": "string", "description": "Date of reservation in YYYY-MM-DD format."},
216
+ "time": {"type": "string", "description": "Time of reservation in HH:MM format."},
217
+ "party_size": {"type": "integer"},
218
+ "special_requests": {"type": "string"},
219
+ "restaurant_id": {"type": "string"},
220
+ "user_name": {"type": "string"}
221
+ },
222
+ "required": []
223
+ }
224
+ }
225
+ }, {
226
+ "type": "function",
227
+ "function": {
228
+ "name": "finalize_booking",
229
+ "description": "Check if all necessary booking information is filled. If complete, return all data.",
230
+ "parameters": {
231
+ "type": "object",
232
+ "properties": {}
233
+ }
234
+ }
235
+ }, {
236
+ "type": "function",
237
+ "function": {
238
+ "name": "make_reservation",
239
+ "description": "Create a confirmed reservation using current booking state and return reservation ID and details.",
240
+ "parameters": {
241
+ "type": "object",
242
+ "properties": {}
243
+ }
244
+ }
245
+ }]
246
+
247
+ SYSTEM_PROMPT = """
248
+ You are a friendly and efficient restaurant reservation assistant.
249
+
250
+ Your role is to help users find and reserve a restaurant based on their preferences like cuisine, location, date, time, and party size. If needed, collect this information in a polite and conversational way.
251
+
252
+ Recommendation and suggestion:
253
+ - ask user politely what they want the suggestions to be based on, location, cuisine, or price_range.
254
+ - when the user gives the value for a suggestion, then show him available restaurants for that value.
255
+ - **DO NOT SHOW MORE THAN 4 OPTIONS AT A TIME**
256
+
257
+ Information Collection:
258
+ - Reservation Details needed to complete a booking: [cuisine_preference, location, date, time, party_size, special_requests, restaurant_id, user_name]
259
+ - **ASK FOR ONE DETAIL ONLY AT A TIME**
260
+
261
+ Once all information is gathered, confirm the booking by calling the `make_reservation` tool. Be proactive in guiding the user. Do not hallucinate values. Rely on tools to fetch available options or complete bookings.
262
+
263
+ Always be warm and polite, like a concierge at a high-end restaurant. Use natural and welcoming phrases like:
264
+ - "Great! Let me note that down."
265
+ - "Could you please tell me…?"
266
+ - "Absolutely, I can help with that."
267
+ """
268
+
269
+ def call_tool(tool_name, args):
270
+ """Direct function calls instead of Flask endpoints"""
271
+ if tool_name == "get_column_options":
272
+ return st.session_state.query_engine.get_options(**args)
273
+ elif tool_name == "update_booking_state":
274
+ return st.session_state.booking_state.update(**args)
275
+ elif tool_name == "make_reservation":
276
+ result = st.session_state.reservation_manager.add_reservation(
277
+ st.session_state.booking_state
278
+ )
279
+ if result['success']:
280
+ st.session_state.booking_state.reset()
281
+ return result
282
+ elif tool_name == "filter_restaurants":
283
+ return st.session_state.query_engine.filter_by(**args)
284
+ elif tool_name == "finalize_booking":
285
+ return st.session_state.booking_state.check_state()
286
+ else:
287
+ return {"error": f"Unknown tool: {tool_name}"}
288
+
289
+ def process_chat_message(message):
290
+ """Process chat message with OpenAI - replaces Flask /chat endpoint"""
291
+ client = get_openai_client()
292
+
293
+ st.session_state.conversation_history.append({
294
+ "role": "user",
295
+ "content": message
296
+ })
297
+
298
+ messages = [{
299
+ "role": "system",
300
+ "content": SYSTEM_PROMPT
301
+ }] + st.session_state.conversation_history
302
+
303
+ continue_processing = True
304
+ final_response = ""
305
+
306
+ while continue_processing:
307
+ response = client.chat.completions.create(
308
+ model="gpt-4o-mini",
309
+ messages=messages,
310
+ tools=tools,
311
+ tool_choice="auto"
312
+ )
313
+
314
+ message_obj = response.choices[0].message
315
+
316
+ if message_obj.content:
317
+ final_response = message_obj.content
318
+ st.session_state.conversation_history.append({
319
+ "role": "assistant",
320
+ "content": message_obj.content
321
+ })
322
+ continue_processing = False
323
+
324
+ if message_obj.tool_calls:
325
+ st.session_state.conversation_history.append({
326
+ "role": "assistant",
327
+ "content": "",
328
+ "tool_calls": message_obj.tool_calls
329
+ })
330
+
331
+ for tool_call in message_obj.tool_calls:
332
+ tool_name = tool_call.function.name
333
+ tool_args = json.loads(tool_call.function.arguments)
334
+ tool_output = call_tool(tool_name, tool_args)
335
+
336
+ st.session_state.conversation_history.append({
337
+ "role": "tool",
338
+ "tool_call_id": tool_call.id,
339
+ "name": tool_name,
340
+ "content": json.dumps(tool_output)
341
+ })
342
+
343
+ messages = [{
344
+ "role": "system",
345
+ "content": SYSTEM_PROMPT
346
+ }] + st.session_state.conversation_history
347
+
348
+ continue_processing = True
349
+
350
+ return final_response
351
+
352
+ # Custom CSS
353
+ st.markdown("""
354
+ <style>
355
+ .backend-button {
356
+ position: fixed;
357
+ top: 20px;
358
+ right: 20px;
359
+ z-index: 999;
360
+ background: linear-gradient(45deg, #ff6b9d, #ff8a9b);
361
+ color: white;
362
+ padding: 10px 20px;
363
+ border: none;
364
+ border-radius: 25px;
365
+ font-weight: bold;
366
+ cursor: pointer;
367
+ box-shadow: 0 4px 12px rgba(255, 107, 157, 0.3);
368
+ transition: all 0.3s ease;
369
+ }
370
+ .backend-button:hover {
371
+ background: linear-gradient(45deg, #ff5588, #ff7799);
372
+ transform: translateY(-2px);
373
+ box-shadow: 0 6px 16px rgba(255, 107, 157, 0.4);
374
+ }
375
+
376
+ .restaurant-tile {
377
+ background: linear-gradient(135deg, #f8f9fa, #e9ecef);
378
+ border-radius: 15px;
379
+ padding: 15px;
380
+ margin: 10px 0;
381
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
382
+ border-left: 4px solid #ff6b9d;
383
+ transition: all 0.3s ease;
384
+ }
385
+
386
+ .restaurant-tile:hover {
387
+ transform: translateY(-2px);
388
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
389
+ }
390
+
391
+ .restaurant-name {
392
+ font-weight: bold;
393
+ color: #333;
394
+ font-size: 16px;
395
+ margin-bottom: 8px;
396
+ }
397
+
398
+ .restaurant-detail {
399
+ color: #666;
400
+ font-size: 14px;
401
+ margin: 4px 0;
402
+ }
403
+
404
+ .restaurant-price {
405
+ color: #ff6b9d;
406
+ font-weight: bold;
407
+ font-size: 14px;
408
+ }
409
+ </style>
410
+ """, unsafe_allow_html=True)
411
+
412
+ # Top navigation
413
+ col1, col2 = st.columns([6, 1])
414
+ with col2:
415
+ if st.button("πŸ”§ Backend", key="backend_btn", help="View reservations dashboard"):
416
+ st.session_state.page = "backend"
417
+ st.rerun()
418
+
419
+ def show_chat_page():
420
+ st.title("πŸ’¬ foodieSpot")
421
+ st.markdown("Restaurant Reservations made easy!")
422
+
423
+ # System ready indicator
424
+ st.success("βœ… System ready")
425
+
426
+ # Display chat history
427
+ for msg in st.session_state.messages:
428
+ with st.chat_message(msg["role"]):
429
+ st.markdown(msg["content"])
430
+
431
+ # Input and send button
432
+ user_input = st.chat_input("Type your message...")
433
+
434
+ if user_input:
435
+ # Handle exit command
436
+ if user_input.lower() in ['exit', 'quit', 'bye']:
437
+ bot_reply = 'Thanks for using foodieSpot! Have a great day! 🍽️'
438
+
439
+ # Save messages
440
+ st.session_state.messages.append({"role": "user", "content": user_input})
441
+ st.session_state.messages.append({"role": "assistant", "content": bot_reply})
442
+
443
+ # Display messages
444
+ with st.chat_message("user"):
445
+ st.markdown(user_input)
446
+ with st.chat_message("assistant"):
447
+ st.markdown(bot_reply)
448
+
449
+ st.stop()
450
+
451
+ # Save user message
452
+ st.session_state.messages.append({"role": "user", "content": user_input})
453
+ with st.chat_message("user"):
454
+ st.markdown(user_input)
455
+
456
+ # Show typing indicator
457
+ with st.chat_message("assistant"):
458
+ message_placeholder = st.empty()
459
+ message_placeholder.markdown("πŸ€” Thinking...")
460
+
461
+ try:
462
+ # Direct function call instead of HTTP request
463
+ bot_reply = process_chat_message(user_input)
464
+ except Exception as e:
465
+ bot_reply = f"❌ An error occurred: {str(e)}"
466
+
467
+ # Update the message placeholder with the actual response
468
+ message_placeholder.markdown(bot_reply)
469
+
470
+ # Save bot message
471
+ st.session_state.messages.append({"role": "assistant", "content": bot_reply})
472
+
473
+ # Sidebar with additional info
474
+ with st.sidebar:
475
+ st.header("ℹ️ App Info")
476
+ st.write("**Session ID:**", st.session_state.session_id[:8] + "...")
477
+ st.write("**Messages:**", len(st.session_state.messages))
478
+
479
+ if st.button("πŸ”„ New Session"):
480
+ st.session_state.messages = []
481
+ st.session_state.conversation_history = []
482
+ st.session_state.booking_state.reset()
483
+ st.session_state.session_id = str(uuid.uuid4())
484
+ st.rerun()
485
+
486
+ if st.button("🧹 Clear Chat"):
487
+ st.session_state.messages = []
488
+ st.session_state.conversation_history = []
489
+ st.rerun()
490
+
491
+ st.header("🍽️ Available Restaurants")
492
+ restaurants = restaurants_data
493
+ for restaurant in restaurants[:8]: # Show only first 8 restaurants
494
+ restaurant_tile = f"""
495
+ <div class="restaurant-tile">
496
+ <div class="restaurant-name">{restaurant['name']}</div>
497
+ <div class="restaurant-detail">🍜 {restaurant['cuisine']}</div>
498
+ <div class="restaurant-detail">πŸ“ {restaurant['locality']}</div>
499
+ <div class="restaurant-price">πŸ’° β‚Ή{restaurant['price_range']}</div>
500
+ </div>
501
+ """
502
+ st.markdown(restaurant_tile, unsafe_allow_html=True)
503
+
504
+ if len(restaurants) > 8:
505
+ st.markdown(f"<div style='text-align: center; color: #666; font-style: italic; margin-top: 10px;'>...and {len(restaurants) - 8} more restaurants</div>", unsafe_allow_html=True)
506
+
507
+ st.header("πŸ’‘ Tips")
508
+ st.write("Try asking:")
509
+ st.write("- 'Show me Chinese restaurants'")
510
+ st.write("- 'I want to book a table'")
511
+ st.write("- 'What cuisines are available?'")
512
+ st.write("- 'Book for 4 people tomorrow at 7 PM'")
513
+
514
+ def show_backend_page():
515
+ st.title("πŸ”§ Backend Dashboard")
516
+ st.markdown("Real-time view of restaurant reservations")
517
+
518
+ if st.button("← Back to Chat"):
519
+ st.session_state.page = "chat"
520
+ st.rerun()
521
+
522
+ if "last_refresh" not in st.session_state:
523
+ st.session_state.last_refresh = time.time()
524
+
525
+ # Auto-refresh every 5 seconds
526
+ current_time = time.time()
527
+ if current_time - st.session_state.last_refresh > 5:
528
+ st.session_state.last_refresh = current_time
529
+ st.rerun()
530
+
531
+ st.markdown(f"πŸ”„ Auto-refreshing every 5 seconds | Last updated: {time.strftime('%H:%M:%S')}")
532
+
533
+ try:
534
+ # Get reservations data directly from session state
535
+ reservations_data_list = st.session_state.reservation_manager.get_all_reservations()
536
+
537
+ if reservations_data_list:
538
+ # Convert to DataFrame for better display
539
+ df = pd.DataFrame(reservations_data_list)
540
+
541
+ st.subheader(f"πŸ“Š Total Reservations: {len(df)}")
542
+
543
+ # Display metrics
544
+ col1, col2, col3 = st.columns(3)
545
+ with col1:
546
+ st.metric("Total Reservations", len(df))
547
+ with col2:
548
+ if 'status' in df.columns:
549
+ confirmed = len(df[df['status'] == 'Confirmed'])
550
+ st.metric("Confirmed", confirmed)
551
+ with col3:
552
+ if 'party_size' in df.columns:
553
+ total_guests = df['party_size'].sum()
554
+ st.metric("Total Guests", total_guests)
555
+
556
+ # Display the table
557
+ st.subheader("πŸ“‹ Reservations Table")
558
+ st.dataframe(
559
+ df,
560
+ use_container_width=True,
561
+ hide_index=True
562
+ )
563
+
564
+ # Download button
565
+ csv = df.to_csv(index=False)
566
+ st.download_button(
567
+ label="πŸ“₯ Download CSV",
568
+ data=csv,
569
+ file_name=f"reservations_{time.strftime('%Y%m%d_%H%M%S')}.csv",
570
+ mime="text/csv"
571
+ )
572
+
573
+ else:
574
+ st.info("πŸ“­ No reservations found")
575
+
576
+ except Exception as e:
577
+ st.error(f"❌ Error fetching data: {str(e)}")
578
+
579
+ # Main app logic
580
+ if st.session_state.page == "chat":
581
+ show_chat_page()
582
+ elif st.session_state.page == "backend":
583
+ show_backend_page()