humanvprojectceo commited on
Commit
0dd3278
·
verified ·
1 Parent(s): 6c8b745

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +18 -36
app.py CHANGED
@@ -1,7 +1,6 @@
1
  import os
2
  import json
3
  import time
4
- import asyncio
5
  from flask import Flask, request, Response, jsonify, render_template, redirect
6
  from google import genai
7
  from google.genai import types
@@ -157,25 +156,25 @@ def set_item_availability(item_name: str, available: bool) -> str:
157
  # --- روت‌های مسیریابی آدرس‌ها (Domain URL Mapping) ---
158
 
159
  @app.route('/')
160
- async def home_redirect():
161
  # ریدایرکت روت اصلی به میز ۱ به عنوان پیش‌فرض
162
  return redirect('/customer/1')
163
 
164
  @app.route('/customer/<table_number>')
165
- async def customer_chat_page(table_number):
166
  # سرو کردن صفحه چت مشتری برای میز انتخابی
167
  return render_template("customer.html", table_number=table_number)
168
 
169
  @app.route('/dashboard')
170
- async def admin_dashboard_page():
171
  # سرو کردن پنل مدیریت کافه
172
  return render_template("cafe.html")
173
 
174
 
175
- # --- مسیرهای ناهمگام سرویس‌های API مشتری ---
176
 
177
  @app.route('/api/customer/chat_stream/<table_number>', methods=['POST'])
178
- async def api_customer_chat_stream(table_number):
179
  api_key = os.environ.get("NILLA_CUSTOMER")
180
  if not api_key:
181
  return jsonify({"error": "کلید NILLA_CUSTOMER در سرور یافت نشد."}), 500
@@ -185,20 +184,15 @@ async def api_customer_chat_stream(table_number):
185
  if not user_message:
186
  return jsonify({"error": "پیام خالی است."}), 400
187
 
188
- # بررسی و تعریف آرایه سابقه چت برای میز جاری
189
  if table_number not in table_chat_histories:
190
  table_chat_histories[table_number] = []
191
 
192
- # پاک کردن سفارش موقت قبلی جهت استخراج فاکتور جدید
193
  global_drafts.pop(table_number, None)
194
-
195
- # افزودن پیام مشتری به آرشیو سوابق میز
196
  table_chat_histories[table_number].append({"role": "user", "content": user_message})
197
  active_tasks[table_number] = True
198
 
199
- async def generate_chunks():
200
  try:
201
- # تبدیل ساختار آرشیو سوابق به انواع داده‌ای مورد پذیرش SDK جدید
202
  contents = []
203
  for msg in table_chat_histories[table_number]:
204
  role = "user" if msg["role"] == "user" else "model"
@@ -209,7 +203,6 @@ async def api_customer_chat_stream(table_number):
209
  )
210
  )
211
 
212
- # راه‌اندازی کلاینت ناهمگام گوگل با متد aio
213
  client = genai.Client(api_key=api_key)
214
  full_generated_text = ""
215
 
@@ -221,13 +214,12 @@ async def api_customer_chat_stream(table_number):
221
  thinking_config=types.ThinkingConfig(thinking_level="MINIMAL")
222
  )
223
 
224
- # فراخوانی استریم ناهمگام گوگل
225
- async for chunk in await client.aio.models.generate_content_stream(
226
  model="gemini-3.1-flash-lite",
227
  contents=contents,
228
  config=config
229
  ):
230
- # اگر کلاینت دکمه توقف را زده باشد، استریم بلافاصله خاتمه یابد
231
  if not active_tasks.get(table_number, True):
232
  break
233
 
@@ -235,14 +227,11 @@ async def api_customer_chat_stream(table_number):
235
  full_generated_text += chunk.text
236
  yield f"data: {json.dumps({'type': 'text', 'content': chunk.text}, ensure_ascii=False)}\n\n"
237
 
238
- # ذخیره پاسخ مدل در پایان جریان چت برای پیوستگی گفتگوهای بعد
239
  if full_generated_text:
240
  table_chat_histories[table_number].append({"role": "model", "content": full_generated_text})
241
 
