arabic-teacher / docs /API_CONTRACT.md
Kelly Diabagate
Agent 2 implementation + baseline eval and small improvements
5c91b05
|
Raw
History Blame Contribute Delete
24.3 kB

A newer version of the Gradio SDK is available: 6.20.0

Upgrade

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
  2. Lesson Initialization
  3. Vocabulary Endpoints
  4. Grammar Endpoints
  5. Exercise Endpoints
  6. Test Endpoints
  7. Error Responses
  8. State Queries

Session Management

Start New Session

Endpoint: POST /session/start

Request:

{
  "user_id": "user_123",
  "lesson_number": 3
}

Response:

{
  "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:

{
  "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:

{
  "session_id": "sess_abc123",
  "lesson_number": 3
}

Response:

{
  "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:

orchestrator.initialize_lesson(lesson_number=3)

Vocabulary Endpoints

Start Vocabulary Section

Endpoint: POST /vocab/start

Request:

{
  "session_id": "sess_abc123"
}

Response:

{
  "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:

{
  "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:

orchestrator.get_vocab_batch(batch_number=1)

Start Batch Quiz

Endpoint: POST /vocab/batch/{batch_number}/quiz/start

Request:

{
  "session_id": "sess_abc123"
}

Response:

{
  "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:

{
  "session_id": "sess_abc123",
  "question_id": "q1",
  "word_id": "w1",
  "student_answer": "book"
}

Response (Correct):

{
  "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):

{
  "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:

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:

{
  "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:

{
  "session_id": "sess_abc123"
}

Response:

{
  "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:

{
  "session_id": "sess_abc123",
  "question_number": 3,
  "word_id": "w5",
  "student_answer": "house"
}

Response:

{
  "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:

{
  "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:

{
  "session_id": "sess_abc123"
}

Response:

{
  "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:

{
  "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:

orchestrator.get_grammar_topic_explanation(topic_id="feminine_nouns")

Start Topic Quiz

Endpoint: POST /grammar/topic/{topic_id}/quiz/start

Request:

{
  "session_id": "sess_abc123"
}

Response:

{
  "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:

{
  "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:

{
  "session_id": "sess_abc123",
  "question_id": "gq1",
  "question_number": 1,
  "student_answer": "feminine"
}

Response (Correct):

{
  "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):

{
  "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:

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):

{
  "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):

{
  "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:

orchestrator.get_topic_quiz_results(topic_id="feminine_nouns")

Exercise Endpoints

Generate Exercises

Endpoint: POST /exercises/generate

Request:

{
  "session_id": "sess_abc123",
  "lesson_number": 3,
  "exercise_type": "fill_in_blank",
  "grammar_point": "gender_agreement",
  "count": 5
}

Response:

{
  "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:

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:

{
  "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:

{
  "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:

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:

{
  "session_id": "sess_abc123",
  "lesson_number": 3,
  "question_count": 10
}

Response:

{
  "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:

{
  "session_id": "sess_abc123",
  "answers": [
    {"question_id": "t1", "answer": "ุงู„ูƒุจูŠุฑ"},
    {"question_id": "t2", "answer": "feminine"}
    // ... all 10 answers
  ]
}

Response:

{
  "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:

{
  "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:

{
  "success": false,
  "error": {
    "code": "INVALID_SESSION",
    "message": "Session not found or expired",
    "details": {
      "session_id": "sess_invalid"
    }
  }
}

Lesson Not Initialized:

{
  "success": false,
  "error": {
    "code": "LESSON_NOT_INITIALIZED",
    "message": "Lesson must be initialized before starting vocabulary",
    "details": {
      "required_action": "POST /lesson/initialize"
    }
  }
}

Content Not Found:

{
  "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:

{
  "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:

{
    "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