Create ORION

#4
by hafo012 - opened

"""
ORION v2.0 - Educational AI Agent Backend
FastAPI + SQLAlchemy + Claude AI + Arabic Voice
"""

from fastapi import FastAPI, HTTPException, File, UploadFile, WebSocket
from fastapi.middleware.cors import CORSMiddleware
from sqlalchemy import create_engine, Column, Integer, String, Float, DateTime, JSON, Text
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, Session
from pydantic import BaseModel
from datetime import datetime, timedelta
from typing import Optional, List, Dict
import json
import os
from anthropic import Anthropic
import uvicorn

============================================================================

DATABASE SETUP

============================================================================

DATABASE_URL = "sqlite:///./orion_data.db" # ابدأ بـ SQLite، بعدين postgres

engine = create_engine(
DATABASE_URL,
connect_args={"check_same_thread": False} if "sqlite" in DATABASE_URL else {}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()

============================================================================

DATABASE MODELS

============================================================================

class Profile(Base):
"""ملف الطفل الشخصي"""
tablename = "profiles"

id = Column(Integer, primary_key=True)
name = Column(String, unique=True, index=True)
age = Column(Integer)
grade = Column(String)
learning_style = Column(String, default="mixed")  # visual, auditory, kinesthetic
adaptation_rate = Column(Float, default=1.0)  # معامل التكيف (يبدأ 1.0)
interaction_count = Column(Integer, default=0)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
metadata = Column(JSON, default={})

class Emotion(Base):
"""تسجيل المشاعر والحالة النفسية"""
tablename = "emotions"

id = Column(Integer, primary_key=True)
profile_id = Column(Integer, index=True)
emotion_type = Column(String)  # happy, anxious, confident, confused
intensity = Column(Float)  # 0-1
confidence = Column(Float)  # 0-1
anxiety = Column(Float)  # 0-1
trigger = Column(String, nullable=True)
response_type = Column(String)  # doctor, teacher, brother, sage
timestamp = Column(DateTime, default=datetime.utcnow, index=True)
notes = Column(Text, nullable=True)

class Question(Base):
"""الأسئلة والإجابات"""
tablename = "questions"

id = Column(Integer, primary_key=True)
profile_id = Column(Integer, index=True)
question = Column(Text)
subject = Column(String)  # math, arabic, science, etc
difficulty = Column(Float)  # 0-1
response = Column(Text)
is_correct = Column(Integer, default=-1)  # -1: not graded, 0: wrong, 1: correct
response_time_seconds = Column(Float)
confidence = Column(Float)  # طول الإجابة (0-1)
teaching_method = Column(String)  # socratic, scaffolding, discovery, direct
timestamp = Column(DateTime, default=datetime.utcnow, index=True)

class Lesson(Base):
"""الدروس والمحتوى التعليمي"""
tablename = "lessons"

id = Column(Integer, primary_key=True)
title = Column(String)
subject = Column(String)
grade_level = Column(String)
content = Column(Text)
learning_objectives = Column(JSON)
activities = Column(JSON)
assessment = Column(JSON)
resources = Column(JSON)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

class Achievement(Base):
"""الإنجازات والنقاط والجوائز"""
tablename = "achievements"

id = Column(Integer, primary_key=True)
profile_id = Column(Integer, index=True)
title = Column(String)
description = Column(Text)
points = Column(Integer, default=10)
badge = Column(String)  # emoji or badge id
timestamp = Column(DateTime, default=datetime.utcnow)

class LibraryEntry(Base):
"""مكتبة الكتب والقصص والمحتوى"""
tablename = "library"

id = Column(Integer, primary_key=True)
title = Column(String)
author = Column(String)
content_type = Column(String)  # book, story, poem, quote
content = Column(Text)
subject = Column(String)
age_range = Column(String)
moral = Column(Text, nullable=True)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

class LearningLog(Base):
"""سجل التعلم المستمر"""
tablename = "learning_logs"

id = Column(Integer, primary_key=True)
profile_id = Column(Integer, index=True)
session_id = Column(String)
patterns_detected = Column(JSON, default=[])
improvement_areas = Column(JSON, default=[])
strength_areas = Column(JSON, default=[])
recommended_methods = Column(JSON, default=[])
confidence_trajectory = Column(JSON, default=[])
interaction_number = Column(Integer)
timestamp = Column(DateTime, default=datetime.utcnow)

class ParentReport(Base):
"""تقارير ولي الأمر الأسبوعية"""
tablename = "parent_reports"

id = Column(Integer, primary_key=True)
profile_id = Column(Integer, index=True)
week_start = Column(DateTime)
total_interactions = Column(Integer)
avg_confidence = Column(Float)
emotion_summary = Column(JSON)
topics_covered = Column(JSON)
strengths = Column(JSON)
areas_for_improvement = Column(JSON)
recommendations = Column(Text)
generated_at = Column(DateTime, default=datetime.utcnow)

============================================================================

PYDANTIC SCHEMAS

============================================================================

class ProfileCreate(BaseModel):
name: str
age: int
grade: str
learning_style: str = "mixed"

class EmotionCreate(BaseModel):
profile_id: int
emotion_type: str
intensity: float
trigger: Optional[str] = None

class QuestionCreate(BaseModel):
profile_id: int
question: str
subject: str

class MessageRequest(BaseModel):
profile_id: int
message: str

class LibraryEntryCreate(BaseModel):
title: str
author: str
content_type: str
content: str
subject: str
age_range: str

============================================================================

FASTAPI APP

============================================================================

app = FastAPI(title="ORION v2.0", version="2.0.0")

CORS

app.add_middleware(
CORSMiddleware,
allow_origins=[""],
allow_credentials=True,
allow_methods=["
"],
allow_headers=["*"],
)

Create tables

Base.metadata.create_all(bind=engine)

AI Client

client = Anthropic()
CONVERSATION_HISTORY = {} # {profile_id: [messages]}

============================================================================

HELPER FUNCTIONS

============================================================================

def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()

def detect_emotion(message: str, db: Session) -> Dict:
"""تحليل المشاعر من الرسالة"""
prompt = f"""
حلل مشاعر هذه الرسالة من طفل واكتب JSON فقط (بدون شرح):
الرسالة: "{message}"

أجب بـ JSON بهذا الشكل:
{{
    "emotion_type": "happy/anxious/confident/confused/sad/excited",
    "intensity": 0.7,
    "confidence": 0.8,
    "anxiety": 0.3,
    "response_type": "doctor/teacher/brother/sage"
}}
"""

response = client.messages.create(
    model="claude-opus-4-1",
    max_tokens=200,
    messages=[{"role": "user", "content": prompt}]
)

try:
    return json.loads(response.content[0].text)
except:
    return {
        "emotion_type": "neutral",
        "intensity": 0.5,
        "confidence": 0.5,
        "anxiety": 0.2,
        "response_type": "teacher"
    }

def get_orion_response(profile_id: int, message: str, emotion_data: Dict, db: Session) -> str:
"""الرد من ORION مع اختيار الشخصية المناسبة"""

# جلب ملف الطفل
profile = db.query(Profile).filter(Profile.id == profile_id).first()
if not profile:
    return "عذراً، لم أجد ملفك. هل تريد إنشاء حساب جديد؟"

# إعداد السياق
response_type = emotion_data.get("response_type", "teacher")

personalities = {
    "doctor": "أنت طبيب نفسي متخصص في تهدئة القلق والخوف. كن لطيفاً، استمع بفهم، قدم تطمينات حقيقية.",
    "teacher": "أنت معلم صبور يشرح بوضوح. استخدم الأسئلة السقراطية، ابني الفهم خطوة بخطوة.",
    "brother": "أنت أخ أكبر صديقاً. كن محفزاً، استخدم لغة الرفقاء، شجع بصدق.",
    "sage": "أنت حكيم درويش تقدم الحكمة والموعظة. كن هادئاً، عميقاً، إنساني."
}

system_prompt = f"""
أنت ORION - وكيل تعليمي ذكي متكامل.
الاسم: {profile.name}
العمر: {profile.age}
الصف: {profile.grade}

شخصيتك الحالية: {response_type}
{personalities.get(response_type, personalities['teacher'])}

الحالة العاطفية:
- النوع: {emotion_data.get('emotion_type')}
- الثقة: {emotion_data.get('confidence', 0.5)}
- القلق: {emotion_data.get('anxiety', 0.2)}

توجيهات:
1. اكتب بالعربية الفصحى البسيطة
2. الردود قصيرة وفعّالة (2-3 جمل)
3. اسأل بدلاً من أن تخبر عندما يكون مناسباً
4. كن حقيقياً، لا تكن مصطنعاً
5. تذكر السياق من المحادثات السابقة
"""

# إعادة المحادثة
if profile_id not in CONVERSATION_HISTORY:
    CONVERSATION_HISTORY[profile_id] = []

messages = CONVERSATION_HISTORY[profile_id]
messages.append({"role": "user", "content": message})

# الحصول على الرد
response = client.messages.create(
    model="claude-opus-4-1",
    max_tokens=500,
    system=system_prompt,
    messages=messages
)

assistant_message = response.content[0].text
messages.append({"role": "assistant", "content": assistant_message})

# الاحتفاظ بـ 20 آخر رسالة فقط
if len(messages) > 40:
    messages[:20] = []

return assistant_message

def update_learning_metrics(profile_id: int, db: Session):
"""تحديث معايير التعلم المستمر"""
profile = db.query(Profile).filter(Profile.id == profile_id).first()
if not profile:
return

# زيادة عدد التفاعلات
profile.interaction_count += 1

# تحديث معامل التكيف
if profile.interaction_count == 10:
    profile.adaptation_rate = 1.1
elif profile.interaction_count == 50:
    profile.adaptation_rate = 1.3
elif profile.interaction_count >= 100:
    profile.adaptation_rate = 1.5

# حفظ اللوج
recent_emotions = db.query(Emotion).filter(
    Emotion.profile_id == profile_id
).order_by(Emotion.timestamp.desc()).limit(10).all()

recent_questions = db.query(Question).filter(
    Question.profile_id == profile_id
).order_by(Question.timestamp.desc()).limit(10).all()

patterns = {
    "favorite_subject": None,
    "best_time": None,
    "learning_style": profile.learning_style
}

learning_log = LearningLog(
    profile_id=profile_id,
    session_id=f"session_{profile.interaction_count}",
    patterns_detected=patterns,
    interaction_number=profile.interaction_count
)

db.add(learning_log)
db.commit()

============================================================================

API ENDPOINTS

============================================================================

@app .get("/api/v1/health")
def health_check():
"""فحص صحة النظام"""
return {
"status": "healthy",
"version": "2.0.0",
"timestamp": datetime.utcnow().isoformat()
}

@app .post("/api/v1/profiles")
def create_profile(profile: ProfileCreate, db: Session = None):
"""إنشاء ملف طفل جديد"""
db = next(get_db())

existing = db.query(Profile).filter(Profile.name == profile.name).first()
if existing:
    raise HTTPException(status_code=400, detail="الملف موجود بالفعل")

new_profile = Profile(
    name=profile.name,
    age=profile.age,
    grade=profile.grade,
    learning_style=profile.learning_style
)
db.add(new_profile)
db.commit()
db.refresh(new_profile)

return {
    "id": new_profile.id,
    "name": new_profile.name,
    "age": new_profile.age,
    "message": "تم إنشاء الملف بنجاح! مرحباً بك في ORION"
}

@app .post("/api/v1/message")
def send_message(request: MessageRequest, db: Session = None):
"""إرسال رسالة والحصول على رد من ORION"""
db = next(get_db())

profile = db.query(Profile).filter(Profile.id == request.profile_id).first()
if not profile:
    raise HTTPException(status_code=404, detail="الملف غير موجود")

# تحليل المشاعر
emotion_data = detect_emotion(request.message, db)

# حفظ المشاعر
emotion_log = Emotion(
    profile_id=request.profile_id,
    emotion_type=emotion_data.get("emotion_type"),
    intensity=emotion_data.get("intensity"),
    confidence=emotion_data.get("confidence"),
    anxiety=emotion_data.get("anxiety"),
    response_type=emotion_data.get("response_type")
)
db.add(emotion_log)

# الحصول على الرد
response = get_orion_response(request.profile_id, request.message, emotion_data, db)

# تحديث معايير التعلم
update_learning_metrics(request.profile_id, db)

db.commit()

return {
    "profile_id": request.profile_id,
    "message": request.message,
    "emotion": emotion_data,
    "response": response,
    "personality": emotion_data.get("response_type"),
    "adaptation_rate": profile.adaptation_rate,
    "interaction_count": profile.interaction_count + 1
}

@app .get("/api/v1/profiles/{profile_id}/emotions/trends")
def get_emotion_trends(profile_id: int, days: int = 7, db: Session = None):
"""الاتجاهات العاطفية"""
db = next(get_db())

since = datetime.utcnow() - timedelta(days=days)
emotions = db.query(Emotion).filter(
    Emotion.profile_id == profile_id,
    Emotion.timestamp >= since
).all()

emotion_counts = {}
confidence_avg = 0
anxiety_avg = 0

for e in emotions:
    emotion_counts[e.emotion_type] = emotion_counts.get(e.emotion_type, 0) + 1
    confidence_avg += e.confidence
    anxiety_avg += e.anxiety

if emotions:
    confidence_avg /= len(emotions)
    anxiety_avg /= len(emotions)

return {
    "profile_id": profile_id,
    "days": days,
    "total_interactions": len(emotions),
    "emotion_distribution": emotion_counts,
    "avg_confidence": round(confidence_avg, 2),
    "avg_anxiety": round(anxiety_avg, 2),
    "emotional_trend": "improving" if confidence_avg > 0.7 else "needs_support"
}

@app .post("/api/v1/library")
def add_library_entry(entry: LibraryEntryCreate, db: Session = None):
"""إضافة كتاب أو قصة للمكتبة"""
db = next(get_db())

new_entry = LibraryEntry(
    title=entry.title,
    author=entry.author,
    content_type=entry.content_type,
    content=entry.content,
    subject=entry.subject,
    age_range=entry.age_range
)
db.add(new_entry)
db.commit()
db.refresh(new_entry)

return {
    "id": new_entry.id,
    "title": new_entry.title,
    "message": "تمت إضافة المحتوى للمكتبة بنجاح"
}

@app .get("/api/v1/library/{age_range}")
def get_library(age_range: str, db: Session = None):
"""الحصول على محتوى من المكتبة"""
db = next(get_db())

entries = db.query(LibraryEntry).filter(
    LibraryEntry.age_range == age_range
).order_by(LibraryEntry.updated_at.desc()).limit(20).all()

return {
    "age_range": age_range,
    "total": len(entries),
    "entries": [
        {
            "id": e.id,
            "title": e.title,
            "author": e.author,
            "type": e.content_type,
            "subject": e.subject
        }
        for e in entries
    ]
}

@app .get("/api/v1/profiles/{profile_id}/report")
def generate_parent_report(profile_id: int, db: Session = None):
"""تقرير ولي الأمر الأسبوعي"""
db = next(get_db())

profile = db.query(Profile).filter(Profile.id == profile_id).first()
if not profile:
    raise HTTPException(status_code=404, detail="الملف غير موجود")

week_start = datetime.utcnow() - timedelta(days=7)

emotions = db.query(Emotion).filter(
    Emotion.profile_id == profile_id,
    Emotion.timestamp >= week_start
).all()

questions = db.query(Question).filter(
    Question.profile_id == profile_id,
    Question.timestamp >= week_start
).all()

emotion_summary = {}
for e in emotions:
    emotion_summary[e.emotion_type] = emotion_summary.get(e.emotion_type, 0) + 1

subjects = {}
for q in questions:
    subjects[q.subject] = subjects.get(q.subject, 0) + 1

correct = len([q for q in questions if q.is_correct == 1])
total = len(questions)
accuracy = (correct / total * 100) if total > 0 else 0

report = ParentReport(
    profile_id=profile_id,
    week_start=week_start,
    total_interactions=len(emotions),
    avg_confidence=sum([e.confidence for e in emotions]) / len(emotions) if emotions else 0,
    emotion_summary=emotion_summary,
    topics_covered=subjects,
    strengths=list(subjects.keys())[:3] if subjects else [],
    areas_for_improvement=["التركيز", "حل المسائل"] if accuracy < 70 else []
)

db.add(report)
db.commit()

return {
    "profile_name": profile.name,
    "week_start": week_start.isoformat(),
    "total_interactions": len(emotions),
    "accuracy": round(accuracy, 1),
    "emotion_summary": emotion_summary,
    "subjects_covered": subjects,
    "adaptation_level": profile.adaptation_rate,
    "recommendation": "استمر في المثابرة! التقدم واضح." if accuracy > 70 else "احتاج دعم إضافي في بعض المواضيع"
}

@app .post("/api/v1/sessions")
def create_session(profile_id: int, db: Session = None):
"""بدء جلسة تعليمية"""
return {
"session_id": f"session_{profile_id}_{datetime.utcnow().timestamp()}",
"status": "started",
"timestamp": datetime.utcnow().isoformat()
}

@app .get("/api/v1/recommendations/{profile_id}")
def get_recommendations(profile_id: int, db: Session = None):
"""الحصول على التوصيات المخصصة"""
db = next(get_db())

profile = db.query(Profile).filter(Profile.id == profile_id).first()
if not profile:
    raise HTTPException(status_code=404, detail="الملف غير موجود")

recent_emotions = db.query(Emotion).filter(
    Emotion.profile_id == profile_id
).order_by(Emotion.timestamp.desc()).limit(10).all()

anxious_count = len([e for e in recent_emotions if e.emotion_type == "anxious"])

recommendations = []

if anxious_count > 3:
    recommendations.append({
        "type": "emotional_support",
        "message": "لاحظت قليلاً من القلق. نقترح جلسات استرخاء موجهة.",
        "action": "psychology_session"
    })

if profile.interaction_count < 10:
    recommendations.append({
        "type": "warm_up",
        "message": "دعنا نبني الثقة معاً! تفاعل أكثر يساعدني أفهمك أفضل.",
        "action": "continue_learning"
    })

if profile.adaptation_rate >= 1.3:
    recommendations.append({
        "type": "advancement",
        "message": "أنت تتقدم بسرعة! يمكننا رفع مستوى الصعوبة.",
        "action": "increase_difficulty"
    })

return {
    "profile_id": profile_id,
    "adaptation_level": profile.adaptation_rate,
    "recommendations": recommendations
}

============================================================================

RUN

============================================================================

if name == "main":
uvicorn.run(
app,
host="0.0.0.0",
port=8000,
log_level="info"
)

hafo012 changed pull request status to closed

CLAUDE_API_KEY = sk-ant-xxxxxx

hafo012 changed pull request status to open

من ORION_v2_COMPLETE/:
✓ app.py
✓ requirements.txt
✓ .env

hafo012 changed pull request status to closed
hafo012 changed pull request status to open

"""
ORION v2.0 - Educational AI Agent Backend
FastAPI + SQLAlchemy + Claude AI + Arabic Voice
"""

from fastapi import FastAPI, HTTPException, File, UploadFile, WebSocket
from fastapi.middleware.cors import CORSMiddleware
from sqlalchemy import create_engine, Column, Integer, String, Float, DateTime, JSON, Text
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, Session
from pydantic import BaseModel
from datetime import datetime, timedelta
from typing import Optional, List, Dict
import json
import os
from anthropic import Anthropic
import uvicorn

============================================================================

DATABASE SETUP

============================================================================

DATABASE_URL = "sqlite:///./orion_data.db" # ابدأ بـ SQLite، بعدين postgres

engine = create_engine(
DATABASE_URL,
connect_args={"check_same_thread": False} if "sqlite" in DATABASE_URL else {}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()

============================================================================

DATABASE MODELS

============================================================================

class Profile(Base):
"""ملف الطفل الشخصي"""
tablename = "profiles"

id = Column(Integer, primary_key=True)
name = Column(String, unique=True, index=True)
age = Column(Integer)
grade = Column(String)
learning_style = Column(String, default="mixed")  # visual, auditory, kinesthetic
adaptation_rate = Column(Float, default=1.0)  # معامل التكيف (يبدأ 1.0)
interaction_count = Column(Integer, default=0)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
metadata = Column(JSON, default={})

class Emotion(Base):
"""تسجيل المشاعر والحالة النفسية"""
tablename = "emotions"

id = Column(Integer, primary_key=True)
profile_id = Column(Integer, index=True)
emotion_type = Column(String)  # happy, anxious, confident, confused
intensity = Column(Float)  # 0-1
confidence = Column(Float)  # 0-1
anxiety = Column(Float)  # 0-1
trigger = Column(String, nullable=True)
response_type = Column(String)  # doctor, teacher, brother, sage
timestamp = Column(DateTime, default=datetime.utcnow, index=True)
notes = Column(Text, nullable=True)

class Question(Base):
"""الأسئلة والإجابات"""
tablename = "questions"

id = Column(Integer, primary_key=True)
profile_id = Column(Integer, index=True)
question = Column(Text)
subject = Column(String)  # math, arabic, science, etc
difficulty = Column(Float)  # 0-1
response = Column(Text)
is_correct = Column(Integer, default=-1)  # -1: not graded, 0: wrong, 1: correct
response_time_seconds = Column(Float)
confidence = Column(Float)  # طول الإجابة (0-1)
teaching_method = Column(String)  # socratic, scaffolding, discovery, direct
timestamp = Column(DateTime, default=datetime.utcnow, index=True)

class Lesson(Base):
"""الدروس والمحتوى التعليمي"""
tablename = "lessons"

id = Column(Integer, primary_key=True)
title = Column(String)
subject = Column(String)
grade_level = Column(String)
content = Column(Text)
learning_objectives = Column(JSON)
activities = Column(JSON)
assessment = Column(JSON)
resources = Column(JSON)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

class Achievement(Base):
"""الإنجازات والنقاط والجوائز"""
tablename = "achievements"

id = Column(Integer, primary_key=True)
profile_id = Column(Integer, index=True)
title = Column(String)
description = Column(Text)
points = Column(Integer, default=10)
badge = Column(String)  # emoji or badge id
timestamp = Column(DateTime, default=datetime.utcnow)

class LibraryEntry(Base):
"""مكتبة الكتب والقصص والمحتوى"""
tablename = "library"

id = Column(Integer, primary_key=True)
title = Column(String)
author = Column(String)
content_type = Column(String)  # book, story, poem, quote
content = Column(Text)
subject = Column(String)
age_range = Column(String)
moral = Column(Text, nullable=True)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

class LearningLog(Base):
"""سجل التعلم المستمر"""
tablename = "learning_logs"

id = Column(Integer, primary_key=True)
profile_id = Column(Integer, index=True)
session_id = Column(String)
patterns_detected = Column(JSON, default=[])
improvement_areas = Column(JSON, default=[])
strength_areas = Column(JSON, default=[])
recommended_methods = Column(JSON, default=[])
confidence_trajectory = Column(JSON, default=[])
interaction_number = Column(Integer)
timestamp = Column(DateTime, default=datetime.utcnow)

class ParentReport(Base):
"""تقارير ولي الأمر الأسبوعية"""
tablename = "parent_reports"

id = Column(Integer, primary_key=True)
profile_id = Column(Integer, index=True)
week_start = Column(DateTime)
total_interactions = Column(Integer)
avg_confidence = Column(Float)
emotion_summary = Column(JSON)
topics_covered = Column(JSON)
strengths = Column(JSON)
areas_for_improvement = Column(JSON)
recommendations = Column(Text)
generated_at = Column(DateTime, default=datetime.utcnow)

============================================================================

PYDANTIC SCHEMAS

============================================================================

class ProfileCreate(BaseModel):
name: str
age: int
grade: str
learning_style: str = "mixed"

class EmotionCreate(BaseModel):
profile_id: int
emotion_type: str
intensity: float
trigger: Optional[str] = None

class QuestionCreate(BaseModel):
profile_id: int
question: str
subject: str

class MessageRequest(BaseModel):
profile_id: int
message: str

class LibraryEntryCreate(BaseModel):
title: str
author: str
content_type: str
content: str
subject: str
age_range: str

============================================================================

FASTAPI APP

============================================================================

app = FastAPI(title="ORION v2.0", version="2.0.0")

CORS

app.add_middleware(
CORSMiddleware,
allow_origins=[""],
allow_credentials=True,
allow_methods=["
"],
allow_headers=["*"],
)

Create tables

Base.metadata.create_all(bind=engine)

AI Client

client = Anthropic()
CONVERSATION_HISTORY = {} # {profile_id: [messages]}

============================================================================

HELPER FUNCTIONS

============================================================================

def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()

def detect_emotion(message: str, db: Session) -> Dict:
"""تحليل المشاعر من الرسالة"""
prompt = f"""
حلل مشاعر هذه الرسالة من طفل واكتب JSON فقط (بدون شرح):
الرسالة: "{message}"

أجب بـ JSON بهذا الشكل:
{{
    "emotion_type": "happy/anxious/confident/confused/sad/excited",
    "intensity": 0.7,
    "confidence": 0.8,
    "anxiety": 0.3,
    "response_type": "doctor/teacher/brother/sage"
}}
"""

response = client.messages.create(
    model="claude-opus-4-1",
    max_tokens=200,
    messages=[{"role": "user", "content": prompt}]
)

try:
    return json.loads(response.content[0].text)
except:
    return {
        "emotion_type": "neutral",
        "intensity": 0.5,
        "confidence": 0.5,
        "anxiety": 0.2,
        "response_type": "teacher"
    }

def get_orion_response(profile_id: int, message: str, emotion_data: Dict, db: Session) -> str:
"""الرد من ORION مع اختيار الشخصية المناسبة"""

# جلب ملف الطفل
profile = db.query(Profile).filter(Profile.id == profile_id).first()
if not profile:
    return "عذراً، لم أجد ملفك. هل تريد إنشاء حساب جديد؟"

# إعداد السياق
response_type = emotion_data.get("response_type", "teacher")

personalities = {
    "doctor": "أنت طبيب نفسي متخصص في تهدئة القلق والخوف. كن لطيفاً، استمع بفهم، قدم تطمينات حقيقية.",
    "teacher": "أنت معلم صبور يشرح بوضوح. استخدم الأسئلة السقراطية، ابني الفهم خطوة بخطوة.",
    "brother": "أنت أخ أكبر صديقاً. كن محفزاً، استخدم لغة الرفقاء، شجع بصدق.",
    "sage": "أنت حكيم درويش تقدم الحكمة والموعظة. كن هادئاً، عميقاً، إنساني."
}

system_prompt = f"""
أنت ORION - وكيل تعليمي ذكي متكامل.
الاسم: {profile.name}
العمر: {profile.age}
الصف: {profile.grade}

شخصيتك الحالية: {response_type}
{personalities.get(response_type, personalities['teacher'])}

الحالة العاطفية:
- النوع: {emotion_data.get('emotion_type')}
- الثقة: {emotion_data.get('confidence', 0.5)}
- القلق: {emotion_data.get('anxiety', 0.2)}

توجيهات:
1. اكتب بالعربية الفصحى البسيطة
2. الردود قصيرة وفعّالة (2-3 جمل)
3. اسأل بدلاً من أن تخبر عندما يكون مناسباً
4. كن حقيقياً، لا تكن مصطنعاً
5. تذكر السياق من المحادثات السابقة
"""

# إعادة المحادثة
if profile_id not in CONVERSATION_HISTORY:
    CONVERSATION_HISTORY[profile_id] = []

messages = CONVERSATION_HISTORY[profile_id]
messages.append({"role": "user", "content": message})

# الحصول على الرد
response = client.messages.create(
    model="claude-opus-4-1",
    max_tokens=500,
    system=system_prompt,
    messages=messages
)

assistant_message = response.content[0].text
messages.append({"role": "assistant", "content": assistant_message})

# الاحتفاظ بـ 20 آخر رسالة فقط
if len(messages) > 40:
    messages[:20] = []

return assistant_message

def update_learning_metrics(profile_id: int, db: Session):
"""تحديث معايير التعلم المستمر"""
profile = db.query(Profile).filter(Profile.id == profile_id).first()
if not profile:
return

# زيادة عدد التفاعلات
profile.interaction_count += 1

# تحديث معامل التكيف
if profile.interaction_count == 10:
    profile.adaptation_rate = 1.1
elif profile.interaction_count == 50:
    profile.adaptation_rate = 1.3
elif profile.interaction_count >= 100:
    profile.adaptation_rate = 1.5

# حفظ اللوج
recent_emotions = db.query(Emotion).filter(
    Emotion.profile_id == profile_id
).order_by(Emotion.timestamp.desc()).limit(10).all()

recent_questions = db.query(Question).filter(
    Question.profile_id == profile_id
).order_by(Question.timestamp.desc()).limit(10).all()

patterns = {
    "favorite_subject": None,
    "best_time": None,
    "learning_style": profile.learning_style
}

learning_log = LearningLog(
    profile_id=profile_id,
    session_id=f"session_{profile.interaction_count}",
    patterns_detected=patterns,
    interaction_number=profile.interaction_count
)

db.add(learning_log)
db.commit()

============================================================================

API ENDPOINTS

============================================================================

@app .get("/api/v1/health")
def health_check():
"""فحص صحة النظام"""
return {
"status": "healthy",
"version": "2.0.0",
"timestamp": datetime.utcnow().isoformat()
}

@app .post("/api/v1/profiles")
def create_profile(profile: ProfileCreate, db: Session = None):
"""إنشاء ملف طفل جديد"""
db = next(get_db())

existing = db.query(Profile).filter(Profile.name == profile.name).first()
if existing:
    raise HTTPException(status_code=400, detail="الملف موجود بالفعل")

new_profile = Profile(
    name=profile.name,
    age=profile.age,
    grade=profile.grade,
    learning_style=profile.learning_style
)
db.add(new_profile)
db.commit()
db.refresh(new_profile)

return {
    "id": new_profile.id,
    "name": new_profile.name,
    "age": new_profile.age,
    "message": "تم إنشاء الملف بنجاح! مرحباً بك في ORION"
}

@app .post("/api/v1/message")
def send_message(request: MessageRequest, db: Session = None):
"""إرسال رسالة والحصول على رد من ORION"""
db = next(get_db())

profile = db.query(Profile).filter(Profile.id == request.profile_id).first()
if not profile:
    raise HTTPException(status_code=404, detail="الملف غير موجود")

# تحليل المشاعر
emotion_data = detect_emotion(request.message, db)

# حفظ المشاعر
emotion_log = Emotion(
    profile_id=request.profile_id,
    emotion_type=emotion_data.get("emotion_type"),
    intensity=emotion_data.get("intensity"),
    confidence=emotion_data.get("confidence"),
    anxiety=emotion_data.get("anxiety"),
    response_type=emotion_data.get("response_type")
)
db.add(emotion_log)

# الحصول على الرد
response = get_orion_response(request.profile_id, request.message, emotion_data, db)

# تحديث معايير التعلم
update_learning_metrics(request.profile_id, db)

db.commit()

return {
    "profile_id": request.profile_id,
    "message": request.message,
    "emotion": emotion_data,
    "response": response,
    "personality": emotion_data.get("response_type"),
    "adaptation_rate": profile.adaptation_rate,
    "interaction_count": profile.interaction_count + 1
}

@app .get("/api/v1/profiles/{profile_id}/emotions/trends")
def get_emotion_trends(profile_id: int, days: int = 7, db: Session = None):
"""الاتجاهات العاطفية"""
db = next(get_db())

since = datetime.utcnow() - timedelta(days=days)
emotions = db.query(Emotion).filter(
    Emotion.profile_id == profile_id,
    Emotion.timestamp >= since
).all()

emotion_counts = {}
confidence_avg = 0
anxiety_avg = 0

for e in emotions:
    emotion_counts[e.emotion_type] = emotion_counts.get(e.emotion_type, 0) + 1
    confidence_avg += e.confidence
    anxiety_avg += e.anxiety

if emotions:
    confidence_avg /= len(emotions)
    anxiety_avg /= len(emotions)

return {
    "profile_id": profile_id,
    "days": days,
    "total_interactions": len(emotions),
    "emotion_distribution": emotion_counts,
    "avg_confidence": round(confidence_avg, 2),
    "avg_anxiety": round(anxiety_avg, 2),
    "emotional_trend": "improving" if confidence_avg > 0.7 else "needs_support"
}

@app .post("/api/v1/library")
def add_library_entry(entry: LibraryEntryCreate, db: Session = None):
"""إضافة كتاب أو قصة للمكتبة"""
db = next(get_db())

new_entry = LibraryEntry(
    title=entry.title,
    author=entry.author,
    content_type=entry.content_type,
    content=entry.content,
    subject=entry.subject,
    age_range=entry.age_range
)
db.add(new_entry)
db.commit()
db.refresh(new_entry)

return {
    "id": new_entry.id,
    "title": new_entry.title,
    "message": "تمت إضافة المحتوى للمكتبة بنجاح"
}

@app .get("/api/v1/library/{age_range}")
def get_library(age_range: str, db: Session = None):
"""الحصول على محتوى من المكتبة"""
db = next(get_db())

entries = db.query(LibraryEntry).filter(
    LibraryEntry.age_range == age_range
).order_by(LibraryEntry.updated_at.desc()).limit(20).all()

return {
    "age_range": age_range,
    "total": len(entries),
    "entries": [
        {
            "id": e.id,
            "title": e.title,
            "author": e.author,
            "type": e.content_type,
            "subject": e.subject
        }
        for e in entries
    ]
}

@app .get("/api/v1/profiles/{profile_id}/report")
def generate_parent_report(profile_id: int, db: Session = None):
"""تقرير ولي الأمر الأسبوعي"""
db = next(get_db())

profile = db.query(Profile).filter(Profile.id == profile_id).first()
if not profile:
    raise HTTPException(status_code=404, detail="الملف غير موجود")

week_start = datetime.utcnow() - timedelta(days=7)

emotions = db.query(Emotion).filter(
    Emotion.profile_id == profile_id,
    Emotion.timestamp >= week_start
).all()

questions = db.query(Question).filter(
    Question.profile_id == profile_id,
    Question.timestamp >= week_start
).all()

emotion_summary = {}
for e in emotions:
    emotion_summary[e.emotion_type] = emotion_summary.get(e.emotion_type, 0) + 1

subjects = {}
for q in questions:
    subjects[q.subject] = subjects.get(q.subject, 0) + 1

correct = len([q for q in questions if q.is_correct == 1])
total = len(questions)
accuracy = (correct / total * 100) if total > 0 else 0

report = ParentReport(
    profile_id=profile_id,
    week_start=week_start,
    total_interactions=len(emotions),
    avg_confidence=sum([e.confidence for e in emotions]) / len(emotions) if emotions else 0,
    emotion_summary=emotion_summary,
    topics_covered=subjects,
    strengths=list(subjects.keys())[:3] if subjects else [],
    areas_for_improvement=["التركيز", "حل المسائل"] if accuracy < 70 else []
)

db.add(report)
db.commit()

return {
    "profile_name": profile.name,
    "week_start": week_start.isoformat(),
    "total_interactions": len(emotions),
    "accuracy": round(accuracy, 1),
    "emotion_summary": emotion_summary,
    "subjects_covered": subjects,
    "adaptation_level": profile.adaptation_rate,
    "recommendation": "استمر في المثابرة! التقدم واضح." if accuracy > 70 else "احتاج دعم إضافي في بعض المواضيع"
}

@app .post("/api/v1/sessions")
def create_session(profile_id: int, db: Session = None):
"""بدء جلسة تعليمية"""
return {
"session_id": f"session_{profile_id}_{datetime.utcnow().timestamp()}",
"status": "started",
"timestamp": datetime.utcnow().isoformat()
}

@app .get("/api/v1/recommendations/{profile_id}")
def get_recommendations(profile_id: int, db: Session = None):
"""الحصول على التوصيات المخصصة"""
db = next(get_db())

profile = db.query(Profile).filter(Profile.id == profile_id).first()
if not profile:
    raise HTTPException(status_code=404, detail="الملف غير موجود")

recent_emotions = db.query(Emotion).filter(
    Emotion.profile_id == profile_id
).order_by(Emotion.timestamp.desc()).limit(10).all()

anxious_count = len([e for e in recent_emotions if e.emotion_type == "anxious"])

recommendations = []

if anxious_count > 3:
    recommendations.append({
        "type": "emotional_support",
        "message": "لاحظت قليلاً من القلق. نقترح جلسات استرخاء موجهة.",
        "action": "psychology_session"
    })

if profile.interaction_count < 10:
    recommendations.append({
        "type": "warm_up",
        "message": "دعنا نبني الثقة معاً! تفاعل أكثر يساعدني أفهمك أفضل.",
        "action": "continue_learning"
    })

if profile.adaptation_rate >= 1.3:
    recommendations.append({
        "type": "advancement",
        "message": "أنت تتقدم بسرعة! يمكننا رفع مستوى الصعوبة.",
        "action": "increase_difficulty"
    })

return {
    "profile_id": profile_id,
    "adaptation_level": profile.adaptation_rate,
    "recommendations": recommendations
}

============================================================================

RUN

============================================================================

if name == "main":
uvicorn.run(
app,
host="0.0.0.0",
port=8000,
log_level="info"
)

hafo012 changed pull request status to closed

Sign up or log in to comment