Spaces:
Sleeping
Sleeping
| # 📱 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<String> 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<List<Document>> 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<void> 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<void> submitBatchSummaries(List<Summary> 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<Document> docs = await getAllDocumentsContents(userId); | |
| // 2. Générer les résumés en local (batch ou séquentiel) | |
| List<Summary> 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<void> 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<String> 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<int> 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! 📱 | |