My_teacher_lesson / chat_tab.py
Asem75's picture
Update chat_tab.py
26274a1 verified
Raw
History Blame
7.37 kB
# ======================== دالة المحادثة المطورة (مع الدرس الكامل) ========================
def teacher_chat_process(
audio_mic,
chat_history,
student_name,
user_email,
device_token,
grade,
semester,
subject,
lesson,
track
):
if audio_mic is None:
return chat_history, None, "⚠️ الرجاء تسجيل سؤالك أولاً عبر المايكروفون."
# 1. تحويل الصوت إلى نص
whisper_model = load_whisper_lazy()
transcription = whisper_model(
audio_mic,
generate_kwargs={"language": "arabic"}
)["text"]
if not transcription.strip():
return chat_history, None, "❌ لم يتم التقاط كلمات واضحة."
print(f"🗣️ الطالب {student_name}: {transcription}")
# 2. البحث عن ملف الدرس في الخزنة وحقنه كاملاً
lesson_content = find_lesson_file_in_vault(grade, semester, subject, lesson)
# 3. بناء سياق النموذج
context_parts = [f"مادة {subject}", f"{lesson}"]
if semester:
context_parts.insert(1, f"الفصل {semester}")
if track:
context_parts.append(f"تخصص {track}")
context_str = " - ".join(context_parts)
system_prompt_base = (
f"أنت الأستاذ عبود، معلم خبير وموجه تربوي ذكي. "
f"السياق الحالي: {context_str}. "
"تتحدث باللغة العربية الفصحى وبنبرة مشوقة. "
"قاعدتك الأساسية: لا تعطي الطالب الشرح كاملاً أو الإجابة النهائية دفعة واحدة أبداً! "
"قم بتقسيم المعلومات وتدرج في إعطائها له فكرة فكرة (فقرة قصيرة لا تتجاوز 30 كلمة). "
"في نهاية كل رد، اطرح على الطالب سؤالاً تفاعلياً قصيراً لتتأكد من فهمه قبل الانتقال للخطوة التالية."
)
# 4. معالجة محتوى الدرس (كاملاً مع تقسيم ذكي إذا لزم الأمر)
lesson_part = 1
total_parts = 1
lesson_text = ""
if lesson_content:
# حساب الحد الأقصى المتاح (4096 توكن ≈ 12000 حرف عربي)
# نترك مساحة للـ system prompt والتاريخ وسؤال المستخدم والرد (≈ 2000 حرف)
MAX_CHARS_FOR_LESSON = 10000
if len(lesson_content) <= MAX_CHARS_FOR_LESSON:
# الدرس صغير بما يكفي - نأخذه كاملاً
lesson_text = lesson_content
print(f"📚 تم تحميل الدرس كاملاً ({len(lesson_content)} حرف)")
else:
# الدرس كبير - نقسمه إلى أجزاء
total_parts = (len(lesson_content) // MAX_CHARS_FOR_LESSON) + 1
# نأخذ الجزء الأول فقط لهذه الجولة
start = 0
end = MAX_CHARS_FOR_LESSON
lesson_text = lesson_content[start:end]
lesson_part = 1
print(f"📚 الدرس كبير ({len(lesson_content)} حرف) - تم تحميل الجزء {lesson_part}/{total_parts}")
# إضافة محتوى الدرس إلى system prompt
if total_parts == 1:
lesson_header = "\n\n📖 المحتوى الكامل للدرس (المرجع الأساسي لإجاباتك):\n"
else:
lesson_header = f"\n\n📖 محتوى الدرس - الجزء {lesson_part}/{total_parts} (المرجع الأساسي لإجاباتك):\n"
system_prompt = system_prompt_base + lesson_header + "```\n" + lesson_text + "\n```"
if total_parts > 1:
system_prompt += (
f"\n⚠️ تنبيه: هذا الجزء {lesson_part} من {total_parts}. "
"عند الانتهاء من شرح هذا الجزء، أخبر الطالب بأن هناك أجزاء أخرى متاحة للمتابعة."
)
else:
system_prompt = system_prompt_base
print("⚠️ لم يتم العثور على محتوى الدرس - سيستخدم النموذج معرفته العامة")
# 5. استدعاء نموذج Phi-3
model, tokenizer = load_teacher_llm_lazy()
messages = [{"role": "system", "content": system_prompt}]
# معالجة تاريخ المحادثة
if chat_history:
for msg in chat_history:
if isinstance(msg, dict):
messages.append({"role": msg["role"], "content": msg["content"]})
elif isinstance(msg, (list, tuple)) and len(msg) == 2:
messages.append({"role": "user", "content": str(msg[0])})
messages.append({"role": "assistant", "content": str(msg[1])})
messages.append({"role": "user", "content": transcription})
# طباعة حجم السياق للمراقبة
total_chars = sum(len(m["content"]) for m in messages)
print(f"📊 حجم السياق الكلي: {total_chars} حرف (~{total_chars // 3} توكن تقريبي)")
prompt = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True
)
inputs = tokenizer(prompt, return_tensors="pt").to(DEVICE)
# التحقق من عدم تجاوز الحد الأقصى
input_length = inputs.input_ids.shape[1]
print(f"📊 عدد التوكنز الفعلي: {input_length} (الحد الأقصى: 4096)")
if input_length > 4000:
print("⚠️ تحذير: السياق كبير جداً وقد يتم اقتطاعه تلقائياً")
with torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=150,
temperature=0.7,
do_sample=True,
pad_token_id=tokenizer.eos_token_id
)
full_response = tokenizer.decode(
outputs[0][inputs.input_ids.shape[1]:],
skip_special_tokens=True
)
print(f"🤖 الأستاذ: {full_response[:80]}...")
# 6. تحديث تاريخ المحادثة
if not chat_history:
chat_history = []
chat_history.append({"role": "user", "content": transcription})
chat_history.append({"role": "assistant", "content": full_response})
# 7. تحديث الخزنة السحابية
ensure_vault_structure(
grade=grade,
semester=semester,
subject=subject,
lesson=lesson,
user_name=student_name,
track=track
)
# 8. إرسال الرد إلى الرادار لتوليد الصوت
print("🔊 توجيه النص ديناميكياً لمساحة الرادار...")
audio_path = call_radar_voice_api(
text_content=full_response,
voice_name="ar-EG-SalmaNeural",
user_email=user_email.strip(),
user_token=device_token.strip()
)
if audio_path is None:
status = "❌ فشل التوليد الصوتي (قد يكون المستخدم غير مسجل كـ VIP في الرادار)"
else:
status = f"✅ تم التوليد بنجاح | الدرس: {lesson}"
if total_parts > 1:
status += f" (الجزء {lesson_part}/{total_parts})"
return chat_history, audio_path, status