242
- # بررسی اینکه آیا ابزار ثبت سفارش (prepare_order_draft) صدا زده شده بود
243
  draft = global_drafts.get(table_number)
244
  if draft:
245
- # ارسال فاکتور استخراج شده به سمت فرانت‌اند جهت نمایش دکمه تایید نهایی دو مرحله‌ای
246
  yield f"data: {json.dumps({'type': 'draft', 'items': draft['items']}, ensure_ascii=False)}\n\n"
247
 
248
  except Exception as e:
@@ -253,13 +242,12 @@ async def api_customer_chat_stream(table_number):
253
  return Response(generate_chunks(), mimetype='text/event-stream')
254
 
255
  @app.route('/api/customer/stop/<table_number>', methods=['POST'])
256
- async def api_customer_stop(table_number):
257
- # متوقف کردن زنده فرآیند استریمینگ در سمت بک‌اند
258
  active_tasks[table_number] = False
259
  return jsonify({"success": True, "message": "فرآیند تولید متوقف شد."})
260
 
261
  @app.route('/api/confirm_order', methods=['POST'])
262
- async def api_confirm_order():
263
  data = request.json or {}
264
  table_number = data.get("table_number")
265
  items = data.get("items")
@@ -268,7 +256,6 @@ async def api_confirm_order():
268
  return jsonify({"error": "شماره میز مشخص نیست."}), 400
269
 
270
  if not items:
271
- # در صورت نبود آیتم‌ها تلاش مجدد برای خواندن از درفت سرور انجام می‌شود
272
  draft = global_drafts.get(table_number)
273
  if not draft:
274
  return jsonify({"error": "اطلاعات پیش‌نویس یافت نشد."}), 404
@@ -285,7 +272,6 @@ async def api_confirm_order():
285
  orders.append(new_order)
286
  save_orders(orders)
287
 
288
- # --- پاکسازی نهایی جهت آزادسازی منابع سیستم (نیاز ادمین) ---
289
  table_chat_histories.pop(table_number, None)
290
  global_drafts.pop(table_number, None)
291
  active_tasks.pop(table_number, None)
@@ -293,16 +279,16 @@ async def api_confirm_order():
293
  return jsonify({"success": True, "message": "سفارش ثبت شد و سشن میز کاملاً آزاد گردید."})
294
 
295
 
296
- # --- مسیرهای ناهمگام سرویس‌های API مدیریت کافه ---
297
 
298
  @app.route('/api/admin/orders', methods=['GET'])
299
- async def api_admin_orders():
300
  orders = load_orders()
301
  active_orders = [o for o in orders if o.get("status", "pending") == "pending"]
302
  return jsonify(active_orders)
303
 
304
  @app.route('/api/admin/complete_order', methods=['POST'])
305
- async def api_admin_complete_order():
306
  data = request.json or {}
307
  order_id = data.get("order_id")
308
 
@@ -323,7 +309,7 @@ async def api_admin_complete_order():
323
  return jsonify({"error": "سفارش پیدا نشد."}), 404
324
 
325
  @app.route('/api/admin/chat', methods=['POST'])
326
- async def api_admin_chat():
327
  api_key = os.environ.get("NILLA_CAFE")
328
  if not api_key:
329
  return jsonify({"error": "کلید NILLA_CAFE یافت نشد."}), 500
@@ -349,8 +335,7 @@ async def api_admin_chat():
349
  thinking_config=types.ThinkingConfig(thinking_level="MINIMAL")
350
  )
351
 
