humanvprojectceo commited on
Commit
7c5d3c3
·
verified ·
1 Parent(s): 1fc5bd0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +71 -27
app.py CHANGED
@@ -149,12 +149,6 @@ def load_orders():
149
  finally:
150
  release_db_connection(conn)
151
 
152
- def save_orders(orders_data):
153
- """Append a new order to database (used by confirm_order)."""
154
- # This function is now only called to add one order at a time (the new_order dict)
155
- # But we keep it for backward compatibility. We'll modify confirm_order to use insert directly.
156
- pass # not used anymore, will insert directly in confirm_order endpoint
157
-
158
  # --- Tool functions (will be called by Gemini) ---
159
  def get_menu() -> str:
160
  """
@@ -171,6 +165,7 @@ def get_menu() -> str:
171
  def prepare_order_draft(table_number: str, items: list[OrderItem]) -> str:
172
  """
173
  Prepares a draft order list for the customer to confirm on their screen.
 
174
  """
175
  table_number_str = str(table_number)
176
  items_list = []
@@ -310,7 +305,17 @@ def delete_menu_item(item_id: int) -> str:
310
 
311
  # --- System Instructions ---
312
  CUSTOMER_SYSTEM_INSTRUCTION = """Your name is Nila, a classic, warm, and highly professional AI cafe assistant for "Cafe AI". You were developed by "Nastaran Data Algorithm".
313
- ... (keep existing)"""
 
 
 
 
 
 
 
 
 
 
314
 
315
  ADMIN_SYSTEM_INSTRUCTION = """Your name is Nila, the smart cafe administrator assistant for "Cafe AI". You manage the cafe menu in real time.
316
  You can view, add, update, delete menu items, and change availability using the following tools:
@@ -340,7 +345,7 @@ def customer_chat_page(table_number):
340
  def admin_dashboard_page():
341
  return render_template("cafe.html")
342
 
343
- # --- Customer API ---
344
  @app.route('/api/customer/chat_stream/<table_number>', methods=['POST'])
345
  def api_customer_chat_stream(table_number):
346
  api_key = os.environ.get("NILLA_CUSTOMER") or os.environ.get("GEMINI_API_KEY")
@@ -361,6 +366,7 @@ def api_customer_chat_stream(table_number):
361
 
362
  def generate_chunks():
363
  try:
 
364
  contents = []
365
  for msg in table_chat_histories[table_number][:-1]:
366
  role = "user" if msg["role"] == "user" else "model"
@@ -382,27 +388,70 @@ def api_customer_chat_stream(table_number):
382
  thinking_config=types.ThinkingConfig(thinking_budget=0)
383
  )
384
 
 
385
  chat = client.chats.create(
386
  model="gemini-3.1-flash-lite",
387
  history=contents,
388
  config=config
389
  )
390
 
 
391
  response = chat.send_message(user_message)
392
 
393
- # Extract text (ignore possible function calls in this simplified customer flow)
394
- if response.candidates and response.candidates[0].content and response.candidates[0].content.parts:
395
- parts = response.candidates[0].content.parts
396
- text_parts = [part.text for part in parts if hasattr(part, "text") and part.text]
397
- if text_parts:
398
- full_generated_text = "".join(text_parts)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
399
  if not full_generated_text:
400
- try:
401
- full_generated_text = response.text or ""
402
- except (ValueError, AttributeError):
403
- full_generated_text = ""
404
 
405
- # Simulate streaming
406
  chunk_size = 4
407
  for i in range(0, len(full_generated_text), chunk_size):
408
  if not active_tasks.get(table_number, True):
@@ -411,10 +460,7 @@ def api_customer_chat_stream(table_number):
411
  yield f"data: {json.dumps({'type': 'text', 'content': chunk}, ensure_ascii=False)}\n\n"
412
  time.sleep(0.015)
413
 
414
- if full_generated_text:
415
- table_chat_histories[table_number].append({"role": "model", "content": full_generated_text})
416
-
417
- # Check draft
418
  draft = global_drafts.get(table_number)
419
  if draft:
420
  yield f"data: {json.dumps({'type': 'draft', 'items': draft['items']}, ensure_ascii=False)}\n\n"
