Spaces:
Runtime error
Runtime error
| #!/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 | |
| async def root(): | |
| return {"message": "Barouia-Cortex API", "version": "4.0.0", "status": "active"} | |
| async def health_check(): | |
| return HealthResponse( | |
| status="ok", | |
| service="Barouia-Cortex Chat", | |
| timestamp=time.time(), | |
| active_conversations=len(chat_engine.conversations) | |
| ) | |
| 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" | |
| } | |
| 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 | |
| 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 | |
| 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 | |
| ) |