352
- # استفاده از متد فراخوانی ناهمگام aio برای جلوگیری از بلاک شدن سایر کاربران
353
- response = await client.aio.models.generate_content(
354
  model="gemini-3.1-flash-lite",
355
  contents=contents,
356
  config=config
@@ -365,7 +350,7 @@ async def api_admin_chat():
365
  return jsonify({"error": f"خطا در بخش سرور: {str(e)}"}), 500
366
 
367
  @app.route('/api/admin/extract_menu', methods=['POST'])
368
- async def api_extract_menu():
369
  api_key = os.environ.get("NILLA_CAFE")
370
  if not api_key:
371
  return jsonify({"error": "کلید NILLA_CAFE یافت نشد."}), 500
@@ -394,8 +379,7 @@ async def api_extract_menu():
394
  client = genai.Client(api_key=api_key)
395
  file_part = types.Part.from_bytes(data=file_bytes, mime_type=mime_type)
396
 
397
- # استخراج منوی آپلود شده به صورت ناهمگام
398
- response = await client.aio.models.generate_content(
399
  model="gemini-3.1-flash-lite",
400
  contents=[file_part, prompt],
401
  config=types.GenerateContentConfig(
@@ -413,18 +397,16 @@ async def api_extract_menu():
413
  return jsonify({"error": f"خطا در استخراج ساختار: {str(e)}"}), 500
414
 
415
  @app.route('/api/admin/save_menu', methods=['POST'])
416
- async def api_admin_save_menu():
417
  data = request.json or {}
418
  menu_list = data.get("menu")
419
 
420
  if not isinstance(menu_list, list):
421
  return jsonify({"error": "فرمت داده ارسال شده نادرست است."}), 400
422
 
423
- # ثبت نهایی منو در قالب فایل دیتابیس ثابت menu.json
424
  save_menu(menu_list)
425
  return jsonify({"success": True, "message": "منوی فعال با موفقیت ثبت شد."})
426
 
427
 
428
  if __name__ == "__main__":
429
- # راه‌اندازی سرور
430
  app.run(host="0.0.0.0", port=7860, debug=True)
 
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
 
156
  # --- روت‌های مسیریابی آدرس‌ها (Domain URL Mapping) ---
157
 
158
  @app.route('/')
159
+ def home_redirect():
160
  # ریدایرکت روت اصلی به میز ۱ به عنوان پیش‌فرض
161
  return redirect('/customer/1')
162
 
163
  @app.route('/customer/<table_number>')
164
+ def customer_chat_page(table_number):
165
  # سرو کردن صفحه چت مشتری برای میز انتخابی
166
  return render_template("customer.html", table_number=table_number)
167
 
168
  @app.route('/dashboard')
169
+ def admin_dashboard_page():
170
  # سرو کردن پنل مدیریت کافه
171
  return render_template("cafe.html")
172
 
173
 
174
+ # --- مسیرهای پایدار سرویس‌های API مشتری ---
175
 
176
  @app.route('/api/customer/chat_stream/<table_number>', methods=['POST'])
177
+ def api_customer_chat_stream(table_number):
178
  api_key = os.environ.get("NILLA_CUSTOMER")
179
  if not api_key:
180
  return jsonify({"error": "کلید NILLA_CUSTOMER در سرور یافت نشد."}), 500
 
184
  if not user_message:
185
  return jsonify({"error": "پیام خالی است."}), 400
186
 
 
187
  if table_number not in table_chat_histories:
188
  table_chat_histories[table_number] = []
189
 
 
190
  global_drafts.pop(table_number, None)
 
 
191
  table_chat_histories[table_number].append({"role": "user", "content": user_message})
192
  active_tasks[table_number] = True
193
 
194
+ def generate_chunks():
195
  try:
 
196
  contents = []
197
  for msg in table_chat_histories[table_number]:
198
  role = "user" if msg["role"] == "user" else "model"
 
203
  )
204
  )
205
 
 
206
  client = genai.Client(api_key=api_key)
207
  full_generated_text = ""
208
 
 
214
  thinking_config=types.ThinkingConfig(thinking_level="MINIMAL")
215
  )
216
 
217
+ # فراخوانی جریان استریم همگام و پایدار
218
+ for chunk in client.models.generate_content_stream(
219
  model="gemini-3.1-flash-lite",
220
  contents=contents,
221
  config=config
222
  ):
 
223
  if not active_tasks.get(table_number, True):
224
  break
225
 
 
227
  full_generated_text += chunk.text
228
  yield f"data: {json.dumps({'type': 'text', 'content': chunk.text}, ensure_ascii=False)}\n\n"
229
 
 
230
  if full_generated_text:
231
  table_chat_histories[table_number].append({"role": "model", "content": full_generated_text})