@@ -537,7 +583,7 @@ def api_admin_chat():
537
  response = chat.send_message(last_user_message)
538
 
539
  # Handle function calls loop
540
- max_iterations = 10 # safety net
541
  while max_iterations > 0:
542
  function_calls = []
543
  if response.candidates and response.candidates[0].content and response.candidates[0].content.parts:
@@ -547,7 +593,6 @@ def api_admin_chat():
547
  if not function_calls:
548
  break
549
 
550
- # Execute functions and build response
551
  function_response_parts = []
552
  for fc in function_calls:
553
  func_name = fc.name
@@ -571,7 +616,6 @@ def api_admin_chat():
571
  function_response_parts.append(
572
  types.Part.from_function_response(name=func_name, response={"result": result})
573
  )
574
- # Send function responses back to the model
575
  response = chat.send_message(
576
  types.Content(role="user", parts=function_response_parts)
577
  )
@@ -594,7 +638,7 @@ def api_admin_chat():
594
  except Exception as e:
595
  return jsonify({"error": str(e)}), 500
596
 
597
- # --- Admin Menu CRUD (manual REST endpoints, kept for the admin UI table) ---
598
  @app.route('/api/admin/menu', methods=['GET'])
599
  def api_admin_get_menu():
600
  menu = load_menu()
 
149
  finally:
150
  release_db_connection(conn)
151
 
 
 
 
 
 
 
152
  # --- Tool functions (will be called by Gemini) ---
153
  def get_menu() -> str:
154
  """
 
165
  def prepare_order_draft(table_number: str, items: list[OrderItem]) -> str:
166
  """
167
  Prepares a draft order list for the customer to confirm on their screen.
168
+ Only call this after verifying that every item exists in the menu (using get_menu).
169
  """
170
  table_number_str = str(table_number)
171
  items_list = []
 
305
 
306
  # --- System Instructions ---
307
  CUSTOMER_SYSTEM_INSTRUCTION = """Your name is Nila, a classic, warm, and highly professional AI cafe assistant for "Cafe AI". You were developed by "Nastaran Data Algorithm".
308
+
309
+ You are currently talking to a customer at a specific table. Your responsibilities:
310
+ 1. Greet warmly and take orders in Persian (you can understand English but always reply in Persian unless the customer insists).
311
+ 2. **Before preparing any order draft**, you MUST check the current menu by calling the tool **get_menu()** to see what items are available.
312
+ 3. If the customer asks for an item that is NOT in the menu (exact or similar name), politely say: "متأسفانه این آیتم در منوی امروز ما موجود نیست. می‌توانم پیشنهادهای دیگری از منو به شما بدهم؟" and then suggest 2-3 similar or popular items from the current menu. Never prepare a draft with items not in the menu.
313
+ 4. Once you have verified all requested items exist, use **prepare_order_draft** to create the order.
314
+ 5. After preparing the draft, tell the customer they can review and confirm on their screen.
315
+ 6. If the menu is empty (no items returned), tell the customer that the cafe is currently not taking orders and to wait for the staff.
316
+
317
+ Always be friendly, speak in Persian, and use the tools to provide accurate information.
318
+ """
319
 
320
  ADMIN_SYSTEM_INSTRUCTION = """Your name is Nila, the smart cafe administrator assistant for "Cafe AI". You manage the cafe menu in real time.
321
  You can view, add, update, delete menu items, and change availability using the following tools:
 
345
  def admin_dashboard_page():
346
  return render_template("cafe.html")
347
 
348
+ # --- Customer API (with function calling loop) ---
349
  @app.route('/api/customer/chat_stream/<table_number>', methods=['POST'])
350
  def api_customer_chat_stream(table_number):
351
  api_key = os.environ.get("NILLA_CUSTOMER") or os.environ.get("GEMINI_API_KEY")
 
366
 
367
  def generate_chunks():
368
  try:
369
+ # Build conversation history for the model
370
  contents = []
371
  for msg in table_chat_histories[table_number][:-1]:
372
  role = "user" if msg["role"] == "user" else "model"
 
388
  thinking_config=types.ThinkingConfig(thinking_budget=0)
389
  )
390
 
