# API Contract **Version:** 1.0 **Base URL:** `/api/v1` **Format:** JSON **Authentication:** Session-based (session_id in headers or cookies) --- ## Table of Contents 1. [Session Management](#session-management) 2. [Lesson Initialization](#lesson-initialization) 3. [Vocabulary Endpoints](#vocabulary-endpoints) 4. [Grammar Endpoints](#grammar-endpoints) 5. [Exercise Endpoints](#exercise-endpoints) 6. [Test Endpoints](#test-endpoints) 7. [Error Responses](#error-responses) 8. [State Queries](#state-queries) --- ## Session Management ### Start New Session **Endpoint:** `POST /session/start` **Request:** ```json { "user_id": "user_123", "lesson_number": 3 } ``` **Response:** ```json { "session_id": "sess_abc123", "user_id": "user_123", "lesson_number": 3, "created_at": "2026-04-04T10:30:00Z", "state": { "vocab_mode": "not_started", "grammar_mode": "not_started", "vocab_batches_completed": [], "grammar_topics_completed": [] } } ``` ### Get Session State **Endpoint:** `GET /session/{session_id}` **Response:** ```json { "session_id": "sess_abc123", "user_id": "user_123", "lesson_number": 3, "state": { "vocab_mode": "batch_quiz", "current_vocab_batch": 2, "vocab_batches_completed": [1], "vocab_batch_scores": {"1": "2/3"}, "vocab_words_missed": ["مَدْرَسَة"], "grammar_mode": "not_started", "grammar_topics_completed": [], "current_grammar_topic": null } } ``` --- ## Lesson Initialization ### Initialize Lesson **Endpoint:** `POST /lesson/initialize` **Description:** Loads all content into memory (Agent 3 caches vocabulary + grammar, Agent 2 loads with grading flexibility rules) **Request:** ```json { "session_id": "sess_abc123", "lesson_number": 3 } ``` **Response:** ```json { "success": true, "lesson_number": 3, "lesson_structure": { "vocabulary": { "total_words": 10, "batch_structure": [3, 3, 3, 1], "batches_count": 4 }, "grammar": { "topics": ["feminine_nouns", "definite_article"], "topics_count": 2, "quiz_questions_per_topic": 5 } }, "message": "Lesson 3 ready! Start with vocabulary or grammar?", "next_actions": [ {"action": "start_vocab", "label": "Start Vocabulary (10 words)"}, {"action": "start_grammar", "label": "Start Grammar (2 topics)"}, {"action": "vocab_final_test", "label": "Skip to Vocabulary Test"} ] } ``` **Orchestrator Mapping:** ```python orchestrator.initialize_lesson(lesson_number=3) ``` --- ## Vocabulary Endpoints ### Start Vocabulary Section **Endpoint:** `POST /vocab/start` **Request:** ```json { "session_id": "sess_abc123" } ``` **Response:** ```json { "success": true, "message": "Let's learn 10 new words in 4 batches! Ready to start?", "total_words": 10, "batches_count": 4, "next_action": { "action": "get_batch", "batch_number": 1 } } ``` --- ### Get Vocabulary Batch **Endpoint:** `GET /vocab/batch/{batch_number}` **Request:** (batch_number in URL) **Response:** ```json { "success": true, "lesson_number": 3, "batch_number": 1, "total_batches": 4, "words": [ { "word_id": "w1", "arabic": "كِتَاب", "transliteration": "kitaab", "english": "book" }, { "word_id": "w2", "arabic": "مَدْرَسَة", "transliteration": "madrasa", "english": "school" }, { "word_id": "w3", "arabic": "قَلَم", "transliteration": "qalam", "english": "pen" } ], "message": "Let's learn Batch 1! Here are your first 3 words:\n\n📝 كِتَاب (kitaab) - book\n📝 مَدْرَسَة (madrasa) - school\n📝 قَلَم (qalam) - pen\n\nTake your time reviewing these. When you're ready, let's test your knowledge!", "next_action": { "action": "start_batch_quiz", "batch_number": 1 } } ``` **Orchestrator Mapping:** ```python orchestrator.get_vocab_batch(batch_number=1) ``` --- ### Start Batch Quiz **Endpoint:** `POST /vocab/batch/{batch_number}/quiz/start` **Request:** ```json { "session_id": "sess_abc123" } ``` **Response:** ```json { "success": true, "batch_number": 1, "total_questions": 3, "question_number": 1, "question": { "question_id": "q1", "word_id": "w1", "type": "arabic_to_english", "prompt": "What does كِتَاب mean?", "arabic": "كِتَاب", "transliteration": "kitaab" } } ``` --- ### Submit Vocabulary Answer **Endpoint:** `POST /vocab/batch/{batch_number}/quiz/answer` **Request:** ```json { "session_id": "sess_abc123", "question_id": "q1", "word_id": "w1", "student_answer": "book" } ``` **Response (Correct):** ```json { "success": true, "correct": true, "feedback": "Correct! ✓ كِتَاب = book. Great job!", "score": { "current": "1/1", "progress": "Question 1 of 3 complete" }, "next_action": { "action": "next_question", "question_number": 2 } } ``` **Response (Incorrect):** ```json { "success": true, "correct": false, "student_answer": "school", "correct_answer": "book", "feedback": "Not quite! The word كِتَاب (kitaab) means 'book', not 'school'. Try to remember: kitaab = book.", "score": { "current": "0/1", "progress": "Question 1 of 3 complete" }, "next_action": { "action": "next_question", "question_number": 2 } } ``` **Orchestrator Mapping:** ```python orchestrator.handle_vocab_quiz_answer( batch_number=1, question_id="q1", word_id="w1", student_answer="book" ) ``` --- ### Get Batch Quiz Summary **Endpoint:** `GET /vocab/batch/{batch_number}/quiz/summary` **Response:** ```json { "success": true, "batch_number": 1, "score": "2/3", "words_correct": ["كِتَاب", "قَلَم"], "words_incorrect": [ { "arabic": "مَدْرَسَة", "transliteration": "madrasa", "correct_answer": "school", "student_answer": "house" } ], "message": "Good effort! You got 2 out of 3 correct.\n\nYou missed:\n• مَدْرَسَة (madrasa) = school", "next_action": { "action": "get_batch", "batch_number": 2, "label": "Continue to Batch 2" } } ``` --- ### Start Vocabulary Final Test **Endpoint:** `POST /vocab/final-test/start` **Request:** ```json { "session_id": "sess_abc123" } ``` **Response:** ```json { "success": true, "message": "Final vocabulary test! All 10 words, randomized order.", "total_questions": 10, "test_type": "vocabulary_final", "next_action": { "action": "get_question", "question_number": 1 } } ``` --- ### Submit Final Test Answer **Endpoint:** `POST /vocab/final-test/answer` **Request:** ```json { "session_id": "sess_abc123", "question_number": 3, "word_id": "w5", "student_answer": "house" } ``` **Response:** ```json { "success": true, "correct": true, "feedback": "Correct! ✓", "score": { "current": "3/3", "total": 10, "progress": "Question 3 of 10 complete" }, "next_action": { "action": "next_question", "question_number": 4 } } ``` --- ### Get Final Test Results **Endpoint:** `GET /vocab/final-test/results` **Response:** ```json { "success": true, "score": "8/10", "percentage": 80, "words_correct": 8, "words_incorrect": [ { "arabic": "مَدْرَسَة", "correct_answer": "school", "student_answer": "house" }, { "arabic": "بَيْت", "correct_answer": "house", "student_answer": "door" } ], "message": "Great job! You scored 8 out of 10! 🎉\n\nWords to review:\n• مَدْرَسَة (madrasa) = school\n• بَيْت (bayt) = house", "next_action": { "action": "start_grammar", "label": "Start Grammar Section" } } ``` --- ## Grammar Endpoints ### Start Grammar Section **Endpoint:** `POST /grammar/start` **Request:** ```json { "session_id": "sess_abc123" } ``` **Response:** ```json { "success": true, "lesson_number": 3, "topics": [ { "topic_id": "feminine_nouns", "topic_name": "Feminine Nouns", "order": 1 }, { "topic_id": "definite_article", "topic_name": "Definite Article", "order": 2 } ], "message": "I'll teach you 2 grammar topics:\n1. Feminine Nouns\n2. Definite Article\n\nEach topic has a 5-question quiz. Ready to start?", "next_action": { "action": "get_topic", "topic_id": "feminine_nouns" } } ``` --- ### Get Grammar Topic Explanation **Endpoint:** `GET /grammar/topic/{topic_id}` **Response:** ```json { "success": true, "lesson_number": 3, "topic_id": "feminine_nouns", "topic_name": "Feminine Nouns", "explanation": "Let's learn about Feminine Nouns! 🌟\n\n**The Rule:**\nIn Arabic, most feminine nouns end with ة (taa marbuuta).\n\n**Examples:**\n- مَدْرَسَة (madrasa) - school ✓ (ends in ة)\n- كِتَاب (kitaab) - book (masculine, no ة)\n\n**Quick Check:** Look at the word بِنْت (bint - girl). Does it end in ة?", "grammar_rule": "Feminine nouns usually end in ة (taa marbuuta)", "examples": [ { "arabic": "مَدْرَسَة", "transliteration": "madrasa", "english": "school", "is_feminine": true, "explanation": "ends in ة" }, { "arabic": "كِتَاب", "transliteration": "kitaab", "english": "book", "is_feminine": false, "explanation": "no ة - masculine" } ], "topics_remaining": 1, "next_action": { "action": "start_topic_quiz", "topic_id": "feminine_nouns" } } ``` **Orchestrator Mapping:** ```python orchestrator.get_grammar_topic_explanation(topic_id="feminine_nouns") ``` --- ### Start Topic Quiz **Endpoint:** `POST /grammar/topic/{topic_id}/quiz/start` **Request:** ```json { "session_id": "sess_abc123" } ``` **Response:** ```json { "success": true, "topic_id": "feminine_nouns", "topic_name": "Feminine Nouns", "total_questions": 5, "message": "Now let's test your understanding with 5 questions!", "next_action": { "action": "get_question", "question_number": 1 } } ``` --- ### Get Topic Quiz Question **Endpoint:** `GET /grammar/topic/{topic_id}/quiz/question/{question_number}` **Response:** ```json { "success": true, "topic_id": "feminine_nouns", "question_number": 1, "total_questions": 5, "question": { "question_id": "gq1", "type": "identification", "prompt": "Is 'مَدْرَسَة' masculine or feminine?", "arabic": "مَدْرَسَة", "transliteration": "madrasa", "options": ["masculine", "feminine"] } } ``` --- ### Submit Topic Quiz Answer **Endpoint:** `POST /grammar/topic/{topic_id}/quiz/answer` **Request:** ```json { "session_id": "sess_abc123", "question_id": "gq1", "question_number": 1, "student_answer": "feminine" } ``` **Response (Correct):** ```json { "success": true, "correct": true, "feedback": "Correct! ✓ مَدْرَسَة is feminine because it ends in ة (taa marbuuta). You're doing great! (1/1 so far)", "explanation": "The ة ending is the key indicator of feminine nouns.", "score": { "current_topic": "1/1", "questions_remaining": 4 }, "next_action": { "action": "next_question", "question_number": 2 } } ``` **Response (Incorrect):** ```json { "success": true, "correct": false, "student_answer": "masculine", "correct_answer": "feminine", "feedback": "Not quite. مَدْرَسَة is feminine because it ends in ة (taa marbuuta). The ة is the key indicator! (0/1 so far)", "errors": [ { "error_type": "gender_identification", "details": "مَدْرَسَة ends in ة, which indicates feminine", "correction": "Look for the ة (taa marbuuta) ending" } ], "score": { "current_topic": "0/1", "questions_remaining": 4 }, "next_action": { "action": "next_question", "question_number": 2 } } ``` **Orchestrator Mapping:** ```python orchestrator.handle_grammar_quiz_answer( topic_id="feminine_nouns", question_id="gq1", question_number=1, student_answer="feminine" ) ``` --- ### Get Topic Quiz Results **Endpoint:** `GET /grammar/topic/{topic_id}/quiz/results` **Response (Score >= 4/5):** ```json { "success": true, "topic_id": "feminine_nouns", "topic_name": "Feminine Nouns", "score": "4/5", "percentage": 80, "passed": true, "message": "Great job! You got 4 out of 5 correct! ✓\n\nYou've mastered feminine nouns. Ready for the next topic?", "questions_correct": 4, "questions_incorrect": 1, "next_action": { "action": "get_topic", "topic_id": "definite_article", "label": "Continue to Next Topic" } } ``` **Response (Score < 4/5 - Suggest Review):** ```json { "success": true, "topic_id": "feminine_nouns", "topic_name": "Feminine Nouns", "score": "3/5", "percentage": 60, "passed": false, "message": "You got 3 out of 5. You might want to review feminine nouns.\n\nWeak areas:\n- Identifying ة endings\n- Exceptions to the rule\n\nReview and re-test, or move on?", "questions_correct": 3, "questions_incorrect": 2, "weak_areas": [ "Identifying ة endings", "Exceptions to the rule" ], "next_actions": [ { "action": "review_topic", "topic_id": "feminine_nouns", "label": "Review and Re-test" }, { "action": "get_topic", "topic_id": "definite_article", "label": "Move to Next Topic" } ] } ``` **Orchestrator Mapping:** ```python orchestrator.get_topic_quiz_results(topic_id="feminine_nouns") ``` --- ## Exercise Endpoints ### Generate Exercises **Endpoint:** `POST /exercises/generate` **Request:** ```json { "session_id": "sess_abc123", "lesson_number": 3, "exercise_type": "fill_in_blank", "grammar_point": "gender_agreement", "count": 5 } ``` **Response:** ```json { "success": true, "exercise_set_id": "exset_123", "lesson_number": 3, "exercise_type": "fill_in_blank", "count": 5, "exercises": [ { "exercise_id": "ex1", "question": "Complete: كِتَاب ___", "options": ["كَبِير", "كَبِيرَة"], "hint": "كتاب is masculine" }, { "exercise_id": "ex2", "question": "Complete: مَدْرَسَة ___", "options": ["صَغِير", "صَغِيرَة"], "hint": "مدرسة is feminine" }, { "exercise_id": "ex3", "question": "Complete: قَلَم ___", "options": ["جَدِيد", "جَدِيدَة"], "hint": "قلم is masculine" }, { "exercise_id": "ex4", "question": "Complete: سَيَّارَة ___", "options": ["كَبِير", "كَبِيرَة"], "hint": "سيارة is feminine" }, { "exercise_id": "ex5", "question": "Complete: بَيْت ___", "options": ["جَمِيل", "جَمِيلَة"], "hint": "بيت is masculine" } ], "message": "Here are 5 fill-in-blank exercises to practice gender agreement!" } ``` **Orchestrator Mapping:** ```python orchestrator.generate_exercises( lesson_number=3, exercise_type="fill_in_blank", grammar_point="gender_agreement", count=5 ) ``` --- ### Submit Exercise Answers **Endpoint:** `POST /exercises/{exercise_set_id}/submit` **Request:** ```json { "session_id": "sess_abc123", "answers": [ {"exercise_id": "ex1", "answer": "كَبِير"}, {"exercise_id": "ex2", "answer": "صَغِيرَة"}, {"exercise_id": "ex3", "answer": "جَدِيد"}, {"exercise_id": "ex4", "answer": "كَبِيرَة"}, {"exercise_id": "ex5", "answer": "جَمِيلَة"} ] } ``` **Response:** ```json { "success": true, "exercise_set_id": "exset_123", "total_score": "4/5", "percentage": 80, "results": [ { "exercise_id": "ex1", "correct": true, "student_answer": "كَبِير" }, { "exercise_id": "ex2", "correct": true, "student_answer": "صَغِيرَة" }, { "exercise_id": "ex3", "correct": true, "student_answer": "جَدِيد" }, { "exercise_id": "ex4", "correct": true, "student_answer": "كَبِيرَة" }, { "exercise_id": "ex5", "correct": false, "student_answer": "جَمِيلَة", "correct_answer": "جَمِيل", "explanation": "بَيْت is masculine, so use جَمِيل (masculine)" } ], "message": "Great work! You got 4 out of 5 correct! 🌟\n\nReview:\n• Exercise 5: بَيْت is masculine, use جَمِيل" } ``` **Orchestrator Mapping:** ```python orchestrator.grade_exercises( exercise_set_id="exset_123", answers=[...] ) # Internally calls Agent 2 (GradingAgent) for each answer # Returns {"correct": true/false} for each, with flexible grading: # - Accepts synonyms (e.g., "instructor" → "teacher") # - Accepts minor typos (e.g., "scool" → "school") # - Accepts capitalization variations (e.g., "Book" → "book") # - For Arabic text: Internal harakaat optional, case endings required ``` --- ## Test Endpoints ### Generate Lesson Test **Endpoint:** `POST /test/generate` **Request:** ```json { "session_id": "sess_abc123", "lesson_number": 3, "question_count": 10 } ``` **Response:** ```json { "success": true, "test_id": "test_456", "lesson_number": 3, "question_count": 10, "grammar_points": ["feminine_nouns", "definite_article"], "message": "Lesson 3 Test - 10 questions covering all topics", "questions": [ { "question_id": "t1", "type": "fill_in_blank", "prompt": "Complete: الكتاب ___", "options": ["الكبير", "كبير"] }, { "question_id": "t2", "type": "identification", "prompt": "Is 'بِنْت' masculine or feminine?", "options": ["masculine", "feminine"] } // ... 8 more questions ] } ``` --- ### Submit Test Answers **Endpoint:** `POST /test/{test_id}/submit` **Request:** ```json { "session_id": "sess_abc123", "answers": [ {"question_id": "t1", "answer": "الكبير"}, {"question_id": "t2", "answer": "feminine"} // ... all 10 answers ] } ``` **Response:** ```json { "success": true, "test_id": "test_456", "total_score": "8/10", "percentage": 80, "passed": true, "results_by_topic": { "feminine_nouns": { "score": "5/5", "status": "excellent" }, "definite_article": { "score": "3/5", "status": "needs_review" } }, "message": "Well done! You scored 8 out of 10! 🎉\n\nStrengths:\n- Excellent at feminine nouns (5/5 correct)\n\nAreas to review:\n- Definite article agreement (3/5 correct)\n\nSuggestion: Review when to use ال with adjectives.\n\nReady to move to Lesson 4?", "next_actions": [ { "action": "review_topic", "topic_id": "definite_article", "label": "Review Definite Article" }, { "action": "next_lesson", "lesson_number": 4, "label": "Start Lesson 4" } ] } ``` --- ## Error Responses ### Standard Error Format All errors follow this structure: ```json { "success": false, "error": { "code": "ERROR_CODE", "message": "Human-readable error message", "details": {} } } ``` ### Error Codes **Authentication/Session:** - `INVALID_SESSION` (401) - Session ID not found or expired - `SESSION_EXPIRED` (401) - Session timed out - `UNAUTHORIZED` (401) - User not authorized for this resource **Validation:** - `INVALID_REQUEST` (400) - Malformed JSON or missing required fields - `INVALID_LESSON` (400) - Lesson number doesn't exist - `INVALID_BATCH` (400) - Batch number out of range - `INVALID_TOPIC` (400) - Topic ID not found in lesson **State:** - `LESSON_NOT_INITIALIZED` (400) - Must call `/lesson/initialize` first - `WRONG_MODE` (400) - Action not allowed in current mode (e.g., trying to quiz before teaching) - `QUIZ_NOT_STARTED` (400) - Must start quiz before submitting answers **RAG/Content:** - `CONTENT_NOT_FOUND` (404) - Requested content not in RAG database - `RAG_TIMEOUT` (504) - RAG query timed out **Agent:** - `AGENT_ERROR` (500) - Agent returned invalid response - `GENERATION_TIMEOUT` (504) - Exercise/question generation timed out - `GRADING_ERROR` (500) - Agent 2 (GradingAgent) failed to grade or returned invalid JSON ### Error Examples **Invalid Session:** ```json { "success": false, "error": { "code": "INVALID_SESSION", "message": "Session not found or expired", "details": { "session_id": "sess_invalid" } } } ``` **Lesson Not Initialized:** ```json { "success": false, "error": { "code": "LESSON_NOT_INITIALIZED", "message": "Lesson must be initialized before starting vocabulary", "details": { "required_action": "POST /lesson/initialize" } } } ``` **Content Not Found:** ```json { "success": false, "error": { "code": "CONTENT_NOT_FOUND", "message": "Lesson 99 not found in database", "details": { "lesson_number": 99, "available_lessons": [1, 2, 3, 4, 5, 6, 7, 8] } } } ``` --- ## State Queries ### Get Current Progress **Endpoint:** `GET /progress/{session_id}` **Response:** ```json { "success": true, "lesson_number": 3, "overall_progress": { "vocabulary": { "status": "in_progress", "batches_completed": 2, "batches_total": 4, "scores": {"1": "2/3", "2": "3/3"}, "words_mastered": 5, "words_total": 10 }, "grammar": { "status": "not_started", "topics_completed": 0, "topics_total": 2, "scores": {} } } } ``` --- ## Implementation Notes ### Session Management **Storage:** Redis or in-memory cache (for demo) **Session Object:** ```python { "session_id": "sess_abc123", "user_id": "user_123", "lesson_number": 3, "created_at": "2026-04-04T10:30:00Z", "last_activity": "2026-04-04T11:00:00Z", "expires_at": "2026-04-04T18:30:00Z", # 8 hours # Full conversation state (from ARCHITECTURE.md) "state": { "vocab_mode": "batch_quiz", "vocab_batches_completed": [1], "current_vocab_batch": 2, # ... full state schema } } ``` ### Rate Limiting - 100 requests per minute per session - 10 concurrent sessions per user ### Response Times Target latencies: - Vocabulary/Grammar content: < 200ms (from cache) - Single answer grading: < 500ms (Agent 2 GradingAgent with 7B model, max 50 tokens) - Exercise generation: < 2s (Agent 3 LLM generation) - Lesson initialization: < 3s (RAG query + caching) ### Agent 2 (Grading Agent) Details **Model:** Fine-tuned Qwen2.5-7B (baseline evaluated 2026-04-13) **Grading Modes:** 1. **grading_vocab** - Vocabulary translation grading - Input: word (Arabic), student_answer, correct_answer - Output: `{"correct": true/false}` - Flexibility: Accepts synonyms, typos, capitalization variations 2. **grading_grammar** (single question) - Grammar quiz question grading - Input: question, student_answer, correct_answer - Output: `{"correct": true/false}` - Flexibility: Accepts abbreviations (m/f for masculine/feminine), synonyms, typos - Arabic text: Internal harakaat optional, case endings (final harakaat) required 3. **grading_grammar** (multiple questions) - Test grading - Input: lesson_number, answers (list of question/answer pairs) - Output: `{"total_score": "X/Y", "results": [{"question_id": "...", "correct": true/false}, ...]}` **Edge Case Handling:** - Synonyms: ✓ "instructor" = "teacher" - Minor typos: ✓ "scool" = "school" - Capitalization: ✓ "Book" = "book" - Articles: ✓ "a pen" = "pen" - Arabic harakaat: Internal marks optional (الكتابُ = الكِتَابُ), case endings required (الكتاب ≠ الكِتَابُ) **Baseline Results (7B model, 2026-04-13):** - JSON compliance: 0-6% (adds explanations after JSON) - Reasoning accuracy: 83% (5/6 correct decisions when ignoring format issues) - Fine-tuning planned to address format compliance and harakaat flexibility --- **Next Steps for Implementation:** 1. Build orchestrator with these exact return formats 2. Wrap orchestrator in FastAPI with these endpoints 3. Frontend consumes these contracts exactly **Contract is versioned:** If we need changes, create `/api/v2`