Upload app.py
Browse files
app.py
CHANGED
|
@@ -38,12 +38,9 @@ MEMORY_FILE = "navigation_memory.json"
|
|
| 38 |
GRID_0_FILE = "floor_0_grid.npy"
|
| 39 |
GRID_1_FILE = "floor_0_grid.npy"
|
| 40 |
|
| 41 |
-
OPENROUTER_KEY
|
| 42 |
-
OPENROUTER_MODEL = "
|
| 43 |
|
| 44 |
-
# ============================================================
|
| 45 |
-
# DATA LOADING
|
| 46 |
-
# ============================================================
|
| 47 |
def load_json_file(path, default):
|
| 48 |
try:
|
| 49 |
with open(path, "r", encoding="utf-8") as f:
|
|
@@ -232,7 +229,7 @@ Mall stores:
|
|
| 232 |
Rules:
|
| 233 |
- If the user asks about a store, answer with store information.
|
| 234 |
- If the user asks for navigation, help them navigate.
|
| 235 |
-
- If the user says
|
| 236 |
- Keep answers concise and helpful.
|
| 237 |
"""
|
| 238 |
|
|
@@ -474,17 +471,25 @@ def save_navigation(username, start_room, dest_room):
|
|
| 474 |
# ============================================================
|
| 475 |
# STORE RECOMMENDATION
|
| 476 |
# ============================================================
|
| 477 |
-
def nearest_store(user_pos):
|
| 478 |
best_room = None
|
| 479 |
best_dist = float("inf")
|
|
|
|
| 480 |
for _, room in STORE_CLUSTER_ROOM.items():
|
|
|
|
|
|
|
|
|
|
|
|
|
| 481 |
centroid = get_centroid(room)
|
| 482 |
if centroid is None:
|
| 483 |
continue
|
|
|
|
| 484 |
dist = np.sqrt((user_pos[0] - centroid[0]) ** 2 + (user_pos[1] - centroid[1]) ** 2)
|
|
|
|
| 485 |
if dist < best_dist:
|
| 486 |
best_dist = dist
|
| 487 |
best_room = room
|
|
|
|
| 488 |
return best_room
|
| 489 |
|
| 490 |
|
|
@@ -1012,8 +1017,7 @@ def chatbot_respond(user_text, history, session):
|
|
| 1012 |
bot = f"{info}\n\nWould you like navigation to {ROOM_INFO[info_room]} (Room {info_room})?"
|
| 1013 |
session["last_referenced_room"] = info_room
|
| 1014 |
session["last_dest_room"] = info_room
|
| 1015 |
-
history.append(
|
| 1016 |
-
history.append({"role": "assistant", "content": bot})
|
| 1017 |
try:
|
| 1018 |
gTTS(bot).save("/content/voice.mp3")
|
| 1019 |
except Exception:
|
|
@@ -1168,12 +1172,12 @@ def recommend_store_for_session(session):
|
|
| 1168 |
return "<div style='color:#ff6b6b;font-family:monospace'>β Please enter your current location first.</div>", session
|
| 1169 |
|
| 1170 |
if session.get("is_new", True):
|
| 1171 |
-
room = nearest_store(session["start_xy"])
|
| 1172 |
msg = "π New user β Nearest store recommended"
|
| 1173 |
else:
|
| 1174 |
room = most_visited_store(session["username"])
|
| 1175 |
if room is None:
|
| 1176 |
-
room = nearest_store(session["start_xy"])
|
| 1177 |
msg = "π Returning user β Most visited store recommended"
|
| 1178 |
|
| 1179 |
store_name = ROOM_INFO.get(room, f"Room {room}")
|
|
@@ -1221,13 +1225,18 @@ def handle_agreement(choice, session):
|
|
| 1221 |
|
| 1222 |
def do_navigate(choice, type_name, cat_name, sub_name, nav_mode_val, danger_room, session):
|
| 1223 |
session = set_session_defaults(session)
|
|
|
|
| 1224 |
if not session.get("username"):
|
| 1225 |
return "β Please login first.", None
|
|
|
|
| 1226 |
if session.get("start_xy") is None or session.get("start_floor") is None:
|
| 1227 |
return "β Please set your location first.", None
|
| 1228 |
|
| 1229 |
reset_grids()
|
| 1230 |
|
|
|
|
|
|
|
|
|
|
| 1231 |
if "Yes" in (choice or ""):
|
| 1232 |
dest_room = session.get("recommended_room")
|
| 1233 |
if dest_room is None:
|
|
@@ -1235,15 +1244,29 @@ def do_navigate(choice, type_name, cat_name, sub_name, nav_mode_val, danger_room
|
|
| 1235 |
else:
|
| 1236 |
if not sub_name:
|
| 1237 |
return "β Please select a sub-category.", None
|
|
|
|
| 1238 |
parent = SUB_TO_CATEGORY.get(sub_name, cat_name)
|
| 1239 |
store = STORE_CLUSTER.get(parent, parent)
|
| 1240 |
dest_room = STORE_CLUSTER_ROOM.get(store)
|
|
|
|
| 1241 |
if dest_room is None:
|
| 1242 |
return "β Could not map the selected category to a store.", None
|
| 1243 |
|
| 1244 |
session["dest_room"] = dest_room
|
| 1245 |
-
session["nav_mode"] = nav_mode_val
|
| 1246 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1247 |
room_val = None
|
| 1248 |
try:
|
| 1249 |
if danger_room is not None and str(danger_room).strip() != "":
|
|
@@ -1261,32 +1284,55 @@ def do_navigate(choice, type_name, cat_name, sub_name, nav_mode_val, danger_room
|
|
| 1261 |
fire_center = None
|
| 1262 |
crowded_center = None
|
| 1263 |
|
| 1264 |
-
if
|
| 1265 |
fire_active = True
|
| 1266 |
fire_room = room_val
|
| 1267 |
fire_center = get_centroid(room_val)
|
|
|
|
| 1268 |
if fire_center is not None:
|
| 1269 |
fire_step += 1
|
| 1270 |
radius = 20 + fire_step * 6
|
|
|
|
| 1271 |
if get_floor(room_val) == 0:
|
| 1272 |
mark_danger(grid_0, fire_center, radius)
|
| 1273 |
else:
|
| 1274 |
mark_danger(grid_1, fire_center, radius)
|
| 1275 |
|
| 1276 |
-
if
|
| 1277 |
crowded_active = True
|
| 1278 |
crowded_room = room_val
|
| 1279 |
crowded_center = get_centroid(room_val)
|
|
|
|
| 1280 |
if crowded_center is not None:
|
| 1281 |
if get_floor(room_val) == 0:
|
| 1282 |
mark_crowd(grid_0, crowded_center)
|
| 1283 |
else:
|
| 1284 |
mark_crowd(grid_1, crowded_center)
|
| 1285 |
|
| 1286 |
-
|
| 1287 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1288 |
|
| 1289 |
-
img_path, instructions = navigate(session["start_floor"], session["start_xy"], dest_room, nav_mode_val)
|
| 1290 |
return instructions, img_path
|
| 1291 |
|
| 1292 |
|
|
@@ -1351,6 +1397,11 @@ with gr.Blocks(
|
|
| 1351 |
auth_name = gr.Textbox(label="Full Name (signup only)", placeholder="John Doe")
|
| 1352 |
auth_age = gr.Number(label="Age (signup only)", value=25)
|
| 1353 |
auth_gender = gr.Dropdown(["male", "female", "other"], label="Gender (signup only)", value="male")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1354 |
auth_budget_l = gr.Number(label="Min Budget EGP (signup only)", value=500)
|
| 1355 |
auth_budget_h = gr.Number(label="Max Budget EGP (signup only)", value=5000)
|
| 1356 |
|
|
@@ -1359,7 +1410,7 @@ with gr.Blocks(
|
|
| 1359 |
login_btn = gr.Button("LOGIN", variant="secondary")
|
| 1360 |
auth_status = gr.HTML()
|
| 1361 |
|
| 1362 |
-
def do_signup(username, password, name, age, gender, low, high, session):
|
| 1363 |
cursor.execute("SELECT username FROM users WHERE username=?", (username,))
|
| 1364 |
if cursor.fetchone():
|
| 1365 |
return "<div style='color:#ff6b6b;font-family:monospace'>β Username already exists.</div>", session
|
|
@@ -1370,6 +1421,8 @@ with gr.Blocks(
|
|
| 1370 |
conn.commit()
|
| 1371 |
session = set_session_defaults(session)
|
| 1372 |
session.update({"username": username, "gender": gender, "low": low, "high": high, "is_new": True})
|
|
|
|
|
|
|
| 1373 |
return f"<div style='color:#00ff88;font-family:monospace'>β
Welcome, {name}! Account created.</div>", session
|
| 1374 |
|
| 1375 |
def do_login(username, password, session):
|
|
@@ -1381,7 +1434,11 @@ with gr.Blocks(
|
|
| 1381 |
session.update({"username": user[0], "gender": user[4], "low": user[5], "high": user[6], "is_new": False})
|
| 1382 |
return f"<div style='color:#00ff88;font-family:monospace'>β
Welcome back, {user[2]}!</div>", session
|
| 1383 |
|
| 1384 |
-
signup_btn.click(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1385 |
login_btn.click(do_login, [auth_username, auth_password, session_state], [auth_status, session_state])
|
| 1386 |
|
| 1387 |
# ----------------------------------------------------
|
|
@@ -1454,16 +1511,14 @@ with gr.Blocks(
|
|
| 1454 |
gr.HTML("<div style='color:#00d4ff;font-family:monospace;font-size:13px;margin-bottom:16px'>Chat with the mall AI assistant</div>")
|
| 1455 |
|
| 1456 |
chatbot_widget = gr.Chatbot(
|
| 1457 |
-
|
| 1458 |
-
|
| 1459 |
-
|
| 1460 |
-
|
| 1461 |
-
],
|
| 1462 |
-
)
|
| 1463 |
|
| 1464 |
with gr.Row():
|
| 1465 |
chat_msg = gr.Textbox(placeholder="Ask me anything about the mall...", label="Message", scale=4)
|
| 1466 |
-
chat_mic = gr.Audio(
|
| 1467 |
|
| 1468 |
chat_audio_out = gr.Audio(label="Voice Response")
|
| 1469 |
chat_img_out = gr.Image(label="Navigation Map", type="filepath")
|
|
@@ -1485,4 +1540,4 @@ with gr.Blocks(
|
|
| 1485 |
chat_mic.change(voice_chat, [chat_mic, chatbot_widget, session_state], [chatbot_widget, chat_img_out, chat_audio_out, session_state])
|
| 1486 |
|
| 1487 |
if __name__ == "__main__":
|
| 1488 |
-
demo.launch(
|
|
|
|
| 38 |
GRID_0_FILE = "floor_0_grid.npy"
|
| 39 |
GRID_1_FILE = "floor_0_grid.npy"
|
| 40 |
|
| 41 |
+
OPENROUTER_KEY = "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"
|
| 42 |
+
OPENROUTER_MODEL = "tencent/hy3-preview:free"
|
| 43 |
|
|
|
|
|
|
|
|
|
|
| 44 |
def load_json_file(path, default):
|
| 45 |
try:
|
| 46 |
with open(path, "r", encoding="utf-8") as f:
|
|
|
|
| 229 |
Rules:
|
| 230 |
- If the user asks about a store, answer with store information.
|
| 231 |
- If the user asks for navigation, help them navigate.
|
| 232 |
+
- If the user says he is hungry, suggest kitchen / dining / food-related stores and when he tells you that he agree os suggestion then help him navigate.
|
| 233 |
- Keep answers concise and helpful.
|
| 234 |
"""
|
| 235 |
|
|
|
|
| 471 |
# ============================================================
|
| 472 |
# STORE RECOMMENDATION
|
| 473 |
# ============================================================
|
| 474 |
+
def nearest_store(user_pos, user_floor):
|
| 475 |
best_room = None
|
| 476 |
best_dist = float("inf")
|
| 477 |
+
|
| 478 |
for _, room in STORE_CLUSTER_ROOM.items():
|
| 479 |
+
# β
ΩΩΨͺΨ±Ψ© ΨΉΩΩ ΩΩΨ³ Ψ§ΩΨ―ΩΨ±
|
| 480 |
+
if get_floor(room) != user_floor:
|
| 481 |
+
continue
|
| 482 |
+
|
| 483 |
centroid = get_centroid(room)
|
| 484 |
if centroid is None:
|
| 485 |
continue
|
| 486 |
+
|
| 487 |
dist = np.sqrt((user_pos[0] - centroid[0]) ** 2 + (user_pos[1] - centroid[1]) ** 2)
|
| 488 |
+
|
| 489 |
if dist < best_dist:
|
| 490 |
best_dist = dist
|
| 491 |
best_room = room
|
| 492 |
+
|
| 493 |
return best_room
|
| 494 |
|
| 495 |
|
|
|
|
| 1017 |
bot = f"{info}\n\nWould you like navigation to {ROOM_INFO[info_room]} (Room {info_room})?"
|
| 1018 |
session["last_referenced_room"] = info_room
|
| 1019 |
session["last_dest_room"] = info_room
|
| 1020 |
+
history.append((user_text, bot))
|
|
|
|
| 1021 |
try:
|
| 1022 |
gTTS(bot).save("/content/voice.mp3")
|
| 1023 |
except Exception:
|
|
|
|
| 1172 |
return "<div style='color:#ff6b6b;font-family:monospace'>β Please enter your current location first.</div>", session
|
| 1173 |
|
| 1174 |
if session.get("is_new", True):
|
| 1175 |
+
room = nearest_store(session["start_xy"], session["start_floor"])
|
| 1176 |
msg = "π New user β Nearest store recommended"
|
| 1177 |
else:
|
| 1178 |
room = most_visited_store(session["username"])
|
| 1179 |
if room is None:
|
| 1180 |
+
room = nearest_store(session["start_xy"], session["start_floor"])
|
| 1181 |
msg = "π Returning user β Most visited store recommended"
|
| 1182 |
|
| 1183 |
store_name = ROOM_INFO.get(room, f"Room {room}")
|
|
|
|
| 1225 |
|
| 1226 |
def do_navigate(choice, type_name, cat_name, sub_name, nav_mode_val, danger_room, session):
|
| 1227 |
session = set_session_defaults(session)
|
| 1228 |
+
|
| 1229 |
if not session.get("username"):
|
| 1230 |
return "β Please login first.", None
|
| 1231 |
+
|
| 1232 |
if session.get("start_xy") is None or session.get("start_floor") is None:
|
| 1233 |
return "β Please set your location first.", None
|
| 1234 |
|
| 1235 |
reset_grids()
|
| 1236 |
|
| 1237 |
+
# =========================================================
|
| 1238 |
+
# DESTINATION SELECTION
|
| 1239 |
+
# =========================================================
|
| 1240 |
if "Yes" in (choice or ""):
|
| 1241 |
dest_room = session.get("recommended_room")
|
| 1242 |
if dest_room is None:
|
|
|
|
| 1244 |
else:
|
| 1245 |
if not sub_name:
|
| 1246 |
return "β Please select a sub-category.", None
|
| 1247 |
+
|
| 1248 |
parent = SUB_TO_CATEGORY.get(sub_name, cat_name)
|
| 1249 |
store = STORE_CLUSTER.get(parent, parent)
|
| 1250 |
dest_room = STORE_CLUSTER_ROOM.get(store)
|
| 1251 |
+
|
| 1252 |
if dest_room is None:
|
| 1253 |
return "β Could not map the selected category to a store.", None
|
| 1254 |
|
| 1255 |
session["dest_room"] = dest_room
|
|
|
|
| 1256 |
|
| 1257 |
+
# =========================================================
|
| 1258 |
+
# NAVIGATION MODE (FIXED LOGIC)
|
| 1259 |
+
# =========================================================
|
| 1260 |
+
if session.get("special_needs"):
|
| 1261 |
+
effective_mode = "Special Needs"
|
| 1262 |
+
else:
|
| 1263 |
+
effective_mode = nav_mode_val
|
| 1264 |
+
|
| 1265 |
+
session["nav_mode"] = effective_mode
|
| 1266 |
+
|
| 1267 |
+
# =========================================================
|
| 1268 |
+
# DANGER / CROWD HANDLING
|
| 1269 |
+
# =========================================================
|
| 1270 |
room_val = None
|
| 1271 |
try:
|
| 1272 |
if danger_room is not None and str(danger_room).strip() != "":
|
|
|
|
| 1284 |
fire_center = None
|
| 1285 |
crowded_center = None
|
| 1286 |
|
| 1287 |
+
if effective_mode == "Fire" and room_val is not None and room_val in ROOM_INFO:
|
| 1288 |
fire_active = True
|
| 1289 |
fire_room = room_val
|
| 1290 |
fire_center = get_centroid(room_val)
|
| 1291 |
+
|
| 1292 |
if fire_center is not None:
|
| 1293 |
fire_step += 1
|
| 1294 |
radius = 20 + fire_step * 6
|
| 1295 |
+
|
| 1296 |
if get_floor(room_val) == 0:
|
| 1297 |
mark_danger(grid_0, fire_center, radius)
|
| 1298 |
else:
|
| 1299 |
mark_danger(grid_1, fire_center, radius)
|
| 1300 |
|
| 1301 |
+
if effective_mode == "Crowded" and room_val is not None and room_val in ROOM_INFO:
|
| 1302 |
crowded_active = True
|
| 1303 |
crowded_room = room_val
|
| 1304 |
crowded_center = get_centroid(room_val)
|
| 1305 |
+
|
| 1306 |
if crowded_center is not None:
|
| 1307 |
if get_floor(room_val) == 0:
|
| 1308 |
mark_crowd(grid_0, crowded_center)
|
| 1309 |
else:
|
| 1310 |
mark_crowd(grid_1, crowded_center)
|
| 1311 |
|
| 1312 |
+
# =========================================================
|
| 1313 |
+
# SAVE HISTORY
|
| 1314 |
+
# =========================================================
|
| 1315 |
+
save_navigation(
|
| 1316 |
+
session["username"],
|
| 1317 |
+
(session["start_floor"], session["start_xy"]),
|
| 1318 |
+
dest_room
|
| 1319 |
+
)
|
| 1320 |
+
|
| 1321 |
+
save_navigation_mem(
|
| 1322 |
+
f"floor={session['start_floor']},x={session['start_xy'][0]},y={session['start_xy'][1]}",
|
| 1323 |
+
dest_room
|
| 1324 |
+
)
|
| 1325 |
+
|
| 1326 |
+
# =========================================================
|
| 1327 |
+
# NAVIGATION CALL (IMPORTANT FIX)
|
| 1328 |
+
# =========================================================
|
| 1329 |
+
img_path, instructions = navigate(
|
| 1330 |
+
session["start_floor"],
|
| 1331 |
+
session["start_xy"],
|
| 1332 |
+
dest_room,
|
| 1333 |
+
effective_mode
|
| 1334 |
+
)
|
| 1335 |
|
|
|
|
| 1336 |
return instructions, img_path
|
| 1337 |
|
| 1338 |
|
|
|
|
| 1397 |
auth_name = gr.Textbox(label="Full Name (signup only)", placeholder="John Doe")
|
| 1398 |
auth_age = gr.Number(label="Age (signup only)", value=25)
|
| 1399 |
auth_gender = gr.Dropdown(["male", "female", "other"], label="Gender (signup only)", value="male")
|
| 1400 |
+
special_needs = gr.Radio(
|
| 1401 |
+
["No", "Yes"],
|
| 1402 |
+
label="Are you a person with special needs?",
|
| 1403 |
+
value="No"
|
| 1404 |
+
)
|
| 1405 |
auth_budget_l = gr.Number(label="Min Budget EGP (signup only)", value=500)
|
| 1406 |
auth_budget_h = gr.Number(label="Max Budget EGP (signup only)", value=5000)
|
| 1407 |
|
|
|
|
| 1410 |
login_btn = gr.Button("LOGIN", variant="secondary")
|
| 1411 |
auth_status = gr.HTML()
|
| 1412 |
|
| 1413 |
+
def do_signup(username, password, name, age, gender, low, high,special, session):
|
| 1414 |
cursor.execute("SELECT username FROM users WHERE username=?", (username,))
|
| 1415 |
if cursor.fetchone():
|
| 1416 |
return "<div style='color:#ff6b6b;font-family:monospace'>β Username already exists.</div>", session
|
|
|
|
| 1421 |
conn.commit()
|
| 1422 |
session = set_session_defaults(session)
|
| 1423 |
session.update({"username": username, "gender": gender, "low": low, "high": high, "is_new": True})
|
| 1424 |
+
session["special_needs"] = (special == "Yes")
|
| 1425 |
+
session["nav_mode"] = "Special Needs" if special == "Yes" else "Normal"
|
| 1426 |
return f"<div style='color:#00ff88;font-family:monospace'>β
Welcome, {name}! Account created.</div>", session
|
| 1427 |
|
| 1428 |
def do_login(username, password, session):
|
|
|
|
| 1434 |
session.update({"username": user[0], "gender": user[4], "low": user[5], "high": user[6], "is_new": False})
|
| 1435 |
return f"<div style='color:#00ff88;font-family:monospace'>β
Welcome back, {user[2]}!</div>", session
|
| 1436 |
|
| 1437 |
+
signup_btn.click(
|
| 1438 |
+
do_signup,
|
| 1439 |
+
[auth_username, auth_password, auth_name, auth_age, auth_gender, auth_budget_l, auth_budget_h, special_needs, session_state],
|
| 1440 |
+
[auth_status, session_state]
|
| 1441 |
+
)
|
| 1442 |
login_btn.click(do_login, [auth_username, auth_password, session_state], [auth_status, session_state])
|
| 1443 |
|
| 1444 |
# ----------------------------------------------------
|
|
|
|
| 1511 |
gr.HTML("<div style='color:#00d4ff;font-family:monospace;font-size:13px;margin-bottom:16px'>Chat with the mall AI assistant</div>")
|
| 1512 |
|
| 1513 |
chatbot_widget = gr.Chatbot(
|
| 1514 |
+
label="SmartMall Assistant",
|
| 1515 |
+
height=480,
|
| 1516 |
+
value=[("", "π Welcome to SmartMall! I can help you navigate, find products, or answer any questions. How can I help?")],
|
| 1517 |
+
)
|
|
|
|
|
|
|
| 1518 |
|
| 1519 |
with gr.Row():
|
| 1520 |
chat_msg = gr.Textbox(placeholder="Ask me anything about the mall...", label="Message", scale=4)
|
| 1521 |
+
chat_mic = gr.Audio(source="microphone", type="filepath", label="π€", scale=1)
|
| 1522 |
|
| 1523 |
chat_audio_out = gr.Audio(label="Voice Response")
|
| 1524 |
chat_img_out = gr.Image(label="Navigation Map", type="filepath")
|
|
|
|
| 1540 |
chat_mic.change(voice_chat, [chat_mic, chatbot_widget, session_state], [chatbot_widget, chat_img_out, chat_audio_out, session_state])
|
| 1541 |
|
| 1542 |
if __name__ == "__main__":
|
| 1543 |
+
demo.launch()
|