yahya1912 commited on
Commit
4820df3
·
verified ·
1 Parent(s): db50d87
Files changed (1) hide show
  1. app.py +36 -31
app.py CHANGED
@@ -3,16 +3,13 @@ from huggingface_hub import InferenceClient
3
  import json, re, os, requests
4
  import csv
5
  from io import StringIO
6
- import functools
7
- import time
8
 
9
  # API Keys from Space settings
10
  hf_token = os.getenv("HF_TOKEN")
11
  google_key = os.getenv("GOOGLE_MAPS_API_KEY")
12
 
13
- # Initialize Client with ALLaM model
14
- # Note: Using the preview instruct version of ALLaM for best Arabic dialect support
15
- client = InferenceClient("sdaia/allam-7b", token=hf_token)
16
 
17
  SYSTEM_PROMPT = """Analyze the Arabic map query and return ONLY a JSON object with these exact keys:
18
 
@@ -69,18 +66,17 @@ def build_search_query(parsed_data):
69
 
70
  return " ".join(parts)
71
 
72
- @functools.lru_cache(maxsize=128)
73
- def search_google_maps_new_api(parsed_data_json):
74
  """استخدام Places API (New) v1"""
75
- parsed_data = json.loads(parsed_data_json)
76
  if not google_key:
77
  return "⚠️ الرجاء إضافة GOOGLE_MAPS_API_KEY في إعدادات الـ Space."
78
 
79
  query = build_search_query(parsed_data)
80
 
81
  if not query.strip():
82
- query = f"{parsed_data.get('category', '')} {parsed_data.get('location', '')}"
83
 
 
84
  url = "https://places.googleapis.com/v1/places:searchText"
85
 
86
  headers = {
@@ -95,7 +91,11 @@ def search_google_maps_new_api(parsed_data_json):
95
  "maxResultCount": 10
96
  }
97
 
 
98
  sort_by = parsed_data.get("sort_by", "relevance")
 
 
 
99
 
100
  try:
101
  response = requests.post(url, headers=headers, json=data, timeout=15)
@@ -103,6 +103,7 @@ def search_google_maps_new_api(parsed_data_json):
103
 
104
  if response.status_code != 200:
105
  error_msg = result.get("error", {}).get("message", "Unknown error")
 
106
  if "not enabled" in error_msg.lower() or response.status_code == 403:
107
  return search_google_maps_legacy(parsed_data, query)
108
  return f"⚠️ خطأ في API: {error_msg}"
@@ -112,6 +113,7 @@ def search_google_maps_new_api(parsed_data_json):
112
  if not places:
113
  return "❌ لم يتم العثور على نتائج. جرب تعديل البحث."
114
 
 
115
  if sort_by == "rating":
116
  places = sorted(places, key=lambda x: x.get("rating", 0), reverse=True)
117
  elif sort_by == "price":
@@ -120,6 +122,7 @@ def search_google_maps_new_api(parsed_data_json):
120
  "PRICE_LEVEL_EXPENSIVE": 4, "PRICE_LEVEL_VERY_EXPENSIVE": 5}
121
  places = sorted(places, key=lambda x: price_order.get(x.get("priceLevel"), 99))
122
 
 
123
  output_list = []
124
  for i, place in enumerate(places[:6], 1):
125
  name = place.get("displayName", {}).get("text", "غير معروف")
@@ -127,6 +130,7 @@ def search_google_maps_new_api(parsed_data_json):
127
  total_ratings = place.get("userRatingCount", 0)
128
  address = place.get("formattedAddress", "العنوان غير متوفر")
129
 
 
130
  price_level = place.get("priceLevel")
131
  price_map = {
132
  "PRICE_LEVEL_INEXPENSIVE": "💰",
@@ -136,10 +140,16 @@ def search_google_maps_new_api(parsed_data_json):
136
  }
137
  price_str = price_map.get(price_level, "")
138
 
 
139
  hours = place.get("currentOpeningHours", {})
140
  open_now = hours.get("openNow")
141
- status = " ✅ مفتوح الآن" if open_now is True else " ❌ مغلق الآن" if open_now is False else ""
 
 
 
 
142
 
 
143
  types = place.get("types", [])
144
  place_type = ", ".join([t.replace("_", " ").replace("restaurant", "مطعم").replace("cafe", "مقهى")
145
  .replace("hospital", "مستشفى").replace("park", "حديقة")
@@ -181,7 +191,7 @@ def search_google_maps_legacy(parsed_data, query=None):
181
  data = response.json()
182
 
183
  if data.get("status") != "OK":
184
- return f"⚠️ خطأ في Legacy API أيضاً: {data.get('status')}. الرجاء تفعيل Places API (New) في Google Cloud Console."
185
 
186
  results = data.get("results", [])
187
 
@@ -215,9 +225,8 @@ def search_google_maps_legacy(parsed_data, query=None):
215
  except Exception as e:
216
  return f"❌ فشل في Legacy API أيضاً: {str(e)}"
217
 
218
- @functools.lru_cache(maxsize=128)
219
  def parse_arabic_query(user_query):
220
- """تحليل الاستعلام باستخدام ALLaM LLM"""
221
  try:
222
  messages = [
223
  {"role": "system", "content": SYSTEM_PROMPT},
@@ -226,7 +235,7 @@ def parse_arabic_query(user_query):
226
 
227
  response = client.chat_completion(
228
  messages=messages,
229
- max_tokens=150, # Optimized for speed
230
  temperature=0.1,
231
  stream=False
232
  )
@@ -249,7 +258,6 @@ def parse_arabic_query(user_query):
249
  return {"error": f"خطأ في التحليل: {str(e)}"}
250
 
251
  def main_process(user_query):
252
- start_time = time.perf_counter()
253
  if not user_query.strip():
254
  return {"error": "الرجاء إدخال استعلام"}, "لا توجد نتائج"
255
 
@@ -258,16 +266,9 @@ def main_process(user_query):
258
  if "error" in parsed_data:
259
  return parsed_data, f"⚠️ {parsed_data.get('error', 'خطأ غير معروف')}"
260
 
261
- # Convert dict to string for caching
262
- parsed_data_json = json.dumps(parsed_data, sort_keys=True)
263
- map_results = search_google_maps_new_api(parsed_data_json)
264
 
265
- end_time = time.perf_counter()
266
- response_time_ms = (end_time - start_time) * 1000
267
-
268
- map_results_with_time = f"⏱️ وقت الاستجابة: {response_time_ms:.2f} مللي ثانية\n\n" + map_results
269
-
270
- return parsed_data, map_results_with_time
271
 
272
  def process_batch_queries(file_obj, progress=gr.Progress()):
273
  if file_obj is None:
@@ -290,8 +291,10 @@ def process_batch_queries(file_obj, progress=gr.Progress()):
290
  for i, query in enumerate(progress.tqdm(queries, desc="معالجة الاستعلامات")):
291
  parsed_data, map_results = main_process(query)
292
 
 
293
  parsed_json_str = json.dumps(parsed_data, ensure_ascii=False) if "error" not in parsed_data else parsed_data.get("error", "")
294
 
 
295
  top_result_name = ""
296
  if "نتائج البحث عن" in map_results and "📍" in map_results:
297
  match = re.search(r'### \d+\. 📍 ([^\n]+)', map_results)
@@ -299,22 +302,24 @@ def process_batch_queries(file_obj, progress=gr.Progress()):
299
  top_result_name = match.group(1).strip()
300
  elif "لم يتم العثور على نتائج" in map_results:
301
  top_result_name = "لا توجد نتائج"
302
- else:
303
- top_result_name = "خطأ أو لا توجد نتائج"
304
 
305
  results.append({
306
  "Original Query": query,
307
  "Parsed JSON": parsed_json_str,
308
  "Top Map Result": top_result_name,
309
- "Full Map Results": map_results.replace('\n', ' ')
310
  })
311
 
 
312
  output_buffer = StringIO()
313
  fieldnames = ["Original Query", "Parsed JSON", "Top Map Result", "Full Map Results"]
314
  writer = csv.DictWriter(output_buffer, fieldnames=fieldnames)
315
  writer.writeheader()
316
  writer.writerows(results)
317
 
 
318
  output_filepath = "./batch_results.csv"
319
  with open(output_filepath, 'w', encoding='utf-8') as f:
320
  f.write(output_buffer.getvalue())
@@ -325,17 +330,17 @@ def process_batch_queries(file_obj, progress=gr.Progress()):
325
 
326
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
327
  gr.Markdown("""
