import 'dart:convert'; import 'package:http/http.dart' as http; /// Définit un contrat pour tout service capable de générer des idées de posts. abstract class PostCreationService { Future> generatePostIdeas({ required String base64Image, required String profession, required String tone, }); } /// L'implémentation Ollama de ce service. class OllamaPostCreationService implements PostCreationService { final String _apiUrl = 'http://192.168.20.200:11434/api/generate'; final String _visionModel = 'llava:7b'; // --- DÉBUT DE L'UNIQUE ET CORRECTE MÉTHODE --- @override Future> generatePostIdeas({ required String base64Image, required String profession, required String tone, }) async { final requestPrompt = ''' You are a social media expert. Act as a "$profession". Analyze the image. Generate 3 short and engaging social media post ideas in french with a "$tone" tone. IMPORTANT: Your output MUST be a valid JSON array of objects, where each object has a "text" key. - DO NOT add any markdown like ```json or ```. - DO NOT add any text before or after the JSON array. - Ensure all strings inside the JSON are properly escaped (especially newlines \n). Example of a perfect response: [{"text": "idée 1"}, {"text": "idée 2"}, {"text": "idée 3"}] '''; final requestBody = { 'model': _visionModel, 'prompt': requestPrompt, 'images': [base64Image], 'stream': false, 'format': 'json', }; try { print('[OllamaPostCreationService] 🚀 Appel pour générer des idées...'); final response = await http.post( Uri.parse(_apiUrl), headers: {'Content-Type': 'application/json'}, body: jsonEncode(requestBody), ).timeout(const Duration(minutes: 3)); if (response.statusCode == 200) { final responseData = jsonDecode(response.body); final rawResponse = (responseData['response'] as String? ?? '').trim(); if (rawResponse.isEmpty) return ["Le modèle n'a retourné aucune réponse."]; String jsonArrayString; try { // --- NOUVELLE LOGIQUE ROBUSTE AVEC REGEX --- // On cherche un bloc ```json ... ``` final regex = RegExp(r'```json\s*([\s\S]*?)\s*```'); final match = regex.firstMatch(rawResponse); if (match != null && match.groupCount > 0) { // On a trouvé un bloc markdown, on extrait le contenu (Groupe 1) jsonArrayString = match.group(1)!.trim(); } else { // Pas de bloc markdown. Peut-être que le modèle a bien répondu ? // On retombe sur l'ancienne logique par sécurité. final startIndex = rawResponse.indexOf('['); final endIndex = rawResponse.lastIndexOf(']'); if (startIndex != -1 && endIndex > startIndex) { jsonArrayString = rawResponse.substring(startIndex, endIndex + 1); } else { throw const FormatException("Aucun bloc JSON ```...``` ni tableau '[]' trouvé."); } } // --- FIN DE LA NOUVELLE LOGIQUE --- // Log de débogage pour voir ce qu'on essaie de parser print('[OllamaPostCreationService] ℹ️ Tentative de parsing sur : "$jsonArrayString"'); final jsonList = jsonDecode(jsonArrayString) as List; // Logique de parsing pour une liste d'objets return jsonList.map((item) { if (item is Map && item.containsKey('text')) { return item['text'].toString(); } return 'Format de l\'idée inattendu'; }) .where((text) => text != 'Format de l\'idée inattendu') .toList(); } catch (e) { // *** LOGGING AMÉLIORÉ *** // Ceci nous dira l'erreur EXACTE de parsing (ex: "Unterminated string") print('[OllamaPostCreationService] ❌ Erreur de parsing JSON.'); print('[OllamaPostCreationService] ❌ Erreur spécifique : ${e.toString()}'); print('[OllamaPostCreationService] ❌ Réponse brute (avant regex) : "$rawResponse"'); return ["Le format de la réponse de l'IA est inattendu."]; } } else { throw Exception('Erreur Ollama (generatePostIdeas) ${response.statusCode}: ${response.body}'); } } catch (e) { print('[OllamaPostCreationService] ❌ Exception : ${e.toString()}'); rethrow; } } // --- FIN DE LA MÉTHODE MISE À JOUR --- }