# GradeM8 API Documentation Comprehensive API reference for the GradeM8 AI-powered paper grading system. ## Table of Contents 1. [Overview](#overview) 2. [Authentication](#authentication) 3. [Core Endpoints](#core-endpoints) 4. [Monitoring Endpoints](#monitoring-endpoints) 5. [Error Handling](#error-handling) 6. [Response Formats](#response-formats) 7. [Code Examples](#code-examples) 8. [Rate Limiting](#rate-limiting) ## Overview GradeM8 provides both HTTP REST APIs and a Gradio web interface for AI-powered grading of academic submissions. **Base URL**: `http://localhost:7860` (Gradio interface) **API Base URL**: `http://localhost:8000/api` (if running as FastAPI app) **Supported Content Types**: - Plain text submissions - PDF documents - Microsoft Word documents (.docx) - Images (JPEG, PNG, TIFF) **AI Backends**: - DeepInfra (primary, Llama 3.1 70B) - Hugging Face Inference (fallback) ## Authentication ### API Key Authentication Include API key in request headers: ```bash curl -H "X-API-Key: your-api-key" https://api.gradem8.app/v1/grade ``` ### Environment Variables ```bash DEEPINFRA_API_KEY=your_deepinfra_key HUGGINGFACE_API_KEY=your_hf_token ``` ## Core Endpoints ### 1. Single Submission Grading **Endpoint**: `POST /api/grading/submit` Request a grade for a single student submission. **Request Body**: ```json { "submission_id": "sub_12345", "content": "Student submission text or document", "rubric": "Detailed grading rubric with criteria", "model": "meta-llama/Llama-2-70b-chat-hf", "max_tokens": 1024, "temperature": 0.7 } ``` **Parameters**: | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `submission_id` | string | Yes | Unique identifier for submission | | `content` | string | Yes | Student submission text (max 50,000 chars) | | `rubric` | string | Yes | Grading rubric with scoring criteria | | `model` | string | No | AI model to use (default: "meta-llama/Llama-2-70b-chat-hf") | | `max_tokens` | integer | No | Maximum response length (default: 1024) | | `temperature` | float | No | Response creativity 0-1 (default: 0.7) | **Example Response**: ```json { "submission_id": "sub_12345", "status": "success", "score": 87, "feedback": "Well-structured essay with strong arguments...", "rubric_scores": { "content_accuracy": 90, "organization": 85, "writing_quality": 82 }, "processing_time_ms": 3420, "tokens_used": 156 } ``` **Status Codes**: | Code | Meaning | |------|---------| | 200 | Grading completed successfully | | 400 | Invalid request (missing fields, format error) | | 401 | Authentication failed | | 429 | Rate limit exceeded - retry after delay | | 500 | Server error - will auto-retry with exponential backoff | | 503 | AI service unavailable | ### 2. Batch Grading **Endpoint**: `POST /api/grading/batch` Grade multiple submissions concurrently. **Request Body**: ```json { "batch_id": "batch_fall2024_001", "rubric": "Common rubric for all submissions", "submissions": [ { "submission_id": "sub_001", "content": "First submission text" }, { "submission_id": "sub_002", "content": "Second submission text" } ], "max_concurrent": 3, "timeout_per_submission": 60 } ``` **Parameters**: | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `batch_id` | string | Yes | Unique batch identifier | | `rubric` | string | Yes | Shared rubric for all submissions | | `submissions` | array | Yes | Array of submission objects (max 100) | | `max_concurrent` | integer | No | Max parallel grading jobs (default: 3) | | `timeout_per_submission` | integer | No | Timeout per submission in seconds (default: 60) | **Example Response**: ```json { "batch_id": "batch_fall2024_001", "status": "completed", "total_submissions": 2, "successful": 2, "failed": 0, "total_processing_time_ms": 7500, "results": [ { "submission_id": "sub_001", "status": "success", "score": 87, "feedback": "..." }, { "submission_id": "sub_002", "status": "success", "score": 92, "feedback": "..." } ] } ``` ### 3. File Upload Grading **Endpoint**: `POST /api/grading/upload` Grade submissions from uploaded files. **Request**: `multipart/form-data` ``` file: rubric: filename: submission.pdf submission_id: sub_12345 ``` **Supported Formats**: - `.txt` - Plain text - `.pdf` - PDF documents - `.docx` - Microsoft Word documents - `.jpg`, `.jpeg`, `.png`, `.tiff` - Images (with OCR) **Example Response**: ```json { "submission_id": "sub_12345", "filename": "submission.pdf", "status": "success", "extracted_text_length": 4562, "score": 85, "feedback": "..." } ``` ### 4. Submission History **Endpoint**: `GET /api/grading/history` Retrieve grading history for a time period. **Query Parameters**: | Parameter | Type | Description | |-----------|------|-------------| | `start_date` | ISO 8601 | Start time (default: 24 hours ago) | | `end_date` | ISO 8601 | End time (default: now) | | `batch_id` | string | Filter by batch ID | | `status` | string | Filter by status: success, failed, all | **Example**: ```bash GET /api/grading/history?start_date=2024-01-01T00:00:00Z&status=success ``` **Response**: ```json { "total": 42, "submissions": [ { "submission_id": "sub_001", "batch_id": "batch_001", "score": 87, "timestamp": "2024-01-15T10:30:00Z", "status": "success" } ] } ``` ## Monitoring Endpoints ### 1. Health Check **Endpoint**: `GET /api/monitoring/health` **Response**: ```json { "status": "healthy", "timestamp": "2024-01-15T10:30:00Z" } ``` ### 2. Current Metrics **Endpoint**: `GET /api/monitoring/metrics/current` Get real-time grading metrics. **Response**: ```json { "timestamp": "2024-01-15T10:30:00Z", "metrics": { "batch_001": { "total_submissions": 10, "successful": 9, "failed": 1, "avg_score": 85.5 } }, "statistics": { "total_processing_time": 3420.5, "avg_grading_time": 342.05, "avg_score": 85.5 } } ``` ### 3. Performance Profiles **Endpoint**: `GET /api/monitoring/metrics/performance` Detailed performance profiling data. **Response**: ```json { "timestamp": "2024-01-15T10:30:00Z", "profiles": { "batch_001": { "total_time_ms": 3420, "extraction_time_ms": 450, "grading_time_ms": 2800, "formatting_time_ms": 170 } }, "total_batches": 5 } ``` ### 4. Metrics Summary **Endpoint**: `GET /api/monitoring/metrics/summary` High-level summary of grading activity. **Response**: ```json { "timestamp": "2024-01-15T10:30:00Z", "total_batches": 5, "total_submissions": 42, "successful_submissions": 41, "success_rate": 97.6, "statistics": { "avg_grading_time": 342.05, "avg_score": 85.5 } } ``` ## Error Handling ### Error Response Format ```json { "error": "InvalidRubricError", "message": "Rubric must contain at least one scoring criterion", "status_code": 400, "request_id": "req_abc123def456" } ``` ### Common Error Codes | Error | Status | Description | |-------|--------|-------------| | `InvalidRubricError` | 400 | Rubric format invalid or missing criteria | | `InvalidSubmissionError` | 400 | Submission content missing or too large | | `APIKeyError` | 401 | Missing or invalid API key | | `RateLimitError` | 429 | Too many requests - implement exponential backoff | | `AIServiceError` | 503 | AI backend unavailable - fallback activated | | `ProcessingTimeoutError` | 408 | Grading took too long | | `InternalServerError` | 500 | Unexpected server error | ### Retry Strategy Implement exponential backoff for retryable errors (429, 5xx): ```python import httpx from typing import Optional async def grade_with_retry( submission: str, rubric: str, max_retries: int = 3, ) -> dict: """Grade with exponential backoff retry logic.""" base_backoff = 1.0 for attempt in range(max_retries): try: return await client.post( "/api/grading/submit", json={"content": submission, "rubric": rubric}, ).json() except httpx.HTTPStatusError as e: if e.response.status_code not in (429, 500, 502, 503, 504): raise if attempt == max_retries - 1: raise wait_time = base_backoff * (2 ** attempt) await asyncio.sleep(wait_time) ``` ## Response Formats ### Grading Result Object ```json { "submission_id": "string", "status": "success|failed|partial", "score": "integer (0-100)", "feedback": "string", "rubric_scores": { "criteria_1": "integer", "criteria_2": "integer" }, "processing_time_ms": "integer", "tokens_used": "integer", "model_used": "string", "timestamp": "ISO 8601" } ``` ### Batch Result Object ```json { "batch_id": "string", "status": "completed|failed|partial", "total_submissions": "integer", "successful": "integer", "failed": "integer", "total_processing_time_ms": "integer", "results": ["Grading Result Object"], "timestamp": "ISO 8601" } ``` ## Code Examples ### Python - Grade Single Submission ```python import httpx import asyncio async def grade_submission(): async with httpx.AsyncClient() as client: response = await client.post( "http://localhost:8000/api/grading/submit", json={ "submission_id": "sub_001", "content": "Student's essay text...", "rubric": "## Essay Rubric\n...", }, ) result = response.json() print(f"Score: {result['score']}") print(f"Feedback: {result['feedback']}") asyncio.run(grade_submission()) ``` ### Python - Batch Grading with Progress ```python import asyncio from lib.telemetry import create_batch_metrics, get_performance_stats async def grade_batch(): batch_id = "batch_001" create_batch_metrics(batch_id) submissions = [ {"submission_id": "sub_001", "content": "..."}, {"submission_id": "sub_002", "content": "..."}, ] async with httpx.AsyncClient() as client: response = await client.post( "http://localhost:8000/api/grading/batch", json={ "batch_id": batch_id, "rubric": "Grading rubric...", "submissions": submissions, "max_concurrent": 3, }, ) results = response.json() stats = get_performance_stats() print(f"Processed {stats['total_submissions']} submissions") print(f"Success Rate: {stats.get('success_rate', 0):.1%}") asyncio.run(grade_batch()) ``` ### JavaScript - Upload and Grade ```javascript async function uploadAndGrade() { const formData = new FormData(); formData.append('file', document.getElementById('fileInput').files[0]); formData.append('submission_id', 'sub_001'); formData.append('rubric', 'Grading criteria...'); const response = await fetch( 'http://localhost:8000/api/grading/upload', { method: 'POST', headers: {'X-API-Key': 'your-api-key'}, body: formData, } ); const result = await response.json(); console.log(`Score: ${result.score}`); console.log(`Feedback: ${result.feedback}`); } ``` ### cURL - Basic Grading ```bash curl -X POST http://localhost:8000/api/grading/submit \ -H "Content-Type: application/json" \ -H "X-API-Key: your-api-key" \ -d '{ "submission_id": "sub_001", "content": "Student essay text...", "rubric": "## Rubric\\n..." }' ``` ### cURL - Batch Grading ```bash curl -X POST http://localhost:8000/api/grading/batch \ -H "Content-Type: application/json" \ -H "X-API-Key: your-api-key" \ -d '{ "batch_id": "batch_001", "rubric": "Grading rubric...", "submissions": [ {"submission_id": "sub_001", "content": "..."}, {"submission_id": "sub_002", "content": "..."} ] }' ``` ## Rate Limiting ### Limits - **Single Requests**: 100 requests/minute - **Batch Requests**: 10 batches/minute - **File Uploads**: 20 uploads/minute ### Rate Limit Headers ``` X-RateLimit-Limit: 100 X-RateLimit-Remaining: 95 X-RateLimit-Reset: 1705329600 ``` ### Handling Rate Limits When you receive a 429 response: 1. Read `Retry-After` header (in seconds) 2. Wait that duration 3. Retry request 4. Implement exponential backoff for subsequent retries Example: ```python import time for attempt in range(3): try: response = client.post(url, json=data) return response.json() except httpx.HTTPStatusError as e: if e.response.status_code == 429: retry_after = int(e.response.headers.get('Retry-After', 60)) print(f"Rate limited. Waiting {retry_after}s...") time.sleep(retry_after) else: raise ``` --- **Last Updated**: 2024-01-15 **API Version**: 1.0 **Status**: Production