humanvprojectceo commited on
Commit
373bb0b
·
verified ·
1 Parent(s): d87a207

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +60 -25
app.py CHANGED
@@ -1,7 +1,7 @@
1
  import os
2
  import json
3
  import time
4
- from flask import Flask, request, Response, jsonify, render_template, redirect
5
  from google import genai
6
  from google.genai import types
7
  from pydantic import BaseModel, Field
@@ -32,7 +32,7 @@ global_drafts = {} # پیش‌نویس‌های موقت سفارش بر
32
  active_tasks = {} # کنترل پردازش‌های فعال جهت متوقف کردن استریمینگ
33
 
34
 
35
- # --- تعریف کلاس‌های پایداری اسکیما با Pydantic (رفع قطعی خطای پارامتر) ---
36
 
37
  class OrderItem(BaseModel):
38
  name: str = Field(description="نام دقیق و کامل آیتم از منوی فعال به زبان فارسی (مثال: کیک شکلاتی خیس)")
@@ -68,7 +68,6 @@ def save_orders(orders_data):
68
 
69
  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".
70
  Your primary goal is to help customers at their tables select drinks, food, and sweets from the menu, and prepare their orders.
71
-
72
  Rules you must strictly follow:
73
  1. Speak in Farsi (Persian) in a warm, polite, and welcoming tone. Use emojis suitable for a cozy cafe (☕️, 🍰, 🥐, 🌸, etc.).
74
  2. If anyone asks who you are or who created you, say: "من نیلا هستم، دستیار هوشمند کافه AI که توسط تیم الگوریتم داده نسترن توسعه پیدا کردم."
@@ -81,7 +80,6 @@ Rules you must strictly follow:
81
 
82
  ADMIN_SYSTEM_INSTRUCTION = """Your name is Nila, the smart cafe administrator assistant for "Cafe AI". You were developed by "Nastaran Data Algorithm".
83
  Your role is to help the cafe staff manage their active menu. You can update item availabilities, edit menu details, and process commands.
84
-
85
  Rules you must strictly follow:
86
  1. Speak in Farsi (Persian) in a professional, clear, and polite tone.
87
  2. If anyone asks who you are or who created you, say: "من نیلا هستم، دستیار هوشمند مدیریت که توسط تیم الگوریتم داده نسترن توسعه پیدا کردم."
@@ -117,8 +115,27 @@ def prepare_order_draft(table_number: str, items: list[OrderItem]) -> str:
117
  table_number: The table number (e.g. '3')
118
  items: A list of ordered items, each containing a name and a quantity.
119
  """
120
- # تبدیل دیتاهای شیء گرا Pydantic به دیکشنری ساده جهت نوشتن در فایل دیتابیس
121
- items_list = [{"name": item.name, "quantity": item.quantity} for item in items]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
 
