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
# 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`