'Non autorisé']); exit; } $user = currentUser(); $db = Database::getInstance(); // Récupérer et valider les données $id_tentative = filter_input(INPUT_POST, 'id_tentative', FILTER_VALIDATE_INT); $id_question = filter_input(INPUT_POST, 'id_question', FILTER_VALIDATE_INT); $message_eleve = trim($_POST['message'] ?? ''); if (!$id_tentative || !$id_question || empty($message_eleve)) { echo json_encode(['error' => 'Données manquantes']); exit; } // Vérifier que la tentative appartient à l'élève et est en cours $tentative = $db->fetchOne( "SELECT id_eleve FROM tentatives_eleves WHERE id_tentative = ? AND statut = 'en_cours'", [$id_tentative] ); if (!$tentative || $tentative['id_eleve'] != $user['id_utilisateur']) { echo json_encode(['error' => 'Tentative invalide']); exit; } // Vérifier limite (3 messages max par question) $usage = $db->fetchOne( "SELECT messages_count FROM chat_usage_limits WHERE id_tentative = ? AND id_question = ?", [$id_tentative, $id_question] ); $messages_count = $usage['messages_count'] ?? 0; if ($messages_count >= 3) { echo json_encode([ 'error' => 'Limite atteinte', 'message' => 'Tu as utilisé toute ton aide pour cette question (3/3). Continue, tu peux le faire ! 💪', 'remaining' => 0 ]); exit; } // Récupérer l'énoncé de la question (SANS la réponse) $question = $db->fetchOne( "SELECT q.enonce, q.type_question, e.titre as titre_eval FROM questions q JOIN evaluations e ON q.id_evaluation = e.id_evaluation WHERE q.id_question = ?", [$id_question] ); if (!$question) { echo json_encode(['error' => 'Question introuvable']); exit; } // Récupérer ou créer la conversation $conversation = $db->fetchOne( "SELECT id FROM chat_conversations WHERE id_tentative = ? AND id_question = ?", [$id_tentative, $id_question] ); if (!$conversation) { // Créer nouvelle conversation $id_conversation = $db->insert( "INSERT INTO chat_conversations (id_tentative, id_question, id_eleve, modele_utilise) VALUES (?, ?, ?, 'qwen2-math:1.5b')", [$id_tentative, $id_question, $user['id_utilisateur']] ); } else { $id_conversation = $conversation['id']; } // Récupérer l'historique de la conversation $historique = $db->fetchAll( "SELECT role, message FROM chat_messages WHERE id_conversation = ? ORDER BY timestamp ASC", [$id_conversation] ); // Construire le prompt système $system_prompt = "Tu es un assistant pédagogique en mathématiques pour élèves de collège/lycée. RÈGLES ABSOLUES : - NE JAMAIS donner la réponse finale ou les calculs complets - Utiliser la méthode socratique : poser des questions pour guider la réflexion - Donner des INDICES progressifs (formules, méthodes, étapes) - Encourager l'élève à décomposer le problème lui-même - Rester positif et encourageant - Répondre en français simple et clair - Réponses courtes (max 3-4 lignes) STRICTEMENT INTERDIT : - Résoudre l'exercice complètement - Donner les chiffres de la réponse finale - Faire les calculs à la place de l'élève CONTEXTE : Évaluation : {$question['titre_eval']} Type de question : {$question['type_question']} ÉNONCÉ : {$question['enonce']} Aide l'élève avec des questions et indices, PAS avec la réponse."; // Construire l'historique pour Ollama $messages_ollama = []; foreach ($historique as $msg) { $messages_ollama[] = [ 'role' => $msg['role'], 'content' => $msg['message'] ]; } // Ajouter le nouveau message de l'élève $messages_ollama[] = [ 'role' => 'user', 'content' => $message_eleve ]; // Appel à Ollama (qwen2-math:1.5b) $start_time = microtime(true); $ch = curl_init('http://localhost:11434/api/chat'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['Content-Type: application/json'], CURLOPT_TIMEOUT => 30, CURLOPT_POSTFIELDS => json_encode([ 'model' => 'qwen2-math:1.5b', 'messages' => array_merge([ ['role' => 'system', 'content' => $system_prompt] ], $messages_ollama), 'stream' => false, 'options' => [ 'temperature' => 0.7, 'num_predict' => 200, // Réponses courtes 'top_p' => 0.9 ] ]) ]); $response = curl_exec($ch); $curl_error = curl_error($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); $end_time = microtime(true); $temps_reponse_ms = round(($end_time - $start_time) * 1000); // Gérer les erreurs if ($curl_error) { error_log("Erreur Ollama CURL: $curl_error"); echo json_encode(['error' => 'Service temporairement indisponible (connexion)']); exit; } if ($http_code !== 200) { error_log("Erreur Ollama HTTP $http_code: $response"); echo json_encode(['error' => 'Service temporairement indisponible (HTTP ' . $http_code . ')']); exit; } $data = json_decode($response, true); if (!isset($data['message']['content'])) { error_log("Réponse Ollama invalide: $response"); echo json_encode(['error' => 'Erreur de génération']); exit; } $reponse_ia = trim($data['message']['content']); // Sauvegarder le message de l'élève $db->query( "INSERT INTO chat_messages (id_conversation, role, message) VALUES (?, 'user', ?)", [$id_conversation, $message_eleve] ); // Sauvegarder la réponse de l'IA $db->query( "INSERT INTO chat_messages (id_conversation, role, message, temps_reponse_ms) VALUES (?, 'assistant', ?, ?)", [$id_conversation, $reponse_ia, $temps_reponse_ms] ); // Incrémenter le compteur d'utilisation $db->query( "INSERT INTO chat_usage_limits (id_tentative, id_question, messages_count) VALUES (?, ?, 1) ON DUPLICATE KEY UPDATE messages_count = messages_count + 1", [$id_tentative, $id_question] ); $messages_restants = 2 - $messages_count; // Réponse succès echo json_encode([ 'success' => true, 'response' => $reponse_ia, 'remaining' => $messages_restants, 'temps_ms' => $temps_reponse_ms ]);