feat: intégration du moteur d'IA (ai.py) et génération de la première réponse
This commit is contained in:
1 parent
00a92de1c4
commit
f553b79510
4 files changed
+98
-10
No files matched your search
@@ -0,0 +1,53 @@
|
||||
from openai import OpenAI
|
||||
from fastapi import HTTPException
|
||||
from .models import SystemSettings
|
||||
|
||||
def get_ai_response(messages: list, settings: SystemSettings) -> str:
|
||||
"""
|
||||
Route la conversation vers le premier fournisseur IA disponible configuré par l'admin.
|
||||
Prend l'historique des messages et retourne le texte généré.
|
||||
"""
|
||||
if not settings:
|
||||
raise HTTPException(status_code=500, detail="Configuration système introuvable.")
|
||||
|
||||
# Formatage de l'historique pour l'API (OpenAI compatible)
|
||||
formatted_messages = [{"role": msg.role, "content": msg.content} for msg in messages]
|
||||
|
||||
# 1. Test OpenRouter (Idéal car donne accès à tout)
|
||||
if settings.openrouter_api_key:
|
||||
client = OpenAI(
|
||||
base_url="https://openrouter.ai/api/v1",
|
||||
api_key=settings.openrouter_api_key,
|
||||
)
|
||||
model = "google/gemini-pro" # Fallback par défaut via OpenRouter
|
||||
|
||||
# 2. Test DeepSeek
|
||||
elif settings.deepseek_api_key:
|
||||
client = OpenAI(
|
||||
base_url="https://api.deepseek.com/v1",
|
||||
api_key=settings.deepseek_api_key,
|
||||
)
|
||||
model = "deepseek-chat"
|
||||
|
||||
# 3. Test Groq
|
||||
elif settings.groq_api_key:
|
||||
client = OpenAI(
|
||||
base_url="https://api.groq.com/openai/v1",
|
||||
api_key=settings.groq_api_key,
|
||||
)
|
||||
model = "llama3-8b-8192"
|
||||
|
||||
else:
|
||||
raise HTTPException(status_code=400, detail="Aucun moteur IA (OpenRouter, DeepSeek, Groq) n'est configuré avec une clé API.")
|
||||
|
||||
try:
|
||||
response = client.chat.completions.create(
|
||||
model=model,
|
||||
messages=formatted_messages,
|
||||
# Identifiant unique de l'application pour OpenRouter
|
||||
extra_headers={"HTTP-Referer": "https://aethas38.duckdns.org", "X-Title": "AETHAS38 Multi-IA"} if settings.openrouter_api_key else {}
|
||||
)
|
||||
return response.choices[0].message.content
|
||||
except Exception as e:
|
||||
print(f"Erreur API IA : {str(e)}")
|
||||
raise HTTPException(status_code=502, detail="Erreur de communication avec le fournisseur IA.")
|
||||
+32
-1
@@ -11,6 +11,8 @@ from .models import User, Project, Message
|
||||
from .auth import get_password_hash, generate_totp_secret, get_totp_uri, verify_password, verify_totp, create_access_token, verify_token
|
||||
from .schemas import AdminCreate, LoginRequest, ProjectCreate, ProjectResponse, ProjectRename, MessageCreate, MessageResponse
|
||||
from .models import User, Project, Message, SystemSettings
|
||||
from .ai import get_ai_response
|
||||
from .models import User, Project, Message, SystemSettings
|
||||
|
||||
# Création des tables dans la base de données
|
||||
Base.metadata.create_all(bind=engine)
|
||||
@@ -245,4 +247,33 @@ def create_message(project_id: int, message: MessageCreate, db: Session = Depend
|
||||
db.add(new_message)
|
||||
db.commit()
|
||||
db.refresh(new_message)
|
||||
return new_message
|
||||
return new_message
|
||||
|
||||
@app.post("/api/projects/{project_id}/messages", response_model=List[MessageResponse])
|
||||
def create_message(project_id: int, message: MessageCreate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
|
||||
"""Ajoute un message, interroge l'IA et retourne l'historique mis à jour."""
|
||||
project = db.query(Project).filter(Project.id == project_id, Project.user_id == current_user.id).first()
|
||||
if not project:
|
||||
raise HTTPException(status_code=404, detail="Projet introuvable")
|
||||
|
||||
# 1. Sauvegarde du message de l'utilisateur
|
||||
user_message = Message(role=message.role, content=message.content, project_id=project_id)
|
||||
db.add(user_message)
|
||||
db.commit()
|
||||
|
||||
# 2. Récupération de l'historique complet pour le contexte
|
||||
history = db.query(Message).filter(Message.project_id == project_id).order_by(Message.created_at.asc()).all()
|
||||
|
||||
# 3. Récupération des clés API depuis la base
|
||||
settings = db.query(SystemSettings).first()
|
||||
|
||||
# 4. Interrogation de l'IA via notre routeur (ai.py)
|
||||
ai_response_text = get_ai_response(history, settings)
|
||||
|
||||
# 5. Sauvegarde de la réponse de l'IA
|
||||
ai_message = Message(role="assistant", content=ai_response_text, project_id=project_id)
|
||||
db.add(ai_message)
|
||||
db.commit()
|
||||
|
||||
# On retourne le nouvel historique contenant la question ET la réponse
|
||||
return db.query(Message).filter(Message.project_id == project_id).order_by(Message.created_at.asc()).all()
|
||||
+11
-8
@@ -264,22 +264,25 @@
|
||||
nextTick(() => lucide.createIcons());
|
||||
|
||||
try {
|
||||
await fetch(`/api/projects/${activeProject.value.id}/messages`, {
|
||||
const response = await fetch(`/api/projects/${activeProject.value.id}/messages`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ role: 'user', content: userText })
|
||||
});
|
||||
|
||||
// SIMULATION TEMPORAIRE : Attente de l'IA (2 secondes)
|
||||
setTimeout(() => {
|
||||
isAiThinking.value = false;
|
||||
scrollToBottom();
|
||||
// La prochaine étape remplacera ce setTimeout par le véritable appel au moteur IA !
|
||||
}, 2000);
|
||||
|
||||
if (response.ok) {
|
||||
// L'API nous renvoie l'historique à jour avec la réponse de l'IA
|
||||
messages.value = await response.json();
|
||||
} else {
|
||||
const err = await response.json();
|
||||
alert("Erreur: " + err.detail);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Erreur envoi message", error);
|
||||
} finally {
|
||||
isAiThinking.value = false;
|
||||
scrollToBottom();
|
||||
nextTick(() => lucide.createIcons());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+2
-1
@@ -10,4 +10,5 @@ python-dotenv
|
||||
python-multipart
|
||||
bcrypt
|
||||
email-validator
|
||||
PyJWT
|
||||
PyJWT
|
||||
openai
|
||||
Reference in new issue
Block a user