# 📱 API pour Modèle T5 Local sur Téléphone ## 🎯 Architecture ``` ┌─────────────────────────────────────────────────┐ │ Application Mobile │ │ ┌──────────────────────────────────────────┐ │ │ │ Modèle T5-Small Finetuned (ONNX) │ │ │ │ • 60 MB quantifié INT8 │ │ │ │ • Génération de résumés locale │ │ │ └──────────────────────────────────────────┘ │ │ ↕ │ │ API Calls (données uniquement) │ └─────────────────────────────────────────────────┘ ↕ ┌─────────────────────────────────────────────────┐ │ Backend API (Hugging Face Spaces) │ │ • Stockage des documents (Supabase) │ │ • Fournit le contenu textuel extrait │ │ • Reçoit et stocke les résumés générés │ └─────────────────────────────────────────────────┘ ``` --- ## 📡 Nouvelles API Ajoutées ### 1️⃣ GET `/documents/{document_id}/content` **Récupérer le contenu textuel d'un document pour génération locale** #### Requête ```bash curl https://k2mar-docuresume-backend.hf.space/documents/DOC_ID/content ``` #### Réponse ```json { "document_id": "uuid-here", "filename": "Mon Document.pdf", "content": "Contenu textuel extrait du PDF avec OCR...", "page_count": 5, "file_size": 245678, "content_length": 2847 } ``` #### Utilisation Mobile ```dart // Flutter Future getDocumentContent(String documentId) async { final response = await http.get( Uri.parse('$baseUrl/documents/$documentId/content') ); if (response.statusCode == 200) { final data = json.decode(response.body); return data['content']; // Texte à résumer } throw Exception('Failed to load content'); } ``` --- ### 2️⃣ GET `/documents/user/{user_id}/contents` **Récupérer le contenu de tous les documents d'un utilisateur** #### Requête ```bash curl "https://k2mar-docuresume-backend.hf.space/documents/user/USER_ID/contents?limit=100&include_content=true" ``` #### Paramètres - `limit`: nombre max de documents (défaut: 100) - `include_content`: inclure le contenu texte (défaut: true) #### Réponse ```json { "user_id": "user-uuid", "documents": [ { "document_id": "doc-uuid-1", "filename": "Document1.pdf", "page_count": 5, "file_size": 245678, "uploaded_at": "2026-01-27T00:00:00", "processing_status": "completed", "content": "Texte du document 1...", "content_length": 2847 }, { "document_id": "doc-uuid-2", "filename": "Document2.pdf", "content": "Texte du document 2...", "content_length": 4521 } ], "count": 2, "total_content_length": 7368 } ``` #### Utilisation Mobile ```dart // Flutter - Télécharger tous les documents pour résumé batch Future> getAllDocumentsContents(String userId) async { final response = await http.get( Uri.parse('$baseUrl/documents/user/$userId/contents?include_content=true') ); if (response.statusCode == 200) { final data = json.decode(response.body); return (data['documents'] as List) .map((doc) => Document.fromJson(doc)) .toList(); } throw Exception('Failed to load documents'); } ``` --- ### 3️⃣ POST `/documents/{document_id}/summaries` **Envoyer un résumé généré localement vers le serveur** #### Requête ```bash curl -X POST https://k2mar-docuresume-backend.hf.space/documents/DOC_ID/summaries \ -H "Content-Type: application/json" \ -d '{ "summary_type": "abstractive", "summary_level": "medium", "content": "Résumé généré par T5-small...", "model_name": "t5-small-finetuned", "generation_time": 3.45, "token_count": 150, "rouge_1_score": 0.87, "rouge_2_score": 0.73, "rouge_l_score": 0.81 }' ``` #### Champs - `content` **(requis)**: texte du résumé généré - `summary_type`: "abstractive" | "extractive" | "hybrid" (défaut: "extractive") - `summary_level`: "short" | "medium" | "long" (défaut: "medium") - `model_name`: nom du modèle (défaut: "t5-small-finetuned") - `generation_time`: temps de génération en secondes (optionnel) - `token_count`: nombre de tokens générés (optionnel) - `rouge_1_score`, `rouge_2_score`, `rouge_l_score`: métriques ROUGE (optionnel) #### Réponse ```json { "message": "Résumé créé avec succès", "summary_id": "summary-uuid", "document_id": "doc-uuid", "summary_type": "abstractive", "created_at": "2026-01-27T02:30:00" } ``` #### Utilisation Mobile ```dart // Flutter - Envoyer le résumé généré Future submitSummary(String documentId, String summaryText, double generationTime) async { final response = await http.post( Uri.parse('$baseUrl/documents/$documentId/summaries'), headers: {'Content-Type': 'application/json'}, body: json.encode({ 'summary_type': 'abstractive', 'summary_level': 'medium', 'content': summaryText, 'model_name': 't5-small-finetuned', 'generation_time': generationTime, 'token_count': summaryText.split(' ').length }) ); if (response.statusCode != 200) { throw Exception('Failed to submit summary'); } } ``` --- ### 4️⃣ POST `/summaries/batch` **Créer plusieurs résumés en batch (génération locale de plusieurs documents)** #### Requête ```bash curl -X POST https://k2mar-docuresume-backend.hf.space/summaries/batch \ -H "Content-Type: application/json" \ -d '[ { "document_id": "doc-uuid-1", "content": "Résumé du document 1...", "summary_type": "abstractive", "generation_time": 2.5 }, { "document_id": "doc-uuid-2", "content": "Résumé du document 2...", "summary_type": "abstractive", "generation_time": 3.1 } ]' ``` #### Réponse ```json { "message": "2 résumés créés avec succès", "count": 2, "summary_ids": ["summary-uuid-1", "summary-uuid-2"] } ``` #### Utilisation Mobile ```dart // Flutter - Soumettre plusieurs résumés d'un coup Future submitBatchSummaries(List summaries) async { final payload = summaries.map((s) => { 'document_id': s.documentId, 'content': s.content, 'summary_type': 'abstractive', 'summary_level': 'medium', 'model_name': 't5-small-finetuned', 'generation_time': s.generationTime }).toList(); final response = await http.post( Uri.parse('$baseUrl/summaries/batch'), headers: {'Content-Type': 'application/json'}, body: json.encode(payload) ); if (response.statusCode != 200) { throw Exception('Failed to submit batch'); } } ``` --- ### 5️⃣ GET `/summaries/user/{user_id}` **Récupérer tous les résumés générés par un utilisateur** #### Requête ```bash curl "https://k2mar-docuresume-backend.hf.space/summaries/user/USER_ID?limit=50&summary_type=abstractive" ``` #### Paramètres - `limit`: nombre max de résumés (défaut: 50) - `summary_type`: filtrer par type (optionnel) #### Réponse ```json { "user_id": "user-uuid", "summaries": [ { "id": "summary-uuid", "document_id": "doc-uuid", "content": "Résumé...", "summary_type": "abstractive", "summary_level": "medium", "model_name": "t5-small-finetuned", "generation_time": 3.45, "created_at": "2026-01-27T02:30:00", "documents": { "original_filename": "Document.pdf" } } ], "count": 1 } ``` --- ## 🔄 Workflow Complet ### Scénario 1: Résumé d'un document ```dart // 1. Récupérer le contenu du document String content = await getDocumentContent(documentId); // 2. Générer le résumé avec le modèle T5 local String summary = await t5Model.generateSummary(content); // 3. Envoyer le résumé au serveur await submitSummary(documentId, summary, generationTime); ``` ### Scénario 2: Résumé de tous les documents ```dart // 1. Récupérer tous les documents avec contenus List docs = await getAllDocumentsContents(userId); // 2. Générer les résumés en local (batch ou séquentiel) List summaries = []; for (var doc in docs) { String summary = await t5Model.generateSummary(doc.content); summaries.add(Summary( documentId: doc.id, content: summary, generationTime: ... )); } // 3. Envoyer tous les résumés d'un coup await submitBatchSummaries(summaries); ``` --- ## 📊 Intégration du Modèle T5 sur Mobile ### Structure du package embarqué ``` assets/ models/ t5-small-finetuned-logits-int8.onnx (60 MB) tokenizer/ config.json tokenizer.json tokenizer_config.json ``` ### Exemple Flutter avec ONNX Runtime ```dart import 'package:onnxruntime/onnxruntime.dart'; class T5SummaryModel { late OrtSession session; late Tokenizer tokenizer; Future loadModel() async { // Charger le modèle ONNX final modelPath = 'assets/models/t5-small-finetuned-logits-int8.onnx'; session = await OrtSession.fromAsset(modelPath); // Charger le tokenizer tokenizer = Tokenizer.fromAsset('assets/models/tokenizer'); } Future generateSummary(String text, {int maxTokens = 150}) async { final startTime = DateTime.now(); // 1. Tokeniser l'entrée final inputIds = tokenizer.encode("summarize: $text", maxLength: 512); // 2. Boucle autoregressive List outputIds = [tokenizer.padTokenId]; for (int i = 0; i < maxTokens; i++) { // Forward pass ONNX final inputs = { 'input_ids': inputIds, 'attention_mask': List.filled(inputIds.length, 1), 'decoder_input_ids': outputIds }; final outputs = await session.run(inputs); final logits = outputs[0]; // Argmax sur le dernier token final nextToken = argmax(logits[-1]); if (nextToken == tokenizer.eosTokenId) break; outputIds.add(nextToken); } // 3. Décoder la sortie final summary = tokenizer.decode(outputIds, skipSpecialTokens: true); final duration = DateTime.now().difference(startTime); print('Génération: ${duration.inSeconds}s'); return summary; } } ``` --- ## ⚙️ Configuration Recommandée ### Contraintes minimales - **RAM disponible:** 1 GB libre (2 GB recommandé) - **Stockage:** 100 MB (modèle + cache) - **Android:** API 24+ (Android 7.0) - **iOS:** iOS 12+ ### Optimisations 1. **Quantification INT8:** déjà appliquée (60 MB) 2. **Chunking:** découper les longs documents (>2000 tokens) 3. **Cache:** stocker les résumés localement 4. **Background processing:** générer en arrière-plan 5. **Batch processing:** traiter plusieurs docs pendant la nuit --- ## 🧪 Test des Nouvelles API ```bash # Test 1: Récupérer contenu d'un document curl https://k2mar-docuresume-backend.hf.space/documents/DOC_ID/content | jq '.content_length' # Test 2: Récupérer tous les contenus curl "https://k2mar-docuresume-backend.hf.space/documents/user/USER_ID/contents?include_content=false" | jq '.count' # Test 3: Soumettre un résumé test curl -X POST https://k2mar-docuresume-backend.hf.space/documents/DOC_ID/summaries \ -H "Content-Type: application/json" \ -d '{ "content": "Ceci est un résumé test généré par T5-small", "summary_type": "abstractive", "model_name": "t5-small-finetuned", "generation_time": 2.5 }' | jq # Test 4: Récupérer résumés d'un user curl https://k2mar-docuresume-backend.hf.space/summaries/user/USER_ID | jq '.count' ``` --- ## 📈 Métriques à Tracker Pour mesurer la performance du modèle local: ```dart class SummaryMetrics { double generationTime; // Temps de génération (secondes) int inputTokens; // Nombre de tokens en entrée int outputTokens; // Nombre de tokens générés double batteryUsed; // Batterie consommée (%) double memoryPeak; // RAM max utilisée (MB) // Optionnel: scores ROUGE si vous avez un résumé de référence double? rouge1; double? rouge2; double? rougeL; } ``` Envoyez ces métriques au serveur lors de la soumission du résumé pour analyse. --- ## 🚀 Déploiement Les nouvelles API sont **déjà déployées** sur: ``` https://k2mar-docuresume-backend.hf.space ``` ✅ Commit: `ba0c701` ✅ Message: "Add APIs for local T5 model: content retrieval and summary submission" ✅ Status: En ligne sur Hugging Face Spaces Vous pouvez maintenant intégrer le modèle T5 dans votre app mobile! 📱