blueprint-api / classifier.py
mohamedhuggig's picture
Update classifier.py
2e8be6e verified
import re
import json
import logging
from llm_provider import llm
logger = logging.getLogger("classifier")
def classify_request(text=None, file_bytes=None, file_type=None):
# تصنيف الملفات أولاً
if file_bytes and file_type:
if "image" in file_type:
return {"task": "image_analysis", "domain": "site"}
if "pdf" in file_type:
return {"task": "pdf_analysis", "domain": "office"}
if not text:
return {"task": "general_chat", "domain": "general"}
t = text.lower().strip()
# ========== 1. القواعد الهندسية الصارمة (Priority 1) ==========
# أ- التصميم الإنشائي (أهم حاجة)
# لو فيه أرقام (أبعاد) + كلمة إنشائية -> تصميم
has_numbers = bool(re.search(r'\d', t))
# قاموس شامل للعناصر الإنشائية
structural_elements = {
"كمرة": "beam_tool", "beam": "beam_tool", "ميدة": "beam_tool",
"بلاطة": "slab_tool", "سقف": "slab_tool", "slab": "slab_tool",
"عمود": "column_tool", "column": "column_tool",
"قاعدة": "foundation_tool", "أساس": "foundation_tool", "foundation": "foundation_tool",
"سلم": "stair_tool", "stair": "stair_tool",
"جدار استنادي": "retaining_wall_tool", "retaining wall": "retaining_wall_tool"
}
for keyword, task in structural_elements.items():
if keyword in t:
# لو فيه أرقام، يبقى أكيد تصميم
if has_numbers:
return {"task": task, "domain": "design"}
# لو مفيش أرقام، ممكن يكون سؤال عام "ايه هي الكمرة؟"، نوجهه للمحادثة العامة
# إلا لو الكلمة صريحة زي "صمم" أو "حساب"
if any(word in t for word in ["صمم", "حساب", "تصميم", "design", "calculate"]):
return {"task": task, "domain": "design"}
# ب- الحصر (BOQ)
boq_keywords = ["حصر", "كمية", "كميات", "حديد", "خرسانة", "بلوك", "أسمنت", "رمل", "طوب",
"نحسب", "حساب", "تكلفة", "سعر", "boq", "ton", "m3", "طن", "متر مكعب"]
if any(word in t for word in boq_keywords):
return {"task": "boq_tool", "domain": "boq"}
# ج- الموقع والتقارير
site_keywords = ["تشك ليست", "checklist", "استلام", "مراجعة", "معاينة", "موقع"]
if any(word in t for word in site_keywords):
return {"task": "checklist_tool", "domain": "site"}
report_keywords = ["تقرير موقع", "تقرير يومي", "تسجيل يومي", "حالة الطقس", "عدد العمال"]
if any(word in t for word in report_keywords):
return {"task": "site_report", "domain": "site"}
# ========== 2. قواعد السياق (Context Awareness) ==========
# لو الجملة فيها أرقام كتير ومفيش كلمات مفتاحية -> ممكن يكون تصميم ضImplicit
# مثال: "5 متر 2 طن" من غير كلمة كمرة -> نعتبره طلب تصميم عام
if has_numbers and len(re.findall(r'\d+', t)) >= 2:
# لو الجملة قصيرة وفيها أرقام -> تصميم
if len(t.split()) < 10:
return {"task": "beam_tool", "domain": "design"}
# ========== 3. الذكاء الاصطناعي (LLM Fallback) ==========
# لو مفيش أي قاعدة اشتغلت، نبعت للـ AI يحاول يفهم
if len(t) > 10:
try:
import asyncio
prompt = f"""
Analyze this user request and determine the domain.
Possible domains: 'design' (structural calculations), 'boq' (quantity takeoff), 'site' (site reports/checklists), 'general' (general engineering chat).
Return JSON: {{"domain": "...", "task": "..."}}
Text: "{text}"
"""
# هنا بنستخدم الموديلز المجانية اللي اشتغلت في الاختبار
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
response = loop.run_until_complete(llm.generate_text(prompt, model_preference=["gemini-flash", "mistral-small", "gpt-4o-mini"]))
loop.close()
json_match = re.search(r'\{.*\}', response, re.DOTALL)
if json_match:
result = json.loads(json_match.group())
# لو الـ AI قال "design" أو "boq"، نصدقه
if result.get("domain") in ["design", "boq", "site"]:
# نضبط الـ task name
task = result.get("task", "general_chat")
if result["domain"] == "design": task = "beam_tool"
if result["domain"] == "boq": task = "boq_tool"
return {"task": task, "domain": result["domain"]}
except Exception as e:
logger.warning(f"LLM fallback failed: {e}")
# ========== 4. الوضع الافتراضي (General Chat) ==========
# لو كل المحاولات فشلت، نخليه يتكلم كـ "Blue" المهندس العام
return {"task": "general_chat", "domain": "general"}