import openai import os from dotenv import load_dotenv, dotenv_values from fastapi import FastAPI, Request from pydantic import BaseModel from fastapi.responses import RedirectResponse, HTMLResponse from fastapi.responses import JSONResponse import urllib.parse import requests # Load environment variables (assuming your API key is stored in a `.env` file) load_dotenv() api_key = os.environ.get('HUGGINGFACEHUB_API_TOKEN') # OpenAI API configuration (specific to Meta-Llama-3-8B) model_link = "meta-llama/Meta-Llama-3-8B-Instruct" base_url = "https://router.huggingface.co/v1" app = FastAPI() class Message(BaseModel): message: str @app.get("/", response_class=HTMLResponse) async def read_root(): return """Welcome to Up to 12 Chat Processor""" @app.post("/processtext") async def receive_updates(request: Request): data = await request.json() print("Received Update:", data) result = process_text(data.get("message", "")) print("Assistant:", result) # ضمان إن الرجوع JSON دايمًا return JSONResponse(content=result) import re INTENTS = { "GREETING": "تحية/بداية محادثة", "COURSES_MENU": "طلب قائمة الكورسات", "COURSE_TYPE_DETAILS": "سؤال عن نوع كورس محدد", "CENTER_INFO": "سؤال عن المركز", "CHILDREN_COURSES": "كورس أطفال", "ONLINE_COURSES": "كورسات أونلاين", "WEEKEND_COURSES": "كورسات ويكند", "HUMAN_AGENT": "طلب خدمة عملاء/موظف", "OTHER": "غير معروف/خارج النطاق", } def detect_intent(text: str) -> str: t = (text or "").strip().lower() # 1) Greetings if re.search(r"\b(hi|hello|hey|start)\b", t) or any(x in t for x in ["اهلا", "أهلا", "السلام", "هاي", "مرحبا", "ابدأ", "ابدء"]): return "GREETING" # 2) Human agent if any(x in t for x in ["خدمة العملاء", "موظف", "حد يرد", "اكلم", "اتواصل", "رقم", "واتساب", "support", "agent"]): return "HUMAN_AGENT" # 3) Courses menu / asking about courses generally if any(x in t for x in ["كورسات", "دورات", "courses", "الكورسات المتاحة", "عايز كورس", "عايز اتعلم", "الالماني عندكم", "الماني"]): return "COURSES_MENU" # 4) Specific course types # Express / Intensive / Regular / Weekend / Online / Children if any(x in t for x in ["express", "اكسبريس", "سريع", "super intensive"]): return "COURSE_TYPE_DETAILS" if any(x in t for x in ["intensive", "انتنسب", "مكثف", "مكثفة"]): return "COURSE_TYPE_DETAILS" if any(x in t for x in ["regular", "ريجولار", "عادي", "منتظم"]): return "COURSE_TYPE_DETAILS" if any(x in t for x in ["weekend", "ويكند", "الجمعة", "السبت", "عطلة"]): return "WEEKEND_COURSES" if any(x in t for x in ["online", "اونلاين", "أونلاين", "زووم", "من البيت"]): return "ONLINE_COURSES" if any(x in t for x in ["children", "kids", "اطفال", "أطفال", "طفل", "سن", "سنين"]): return "CHILDREN_COURSES" # 5) Center info if any(x in t for x in ["المركز", "adk", "ädk", "فروع", "branch", "اتأسس", "تاريخ", "goethe", "معتمد"]): return "CENTER_INFO" return "OTHER" KB_TEXT = """ المركز: (ÄDK)- Egyptian-German Cultural Centre was established in 1998. With various branches in Cairo, ÄDK offers German language courses for adults from A1 to C1 and for children from A1 to B1. Over the years thousands have graduated and have learned in ÄDK. In addition to our language courses, ÄDK organizes cultural projects independently, or in conjunction with the Goethe institute. ÄDK is the first institute to be accredited from the Goethe institute in the Middle East and North Africa region. أنواع الكورسات: 1) Express Courses: - Super intensive courses لتعلم الألماني بسرعة وكفاءة. Challenging وتحتاج مجهود. - ممكن تحجز Stage كاملة (A1-A2-B1). - 3 محاضرات في الأسبوع، كل محاضرة 4 ساعات. - مدة الـ stage: شهرين ونصف. 2) Intensive Courses: - ممكن تحجز Stage كاملة (A1-A2-B1). - محاضرتين في الأسبوع، كل محاضرة 4 ساعات. - مدة الـ stage: 3 شهور ونصف. 3) Regular Courses: - ممكن تحجز كل level لوحده من (A1.1 إلى B1.3). - محاضرتين في الأسبوع، كل محاضرة 4 ساعات. - مدة الكورس: شهر ونصف. 4) Weekend Courses: - للناس اللي مش فاضيين خلال الأسبوع بسبب شغل/دراسة. - بتتبع نفس outline وساعات الـ Regular (intensive و normal). 5) Online Courses: - attending online. 6) Children Courses: - من 5 سنوات حتى 15 سنة. - الهدف: تنمية مهارات الطفل واللغة وتشجيع التفكير الحر والنقدي والاستقلالية. """ def build_system_prompt(intent: str) -> str: return f""" You are the official WhatsApp assistant for ÄDK (Egyptian-German Cultural Centre). OUTPUT LANGUAGE: - Reply in Egyptian Arabic (عامية مصرية) using short, natural sentences. - Do NOT use formal greetings like "مرحباً" or "أهلاً". Reply directly to the user's message. STYLE RULES (must follow): - Do NOT produce Arabic grammar mistakes such as "اقترحك" or "يهتم بيك". Use: "أقترح" / "أنسب" / "تحب" / "ممكن". - Use the second-person pronoun consistently: use "إنت" (not حضرتك). - Ask choices correctly: "إنت مهتم بأي نوع كورس؟" / "تحب تعرف تفاصيل أنهي نوع؟" Never say: "يهتم بيك". - Max 5 lines. No JSON. No headings. No bullet spam. SCOPE RULES: - Use ONLY the information inside KNOWLEDGE BASE below. - Never invent prices, schedules, branch addresses, locations, or any details not in KB. - Never compare competitors or provide general German-course information outside KB. OUT-OF-SCOPE HANDLING: - Do NOT say "المعلومة مش عندي" or apologize at length. - Instead, use one of these Arabic templates: 1) "تمام — هحوّل سؤالك لفريق خدمة العملاء عشان نديك إجابة دقيقة." 2) "تمام — هسجّل استفسارك، وخدمة العملاء هيردّوا عليك بالتفاصيل." 3) "تمام — عشان نضمن الدقة، فريق خدمة العملاء هيتواصل معاك ويوضح لك كل التفاصيل." - Then ask ONE helpful follow-up question (if needed): "تحب أونلاين ولا حضور؟" (do NOT ask for phone number). INTENT BEHAVIOR: - GREETING: send a short welcome + 3 options. - COURSES_MENU: list available types only (Regular / Intensive / Express / Weekend / Online / Children), then ask which type. - COURSE_TYPE_DETAILS / ONLINE_COURSES / WEEKEND_COURSES / CHILDREN_COURSES: answer ONLY from KB, then ask 1 follow-up question. - CENTER_INFO: answer ONLY from KB, then offer connecting to customer service. CURRENT INTENT: {intent} KNOWLEDGE BASE: {KB_TEXT} """.strip() def process_text(user_text: str): client = openai.OpenAI(api_key=api_key, base_url=base_url) intent = detect_intent(user_text) system_prompt = build_system_prompt(intent) try: resp = client.chat.completions.create( model=model_link, messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_text}, ], max_tokens=500, temperature=0.2, stream=False, ) answer = resp.choices[0].message.content.strip() # لو تحب ترجع intent كمان (مفيد للـ webhook logic) return {"ok": True, "intent": intent, "reply": answer} except Exception as e: return {"ok": False, "error": str(e)}