232
 
 
233
  draft = global_drafts.get(table_number)
234
  if draft:
 
235
  yield f"data: {json.dumps({'type': 'draft', 'items': draft['items']}, ensure_ascii=False)}\n\n"
236
 
237
  except Exception as e:
 
242
  return Response(generate_chunks(), mimetype='text/event-stream')
243
 
244
  @app.route('/api/customer/stop/<table_number>', methods=['POST'])
245
+ def api_customer_stop(table_number):
 
246
  active_tasks[table_number] = False
247
  return jsonify({"success": True, "message": "فرآیند تولید متوقف شد."})
248
 
249
  @app.route('/api/confirm_order', methods=['POST'])
250
+ def api_confirm_order():
251
  data = request.json or {}
252
  table_number = data.get("table_number")
253
  items = data.get("items")
 
256
  return jsonify({"error": "شماره میز مشخص نیست."}), 400
257
 
258
  if not items:
 
259
  draft = global_drafts.get(table_number)
260
  if not draft:
261
  return jsonify({"error": "اطلاعات پیش‌نویس یافت نشد."}), 404
 
272
  orders.append(new_order)
273
  save_orders(orders)
274
 
 
275
  table_chat_histories.pop(table_number, None)
276
  global_drafts.pop(table_number, None)
277
  active_tasks.pop(table_number, None)
 
279
  return jsonify({"success": True, "message": "سفارش ثبت شد و سشن میز کاملاً آزاد گردید."})
280
 
281
 
282
+ # --- سرویس‌های API مدیریت کافه ---
283
 
284
  @app.route('/api/admin/orders', methods=['GET'])
285
+ def api_admin_orders():
286
  orders = load_orders()
287
  active_orders = [o for o in orders if o.get("status", "pending") == "pending"]
288
  return jsonify(active_orders)
289
 
290
  @app.route('/api/admin/complete_order', methods=['POST'])
291
+ def api_admin_complete_order():
292
  data = request.json or {}
293
  order_id = data.get("order_id")
294
 
 
309
  return jsonify({"error": "سفارش پیدا نشد."}), 404
310
 
311
  @app.route('/api/admin/chat', methods=['POST'])
312
+ def api_admin_chat():
313
  api_key = os.environ.get("NILLA_CAFE")
314
  if not api_key:
315
  return jsonify({"error": "کلید NILLA_CAFE یافت نشد."}), 500
 
335
  thinking_config=types.ThinkingConfig(thinking_level="MINIMAL")
336
  )
337
 
338
+ response = client.models.generate_content(
 
339
  model="gemini-3.1-flash-lite",
340
  contents=contents,
341
  config=config
 
350
  return jsonify({"error": f"خطا در بخش سرور: {str(e)}"}), 500
351
 
352
  @app.route('/api/admin/extract_menu', methods=['POST'])
353
+ def api_extract_menu():
354
  api_key = os.environ.get("NILLA_CAFE")
355
  if not api_key:
356
  return jsonify({"error": "کلید NILLA_CAFE یافت نشد."}), 500
 
379
  client = genai.Client(api_key=api_key)
380
  file_part = types.Part.from_bytes(data=file_bytes, mime_type=mime_type)
381
 
382
+ response = client.models.generate_content(
 
383
  model="gemini-3.1-flash-lite",
384
  contents=[file_part, prompt],
385
  config=types.GenerateContentConfig(
 
397
  return jsonify({"error": f"خطا در استخراج ساختار: {str(e)}"}), 500
398
 
399
  @app.route('/api/admin/save_menu', methods=['POST'])
400
+ def api_admin_save_menu():
401
  data = request.json or {}
402
  menu_list = data.get("menu")
403
 
404
  if not isinstance(menu_list, list):
405
  return jsonify({"error": "فرمت داده ارسال شده نادرست است."}), 400
406
 
 
407
  save_menu(menu_list)
408
  return jsonify({"success": True, "message": "منوی فعال با موفقیت ثبت شد."})
409
 
410
 
411
  if __name__ == "__main__":
 
412
  app.run(host="0.0.0.0", port=7860, debug=True)