328
- # 🗺️ تطبيق البحث الذكي في الخرائط (مدعوم بـ ALLaM)
329
  **يدعم جميع الفئات:** مطاعم، فنادق، مستشفيات، محطات وقود، حدائق، متاجر، وغيرها...
330
 
331
- 🚀 **تم التحديث لاستخدام نموذج ALLaM المتخصص في اللغة العربية لنتائج أدق وأسرع.**
332
  """)
333
 
334
  with gr.Tab("استعلام فردي"):
335
  with gr.Row():
336
  input_text = gr.Textbox(
337
  label="📝 ماذا تبحث عنه؟",
338
- placeholder="مثال: أرخص مستشفى طوارئ في جدة، أو حديقة أطفال نظيفة في الرياض",
339
  lines=2
340
  )
341
 
@@ -343,7 +348,7 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
343
 
344
  with gr.Row():
345
  with gr.Column(scale=1):
346
- output_json = gr.JSON(label="⚙️ تحليل ALLaM")
347
  with gr.Column(scale=2):
348
  output_map = gr.Markdown(label="🗺️ نتائج Google Maps")
349
 
 
3
  import json, re, os, requests
4
  import csv
5
  from io import StringIO
 
 
6
 
7
  # API Keys from Space settings
8
  hf_token = os.getenv("HF_TOKEN")
9
  google_key = os.getenv("GOOGLE_MAPS_API_KEY")
10
 
11
+ # Initialize Client
12
+ client = InferenceClient("meta-llama/Meta-Llama-3.1-8B-Instruct", token=hf_token)
 
13
 
14
  SYSTEM_PROMPT = """Analyze the Arabic map query and return ONLY a JSON object with these exact keys:
