File size: 5,383 Bytes
a44ef87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2e8be6e
a44ef87
2e8be6e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a44ef87
2e8be6e
 
 
 
 
 
 
 
 
a44ef87
2e8be6e
 
 
a44ef87
 
 
2e8be6e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a44ef87
2e8be6e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a44ef87
2e8be6e
 
a44ef87
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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"}