Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,12 +1,14 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
from huggingface_hub import InferenceClient
|
| 3 |
import json, re, os, requests
|
|
|
|
|
|
|
| 4 |
|
| 5 |
-
#
|
| 6 |
hf_token = os.getenv("HF_TOKEN")
|
| 7 |
google_key = os.getenv("GOOGLE_MAPS_API_KEY")
|
| 8 |
|
| 9 |
-
#
|
| 10 |
client = InferenceClient("Qwen/Qwen2.5-7B-Instruct", token=hf_token)
|
| 11 |
|
| 12 |
SYSTEM_PROMPT = """Analyze the Arabic map query and return ONLY a JSON object with these exact keys:
|
|
@@ -31,7 +33,15 @@ SYSTEM_PROMPT = """Analyze the Arabic map query and return ONLY a JSON object wi
|
|
| 31 |
|
| 32 |
Examples:
|
| 33 |
Input: "أرخص وأقرب مستشفى طوارئ في جدة"
|
| 34 |
-
Output: {"location": "جدة", "category": "مستشفى", "sub_type": "طوارئ", "features": ["أرخص", "أقرب"], "sort_by": "price"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
|
| 36 |
def build_search_query(parsed_data):
|
| 37 |
"""بناء جملة بحث محسّنة"""
|
|
@@ -64,7 +74,7 @@ def search_google_maps_new_api(parsed_data):
|
|
| 64 |
query = build_search_query(parsed_data)
|
| 65 |
|
| 66 |
if not query.strip():
|
| 67 |
-
query = f"{parsed_data.get(
|
| 68 |
|
| 69 |
# Places API (New) - Text Search v1
|
| 70 |
url = "https://places.googleapis.com/v1/places:searchText"
|
|
@@ -143,7 +153,7 @@ def search_google_maps_new_api(parsed_data):
|
|
| 143 |
types = place.get("types", [])
|
| 144 |
place_type = ", ".join([t.replace("_", " ").replace("restaurant", "مطعم").replace("cafe", "مقهى")
|
| 145 |
.replace("hospital", "مستشفى").replace("park", "حديقة")
|
| 146 |
-
.replace("store", "متجر").replace("gas_station", "محطة وقود")
|
| 147 |
for t in types[:2]])
|
| 148 |
|
| 149 |
card = f"""### {i}. 📍 {name}
|
|
@@ -163,6 +173,9 @@ def search_google_maps_new_api(parsed_data):
|
|
| 163 |
|
| 164 |
def search_google_maps_legacy(parsed_data, query=None):
|
| 165 |
"""الرجوع إلى Legacy API كاحتياط"""
|
|
|
|
|
|
|
|
|
|
| 166 |
if not query:
|
| 167 |
query = build_search_query(parsed_data)
|
| 168 |
|
|
@@ -178,7 +191,7 @@ def search_google_maps_legacy(parsed_data, query=None):
|
|
| 178 |
data = response.json()
|
| 179 |
|
| 180 |
if data.get("status") != "OK":
|
| 181 |
-
return f"⚠️ خطأ في Legacy API أيضاً: {data.get(
|
| 182 |
|
| 183 |
results = data.get("results", [])
|
| 184 |
|
|
@@ -253,11 +266,66 @@ def main_process(user_query):
|
|
| 253 |
if "error" in parsed_data:
|
| 254 |
return parsed_data, f"⚠️ {parsed_data.get('error', 'خطأ غير معروف')}"
|
| 255 |
|
| 256 |
-
# استخدام الـ New API مع fallback للـ Legacy
|
| 257 |
map_results = search_google_maps_new_api(parsed_data)
|
| 258 |
|
| 259 |
return parsed_data, map_results
|
| 260 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 261 |
# ==================== واجهة المستخدم ====================
|
| 262 |
|
| 263 |
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
|
@@ -268,41 +336,58 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
|
| 268 |
⚠️ **تأكد من تفعيل Places API (New) في Google Cloud Console**
|
| 269 |
""")
|
| 270 |
|
| 271 |
-
with gr.
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 276 |
)
|
| 277 |
-
|
| 278 |
-
btn = gr.Button("🔍 ابحث الآن", variant="primary")
|
| 279 |
-
|
| 280 |
-
with gr.Row():
|
| 281 |
-
with gr.Column(scale=1):
|
| 282 |
-
output_json = gr.JSON(label="⚙️ تحليل الذكاء الاصطناعي")
|
| 283 |
-
with gr.Column(scale=2):
|
| 284 |
-
output_map = gr.Markdown(label="🗺️ نتائج Google Maps")
|
| 285 |
-
|
| 286 |
-
gr.Markdown("### 💡 أمثلة للبحث:")
|
| 287 |
-
examples = [
|
| 288 |
-
"أرخص وأقرب مستشفى طوارئ في جدة",
|
| 289 |
-
"حديقة أطفال نظيفة في الرياض",
|
| 290 |
-
"أفضل فندق 5 نجوم في دبي",
|
| 291 |
-
"مقهى هادئ للعمل في الخبر",
|
| 292 |
-
"محطة وقود 24 ساعة رخيصة",
|
| 293 |
-
"صيدلية قريبة مفتوحة الآن"
|
| 294 |
-
]
|
| 295 |
-
|
| 296 |
-
with gr.Row():
|
| 297 |
-
for ex in examples[:3]:
|
| 298 |
-
btn_ex = gr.Button(ex, size="sm")
|
| 299 |
-
btn_ex.click(lambda x=ex: x, None, input_text)
|
| 300 |
-
|
| 301 |
-
with gr.Row():
|
| 302 |
-
for ex in examples[3:]:
|
| 303 |
-
btn_ex = gr.Button(ex, size="sm")
|
| 304 |
-
btn_ex.click(lambda x=ex: x, None, input_text)
|
| 305 |
-
|
| 306 |
-
btn.click(fn=main_process, inputs=input_text, outputs=[output_json, output_map])
|
| 307 |
|
| 308 |
-
demo.launch()
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
from huggingface_hub import InferenceClient
|
| 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("Qwen/Qwen2.5-7B-Instruct", token=hf_token)
|
| 13 |
|
| 14 |
SYSTEM_PROMPT = """Analyze the Arabic map query and return ONLY a JSON object with these exact keys:
|
|
|
|
| 33 |
|
| 34 |
Examples:
|
| 35 |
Input: "أرخص وأقرب مستشفى طوارئ في جدة"
|
| 36 |
+
Output: {"location": "جدة", "category": "مستشفى", "sub_type": "طوارئ", "features": ["أرخص", "أقرب"], "sort_by": "price"}
|
| 37 |
+
Input: "حديقة أطفال نظيفة في الرياض"
|
| 38 |
+
Output: {"location": "الرياض", "category": "حديقة", "sub_type": "أطفال", "features": ["نظيفة"], "sort_by": "relevance"}
|
| 39 |
+
Input: "أفضل فندق 5 نجوم قريب من المطار في دبي"
|
| 40 |
+
Output: {"location": "دبي", "category": "فندق", "sub_type": "5 نجوم", "features": ["أفضل", "قريب من المطار"], "sort_by": "rating"}
|
| 41 |
+
Input: "محطة وقود 24 ساعة رخيصة"
|
| 42 |
+
Output: {"location": "near me", "category": "محطة وقود", "sub_type": null, "features": ["24 ساعة", "رخيصة"], "sort_by": "price"}
|
| 43 |
+
Input: "مقهى هادئ للعمل في الرياض"
|
| 44 |
+
Output: {"location": "الرياض", "category": "مقهى", "sub_type": null, "features": ["هادئ", "للعمل"], "sort_by": "relevance"}"""
|
| 45 |
|
| 46 |
def build_search_query(parsed_data):
|
| 47 |
"""بناء جملة بحث محسّنة"""
|
|
|
|
| 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"
|
|
|
|
| 153 |
types = place.get("types", [])
|
| 154 |
place_type = ", ".join([t.replace("_", " ").replace("restaurant", "مطعم").replace("cafe", "مقهى")
|
| 155 |
.replace("hospital", "مستشفى").replace("park", "حديقة")
|
| 156 |
+
.replace("store", "متجر").replace("gas_station", "محطة وقود")
|
| 157 |
for t in types[:2]])
|
| 158 |
|
| 159 |
card = f"""### {i}. 📍 {name}
|
|
|
|
| 173 |
|
| 174 |
def search_google_maps_legacy(parsed_data, query=None):
|
| 175 |
"""الرجوع إلى Legacy API كاحتياط"""
|
| 176 |
+
if not google_key:
|
| 177 |
+
return "⚠️ الرجاء إضافة GOOGLE_MAPS_API_KEY في إعدادات الـ Space."
|
| 178 |
+
|
| 179 |
if not query:
|
| 180 |
query = build_search_query(parsed_data)
|
| 181 |
|
|
|
|
| 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 |
|
|
|
|
| 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:
|
| 275 |
+
return None, "❌ الرجاء تحميل ملف للاستعلامات الجماعية."
|
| 276 |
+
|
| 277 |
+
queries = []
|
| 278 |
+
try:
|
| 279 |
+
with open(file_obj.name, 'r', encoding='utf-8') as f:
|
| 280 |
+
for line in f:
|
| 281 |
+
query = line.strip()
|
| 282 |
+
if query:
|
| 283 |
+
queries.append(query)
|
| 284 |
+
except Exception as e:
|
| 285 |
+
return None, f"❌ خطأ في قراءة الملف: {str(e)}"
|
| 286 |
+
|
| 287 |
+
if not queries:
|
| 288 |
+
return None, "❌ الملف فارغ أو لا يحتوي على استعلامات صالحة."
|
| 289 |
+
|
| 290 |
+
results = []
|
| 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)
|
| 301 |
+
if match:
|
| 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())
|
| 326 |
+
|
| 327 |
+
return output_filepath, "✅ تم الانتهاء من معالجة الدفعة. يمكنك تحميل النتائج."
|
| 328 |
+
|
| 329 |
# ==================== واجهة المستخدم ====================
|
| 330 |
|
| 331 |
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
|
|
|
| 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 |
+
|
| 347 |
+
btn = gr.Button("🔍 ابحث الآن", variant="primary")
|
| 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 |
+
|
| 355 |
+
gr.Markdown("### 💡 أمثلة للبحث:")
|
| 356 |
+
examples = [
|
| 357 |
+
"أرخص وأقرب مستشفى طوارئ في جدة",
|
| 358 |
+
"حديقة أطفال نظيفة في الرياض",
|
| 359 |
+
"أفضل فندق 5 نجوم في دبي",
|
| 360 |
+
"مقهى هادئ للعمل في الخبر",
|
| 361 |
+
"محطة وقود 24 ساعة رخيصة",
|
| 362 |
+
"صيدلية قريبة مفتوحة الآن"
|
| 363 |
+
]
|
| 364 |
+
|
| 365 |
+
with gr.Row():
|
| 366 |
+
for ex in examples[:3]:
|
| 367 |
+
btn_ex = gr.Button(ex, size="sm")
|
| 368 |
+
btn_ex.click(lambda x=ex: x, None, input_text)
|
| 369 |
+
|
| 370 |
+
with gr.Row():
|
| 371 |
+
for ex in examples[3:]:
|
| 372 |
+
btn_ex = gr.Button(ex, size="sm")
|
| 373 |
+
btn_ex.click(lambda x=ex: x, None, input_text)
|
| 374 |
+
|
| 375 |
+
btn.click(fn=main_process, inputs=input_text, outputs=[output_json, output_map])
|
| 376 |
+
|
| 377 |
+
with gr.Tab("معالجة دفعة"):
|
| 378 |
+
gr.Markdown("""
|
| 379 |
+
### ⬆️ تحميل ملف الاستعلامات
|
| 380 |
+
قم بتحميل ملف نصي (.txt) أو CSV يحتوي على استعلام واحد في كل سطر.
|
| 381 |
+
""")
|
| 382 |
+
file_input = gr.File(label="ملف الاستعلامات (TXT/CSV)", file_count="single", type="filepath")
|
| 383 |
+
batch_btn = gr.Button("🚀 ابدأ معالجة الدفعة", variant="primary")
|
| 384 |
+
batch_output_file = gr.File(label="ملف نتائج الدفعة (CSV)")
|
| 385 |
+
batch_status_message = gr.Markdown(label="الحالة")
|
| 386 |
+
|
| 387 |
+
batch_btn.click(
|
| 388 |
+
fn=process_batch_queries,
|
| 389 |
+
inputs=file_input,
|
| 390 |
+
outputs=[batch_output_file, batch_status_message]
|
| 391 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 392 |
|
| 393 |
+
demo.launch()
|