Spaces:
Sleeping
Sleeping
| # ============================================ | |
| # apps/ai_tools/views.py | |
| # ============================================ | |
| from rest_framework import viewsets, status | |
| from rest_framework.decorators import action | |
| from rest_framework.response import Response | |
| from rest_framework.permissions import IsAuthenticated | |
| from django.conf import settings | |
| from apps.ai_tools.serializers import ( | |
| AITutorRequestSerializer, AITutorResponseSerializer | |
| ) | |
| class AIToolsViewSet(viewsets.GenericViewSet): | |
| """Outils IA""" | |
| permission_classes = [IsAuthenticated] | |
| def tutor(self, request): | |
| """POST /api/ai/tutor/ - Proxy vers l'API Groq/OpenAI""" | |
| serializer = AITutorRequestSerializer(data=request.data) | |
| serializer.is_valid(raise_exception=True) | |
| question = serializer.validated_data['question'] | |
| subject = serializer.validated_data.get('subject', '') | |
| level = serializer.validated_data.get('level', '') | |
| style = serializer.validated_data.get('style', '') | |
| # Contexte pays injecté depuis l'Atlas Interactif (optionnel) | |
| system_context = request.data.get('system_context', None) | |
| try: | |
| if getattr(settings, 'GROQ_API_KEY', None): | |
| from apps.ai_tools.groq_client import call_groq | |
| # Construction du style instruction | |
| style_instruction = "" | |
| if style: | |
| if "Simple" in style or "Concis" in style: | |
| style_instruction = "Sois concis et va droit au but. Utilise un langage simple." | |
| elif "Détaillé" in style or "Académique" in style: | |
| style_instruction = "Fournis une explication détaillée et académique avec des références théoriques." | |
| elif "5 ans" in style or "ELI5" in style: | |
| style_instruction = "Explique comme si tu parlais à un enfant de 5 ans. Utilise des analogies simples et amusantes." | |
| elif "Pratique" in style or "Exemples" in style: | |
| style_instruction = "Concentre-toi sur des exemples pratiques et concrets. Montre comment appliquer les concepts." | |
| elif "Socratique" in style: | |
| style_instruction = "Utilise la méthode socratique : pose des questions pour guider l'étudiant vers la réponse." | |
| # System prompt de base du tuteur | |
| tutor_prompt = ( | |
| f"Tu es un tuteur pédagogique bienveillant et patient pour EduLab Africa.\n" | |
| f"L'étudiant est basé en Afrique. Matière: {subject}. Niveau: {level}. {style_instruction}\n" | |
| f"Utilise TOUJOURS le format LaTeX ($formule$ ou $$formule$$) pour les maths/chimie." | |
| ) | |
| # Si un contexte pays est fourni depuis l'Atlas, on le place EN TÊTE du system prompt | |
| # pour qu'il prenne la priorité sur le comportement général du tuteur. | |
| if system_context: | |
| sys_prompt = ( | |
| f"{system_context}\n\n" | |
| f"--- Contexte pédagogique additionnel ---\n" | |
| f"{tutor_prompt}" | |
| ) | |
| else: | |
| sys_prompt = tutor_prompt | |
| answer, tokens = call_groq(question, sys_prompt) | |
| elif settings.OPENAI_API_KEY: | |
| answer, tokens = self._call_openai(question, subject, level, style) | |
| else: | |
| return Response( | |
| {'error': 'Aucune API IA configurée (GROQ_API_KEY ou OPENAI_API_KEY requises)'}, | |
| status=status.HTTP_503_SERVICE_UNAVAILABLE | |
| ) | |
| # Sauvegarder dans l'historique | |
| from apps.ai_tools.models import AITutorSession, AITutorQuestion | |
| from django.utils import timezone | |
| import time | |
| start_time = time.time() | |
| session = AITutorSession.objects.create( | |
| user=request.user, | |
| subject=subject, | |
| level=level | |
| ) | |
| AITutorQuestion.objects.create( | |
| session=session, | |
| question=question, | |
| answer=answer, | |
| model_used='groq' if getattr(settings, 'GROQ_API_KEY', None) else 'openai', | |
| tokens_used=tokens, | |
| response_time=time.time() - start_time | |
| ) | |
| session.total_questions += 1 | |
| session.save() | |
| response_serializer = AITutorResponseSerializer({ | |
| 'answer': answer, | |
| 'session_id': session.id, | |
| 'tokens_used': tokens | |
| }) | |
| return Response(response_serializer.data) | |
| except Exception as e: | |
| import traceback | |
| error_details = traceback.format_exc() | |
| print(f"GROQ/AI ERROR: {error_details}") # Pour les logs | |
| return Response( | |
| { | |
| 'error': str(e), | |
| 'details': 'Vérifiez que votre clé API Groq est valide et configurée dans GROQ_API_KEY', | |
| 'type': type(e).__name__ | |
| }, | |
| status=status.HTTP_500_INTERNAL_SERVER_ERROR | |
| ) | |
| def history(self, request): | |
| """GET /api/ai/history/ - Récupérer l'historique des sessions""" | |
| from apps.ai_tools.models import AITutorSession | |
| sessions = AITutorSession.objects.filter( | |
| user=request.user, | |
| is_active=True | |
| ).prefetch_related('questions')[:20] # Dernières 20 sessions | |
| sessions_data = [] | |
| for session in sessions: | |
| questions_list = list(session.questions.all()) | |
| sessions_data.append({ | |
| 'id': session.id, | |
| 'subject': session.subject, | |
| 'level': session.level, | |
| 'total_questions': session.total_questions, | |
| 'created_at': session.created_at.isoformat(), | |
| 'first_question': questions_list[0].question if questions_list else '', | |
| 'questions': [ | |
| { | |
| 'question': q.question, | |
| 'answer': q.answer, | |
| 'created_at': q.created_at.isoformat() | |
| } for q in questions_list | |
| ] | |
| }) | |
| return Response(sessions_data) | |
| def _call_openai(self, question, subject, level, style=''): | |
| """Appel à l'API OpenAI""" | |
| import openai | |
| from django.conf import settings | |
| openai.api_key = settings.OPENAI_API_KEY | |
| response = openai.ChatCompletion.create( | |
| model="gpt-3.5-turbo", | |
| messages=[ | |
| { | |
| "role": "system", | |
| "content": f"Tu es un tuteur pédagogique pour la matière {subject} au niveau {level}." | |
| }, | |
| { | |
| "role": "user", | |
| "content": question | |
| } | |
| ] | |
| ) | |
| answer = response.choices[0].message.content | |
| tokens = response.usage.total_tokens | |
| return answer, tokens | |