391
+ # Create a chat session
392
  chat = client.chats.create(
393
  model="gemini-3.1-flash-lite",
394
  history=contents,
395
  config=config
396
  )
397
 
398
+ # Send user message and handle any function calls
399
  response = chat.send_message(user_message)
400
 
401
+ # Loop to process function calls
402
+ max_iterations = 10
403
+ while max_iterations > 0 and active_tasks.get(table_number, True):
404
+ function_calls = []
405
+ if response.candidates and response.candidates[0].content and response.candidates[0].content.parts:
406
+ for part in response.candidates[0].content.parts:
407
+ if hasattr(part, "function_call") and part.function_call:
408
+ function_calls.append(part.function_call)
409
+
410
+ if not function_calls:
411
+ # No more function calls, extract the text
412
+ if response.candidates and response.candidates[0].content and response.candidates[0].content.parts:
413
+ text_parts = [part.text for part in response.candidates[0].content.parts if hasattr(part, "text") and part.text]
414
+ full_generated_text = "".join(text_parts)
415
+ if not full_generated_text:
416
+ try:
417
+ full_generated_text = response.text or ""
418
+ except (ValueError, AttributeError):
419
+ full_generated_text = "متأسفانه مشکلی پیش آمده، لطفاً دوباره تلاش کنید."
420
+ break
421
+
422
+ # Execute function calls
423
+ function_response_parts = []
424
+ for fc in function_calls:
425
+ func_name = fc.name
426
+ args = dict(fc.args) if hasattr(fc, "args") else {}
427
+ print(f"Customer function call: {func_name}({args})")
428
+ try:
429
+ if func_name == "get_menu":
430
+ result = get_menu()
431
+ elif func_name == "prepare_order_draft":
432
+ result = prepare_order_draft(**args)
433
+ else:
434
+ result = f"Unknown function: {func_name}"
435
+ except Exception as e:
436
+ result = f"Error executing {func_name}: {str(e)}"
437
+ function_response_parts.append(
438
+ types.Part.from_function_response(name=func_name, response={"result": result})
439
+ )
440
+
441
+ # Send the results back to the model
442
+ response = chat.send_message(
443
+ types.Content(role="user", parts=function_response_parts)
444
+ )
445
+ max_iterations -= 1
446
+
447
+ # If no text was generated after all iterations
448
  if not full_generated_text:
449
+ full_generated_text = "پاسخی دریافت نشد، لطفاً دوباره تلاش کنید."
450
+
451
+ # Save the model's final text to history
452
+ table_chat_histories[table_number].append({"role": "model", "content": full_generated_text})
453
 
454
+ # Stream the text chunk by chunk (simulate streaming)
455
  chunk_size = 4
456
  for i in range(0, len(full_generated_text), chunk_size):
457
  if not active_tasks.get(table_number, True):
 
460
  yield f"data: {json.dumps({'type': 'text', 'content': chunk}, ensure_ascii=False)}\n\n"
461
  time.sleep(0.015)
462
 
463
+ # After streaming text, check if there's a draft for this table
 
 
 
464
  draft = global_drafts.get(table_number)
465
  if draft:
466
  yield f"data: {json.dumps({'type': 'draft', 'items': draft['items']}, ensure_ascii=False)}\n\n"
 
583
  response = chat.send_message(last_user_message)
584
 
585
  # Handle function calls loop
586
+ max_iterations = 10
587
  while max_iterations > 0:
588
  function_calls = []
589
  if response.candidates and response.candidates[0].content and response.candidates[0].content.parts:
 
593
  if not function_calls:
594
  break
595
 
 
596
  function_response_parts = []
597
  for fc in function_calls:
598
  func_name = fc.name
 
616
  function_response_parts.append(
617
  types.Part.from_function_response(name=func_name, response={"result": result})
618
  )
 
619
  response = chat.send_message(
620
  types.Content(role="user", parts=function_response_parts)
621
  )
 
638
  except Exception as e:
639
  return jsonify({"error": str(e)}), 500
640
 
641
+ # --- Admin Menu CRUD (manual REST endpoints) ---
642
  @app.route('/api/admin/menu', methods=['GET'])
643
  def api_admin_get_menu():
644
  menu = load_menu()