123
  global_drafts[table_number] = {
124
  "table": table_number,
@@ -163,11 +180,12 @@ def set_item_availability(item_name: str, available: bool) -> str:
163
  return f"آیتم '{item_name}' در منوی کافه یافت نشد."
164
 
165
 
166
- # --- روت‌های مسیریابی آدرس‌ها (Domain URL Mapping) ---
167
 
168
  @app.route('/')
169
- def home_redirect():
170
- return redirect('/customer/1')
 
171
 
172
  @app.route('/customer/<table_number>')
173
  def customer_chat_page(table_number):
@@ -182,9 +200,10 @@ def admin_dashboard_page():
182
 
183
  @app.route('/api/customer/chat_stream/<table_number>', methods=['POST'])
184
  def api_customer_chat_stream(table_number):
185
- api_key = os.environ.get("NILLA_CUSTOMER")
 
186
  if not api_key:
187
- return jsonify({"error": "کلید NILLA_CUSTOMER در سرور یافت نشد."}), 500
188
 
189
  data = request.json or {}
190
  user_message = data.get("message")
@@ -200,8 +219,9 @@ def api_customer_chat_stream(table_number):
200
 
201
  def generate_chunks():
202
  try:
 
203
  contents = []
204
- for msg in table_chat_histories[table_number]:
205
  role = "user" if msg["role"] == "user" else "model"
206
  contents.append(
207
  types.Content(
@@ -215,18 +235,23 @@ def api_customer_chat_stream(table_number):
215
 
216
  dynamic_instruction = CUSTOMER_SYSTEM_INSTRUCTION + f"\nYou are currently serving table: {table_number}. Maintain context based on this table number."
217
 
 
218
  config = types.GenerateContentConfig(
219
  system_instruction=dynamic_instruction,
220
  tools=[get_menu, prepare_order_draft],
221
- thinking_config=types.ThinkingConfig(thinking_level="MINIMAL")
222
  )
223
 
224
- # فراخوانی جریان استریم همگام
225
- for chunk in client.models.generate_content_stream(
226
  model="gemini-3.1-flash-lite",
227
- contents=contents,
228
  config=config
229
- ):
 
 
 
 
230
  if not active_tasks.get(table_number, True):
231
  break
232
 
@@ -237,6 +262,7 @@ def api_customer_chat_stream(table_number):
237
  if full_generated_text:
238
  table_chat_histories[table_number].append({"role": "model", "content": full_generated_text})
239
 
 
240
  draft = global_drafts.get(table_number)
241
  if draft:
242
  yield f"data: {json.dumps({'type': 'draft', 'items': draft['items']}, ensure_ascii=False)}\n\n"
@@ -279,6 +305,7 @@ def api_confirm_order():
279
  orders.append(new_order)
280
  save_orders(orders)
281
 
 
282
  table_chat_histories.pop(table_number, None)
283
  global_drafts.pop(table_number, None)
284
  active_tasks.pop(table_number, None)
@@ -317,17 +344,20 @@ def api_admin_complete_order():
317
 
318
  @app.route('/api/admin/chat', methods=['POST'])
319
  def api_admin_chat():
320
- api_key = os.environ.get("NILLA_CAFE")
321
  if not api_key:
322
- return jsonify({"error": "کلید NILLA_CAFE یافت نشد."}), 500
323
 
324
  data = request.json or {}
325
  messages = data.get("messages", [])
 
 
326
 
327
  try:
328
  client = genai.Client(api_key=api_key)
329
  contents = []
330
- for msg in messages:
 
331
  role = "user" if msg["role"] == "user" else "model"
332
  contents.append(
333
  types.Content(
@@ -339,15 +369,20 @@ def api_admin_chat():
339
  config = types.GenerateContentConfig(
340
  system_instruction=ADMIN_SYSTEM_INSTRUCTION,
341
  tools=[get_full_menu, set_item_availability],
342
- thinking_config=types.ThinkingConfig(thinking_level="MINIMAL")
343
  )
344
 
345
- response = client.models.generate_content(
 
 
 
346
  model="gemini-3.1-flash-lite",
347
- contents=contents,
348
  config=config
349
  )
350
 
 
 
351
  return jsonify({
352
  "success": True,
353
  "response": response.text
@@ -358,9 +393,9 @@ def api_admin_chat():
358
 
359
  @app.route('/api/admin/extract_menu', methods=['POST'])
360
  def api_extract_menu():
361
- api_key = os.environ.get("NILLA_CAFE")
362
  if not api_key:
363
- return jsonify({"error": "کلید NILLA_CAFE یافت نشد."}), 500
364
 
365
  if "file" not in request.files:
366
  return jsonify({"error": "فایل ارسال نشده"}), 400
 
1
  import os
2
  import json
3
  import time
4
+ from flask import Flask, request, Response, jsonify, render_template, redirect, url_for
5
  from google import genai
6
  from google.genai import types
7
  from pydantic import BaseModel, Field
 
32
  active_tasks = {} # کنترل پردازش‌های فعال جهت متوقف کردن استریمینگ
33
 
34
 
35
+ # --- تعریف کلاس‌های پایداری اسکیما با Pydantic ---
36
 
37
  class OrderItem(BaseModel):
38
  name: str = Field(description="نام دقیق و کامل آیتم از منوی فعال به زبان فارسی (مثال: کیک شکلاتی خیس)")
 
68
 
69
  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".
70
  Your primary goal is to help customers at their tables select drinks, food, and sweets from the menu, and prepare their orders.
 
71
  Rules you must strictly follow:
72
  1. Speak in Farsi (Persian) in a warm, polite, and welcoming tone. Use emojis suitable for a cozy cafe (☕️, 🍰, 🥐, 🌸, etc.).
73
  2. If anyone asks who you are or who created you, say: "من نیلا هستم، دستیار هوشمند کافه AI که توسط تیم الگوریتم داده نسترن توسعه پیدا کردم."
 
80
 
81
  ADMIN_SYSTEM_INSTRUCTION = """Your name is Nila, the smart cafe administrator assistant for "Cafe AI". You were developed by "Nastaran Data Algorithm".
82
  Your role is to help the cafe staff manage their active menu. You can update item availabilities, edit menu details, and process commands.
 
83
  Rules you must strictly follow:
84
  1. Speak in Farsi (Persian) in a professional, clear, and polite tone.
85
  2. If anyone asks who you are or who created you, say: "من نیلا هستم، دستیار هوشمند مدیریت که توسط تیم الگوریتم داده نسترن توسعه پیدا کردم."
 
115
  table_number: The table number (e.g. '3')
116
  items: A list of ordered items, each containing a name and a quantity.
117
  """
118
+ items_list = []
119
+ # پردازش دفاعی برای پشتیبانی همزمان از کلاس‌های Pydantic و ساختارهای دیکشنری خام
120
+ for item in items:
121
+ if isinstance(item, dict):
122
+ items_list.append({
123
+ "name": item.get("name", ""),
124
+ "quantity": item.get("quantity", 1)
125
+ })
126
+ elif hasattr(item, "name"):
127
+ items_list.append({
128
+ "name": item.name,
129
+ "quantity": item.quantity
130
+ })
131
+ else:
132
+ try:
133
+ items_list.append({
134
+ "name": getattr(item, "name", str(item)),
135
+ "quantity": getattr(item, "quantity", 1)
136
+ })
137
+ except Exception:
138
+ pass
139
 
140
  global_drafts[table_number] = {
141
  "table": table_number,
 
180
  return f"آیتم '{item_name}' در منوی کافه یافت نشد."
181
 
182
 
183
+ # --- روت‌های مسیریابی آدرس‌ها ---
184
 
185
  @app.route('/')
186
+ def home():
187
+ # فرستادن مقدار None به صفحه کلاینت تا ابتدا فرم انتخاب شماره میز باز شود
188
+ return render_template("customer.html", table_number=None)
189
 
190
  @app.route('/customer/<table_number>')
191
  def customer_chat_page(table_number):
 
200
 
201
  @app.route('/api/customer/chat_stream/<table_number>', methods=['POST'])
202
  def api_customer_chat_stream(table_number):
203
+ # پشتیبانی از کلید عمومی پیش‌فرض در صورت عدم تنظیم متغیرهای اختصاصی
204
+ api_key = os.environ.get("NILLA_CUSTOMER") or os.environ.get("GEMINI_API_KEY")
205
  if not api_key:
206
+ return jsonify({"error": "کلید API در سرور یافت نشد. لطفاً NILLA_CUSTOMER یا GEMINI_API_KEY را بررسی کنید."}), 500
207
 
208
  data = request.json or {}
209
  user_message = data.get("message")
 
219
 
220
  def generate_chunks():
221
  try:
222
+ # بازسازی ساختار تاریخچه به جز پیام جاری برای ارسال مستقیم به موتور چت استریمینگ
223
  contents = []
224
+ for msg in table_chat_histories[table_number][:-1]:
225
  role = "user" if msg["role"] == "user" else "model"
226
  contents.append(
227
  types.Content(
 
235
 
236
  dynamic_instruction = CUSTOMER_SYSTEM_INSTRUCTION + f"\nYou are currently serving table: {table_number}. Maintain context based on this table number."
237
 
238
+ # تنظیم بودجه تفکر روی صفر جهت دریافت سریع‌ترین پاسخ با حداقل تاخیر برای مشتری
239
  config = types.GenerateContentConfig(
240
  system_instruction=dynamic_instruction,
241
  tools=[get_menu, prepare_order_draft],
242
+ thinking_config=types.ThinkingConfig(thinking_budget=0)
243
  )
244
 
245
+ # استفاده از موتور چت SDK جهت مدیریت اتوماتیک و اجرای موازی ابزارها (Automatic Function Execution)
246
+ chat = client.chats.create(
247
  model="gemini-3.1-flash-lite",
248
+ history=contents,
249
  config=config
250
+ )
251
+
252
+ response_stream = chat.send_message_stream(user_message)
253
+
254
+ for chunk in response_stream:
255
  if not active_tasks.get(table_number, True):
256
  break
257
 
 
262
  if full_generated_text:
263
  table_chat_histories[table_number].append({"role": "model", "content": full_generated_text})
264
 
265
+ # بررسی و ارسال پیش‌نویس سفارش در صورتی که ابزار مربوطه در چرخه گفتگو اجرا شده باشد
266
  draft = global_drafts.get(table_number)
267
  if draft:
268
  yield f"data: {json.dumps({'type': 'draft', 'items': draft['items']}, ensure_ascii=False)}\n\n"
 
305
  orders.append(new_order)
306
  save_orders(orders)
307
 
308
+ # آزاد کردن سشن و سوابق حافظه موقت
309
  table_chat_histories.pop(table_number, None)
310
  global_drafts.pop(table_number, None)
311
  active_tasks.pop(table_number, None)
 
344
 
345
  @app.route('/api/admin/chat', methods=['POST'])
346
  def api_admin_chat():
347
+ api_key = os.environ.get("NILLA_CAFE") or os.environ.get("GEMINI_API_KEY")
348
  if not api_key:
349
+ return jsonify({"error": "کلید API یافت نشد."}), 500
350
 
351
  data = request.json or {}
352
  messages = data.get("messages", [])
353
+ if not messages:
354
+ return jsonify({"error": "تاریخچه پیام‌ها خالی است."}), 400
355
 
356
  try:
357
  client = genai.Client(api_key=api_key)
358
  contents = []
359
+ # تبدیل ساختار پیام‌ها به فرمت Content شیءگرا، به استثنای پیام آخر
360
+ for msg in messages[:-1]:
361
  role = "user" if msg["role"] == "user" else "model"
362
  contents.append(
363
  types.Content(
 
369
  config = types.GenerateContentConfig(
370
  system_instruction=ADMIN_SYSTEM_INSTRUCTION,
371
  tools=[get_full_menu, set_item_availability],
372
+ thinking_config=types.ThinkingConfig(thinking_budget=0)
373
  )
374
 
375
+ last_user_message = messages[-1]["content"]
376
+
377
+ # استفاده از مدل گفتگو چت جهت اعمال خودکار تغییرات روی منو
378
+ chat = client.chats.create(
379
  model="gemini-3.1-flash-lite",
380
+ history=contents,
381
  config=config
382
  )
383
 
384
+ response = chat.send_message(last_user_message)
385
+
386
  return jsonify({
387
  "success": True,
388
  "response": response.text
 
393
 
394
  @app.route('/api/admin/extract_menu', methods=['POST'])
395
  def api_extract_menu():
396
+ api_key = os.environ.get("NILLA_CAFE") or os.environ.get("GEMINI_API_KEY")
397
  if not api_key:
398
+ return jsonify({"error": "کلید API یافت نشد."}), 500
399
 
400
  if "file" not in request.files:
401
  return jsonify({"error": "فایل ارسال نشده"}), 400