IA / app.py
Barouia's picture
Update app.py
e97baf5 verified
Raw
History Blame
28 kB
#!/usr/bin/env python3
"""
Application Barouia-Cortex avec Chat Réel - Version Adaptée
Interface web + moteur de conversation intelligent
"""
from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel
import uvicorn
import logging
import json
import uuid
import time
import asyncio
from typing import Dict, List, Any, Optional
# Configuration du logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger("barouia_cortex")
# Moteur de chat intégré (fallback)
class RealChatEngine:
def __init__(self):
self.conversations: Dict[str, List[Dict]] = {}
self.responses = {
"salutations": [
"Bonjour ! Je suis Barouia-Cortex, votre assistant IA. Comment puis-je vous aider ?",
"Salut ! Ravie de vous rencontrer. Que souhaitez-vous discuter aujourd'hui ?",
"Bonjour ! Je suis là pour vous accompagner. Quelle est votre question ?"
],
"questions_simples": {
"ça va": "Je fonctionne parfaitement bien, merci ! Et de votre côté, comment allez-vous ?",
"heure": f"Selon mon horloge, il est actuellement {time.strftime('%H:%M')}.",
"nom": "Je m'appelle Barouia-Cortex, un assistant conversationnel intelligent.",
"aide": "Je peux vous aider à répondre à des questions, discuter de divers sujets, ou vous assister dans vos recherches."
},
"reponses_generiques": [
"C'est une question intéressante. Que pensez-vous de ce sujet ?",
"Je comprends votre point de vue. Pourriez-vous développer un peu plus ?",
"Très bonne question ! Mes algorithmes traitent actuellement votre requête.",
"Je note votre interrogation. Y a-t-il autre chose que vous aimeriez savoir ?"
]
}
logger.info("🔧 Moteur Barouia-Cortex initialisé")
def process_message(self, conversation_id: str, message: str) -> str:
try:
logger.info(f"📨 Message reçu: '{message}' (conv: {conversation_id[:8]}...)")
# Initialiser la conversation si nécessaire
if conversation_id not in self.conversations:
self.conversations[conversation_id] = []
# Ajouter à l'historique
self.conversations[conversation_id].append({
"role": "user",
"content": message,
"timestamp": time.time()
})
# Nettoyer le message
msg_lower = message.lower().strip()
# Réponses aux salutations
if any(word in msg_lower for word in ['bonjour', 'salut', 'hello', 'coucou', 'yo']):
response = self.responses["salutations"][len(conversation_id) % len(self.responses["salutations"])]
# Réponses aux questions simples
elif "ça va" in msg_lower or "comment vas" in msg_lower:
response = self.responses["questions_simples"]["ça va"]
elif "heure" in msg_lower:
response = self.responses["questions_simples"]["heure"]
elif "appelles" in msg_lower or "nom" in msg_lower:
response = self.responses["questions_simples"]["nom"]
elif "aide" in msg_lower or "que peux" in msg_lower or "fonction" in msg_lower:
response = self.responses["questions_simples"]["aide"]
# Réponses aux remerciements
elif any(word in msg_lower for word in ['merci', 'thank', 'nice']):
response = "Je vous en prie ! N'hésitez pas si vous avez d'autres questions."
# Au revoir
elif any(word in msg_lower for word in ['au revoir', 'bye', 'adieu', 'quit']):
response = "Au revoir ! Ce fut un plaisir de discuter avec vous. Revenez quand vous voulez !"
# Réponse générique
else:
response = self.responses["reponses_generiques"][len(msg_lower) % len(self.responses["reponses_generiques"])]
response += f"\n\nJ'ai analysé votre message : « {message} »"
# Sauvegarder la réponse
self.conversations[conversation_id].append({
"role": "assistant",
"content": response,
"timestamp": time.time()
})
logger.info(f"📤 Réponse envoyée: '{response[:50]}...'")
return response
except Exception as e:
logger.error(f"❌ Erreur dans process_message: {e}")
return "Désolé, une erreur s'est produite lors du traitement de votre message. Veuillez réessayer."
# Initialisation de FastAPI
app = FastAPI(
title="Barouia-Cortex Real Chat",
description="Système de conversation IA réel avec Barouia-Cortex",
version="4.0.0",
docs_url="/api/docs",
redoc_url="/api/redoc"
)
# CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allow_headers=["*"],
)
# Initialisation du moteur
chat_engine = RealChatEngine()
# Modèles de données
class ChatRequest(BaseModel):
conversation_id: str
message: str
class StartChatRequest(BaseModel):
user_id: Optional[str] = "default_user"
class HealthResponse(BaseModel):
status: str
service: str
timestamp: float
active_conversations: int
# Gestionnaire WebSocket
class ConnectionManager:
def __init__(self):
self.active_connections: Dict[str, WebSocket] = {}
async def connect(self, websocket: WebSocket, conversation_id: str):
await websocket.accept()
self.active_connections[conversation_id] = websocket
logger.info(f"🔗 WebSocket connecté: {conversation_id[:8]}...")
def disconnect(self, conversation_id: str):
if conversation_id in self.active_connections:
del self.active_connections[conversation_id]
logger.info(f"🔌 WebSocket déconnecté: {conversation_id[:8]}...")
async def send_message(self, conversation_id: str, message: str):
if conversation_id in self.active_connections:
await self.active_connections[conversation_id].send_text(message)
connection_manager = ConnectionManager()
# Routes API
@app.get("/")
async def root():
return {"message": "Barouia-Cortex API", "version": "4.0.0", "status": "active"}
@app.get("/health")
async def health_check():
return HealthResponse(
status="ok",
service="Barouia-Cortex Chat",
timestamp=time.time(),
active_conversations=len(chat_engine.conversations)
)
@app.post("/api/chat/start")
async def start_chat(request: StartChatRequest = StartChatRequest()):
conversation_id = str(uuid.uuid4())
logger.info(f"🚀 Nouvelle conversation {conversation_id[:8]}... démarrée pour {request.user_id}")
return {
"success": True,
"conversation_id": conversation_id,
"message": "Conversation Barouia-Cortex démarrée"
}
@app.post("/api/chat/send")
async def send_message(request: ChatRequest):
try:
logger.info(f"📝 Traitement message pour {request.conversation_id[:8]}...")
# Simulation délai de traitement
await asyncio.sleep(0.5)
response = chat_engine.process_message(request.conversation_id, request.message)
return {
"success": True,
"response": response,
"conversation_id": request.conversation_id,
"timestamp": time.time()
}
except Exception as e:
logger.error(f"❌ Erreur endpoint /api/chat/send: {e}")
return {
"success": False,
"response": "Erreur interne du serveur Barouia-Cortex.",
"error": str(e)
}
# Route WebSocket
@app.websocket("/ws/{conversation_id}")
async def websocket_endpoint(websocket: WebSocket, conversation_id: str):
await connection_manager.connect(websocket, conversation_id)
try:
while True:
data = await websocket.receive_text()
logger.info(f"🔁 WebSocket message: {data[:50]}...")
# Traitement du message
response = chat_engine.process_message(conversation_id, data)
await connection_manager.send_message(conversation_id, response)
except WebSocketDisconnect:
connection_manager.disconnect(conversation_id)
except Exception as e:
logger.error(f"❌ Erreur WebSocket: {e}")
connection_manager.disconnect(conversation_id)
# Interface Web principale
@app.get("/chat", response_class=HTMLResponse)
async def chat_interface():
return get_chat_html()
def get_chat_html():
return """
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Barouia-Cortex - Chat Intelligent</title>
<style>
:root {
--primary: #667eea;
--secondary: #764ba2;
--dark: #0f0f23;
--darker: #0a0a1a;
--light: #a0a0ff;
--text: #ffffff;
--success: #00ff88;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, var(--dark) 0%, var(--darker) 100%);
color: var(--text);
height: 100vh;
overflow: hidden;
}
.chat-container {
max-width: 900px;
height: 100vh;
margin: 0 auto;
display: flex;
flex-direction: column;
background: rgba(15, 15, 35, 0.95);
box-shadow: 0 0 50px rgba(102, 126, 234, 0.1);
}
.header {
background: linear-gradient(135deg, var(--primary), var(--secondary));
padding: 1.5rem;
text-align: center;
border-bottom: 3px solid rgba(255, 255, 255, 0.1);
}
.header h1 {
font-size: 2.2rem;
margin-bottom: 0.5rem;
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
}
.header p {
opacity: 0.9;
font-size: 1.1rem;
}
.messages-container {
flex: 1;
overflow-y: auto;
padding: 1.5rem;
display: flex;
flex-direction: column;
gap: 1rem;
background: rgba(10, 10, 20, 0.5);
}
.message {
max-width: 75%;
padding: 1rem 1.3rem;
border-radius: 1.2rem;
animation: messageAppear 0.4s ease-out;
line-height: 1.5;
word-wrap: break-word;
}
@keyframes messageAppear {
from {
opacity: 0;
transform: translateY(20px) scale(0.95);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.user-message {
align-self: flex-end;
background: linear-gradient(135deg, var(--primary), var(--secondary));
margin-left: auto;
border-bottom-right-radius: 0.3rem;
}
.ai-message {
align-self: flex-start;
background: rgba(255, 255, 255, 0.08);
backdrop-filter: blur(20px);
border: 1px solid rgba(102, 126, 234, 0.2);
border-bottom-left-radius: 0.3rem;
position: relative;
}
.ai-message::before {
content: '🧠';
position: absolute;
left: -45px;
top: 50%;
transform: translateY(-50%);
font-size: 1.4rem;
background: rgba(102, 126, 234, 0.2);
padding: 0.5rem;
border-radius: 50%;
}
.input-container {
padding: 1.5rem;
background: rgba(26, 26, 46, 0.95);
backdrop-filter: blur(20px);
border-top: 1px solid rgba(102, 126, 234, 0.3);
}
.input-group {
display: flex;
gap: 1rem;
align-items: center;
}
#messageInput {
flex: 1;
padding: 1.2rem 1.5rem;
border: none;
border-radius: 2rem;
background: rgba(255, 255, 255, 0.1);
color: white;
font-size: 1rem;
backdrop-filter: blur(10px);
transition: all 0.3s ease;
border: 1px solid transparent;
}
#messageInput:focus {
outline: none;
background: rgba(255, 255, 255, 0.15);
box-shadow: 0 0 30px rgba(102, 126, 234, 0.3);
border-color: var(--primary);
}
#messageInput::placeholder {
color: rgba(255, 255, 255, 0.5);
}
#sendButton {
padding: 1.2rem 2.5rem;
border: none;
border-radius: 2rem;
background: linear-gradient(135deg, var(--primary), var(--secondary));
color: white;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
}
#sendButton:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.5);
}
#sendButton:active {
transform: translateY(0);
}
#sendButton:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.typing-indicator {
display: none;
align-self: flex-start;
padding: 1rem 1.3rem;
background: rgba(255, 255, 255, 0.08);
border-radius: 1.2rem;
color: var(--light);
font-style: italic;
margin-bottom: 0.5rem;
border: 1px solid rgba(102, 126, 234, 0.2);
}
.typing-dots {
display: inline-block;
}
.typing-dots span {
animation: typing 1.4s infinite;
opacity: 0.3;
}
.typing-dots span:nth-child(2) {
animation-delay: 0.2s;
}
.typing-dots span:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes typing {
0%, 60%, 100% { opacity: 0.3; }
30% { opacity: 1; }
}
.status-bar {
padding: 0.8rem 1.5rem;
background: rgba(15, 15, 25, 0.8);
border-top: 1px solid rgba(102, 126, 234, 0.2);
font-size: 0.9rem;
color: var(--light);
display: flex;
justify-content: space-between;
align-items: center;
}
.connection-status {
display: flex;
align-items: center;
gap: 0.7rem;
}
.status-dot {
width: 10px;
height: 10px;
border-radius: 50%;
background: var(--success);
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% {
opacity: 1;
box-shadow: 0 0 10px var(--success);
}
50% {
opacity: 0.7;
box-shadow: 0 0 5px var(--success);
}
}
.message-time {
font-size: 0.75rem;
opacity: 0.6;
margin-top: 0.3rem;
}
/* Scrollbar personnalisée */
.messages-container::-webkit-scrollbar {
width: 6px;
}
.messages-container::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
border-radius: 3px;
}
.messages-container::-webkit-scrollbar-thumb {
background: var(--primary);
border-radius: 3px;
}
.messages-container::-webkit-scrollbar-thumb:hover {
background: var(--secondary);
}
/* Responsive */
@media (max-width: 768px) {
.chat-container {
max-width: 100%;
height: 100vh;
}
.message {
max-width: 85%;
}
.ai-message::before {
left: -35px;
font-size: 1.2rem;
}
.header h1 {
font-size: 1.8rem;
}
}
</style>
</head>
<body>
<div class="chat-container">
<div class="header">
<h1>🧠 Barouia-Cortex</h1>
<p>Assistant IA conversationnel intelligent</p>
</div>
<div class="messages-container" id="messagesContainer">
<div class="message ai-message">
Bonjour ! Je suis Barouia-Cortex, votre assistant IA personnel. 🤖<br><br>
Je suis là pour discuter, répondre à vos questions et vous accompagner dans vos réflexions. Comment puis-je vous aider aujourd'hui ?
<div class="message-time" id="welcomeTime"></div>
</div>
</div>
<div class="typing-indicator" id="typingIndicator">
Barouia-Cortex réfléchit<span class="typing-dots"><span>.</span><span>.</span><span>.</span></span>
</div>
<div class="input-container">
<div class="input-group">
<input type="text" id="messageInput" placeholder="Tapez votre message ici..." autocomplete="off" autofocus>
<button id="sendButton">Envoyer</button>
</div>
</div>
<div class="status-bar">
<div class="connection-status">
<div class="status-dot"></div>
<span id="connectionStatus">Connecté à Barouia-Cortex</span>
</div>
<div id="messageCount">Messages: 1</div>
</div>
</div>
<script>
// Variables globales
let conversationId = null;
let messageCount = 1;
let isConnected = true;
// Initialisation au chargement
document.addEventListener('DOMContentLoaded', function() {
console.log('🚀 Initialisation de Barouia-Cortex...');
initializeChat();
setupEventListeners();
setWelcomeTime();
});
function setWelcomeTime() {
const now = new Date();
const timeString = now.toLocaleTimeString('fr-FR', {
hour: '2-digit',
minute: '2-digit'
});
document.getElementById('welcomeTime').textContent = timeString;
}
async function initializeChat() {
try {
console.log('🔄 Démarrage d\'une nouvelle conversation...');
showStatus('Connexion à Barouia-Cortex...', 'connecting');
const response = await fetch('/api/chat/start', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({ user_id: 'web_user' })
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
if (data.success) {
conversationId = data.conversation_id;
console.log('✅ Conversation démarrée:', conversationId);
showStatus('Connecté à Barouia-Cortex', 'connected');
// Focus sur l'input
document.getElementById('messageInput').focus();
} else {
throw new Error('Échec du démarrage de la conversation');
}
} catch (error) {
console.error('❌ Erreur initialisation:', error);
showStatus('Erreur de connexion', 'error');
addMessageToChat('ai', '⚠️ Désolé, impossible de démarrer la conversation. Veuillez rafraîchir la page.');
}
}
function setupEventListeners() {
const sendButton = document.getElementById('sendButton');
const messageInput = document.getElementById('messageInput');
// Envoi par bouton
sendButton.addEventListener('click', sendMessage);
// Envoi par Entrée
messageInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
sendMessage();
}
});
// Amélioration UX: désactiver le bouton si input vide
messageInput.addEventListener('input', function() {
sendButton.disabled = this.value.trim() === '';
});
// Focus automatique
messageInput.focus();
}
async function sendMessage() {
const input = document.getElementById('messageInput');
const sendButton = document.getElementById('sendButton');
const message = input.value.trim();
if (!message || !conversationId) {
return;
}
// Désactiver l'input pendant l'envoi
input.disabled = true;
sendButton.disabled = true;
console.log('📤 Envoi du message:', message);
// Ajouter le message utilisateur avec horodatage
addMessageToChat('user', message);
input.value = '';
messageCount++;
updateMessageCount();
// Afficher l'indicateur de frappe
showTypingIndicator();
showStatus('Barouia-Cortex répond...', 'typing');
try {
const response = await fetch('/api/chat/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
conversation_id: conversationId,
message: message
})
});
console.log('📥 Réponse reçue, status:', response.status);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
console.log('📋 Données reçues:', data);
hideTypingIndicator();
if (data.success) {
addMessageToChat('ai', data.response);
showStatus('Connecté à Barouia-Cortex', 'connected');
} else {
throw new Error(data.response || 'Erreur inconnue');
}
} catch (error) {
console.error('❌ Erreur envoi message:', error);
hideTypingIndicator();
showStatus('Erreur de connexion', 'error');
addMessageToChat('ai', '❌ Désolé, une erreur est survenue. Veuillez réessayer.');
} finally {
// Réactiver l'input
input.disabled = false;
input.focus();
}
}
function addMessageToChat(role, content) {
const container = document.getElementById('messagesContainer');
const messageDiv = document.createElement('div');
messageDiv.className = `message ${role}-message`;
const now = new Date();
const timeString = now.toLocaleTimeString('fr-FR', {
hour: '2-digit',
minute: '2-digit'
});
messageDiv.innerHTML = `
${content}
<div class="message-time">${timeString}</div>
`;
container.appendChild(messageDiv);
container.scrollTop = container.scrollHeight;
}
function showTypingIndicator() {
const indicator = document.getElementById('typingIndicator');
indicator.style.display = 'block';
const container = document.getElementById('messagesContainer');
container.scrollTop = container.scrollHeight;
}
function hideTypingIndicator() {
document.getElementById('typingIndicator').style.display = 'none';
}
function updateMessageCount() {
document.getElementById('messageCount').textContent = `Messages: ${messageCount}`;
}
function showStatus(text, status) {
const statusElement = document.getElementById('connectionStatus');
const dotElement = document.querySelector('.status-dot');
statusElement.textContent = text;
// Changer la couleur du point selon le statut
dotElement.style.animation = 'none'; // Reset animation
void dotElement.offsetWidth; // Trigger reflow
switch(status) {
case 'connected':
dotElement.style.background = 'var(--success)';
dotElement.style.animation = 'pulse 2s infinite';
break;
case 'connecting':
dotElement.style.background = '#ffaa00';
dotElement.style.animation = 'pulse 1s infinite';
break;
case 'typing':
dotElement.style.background = '#0099ff';
dotElement.style.animation = 'pulse 1s infinite';
break;
case 'error':
dotElement.style.background = '#ff4444';
dotElement.style.animation = 'pulse 0.5s infinite';
break;
}
}
// Gestion des erreurs globales
window.addEventListener('error', function(e) {
console.error('🚨 Erreur globale:', e.error);
});
window.addEventListener('unhandledrejection', function(e) {
console.error('🚨 Promise rejetée:', e.reason);
});
// Gestionnaire de visibilité de page
document.addEventListener('visibilitychange', function() {
if (!document.hidden) {
document.getElementById('messageInput').focus();
}
});
</script>
</body>
</html>
"""
# Point d'entrée principal
if __name__ == "__main__":
logger.info("🚀 Démarrage de Barouia-Cortex v4.0...")
uvicorn.run(
app,
host="0.0.0.0",
port=8000,
log_level="info",
access_log=True
)