15
 
 
66
 
67
  return " ".join(parts)
68
 
69
+ def search_google_maps_new_api(parsed_data):
 
70
  """استخدام Places API (New) v1"""
 
71
  if not google_key:
72
  return "⚠️ الرجاء إضافة GOOGLE_MAPS_API_KEY في إعدادات الـ Space."
73
 
74
  query = build_search_query(parsed_data)
75
 
76
  if not query.strip():
77
+ query = f"{parsed_data.get("category", "")} {parsed_data.get("location", "")}"
78
 
79
+ # Places API (New) - Text Search v1
80
  url = "https://places.googleapis.com/v1/places:searchText"
81
 
82
  headers = {
 
91
  "maxResultCount": 10
92
  }
93
 
94
+ # إضافة تفضيل الموقع إذا كان الترتيب حسب المسافة
95
  sort_by = parsed_data.get("sort_by", "relevance")
96
+ if sort_by == "distance" and parsed_data.get("location") != "near me":
97
+ # يمكن إضافة locationBias لاحقاً
98
+ pass
99
 
100
  try:
101
  response = requests.post(url, headers=headers, json=data, timeout=15)
 
103
 
104
  if response.status_code != 200:
105
  error_msg = result.get("error", {}).get("message", "Unknown error")
106
+ # إذا كان الخطأ بسبب عدم تفعيل API، نحاول الـ Legacy كاحتياط
107
  if "not enabled" in error_msg.lower() or response.status_code == 403:
108
  return search_google_maps_legacy(parsed_data, query)
109
  return f"⚠️ خطأ في API: {error_msg}"
 
113
  if not places:
114
  return "❌ لم يتم العثور على نتائج. جرب تعديل البحث."
115
 
116
+ # ترتيب النتائج
117
  if sort_by == "rating":
118
  places = sorted(places, key=lambda x: x.get("rating", 0), reverse=True)
119
  elif sort_by == "price":
 
122
  "PRICE_LEVEL_EXPENSIVE": 4, "PRICE_LEVEL_VERY_EXPENSIVE": 5}
123
  places = sorted(places, key=lambda x: price_order.get(x.get("priceLevel"), 99))
124
 
125
+ # تنسيق النتائج
126
  output_list = []
127
  for i, place in enumerate(places[:6], 1):
128
  name = place.get("displayName", {}).get("text", "غير معروف")
 
130
  total_ratings = place.get("userRatingCount", 0)
131
  address = place.get("formattedAddress", "العنوان غير متوفر")
132
 
133
+ # مستوى السعر
134
  price_level = place.get("priceLevel")
135
  price_map = {
136
  "PRICE_LEVEL_INEXPENSIVE": "💰",
 
140
  }
141
  price_str = price_map.get(price_level, "")
142
 
143
+ # حالة الافتتاح
144
  hours = place.get("currentOpeningHours", {})
145
  open_now = hours.get("openNow")
146
+ status = ""
147
+ if open_now is True:
148
+ status = " ✅ مفتوح الآن"
149
+ elif open_now is False:
150
+ status = " ❌ مغلق الآن"
151
 
152
+ # الأنواع
153
  types = place.get("types", [])
