import os from pathlib import Path from dotenv import load_dotenv load_dotenv(dotenv_path=Path(__file__).parent / "env") # ── Paths ───────────────────────────────────────────────────────────────── BASE_DIR = Path(__file__).parent INDEX_DIR = BASE_DIR / "faiss_index" INDEX_FILE = INDEX_DIR / "index.faiss" METADATA_FILE = INDEX_DIR / "metadata.json" # ── API keys ────────────────────────────────────────────────────────────── OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", "") OPENROUTER_API_KEY = os.environ.get("OPENROUTER_API_KEY", "") OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1" # ── Embedding & query expansion ────────────────────────────────────────── EMBEDDING_MODEL = "text-embedding-3-large" QUERY_EXPANSION_MODEL = "gpt-4o-mini" # ── Available LLM models ───────────────────────────────────────────────── AVAILABLE_MODELS = { "gpt-4o": { "label": "GPT-4o", "desc": "Flagship multimodal · best accuracy", "provider": "openai", "price_in": 2.50, "price_out": 10.00, }, "gpt-4o-mini": { "label": "GPT-4o mini", "desc": "Fast & affordable · great for simple tasks", "provider": "openai", "price_in": 0.15, "price_out": 0.60, }, "gpt-4.1-mini": { "label": "GPT-4.1 mini", "desc": "Latest mini · improved instruction-following", "provider": "openai", "price_in": 0.40, "price_out": 1.60, }, "gpt-5.4-mini": { "label": "GPT-5.4 mini", "desc": "Next-gen mini · advanced reasoning at low cost", "provider": "openai", "price_in": 0.25, "price_out": 1.00, }, "openai/gpt-4o-mini-2024-07-18": { "label": "GPT-4o OSS 120B", "desc": "Open-weight GPT-4o class · 120B params", "provider": "openrouter", "price_in": 0.15, "price_out": 0.60, }, "anthropic/claude-sonnet-4.6": { "label": "Claude Sonnet 4.6", "desc": "Anthropic · strong reasoning & long context", "provider": "openrouter", "price_in": 3.00, "price_out": 15.00, }, "moonshotai/kimi-k2": { "label": "Kimi K2", "desc": "Moonshot AI · efficient multilingual", "provider": "openrouter", "price_in": 0.60, "price_out": 2.50, }, "deepseek/deepseek-chat-v3-0324": { "label": "DeepSeek V3 Flash", "desc": "DeepSeek · ultra-low cost, strong reasoning", "provider": "openrouter", "price_in": 0.14, "price_out": 0.28, }, "google/gemini-2.0-flash-001": { "label": "Gemini 2.0 Flash", "desc": "Google · fast multimodal, 1M context window", "provider": "openrouter", "price_in": 0.10, "price_out": 0.40, }, "qwen/qwen3-235b-a22b": { "label": "Qwen3 235B", "desc": "Alibaba · massive MoE, hybrid thinking mode", "provider": "openrouter", "price_in": 0.13, "price_out": 0.40, }, "meta-llama/llama-4-maverick": { "label": "Llama 4 Maverick", "desc": "Meta · mixture-of-experts, best open-weight", "provider": "openrouter", "price_in": 0.20, "price_out": 0.60, }, } DEFAULT_MODEL = "gpt-4o-mini" # ── Retrieval tuning ───────────────────────────────────────────────────── TOP_K_VECTOR = 15 TOP_K_BM25 = 15 TOP_K_FINAL = 5 MIN_RETRIEVAL_SCORE = 0.25 # ── Generation tuning ──────────────────────────────────────────────────── TEMPERATURE = 0.2 MAX_RESPONSE_TOKENS = 800 # ── Request limits ──────────────────────────────────────────────────────── MAX_MESSAGE_LENGTH = 2_000 MAX_MESSAGES_PER_REQ = 50 MAX_HISTORY_MESSAGES = 8 # ── System prompts ──────────────────────────────────────────────────────── SYSTEM_EN = """\ You are KASITBot, the official academic assistant for KASIT \ (King Abdullah II School of Information Technology) at the University of Jordan. Your role: - Help students with courses, exams, fees, graduation requirements, office hours, \ scholarships (makruma), academic regulations, study plans, and faculty contacts. - Answer ONLY from the provided context documents. Do NOT invent, guess, or fabricate \ any information such as dates, numbers, names, fees, or schedules. - If the context does not contain the answer, say clearly: \ "I don't have this information in my current documents. Please check with the KASIT \ administration or visit the official University of Jordan website." - Never hallucinate course codes, professor names, exam dates, fee amounts, or any factual data. - When listing information (courses, schedules, fees), present it in a clear, structured format \ using tables or bullet points. - Reply in the same language the student used. If they write in Arabic, respond in Arabic. \ If in English, respond in English. - Be concise, accurate, and helpful. Students depend on you for correct academic guidance.\ """ SYSTEM_AR = """\ أنت كاسيت بوت، المساعد الأكاديمي الرسمي لكلية الملك عبدالله الثاني لتكنولوجيا المعلومات \ (KASIT) في الجامعة الأردنية. دورك: - مساعدة الطلاب في المساقات والامتحانات والرسوم ومتطلبات التخرج وساعات المكتب \ والمكرمات والأنظمة الأكاديمية والخطط الدراسية والتواصل مع أعضاء هيئة التدريس. - أجب فقط من الوثائق والسياق المتاح لك. لا تخترع أو تتوقع أو تُلفّق أي معلومات \ مثل التواريخ أو الأرقام أو الأسماء أو الرسوم أو الجداول. - إذا لم يحتوِ السياق على الإجابة، قل بوضوح: \ "لا تتوفر لدي هذه المعلومة في الوثائق الحالية. يرجى مراجعة إدارة كلية كاسيت \ أو زيارة الموقع الرسمي للجامعة الأردنية." - لا تختلق أبداً أرقام مساقات أو أسماء أساتذة أو تواريخ امتحانات أو مبالغ رسوم \ أو أي بيانات واقعية. - عند عرض المعلومات (مساقات، جداول، رسوم)، قدمها بشكل منظم وواضح \ باستخدام جداول أو نقاط. - أجب بنفس اللغة التي يستخدمها الطالب. - كن موجزاً ودقيقاً ومفيداً. الطلاب يعتمدون عليك للحصول على إرشاد أكاديمي صحيح.\ """ # ── Student portal tool definitions ─────────────────────────────────────── PORTAL_TOOLS = [ { "type": "function", "function": { "name": "get_student_profile", "description": ( "Retrieve the registered student's full profile: name, student ID, email, " "GPA, major, year, semester, academic year, enrollment status, total enrolled " "courses, and total credit hours this semester. Call this whenever the user " "asks about who they are, their GPA, their ID, their email, their major, or " "a general summary of their registration." ), "parameters": {"type": "object", "properties": {}, "required": []}, }, }, { "type": "function", "function": { "name": "get_enrolled_courses", "description": ( "Return the list of courses the student is currently enrolled in this semester, " "including course number, name, instructor, days, times, room, credits, and category. " "Call this when the user asks about their current courses, schedule, what they are " "taking, credit hours, or any specific enrolled course detail." ), "parameters": {"type": "object", "properties": {}, "required": []}, }, }, { "type": "function", "function": { "name": "get_available_courses", "description": ( "Return all courses currently available for the student to add/register, " "including seats available, prerequisites, instructor, and schedule. " "Call this when the user asks what courses they can add, available courses, " "or courses offered this semester that they are NOT enrolled in." ), "parameters": {"type": "object", "properties": {}, "required": []}, }, }, { "type": "function", "function": { "name": "get_schedule", "description": ( "Return the student's full weekly schedule with all enrolled courses ordered by time. " "Call this when the user asks for their schedule, timetable, or wants to print/download " "their course schedule." ), "parameters": {"type": "object", "properties": {}, "required": []}, }, }, { "type": "function", "function": { "name": "enroll_course", "description": ( "Enroll (add/register) the student into a course. Call this when the user explicitly " "asks to add, enroll in, register for, or sign up for a course. " "You must provide the exact course_number. If the user gives a course name instead, " "first call get_available_courses to find the matching course_number." ), "parameters": { "type": "object", "properties": { "course_number": { "type": "string", "description": "The exact course code/number (e.g., 'CS5502', '1915471')", } }, "required": ["course_number"], }, }, }, { "type": "function", "function": { "name": "drop_course", "description": ( "Drop (withdraw/remove) the student from an enrolled course. Call this when the user " "explicitly asks to drop, remove, withdraw from, or delete a course. " "You must provide the exact course_number. If the user gives a course name instead, " "first call get_enrolled_courses to find the matching course_number." ), "parameters": { "type": "object", "properties": { "course_number": { "type": "string", "description": "The exact course code/number to drop (e.g., '1915471')", } }, "required": ["course_number"], }, }, }, { "type": "function", "function": { "name": "update_gpa", "description": ( "Update the student's GPA to a new value. Only call this when the user explicitly " "asks to update, change, or set their GPA. The GPA must be between 0.0 and 4.0." ), "parameters": { "type": "object", "properties": { "gpa": { "type": "number", "description": "The new GPA value, must be between 0.0 and 4.0", } }, "required": ["gpa"], }, }, }, { "type": "function", "function": { "name": "get_semester_history", "description": ( "Return the student's complete semester-by-semester academic history: " "every semester's GPA, credit hours taken, cumulative GPA, cumulative credits, " "year level, and registration status — going back to the first semester. " "Call this for ANY question about past or historical GPA, a specific semester's " "performance, grade trends, cumulative credits, academic level, or year-by-year progress." ), "parameters": {"type": "object", "properties": {}, "required": []}, }, }, { "type": "function", "function": { "name": "get_absence_records", "description": ( "Return the student's absence count for each currently enrolled course, " "including warning flags for courses approaching the 25% absence limit. " "Call this for ANY question about absences, attendance, missed classes, " "or risk of being barred from exams." ), "parameters": {"type": "object", "properties": {}, "required": []}, }, }, ]