File size: 8,115 Bytes
f4cb00a 527af48 41c28af 527af48 f4cb00a 527af48 f4cb00a 527af48 f4cb00a 527af48 f4cb00a 527af48 f4cb00a 527af48 f4cb00a 527af48 |
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 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
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)} |