154
  place_type = ", ".join([t.replace("_", " ").replace("restaurant", "مطعم").replace("cafe", "مقهى")
155
  .replace("hospital", "مستشفى").replace("park", "حديقة")
 
191
  data = response.json()
192
 
193
  if data.get("status") != "OK":
194
+ return f"⚠️ خطأ في Legacy API أيضاً: {data.get("status")}. الرجاء تفعيل Places API (New) في Google Cloud Console."
195
 
196
  results = data.get("results", [])
197
 
 
225
  except Exception as e:
226
  return f"❌ فشل في Legacy API أيضاً: {str(e)}"
227
 
 
228
  def parse_arabic_query(user_query):
229
+ """تحليل الاستعلام باستخدام LLM"""
230
  try:
231
  messages = [
232
  {"role": "system", "content": SYSTEM_PROMPT},
 
235
 
236
  response = client.chat_completion(
237
  messages=messages,
238
+ max_tokens=300,
239
  temperature=0.1,
240
  stream=False
241
  )
 
258
  return {"error": f"خطأ في التحليل: {str(e)}"}
259
 
260
  def main_process(user_query):
 
261
  if not user_query.strip():
262
  return {"error": "الرجاء إدخال استعلام"}, "لا توجد نتائج"
263
 
 
266
  if "error" in parsed_data:
267
  return parsed_data, f"⚠️ {parsed_data.get('error', 'خطأ غير معروف')}"
268
 
269
+ map_results = search_google_maps_new_api(parsed_data)
 
 
270
 
271
+ return parsed_data, map_results
 
 
 
 
 
272
 
273
  def process_batch_queries(file_obj, progress=gr.Progress()):
274
  if file_obj is None:
 
291
  for i, query in enumerate(progress.tqdm(queries, desc="معالجة الاستعلامات")):
292
  parsed_data, map_results = main_process(query)
293
 
294
+ # Flatten parsed_data for CSV, handle errors
295
  parsed_json_str = json.dumps(parsed_data, ensure_ascii=False) if "error" not in parsed_data else parsed_data.get("error", "")
296
 
297
+ # Extract top result name if available, otherwise indicate no results
298
  top_result_name = ""
299
  if "نتائج البحث عن" in map_results and "📍" in map_results:
300
  match = re.search(r'### \d+\. 📍 ([^\n]+)', map_results)
 
302
  top_result_name = match.group(1).strip()
303
  elif "لم يتم العثور على نتائج" in map_results:
304
  top_result_name = "لا توجد نتائج"
305
+ elif "خطأ" in map_results:
306
+ top_result_name = f"خطأ في API: {map_results}"
307
 
308
  results.append({
309
  "Original Query": query,
310
  "Parsed JSON": parsed_json_str,
311
  "Top Map Result": top_result_name,
312
+ "Full Map Results": map_results.replace('\n', ' ') # Replace newlines for CSV readability
313
  })
314
 
315
+ # Write results to a CSV in memory
316
  output_buffer = StringIO()
317
  fieldnames = ["Original Query", "Parsed JSON", "Top Map Result", "Full Map Results"]
318
  writer = csv.DictWriter(output_buffer, fieldnames=fieldnames)
319
  writer.writeheader()
320
  writer.writerows(results)
321
 
322
+ # Save to a temporary file for Gradio to handle
323
  output_filepath = "./batch_results.csv"
324
  with open(output_filepath, 'w', encoding='utf-8') as f:
325
  f.write(output_buffer.getvalue())
 
330
 
331
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
332
  gr.Markdown("""
333
+ # 🗺️ تطبيق البحث الذكي في الخرائط
334
  **يدعم جميع الفئات:** مطاعم، فنادق، مستشفيات، محطات وقود، حدائق، متاجر، وغيرها...
335
 
336
+ ⚠️ **تأكد من تفعيل Places API (New) في Google Cloud Console**
337
  """)
338
 
339
  with gr.Tab("استعلام فردي"):
340
  with gr.Row():
341
  input_text = gr.Textbox(
342
  label="📝 ماذا تبحث عنه؟",
343
+ placeholder="��ثال: أرخص مستشفى طوارئ في جدة، أو حديقة أطفال نظيفة في الرياض، أو فندق 5 نجوم في دبي",
344
  lines=2
345
  )
346
 
 
348
 
349
  with gr.Row():
350
  with gr.Column(scale=1):
351
+ output_json = gr.JSON(label="⚙️ تحليل الذكاء الاصطناعي")
352
  with gr.Column(scale=2):
353
  output_map = gr.Markdown(label="🗺️ نتائج Google Maps")
354