232 lines
6.8 KiB
PHP
232 lines
6.8 KiB
PHP
<?php
|
|
/**
|
|
* API Chatbot IA - MVP
|
|
* Modèle : qwen2-math:1.5b via Ollama local
|
|
*/
|
|
|
|
require_once __DIR__ . '/../config/config.php';
|
|
require_once __DIR__ . '/../config/database.php';
|
|
|
|
header('Content-Type: application/json');
|
|
|
|
// Vérifier authentification élève
|
|
if (!isLoggedIn() || !isEleve()) {
|
|
echo json_encode(['error' => '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;
|
|
}
|
|
$enonce_question = $question['enonce'];
|
|
$titre_eval = $question['titre_eval'];
|
|
$type_question = $question['type_question'];
|
|
|
|
|
|
// 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]
|
|
);
|
|
|
|
$system_prompt = "Tu es un assistant pédagogique en mathématiques. Ton rôle est d'AIDER l'élève à COMPRENDRE, PAS de donner la réponse.
|
|
|
|
|
|
⚠️ RÈGLE ABSOLUE ET NON-NÉGOCIABLE ⚠️
|
|
TU NE DOIS JAMAIS, SOUS AUCUN PRÉTEXTE, DONNER LA RÉPONSE FINALE.
|
|
|
|
TON RÔLE :
|
|
- Poser des QUESTIONS pour faire réfléchir l'élève
|
|
- Donner des INDICES progressifs
|
|
- Rappeler les méthodes et formules
|
|
|
|
CE QUE TU NE DOIS JAMAIS FAIRE :
|
|
❌ Donner le résultat final (par exemple : \"La réponse est 17\")
|
|
❌ Faire les calculs à la place de l'élève
|
|
❌ Résoudre l'exercice complètement
|
|
|
|
CE QUE TU DOIS FAIRE :
|
|
✅ Poser des questions : \"Quelle opération faut-il faire en premier ?\"
|
|
✅ Donner des indices : \"Rappelle-toi les priorités opératoires...\"
|
|
✅ Décomposer : \"Essayons étape par étape. D'abord, que remarques-tu ?\"
|
|
|
|
EXEMPLE CORRECT :
|
|
Élève : \"9 + 8 ?\"
|
|
Assistant : \"D'accord ! Peux-tu essayer de calculer cette addition ? Si tu as du mal, tu peux utiliser tes doigts ou décomposer : 9 + 1 = 10, puis 10 + 7 = ?\"
|
|
|
|
EXEMPLE INTERDIT :
|
|
Élève : \"9 + 8 ?\"
|
|
Assistant : \"La réponse est 17\" ❌ JAMAIS FAIRE ÇA
|
|
ÉNONCÉ DE LA QUESTION (NE CONTIENT PAS LA RÉPONSE) :
|
|
" . $enonce_question;
|
|
|
|
// 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' => 'ministral-3:3b',
|
|
'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
|
|
]); |