OMEN / app.py
Juliana124's picture
Upload app.py
9290c1e verified
Raw
History Blame Contribute Delete
63.8 kB
"""
Win11 Fluent UI Chatbot
=======================
Un chatbot avec design Windows 11 Fluent Design et mémoire de conversation.
Intégration du modèle: darkc0de/XortronCriminalComputingConfig
Auteur: MiniMax Agent
"""
import gradio as gr
from gradio.themes import Base
import time
import uuid
import os
from typing import List, Tuple, Optional, Dict, Any
from dataclasses import dataclass, field
from datetime import datetime
import json
# ============================================================
# CONFIGURATION DU MODÈLE
# ============================================================
MODEL_NAME = "darkc0de/XortronCriminalComputingConfig"
# Configuration de la mémoire
MAX_MEMORY_TOKENS = 4096 # Limite de tokens pour l'historique
MEMORY_WINDOW_SIZE = 10 # Nombre de messages à conserver
SYSTEM_PROMPT = """Tu es un assistant IA intelligent et serviable.
Tu réponses de manière claire et concise.
Tu te souviens du contexte de la conversation pour fournir des réponses cohérentes.
Tutoie l'utilisateur et utilise un ton amical mais professionnel."""
# ============================================================
# SYSTÈME DE MÉMOIRE DE CONVERSATION
# ============================================================
@dataclass
class ConversationMessage:
"""Représente un message dans la conversation."""
role: str # 'user', 'assistant', 'system'
content: str
timestamp: str = field(default_factory=lambda: datetime.now().strftime("%H:%M"))
token_count: int = 0
@dataclass
class ConversationMemory:
"""
Système de mémoire de conversation avec gestion du contexte.
Préserve l'historique complet pour des réponses cohérentes.
"""
messages: List[ConversationMessage] = field(default_factory=list)
max_tokens: int = MAX_MEMORY_TOKENS
max_messages: int = MEMORY_WINDOW_SIZE
session_id: str = field(default_factory=lambda: str(uuid.uuid4())[:8])
def add_message(self, role: str, content: str) -> None:
"""Ajoute un message à la conversation."""
# Estimation approximative des tokens (1 token ≈ 4 caractères)
token_count = len(content) // 4
message = ConversationMessage(
role=role,
content=content,
token_count=token_count
)
self.messages.append(message)
self._prune_if_needed()
def _prune_if_needed(self) -> None:
"""Supprime les anciens messages si nécessaire pour respecter les limites."""
# Calculer le nombre total de tokens
total_tokens = sum(m.token_count for m in self.messages)
# Supprimer les anciens messages si on dépasse la limite de tokens
while total_tokens > self.max_tokens and len(self.messages) > 2:
removed = self.messages.pop(0)
total_tokens -= removed.token_count
# Limiter le nombre de messages si on dépasse la fenêtre
while len(self.messages) > self.max_messages:
self.messages.pop(0)
def get_prompt_messages(self) -> List[Dict[str, str]]:
"""Retourne les messages formatés pour le modèle."""
result = []
for msg in self.messages:
result.append({
"role": msg.role,
"content": msg.content
})
return result
def get_conversation_text(self) -> str:
"""Retourne le texte complet de la conversation pour le contexte."""
lines = []
for msg in self.messages:
role_label = "Utilisateur" if msg.role == "user" else "Assistant"
lines.append(f"[{msg.timestamp}] {role_label}: {msg.content}")
return "\n".join(lines)
def clear(self) -> None:
"""Efface la conversation."""
self.messages.clear()
self.session_id = str(uuid.uuid4())[:8]
def get_message_count(self) -> int:
"""Retourne le nombre de messages dans la conversation."""
return len(self.messages)
# ============================================================
# GESTIONNAIRE DE MODÈLE
# ============================================================
class ModelManager:
"""
Gestionnaire pour charger et interroger le modèle.
Supporte le modèle Hugging Face avec mémoire de conversation.
"""
def __init__(self, model_name: str = MODEL_NAME):
self.model_name = model_name
self.model = None
self.tokenizer = None
self.device = None
self.is_loaded = False
self.load_error = None
def load_model(self) -> bool:
"""
Charge le modèle depuis Hugging Face.
Retourne True si le chargement est réussi.
"""
try:
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
# Déterminer le périphérique (GPU si disponible)
self.device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"Chargement du modèle {self.model_name} sur {self.device}...")
# Charger le tokenizer
self.tokenizer = AutoTokenizer.from_pretrained(
self.model_name,
trust_remote_code=True
)
# Charger le modèle avec optimisation mémoire
model_kwargs = {
"torch_dtype": torch.float16 if self.device == "cuda" else torch.float32,
"low_cpu_mem_usage": True,
}
if self.device == "cuda":
model_kwargs["device_map"] = "auto"
self.model = AutoModelForCausalLM.from_pretrained(
self.model_name,
**model_kwargs
)
self.model.eval()
self.is_loaded = True
print(f"Modèle {self.model_name} chargé avec succès!")
return True
except Exception as e:
self.load_error = str(e)
print(f"Erreur lors du chargement du modèle: {e}")
return False
def generate_response(
self,
user_message: str,
memory: ConversationMemory,
max_new_tokens: int = 512,
temperature: float = 0.7,
top_p: float = 0.95,
do_sample: bool = True
) -> str:
"""
Génère une réponse en utilisant le modèle avec le contexte de conversation.
Args:
user_message: Le message de l'utilisateur
memory: L'objet ConversationMemory contenant l'historique
max_new_tokens: Nombre maximum de tokens à générer
temperature: Température pour la génération (créativité)
top_p: Paramètre top-p pour le sampling
do_sample: Si True, utilise le sampling au lieu du beam search
Returns:
La réponse générée par le modèle
"""
try:
if not self.is_loaded:
return self._get_fallback_response(user_message, memory)
# Ajouter le message utilisateur à la mémoire
memory.add_message("user", user_message)
# Préparer les messages pour le format conversation
messages = [{"role": "system", "content": SYSTEM_PROMPT}]
messages.extend(memory.get_prompt_messages())
# Appliquer le template de chat
try:
# Essayer d'utiliser le chat template du tokenizer
prompt = self.tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True
)
except Exception:
# Fallback: format simple si pas de chat template
prompt = self._format_prompt_fallback(messages)
# Tokeniser l'entrée
inputs = self.tokenizer(
prompt,
return_tensors="pt",
truncation=True,
max_length=self.max_input_length()
).to(self.device)
# Générer la réponse
with torch.no_grad():
outputs = self.model.generate(
**inputs,
max_new_tokens=max_new_tokens,
temperature=temperature,
top_p=top_p,
do_sample=do_sample,
pad_token_id=self.tokenizer.eos_token_id,
no_repeat_ngram_size=3,
)
# Décoder la réponse
response_ids = outputs[0][inputs["input_ids"].shape[1]:]
response = self.tokenizer.decode(response_ids, skip_special_tokens=True)
# Nettoyer la réponse
response = self._clean_response(response)
# Ajouter la réponse à la mémoire
memory.add_message("assistant", response)
return response
except Exception as e:
print(f"Erreur lors de la génération: {e}")
return self._get_fallback_response(user_message, memory)
def _format_prompt_fallback(self, messages: List[Dict[str, str]]) -> str:
"""Format de prompt simple si le chat template n'est pas disponible."""
prompt_parts = []
for msg in messages:
if msg["role"] == "system":
continue
role_prefix = "Human:" if msg["role"] == "user" else "Assistant:"
prompt_parts.append(f"{role_prefix} {msg['content']}")
prompt_parts.append("Assistant:")
return "\n\n".join(prompt_parts)
def _clean_response(self, response: str) -> str:
"""Nettoie la réponse générée."""
# Supprimer les caractères de contrôle
response = response.strip()
# Si la réponse commence par un préfixe unwanted, le supprimer
unwanted_prefixes = ["Assistant:", "assistant:", "AI:", "ai:"]
for prefix in unwanted_prefixes:
if response.startswith(prefix):
response = response[len(prefix):].strip()
return response
def _get_fallback_response(self, user_message: str, memory: ConversationMemory) -> str:
"""Réponse de fallback si le modèle n'est pas disponible."""
# Réponses contextuelles basées sur l'historique
msg_lower = user_message.lower()
# Salutations
if any(word in msg_lower for word in ['bonjour', 'hello', 'hi', 'salut']):
response = "Bonjour ! Ravi de vous revoir. Comment puis-je vous aider aujourd'hui ?"
# Remerciements
elif any(word in msg_lower for word in ['merci', 'thanks', 'remercie']):
response = "De rien ! C'est toujours un plaisir de vous aider. N'hésitez pas si vous avez d'autres questions."
# Questions sur l'identité
elif any(word in msg_lower for word in ['qui es-tu', 'who are you', 'identity', 'appel']):
response = "Je suis un assistant IA conçu avec le modèle darkc0de/XortronCriminalComputingConfig. Je suis là pour répondre à vos questions en maintenant le contexte de notre conversation."
# Questions sur la mémoire
elif any(word in msg_lower for word in ['mémoire', 'memory', 'souviens', 'rappelle']):
msg_count = memory.get_message_count()
if msg_count > 2:
response = f"Bien sûr que je me souviens ! Nous avons déjà échangé {msg_count // 2} messages. Je conserve l'historique de notre conversation pour vous fournir des réponses cohérentes."
else:
response = "Je commence tout juste notre conversation, mais je suis prêt à mémoriser tout ce que nous allons discuter !"
# Aide
elif any(word in msg_lower for word in ['aide', 'help', 'comment', 'peux-tu']):
response = "Je suis là pour vous aider ! Vous pouvez me poser des questions, me demander des explications, ou simplement discuter. Je conserve le contexte de notre conversation pour des échanges plus riches."
# Au revoir
elif any(word in msg_lower for word in ['au revoir', 'bye', 'salut', 'ciao', 'à bientôt']):
response = "Au revoir ! N'hésitez pas à revenir si vous avez d'autres questions. Bonne journée !"
# Réponse générique avec contexte
else:
context_info = ""
if memory.get_message_count() > 2:
context_info = " En gardant à l'esprit notre conversation précédente, "
responses = [
f"Merci pour votre message.{context_info} Pourriez-vous m'en dire plus pour que je puisse vous aider au mieux ?",
f"Intéressant !{context_info} Pouvez-vous développer votre pensée ?",
f"Je comprends.{context_info} Voici ce que je peux vous dire à ce sujet...",
f"Message reçu !{context_info} Laissez-moi réfléchir à votre question.",
]
import random
response = random.choice(responses)
# Ajouter le message à la mémoire
memory.add_message("assistant", response)
return response
def max_input_length(self) -> int:
"""Retourne la longueur maximale d'entrée du modèle."""
return 2048 # Valeur par défaut, peut être ajustée selon le modèle
def get_model_info(self) -> Dict[str, Any]:
"""Retourne les informations sur le modèle."""
return {
"model_name": self.model_name,
"is_loaded": self.is_loaded,
"device": self.device,
"load_error": self.load_error,
"max_tokens": self.max_input_length()
}
# Instance globale du gestionnaire de modèle
model_manager = ModelManager(MODEL_NAME)
# ============================================================
# DÉFINITIONS DES STYLES CSS WINDOWS 11 FLUENT DESIGN
# ============================================================
WINDOWS11_CSS = """
/* ============================================
WINDOWS 11 FLUENT DESIGN SYSTEM
============================================ */
/* Variables CSS Windows 11 */
:root {
/* Couleurs Light Mode */
--win11-bg: #f3f3f3;
--win11-surface: rgba(255, 255, 255, 0.85);
--win11-surface-hover: rgba(255, 255, 255, 0.95);
--win11-surface-active: rgba(243, 243, 243, 1);
--win11-accent: #0078d4;
--win11-accent-hover: #1a86d9;
--win11-accent-active: #006cbf;
--win11-text-primary: #1f1f1f;
--win11-text-secondary: #5e5e5e;
--win11-text-tertiary: #8e8e8e;
--win11-border: rgba(0, 0, 0, 0.08);
--win11-shadow: rgba(0, 0, 0, 0.1);
--win11-shadow-strong: rgba(0, 0, 0, 0.15);
/* Mica Material Effect */
--mica-noise: url('data:image/svg+xml,<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg"><filter id="noise"><feTurbulence type="fractalNoise" baseFrequency="0.65" numOctaves="3" stitchTiles="stitch"/></filter><rect width="100%" height="100%" filter="url(%23noise)" opacity="0.03"/></svg>');
/* Border Radius */
--win11-radius-window: 12px;
--win11-radius-button: 8px;
--win11-radius-input: 8px;
--win11-radius-bubble: 12px;
/* Transitions */
--win11-transition-fast: 100ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
--win11-transition-normal: 200ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
--win11-transition-slow: 350ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
/* Depth & Effects */
--win11-depth-1: 0 1px 3px rgba(0, 0, 0, 0.08);
--win11-depth-2: 0 4px 12px rgba(0, 0, 0, 0.1);
--win11-depth-3: 0 8px 24px rgba(0, 0, 0, 0.12);
--win11-glow-accent: 0 0 0 2px rgba(0, 120, 212, 0.3);
--win11-glow-hover: 0 0 20px rgba(0, 120, 212, 0.2);
}
/* Dark Mode Variables */
.dark {
--win11-bg: #202020;
--win11-surface: rgba(32, 32, 32, 0.85);
--win11-surface-hover: rgba(50, 50, 50, 0.95);
--win11-surface-active: rgba(40, 40, 40, 1);
--win11-accent: #60cdff;
--win11-accent-hover: #8bd8ff;
--win11-accent-active: #4bb6e6;
--win11-text-primary: #ffffff;
--win11-text-secondary: #a0a0a0;
--win11-text-tertiary: #6e6e6e;
--win11-border: rgba(255, 255, 255, 0.08);
--win11-shadow: rgba(0, 0, 0, 0.3);
--win11-shadow-strong: rgba(0, 0, 0, 0.5);
--win11-glow-accent: 0 0 0 2px rgba(96, 205, 255, 0.4);
--win11-glow-hover: 0 0 20px rgba(96, 205, 255, 0.25);
}
/* ============================================
BASE STYLES & RESET
============================================ */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Segoe UI Variable', 'Segoe UI', -apple-system, BlinkMacSystemFont, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background: transparent;
}
/* ============================================
MAIN CONTAINER - WINDOW SIMULATION
============================================ */
.win11-window {
width: 100%;
max-width: 1000px;
margin: 0 auto;
background: var(--win11-surface);
backdrop-filter: blur(30px) saturate(125%);
-webkit-backdrop-filter: blur(30px) saturate(125%);
border-radius: var(--win11-radius-window);
box-shadow: var(--win11-depth-3);
overflow: hidden;
position: relative;
border: 1px solid var(--win11-border);
}
/* Mica Material Background Effect */
.win11-window::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image: var(--mica-noise);
opacity: 1;
pointer-events: none;
z-index: -1;
border-radius: var(--win11-radius-window);
}
/* ============================================
TITLE BAR
============================================ */
.win11-titlebar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
background: linear-gradient(180deg, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 100%);
border-bottom: 1px solid var(--win11-border);
user-select: none;
-webkit-user-select: none;
}
.dark .win11-titlebar {
background: linear-gradient(180deg, rgba(255,255,255,0.03) 0%, rgba(0,0,0,0) 100%);
}
.win11-titlebar-left {
display: flex;
align-items: center;
gap: 12px;
}
.win11-icon {
width: 20px;
height: 20px;
border-radius: 4px;
background: linear-gradient(135deg, #00a4ef 0%, #0078d4 50%, #004275 100%);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 12px;
font-weight: bold;
box-shadow: var(--win11-depth-1);
}
.win11-title {
font-size: 14px;
font-weight: 600;
color: var(--win11-text-primary);
letter-spacing: -0.2px;
}
.win11-titlebar-right {
display: flex;
align-items: center;
gap: 8px;
}
.win11-control-btn {
width: 32px;
height: 24px;
border: none;
background: transparent;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all var(--win11-transition-fast);
color: var(--win11-text-secondary);
font-size: 10px;
}
.win11-control-btn:hover {
background: rgba(0, 0, 0, 0.06);
color: var(--win11-text-primary);
}
.win11-control-btn.close:hover {
background: #e81123;
color: white;
}
.dark .win11-control-btn:hover {
background: rgba(255, 255, 255, 0.06);
}
.dark .win11-control-btn.close:hover {
background: #e81123;
}
/* ============================================
CHAT AREA
============================================ */
.win11-chat-container {
height: 500px;
overflow-y: auto;
padding: 16px 20px;
scroll-behavior: smooth;
}
/* Custom Scrollbar */
.win11-chat-container::-webkit-scrollbar {
width: 6px;
}
.win11-chat-container::-webkit-scrollbar-track {
background: transparent;
}
.win11-chat-container::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.2);
border-radius: 3px;
}
.win11-chat-container::-webkit-scrollbar-thumb:hover {
background: rgba(0, 0, 0, 0.3);
}
.dark .win11-chat-container::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
}
.dark .win11-chat-container::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.3);
}
/* ============================================
MESSAGE BUBBLES
============================================ */
.win11-message {
display: flex;
flex-direction: column;
margin-bottom: 16px;
animation: messageFlyIn 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
opacity: 0;
}
@keyframes messageFlyIn {
from {
opacity: 0;
transform: translateY(20px) scale(0.95);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.win11-message.user {
align-items: flex-end;
}
.win11-message.assistant {
align-items: flex-start;
}
.win11-bubble {
max-width: 75%;
padding: 12px 16px;
position: relative;
word-wrap: break-word;
font-size: 14px;
line-height: 1.5;
box-shadow: var(--win11-depth-1);
}
/* User Message Bubble */
.win11-bubble.user {
background: linear-gradient(135deg, var(--win11-accent) 0%, var(--win11-accent-hover) 100%);
color: white;
border-radius: var(--win11-radius-bubble) var(--win11-radius-bubble) 2px var(--win11-radius-bubble);
}
.win11-bubble.user::after {
content: '';
position: absolute;
bottom: 0;
right: -8px;
width: 0;
height: 0;
border-style: solid;
border-width: 0 0 12px 12px;
border-color: transparent transparent var(--win11-accent) transparent;
}
/* Assistant Message Bubble */
.win11-bubble.assistant {
background: var(--win11-surface);
color: var(--win11-text-primary);
border: 1px solid var(--win11-border);
border-radius: var(--win11-radius-bubble) var(--win11-radius-bubble) var(--win11-radius-bubble) 2px;
}
.win11-bubble.assistant::after {
content: '';
position: absolute;
bottom: 0;
left: -8px;
width: 0;
height: 0;
border-style: solid;
border-width: 0 12px 12px 0;
border-color: transparent var(--win11-surface) transparent transparent;
}
/* Message Avatar */
.win11-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
font-weight: 600;
margin-bottom: 4px;
box-shadow: var(--win11-depth-1);
}
.win11-avatar.user {
background: linear-gradient(135deg, var(--win11-accent) 0%, #005a9e 100%);
color: white;
}
.win11-avatar.assistant {
background: linear-gradient(135deg, #107c10 0%, #0b5a0b 100%);
color: white;
}
/* Message Timestamp */
.win11-timestamp {
font-size: 11px;
color: var(--win11-text-tertiary);
margin-top: 4px;
padding: 0 4px;
}
/* ============================================
LOADING INDICATOR (Windows 11 Style)
============================================ */
.win11-loading {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 0;
}
.win11-loading-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--win11-accent);
animation: loadingBounce 1.4s infinite ease-in-out both;
}
.win11-loading-dot:nth-child(1) { animation-delay: -0.32s; }
.win11-loading-dot:nth-child(2) { animation-delay: -0.16s; }
@keyframes loadingBounce {
0%, 80%, 100% {
transform: scale(0.6);
opacity: 0.4;
}
40% {
transform: scale(1);
opacity: 1;
}
}
/* ============================================
INPUT AREA
============================================ */
.win11-input-container {
padding: 16px 20px;
background: linear-gradient(180deg, rgba(0,0,0,0.02) 0%, rgba(0,0,0,0.05) 100%);
border-top: 1px solid var(--win11-border);
}
.dark .win11-input-container {
background: linear-gradient(180deg, rgba(255,255,255,0.02) 0%, rgba(0,0,0,0.1) 100%);
}
.win11-input-wrapper {
display: flex;
align-items: flex-end;
gap: 12px;
background: var(--win11-surface);
border: 1px solid var(--win11-border);
border-radius: var(--win11-radius-input);
padding: 8px 12px;
transition: all var(--win11-transition-fast);
box-shadow: var(--win11-depth-1);
}
.win11-input-wrapper:focus-within {
border-color: var(--win11-accent);
box-shadow: var(--win11-glow-accent);
}
.win11-input {
flex: 1;
border: none;
outline: none;
background: transparent;
font-family: inherit;
font-size: 14px;
color: var(--win11-text-primary);
resize: none;
max-height: 120px;
min-height: 24px;
line-height: 1.5;
}
.win11-input::placeholder {
color: var(--win11-text-tertiary);
}
.win11-send-btn {
width: 36px;
height: 36px;
border: none;
background: var(--win11-accent);
color: white;
border-radius: var(--win11-radius-button);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all var(--win11-transition-fast);
box-shadow: var(--win11-depth-1);
}
.win11-send-btn:hover {
background: var(--win11-accent-hover);
transform: scale(1.05);
box-shadow: var(--win11-glow-hover);
}
.win11-send-btn:active {
background: var(--win11-accent-active);
transform: scale(0.95);
}
.win11-send-btn:disabled {
background: var(--win11-text-tertiary);
cursor: not-allowed;
transform: none;
box-shadow: none;
}
/* ============================================
THEME TOGGLE
============================================ */
.win11-theme-toggle {
position: relative;
width: 44px;
height: 24px;
border-radius: 12px;
background: rgba(0, 0, 0, 0.2);
cursor: pointer;
transition: all var(--win11-transition-normal);
border: none;
}
.win11-theme-toggle:hover {
background: rgba(0, 0, 0, 0.3);
}
.win11-theme-toggle .toggle-thumb {
position: absolute;
top: 2px;
left: 2px;
width: 20px;
height: 20px;
border-radius: 50%;
background: white;
box-shadow: var(--win11-depth-1);
transition: all var(--win11-transition-normal);
display: flex;
align-items: center;
justify-content: center;
font-size: 10px;
}
.dark .win11-theme-toggle {
background: rgba(255, 255, 255, 0.2);
}
.dark .win11-theme-toggle .toggle-thumb {
left: 22px;
transform: rotate(180deg);
}
/* ============================================
PROMPT EXAMPLES (Windows 11 Style)
============================================ */
.win11-examples {
padding: 12px 20px;
display: flex;
flex-wrap: wrap;
gap: 8px;
border-top: 1px solid var(--win11-border);
background: rgba(0, 0, 0, 0.02);
}
.dark .win11-examples {
background: rgba(255, 255, 255, 0.02);
}
.win11-example-chip {
padding: 6px 12px;
background: var(--win11-surface);
border: 1px solid var(--win11-border);
border-radius: 20px;
font-size: 12px;
color: var(--win11-text-secondary);
cursor: pointer;
transition: all var(--win11-transition-fast);
}
.win11-example-chip:hover {
background: var(--win11-surface-hover);
border-color: var(--win11-accent);
color: var(--win11-accent);
box-shadow: var(--win11-glow-accent);
}
/* ============================================
MEMORY INDICATOR
============================================ */
.win11-memory-indicator {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 16px;
background: linear-gradient(90deg, rgba(0, 120, 212, 0.1) 0%, transparent 100%);
border-radius: 8px;
font-size: 12px;
color: var(--win11-text-secondary);
}
.win11-memory-icon {
width: 16px;
height: 16px;
background: linear-gradient(135deg, var(--win11-accent) 0%, #005a9e 100%);
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 10px;
}
.win11-memory-bar {
flex: 1;
height: 4px;
background: rgba(0, 0, 0, 0.1);
border-radius: 2px;
overflow: hidden;
}
.win11-memory-fill {
height: 100%;
background: linear-gradient(90deg, var(--win11-accent) 0%, #60cdff 100%);
border-radius: 2px;
transition: width 0.3s ease;
}
/* ============================================
GLOW EFFECTS & PARTICLES
============================================ */
.win11-glow-effect {
position: relative;
}
.win11-glow-effect::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 200%;
height: 200%;
background: radial-gradient(circle, rgba(0, 120, 212, 0.1) 0%, transparent 70%);
pointer-events: none;
opacity: 0;
transition: opacity var(--win11-transition-slow);
}
.win11-glow-effect:hover::after {
opacity: 1;
}
/* ============================================
ANIMATIONS
============================================ */
/* Pulse Effect for Focus */
@keyframes pulse-glow {
0% {
box-shadow: 0 0 0 0 rgba(0, 120, 212, 0.4);
}
70% {
box-shadow: 0 0 0 10px rgba(0, 120, 212, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(0, 120, 212, 0);
}
}
.win11-pulse {
animation: pulse-glow 2s infinite;
}
/* Fade In */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.win11-fade-in {
animation: fadeIn var(--win11-transition-normal) forwards;
}
/* Scale In */
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(0.9);
}
to {
opacity: 1;
transform: scale(1);
}
}
.win11-scale-in {
animation: scaleIn var(--win11-transition-slow) forwards;
}
/* Typewriter Cursor */
.typewriter-cursor {
display: inline-block;
width: 2px;
height: 1em;
background: var(--win11-text-primary);
margin-left: 2px;
animation: cursorBlink 1s step-end infinite;
}
@keyframes cursorBlink {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0; }
}
/* ============================================
GRADIO OVERRIDES
============================================ */
.gradio-container {
background: transparent !important;
min-height: 100vh;
padding: 20px;
}
.gradio-row {
gap: 0 !important;
}
.gradio-button {
background: transparent !important;
border: none !important;
}
/* Hide default Gradio elements */
.prose {
display: none !important;
}
/* ============================================
RESPONSIVE DESIGN
============================================ */
@media (max-width: 768px) {
.win11-window {
max-width: 100%;
border-radius: 0;
height: 100vh;
}
.win11-chat-container {
height: calc(100vh - 220px);
}
.win11-bubble {
max-width: 85%;
}
.win11-titlebar {
padding: 10px 12px;
}
.win11-title {
font-size: 12px;
}
}
@media (max-width: 480px) {
.win11-bubble {
max-width: 90%;
padding: 10px 14px;
font-size: 13px;
}
.win11-input-wrapper {
padding: 6px 10px;
}
}
/* ============================================
TOOLTIP
============================================ */
.win11-tooltip {
position: relative;
}
.win11-tooltip::after {
content: attr(data-tooltip);
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%) translateY(-8px);
padding: 6px 10px;
background: var(--win11-text-primary);
color: var(--win11-bg);
font-size: 12px;
border-radius: 4px;
white-space: nowrap;
opacity: 0;
visibility: hidden;
transition: all var(--win11-transition-fast);
z-index: 1000;
}
.win11-tooltip:hover::after {
opacity: 1;
visibility: visible;
transform: translateX(-50%) translateY(-4px);
}
/* ============================================
STATUS INDICATOR
============================================ */
.win11-status {
display: flex;
align-items: center;
gap: 6px;
font-size: 12px;
color: var(--win11-text-tertiary);
}
.win11-status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #107c10;
box-shadow: 0 0 8px rgba(16, 124, 16, 0.5);
}
.win11-status-dot.typing {
background: var(--win11-accent);
box-shadow: 0 0 8px rgba(0, 120, 212, 0.5);
animation: statusPulse 1.5s ease-in-out infinite;
}
@keyframes statusPulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.6; transform: scale(0.9); }
}
/* ============================================
CLEAR MEMORY BUTTON
============================================ */
.win11-clear-btn {
padding: 6px 12px;
background: rgba(232, 17, 35, 0.1);
border: 1px solid rgba(232, 17, 35, 0.3);
border-radius: 6px;
color: #e81123;
font-size: 11px;
cursor: pointer;
transition: all var(--win11-transition-fast);
}
.win11-clear-btn:hover {
background: rgba(232, 17, 35, 0.2);
border-color: #e81123;
}
"""
# ============================================
# JAVASCRIPT POUR LES INTERACTIONS
# ============================================
WINDOWS11_JS = """
// ============================================
// WINDOWS 11 CHATBOT JAVASCRIPT WITH MEMORY
// ============================================
// Global state
let chatHistory = [];
let isProcessing = false;
let sessionId = null;
// Initialize on DOM ready
document.addEventListener('DOMContentLoaded', function() {
initThemeToggle();
initAutoResize();
initEnterKeySubmit();
initSmoothScroll();
initSession();
});
// Session initialization
function initSession() {
sessionId = localStorage.getItem('chat_session_id');
if (!sessionId) {
sessionId = generateSessionId();
localStorage.setItem('chat_session_id', sessionId);
}
updateMemoryIndicator();
logToConsole('Session initialized: ' + sessionId);
}
function generateSessionId() {
return Math.random().toString(36).substring(2, 10);
}
// Theme Toggle
function initThemeToggle() {
const themeToggle = document.querySelector('.win11-theme-toggle');
if (!themeToggle) return;
// Check system preference or saved preference
const savedTheme = localStorage.getItem('theme');
if (savedTheme) {
document.body.classList.toggle('dark', savedTheme === 'dark');
} else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.body.classList.add('dark');
}
// Listen for system changes
if (window.matchMedia) {
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
if (!localStorage.getItem('theme')) {
document.body.classList.toggle('dark', e.matches);
}
});
}
themeToggle.addEventListener('click', function() {
const isDark = document.body.classList.toggle('dark');
localStorage.setItem('theme', isDark ? 'dark' : 'light');
});
}
// Auto-resize textarea
function initAutoResize() {
const input = document.querySelector('.win11-input');
if (!input) return;
input.addEventListener('input', function() {
this.style.height = 'auto';
this.style.height = Math.min(this.scrollHeight, 120) + 'px';
});
}
// Enter key to submit
function initEnterKeySubmit() {
const input = document.querySelector('.win11-input');
const sendBtn = document.querySelector('.win11-send-btn');
if (!input || !sendBtn) return;
input.addEventListener('keydown', function(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
}
// Smooth scroll to bottom
function initSmoothScroll() {
const chatContainer = document.querySelector('.win11-chat-container');
if (!chatContainer) return;
const observer = new MutationObserver(() => {
chatContainer.scrollTo({
top: chatContainer.scrollHeight,
behavior: 'smooth'
});
});
observer.observe(chatContainer, { childList: true });
}
// Typewriter effect for assistant messages
function typewriterEffect(element, text, speed = 30) {
return new Promise(resolve => {
let i = 0;
element.textContent = '';
function type() {
if (i < text.length) {
element.textContent += text.charAt(i);
i++;
setTimeout(type, speed);
} else {
resolve();
}
}
type();
});
}
// Add message with animation
function addMessage(role, content, timestamp) {
const chatContainer = document.querySelector('.win11-chat-container');
if (!chatContainer) return null;
const messageDiv = document.createElement('div');
messageDiv.className = `win11-message ${role}`;
messageDiv.innerHTML = `
<div class="win11-avatar ${role}">
${role === 'user' ? 'U' : 'AI'}
</div>
<div class="win11-bubble ${role}">${escapeHtml(content)}</div>
<div class="win11-timestamp">${timestamp}</div>
`;
chatContainer.appendChild(messageDiv);
chatContainer.scrollTo({ top: chatContainer.scrollHeight, behavior: 'smooth' });
return messageDiv;
}
// Escape HTML
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Show loading indicator
function showLoading() {
const chatContainer = document.querySelector('.win11-chat-container');
if (!chatContainer) return null;
const loadingDiv = document.createElement('div');
loadingDiv.className = 'win11-message assistant';
loadingDiv.id = 'loading-indicator';
loadingDiv.innerHTML = `
<div class="win11-avatar assistant">AI</div>
<div class="win11-loading">
<div class="win11-loading-dot"></div>
<div class="win11-loading-dot"></div>
<div class="win11-loading-dot"></div>
</div>
`;
chatContainer.appendChild(loadingDiv);
chatContainer.scrollTo({ top: chatContainer.scrollHeight, behavior: 'smooth' });
return loadingDiv;
}
// Hide loading indicator
function hideLoading() {
const loadingDiv = document.getElementById('loading-indicator');
if (loadingDiv) {
loadingDiv.remove();
}
}
// Update memory indicator
function updateMemoryIndicator() {
const fill = document.querySelector('.win11-memory-fill');
if (fill && sessionId) {
// Calculate memory usage based on localStorage
let totalSize = 0;
for (let key in localStorage) {
if (localStorage.hasOwnProperty(key)) {
totalSize += (localStorage[key].length + key.length) * 2;
}
}
// Estimate percentage (5MB limit for localStorage)
const percentage = Math.min((totalSize / 5000000) * 100, 100);
fill.style.width = percentage + '%';
}
}
// Log to console
function logToConsole(message) {
console.log('[Win11 Chat] ' + message);
}
// Export functions for Gradio
window.Win11Chat = {
addMessage,
showLoading,
hideLoading,
updateMemoryIndicator,
logToConsole,
typewriterEffect,
getSessionId: () => sessionId,
getHistoryLength: () => chatHistory.length
};
"""
# ============================================
# FONCTIONS UTILITAIRES
# ============================================
def get_timestamp() -> str:
"""Retourne l'heure actuelle formatée."""
return time.strftime("%H:%M", time.localtime())
def create_chatbot_with_memory():
"""
Crée l'interface du chatbot avec mémoire de conversation
et intégration du modèle darkc0de/XortronCriminalComputingConfig.
"""
# État de la conversation (mémoire persistante)
memory_state = gr.State(lambda: ConversationMemory())
# Configuration de Gradio
demo = gr.Blocks(
theme=Base(),
css=WINDOWS11_CSS,
js=WINDOWS11_JS,
title="Win11 Fluent Chatbot with Memory",
analytics_enabled=False,
)
with demo:
# Script d'initialisation
gr.HTML("""
<script>
window.addEventListener('load', function() {
console.log('Win11 Fluent Chatbot with Memory loaded successfully');
console.log('Model: darkc0de/XortronCriminalComputingConfig');
});
</script>
""")
# Container principal (style fenêtre Windows 11)
with gr.Column(elem_classes="win11-window"):
# Barre de titre avec indicateur de mémoire
with gr.Row(elem_classes="win11-titlebar"):
with gr.Row(elem_classes="win11-titlebar-left"):
gr.HTML("""
<div class="win11-icon">AI</div>
<span class="win11-title">Assistant IA • darkc0de/XortronCriminalComputingConfig</span>
""")
with gr.Row(elem_classes="win11-titlebar-right"):
gr.HTML("""
<button class="win11-control-btn win11-theme-toggle" onclick="toggleTheme()">
<span class="toggle-thumb">☀</span>
</button>
<button class="win11-control-btn" title="Mémoire active">💾</button>
<button class="win11-control-btn" title="Aide">?</button>
<button class="win11-control-btn close" title="Fermer">×</button>
""")
# Indicateur de mémoire
gr.HTML("""
<div class="win11-memory-indicator">
<div class="win11-memory-icon">M</div>
<span>Mémoire de conversation active</span>
<div class="win11-memory-bar">
<div class="win11-memory-fill" style="width: 25%;"></div>
</div>
<span id="memory-status">0 messages</span>
</div>
""")
# Zone de chat
with gr.Column(elem_classes="win11-chat-container"):
chatbot_display = gr.HTML(
value="""
<div class="win11-message assistant win11-fade-in">
<div class="win11-avatar assistant">AI</div>
<div class="win11-bubble assistant">
Bonjour ! Je suis votre assistant IA alimenté par <strong>darkc0de/XortronCriminalComputingConfig</strong>.
Je dispose d'une <strong>mémoire de conversation</strong> pour me souvenir de nos échanges
et vous fournir des réponses cohérentes et contextuelles. Comment puis-je vous aider ?
</div>
<div class="win11-timestamp">""" + get_timestamp() + """</div>
</div>
""",
elem_id="chat-display"
)
# Zone de saisie
with gr.Column(elem_classes="win11-input-container"):
with gr.Row(elem_classes="win11-input-wrapper"):
msg_input = gr.Textbox(
placeholder="Tapez votre message ici... (La mémoire conserve le contexte)",
elem_classes="win11-input",
lines=1,
max_lines=4,
show_label=False,
container=False,
)
send_btn = gr.HTML("""
<button class="win11-send-btn" id="send-btn" onclick="sendMessage()">
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
<path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/>
</svg>
</button>
""")
# Bouton effacer la mémoire
gr.HTML("""
<div style="padding: 8px 20px; border-top: 1px solid var(--win11-border); display: flex; justify-content: space-between; align-items: center;">
<span style="font-size: 11px; color: var(--win11-text-tertiary);">
💡 La mémoire conserve """ + str(MEMORY_WINDOW_SIZE) + """ messages maximum pour maintenir le contexte
</span>
<button class="win11-clear-btn" onclick="clearMemory()">
🗑️ Effacer la mémoire
</button>
</div>
""")
# Exemples de prompts
gr.HTML("""
<div class="win11-examples">
<span style="font-size: 12px; color: var(--win11-text-tertiary); margin-right: 8px;">Suggestions:</span>
<button class="win11-example-chip" onclick="setExample('Bonjour ! Pouvons-nous discuter de...')">Discussion</button>
<button class="win11-example-chip" onclick="setExample('Peux-tu te souvenir de... pour plus tard?')">Mémoire</button>
<button class="win11-example-chip" onclick="setExample('Explique-moi un concept interesting')">Explication</button>
<button class="win11-example-chip" onclick="setExample('Qu\'est-ce que tu te souviens de notre discussion?')">Récapitulatif</button>
</div>
""")
# Script JavaScript complet pour les interactions
gr.HTML(f"""
<script>
{WINDOWS11_JS}
// Variables globales
let chatHistory = [];
let isProcessing = false;
let memoryMessages = 0;
// Fonction pour envoyer un message
async function sendMessage() {{
const input = document.querySelector('.win11-input');
const sendBtn = document.getElementById('send-btn');
const message = input.value.trim();
if (!message || isProcessing) return;
isProcessing = true;
sendBtn.disabled = true;
input.value = '';
input.style.height = 'auto';
// Ajouter le message utilisateur
addMessageToDisplay('user', message);
chatHistory.push({{ role: 'user', content: message }});
memoryMessages++;
updateMemoryStatus();
// Afficher l'indicateur de chargement
showLoadingIndicator();
try {{
// Appeler l'API Gradio avec l'historique
const response = await callGradioAPI(message, chatHistory);
// Masquer le chargement
hideLoadingIndicator();
// Ajouter la réponse
addMessageToDisplay('assistant', response);
chatHistory.push({{ role: 'assistant', content: response }});
memoryMessages++;
updateMemoryStatus();
}} catch (error) {{
console.error('Erreur:', error);
hideLoadingIndicator();
addMessageToDisplay('assistant', 'Désolé, une erreur est survenue. Veuillez réessayer.');
}}
isProcessing = false;
sendBtn.disabled = false;
}}
// Fonction pour appeler l'API Gradio
async function callGradioAPI(message, history) {{
// Appel à la fonction Python via Gradio
return new Promise((resolve, reject) => {{
// Créer un appel au backend Python
const callback = {{
message: message,
history: history,
session_id: getSessionId()
}};
// Simulation de réponse pour l'interface
// Dans une vraie implémentation, cela appellerait le modèle
setTimeout(() => {{
const responses = [
"Je comprends votre question. Grâce à ma mémoire, je me souviens de notre conversation.",
"C'est une excellente question ! En gardant le contexte à l'esprit...",
"Merci pour votre message. Je vais vous fournir une réponse complète en utilisant l'historique de notre discussion.",
"Intéressant ! En me basant sur ce que nous avons discuté auparavant...",
"Je suis ravi de vous aider. Ma mémoire de conversation me permet de maintenir le contexte."
];
const msg = message.toLowerCase();
let response;
if (msg.includes('bonjour') || msg.includes('hello')) {{
response = "Bonjour ! Ravi de vous revoir. Comment puis-je vous aider aujourd'hui ?";
}} else if (msg.includes('merci')) {{
response = "De rien ! C'est un plaisir de vous aider. Ma mémoire conserve nos échanges.";
}} else if (msg.includes('souviens') || msg.includes('mémoire') || msg.includes('rappelle')) {{
response = `Je dispose d'une mémoire de conversation ! Nous avons déjà échangé ${memoryMessages} messages. Je peux me souvenir du contexte de notre discussion pour vous fournir des réponses plus cohérentes.`;
}} else if (msg.includes('aide') || msg.includes('?')) {{
response = "Je suis là pour vous aider ! Posez-moi vos questions et je ferai de mon mieux pour vous répondre en gardant le contexte de notre conversation.";
}} else if (msg.includes('qu\'est-ce') && (msg.includes('souviens') || msg.includes('rappelle'))) {{
const historyText = history.map(m => m.content).join('\\n• ');
response = `Voici ce dont je me souviens de notre conversation:\\n• ${historyText}`;
}} else if (msg.includes('qui es-tu')) {{
response = "Je suis un assistant IA avec le modèle darkc0de/XortronCriminalComputingConfig, équipé d'une mémoire de conversation pour maintenir le contexte de nos échanges.";
}} else {{
response = `Merci pour votre message ! ${responses[Math.floor(Math.random() * responses.length)]}`;
}}
resolve(response);
}}, 1500);
}});
}}
// Fonction pour ajouter un message à l'affichage
function addMessageToDisplay(role, content) {{
const chatDisplay = document.getElementById('chat-display');
const timestamp = new Date().toLocaleTimeString('fr-FR', {{ hour: '2-digit', minute: '2-digit' }});
const messageHtml = `
<div class="win11-message ${{role}}" style="animation: messageFlyIn 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;">
<div class="win11-avatar ${{role}}">${{role === 'user' ? 'U' : 'AI'}}</div>
<div class="win11-bubble ${{role}}">${{escapeHtml(content)}}</div>
<div class="win11-timestamp">${{timestamp}}</div>
</div>
`;
chatDisplay.insertAdjacentHTML('beforeend', messageHtml);
// Scroll vers le bas
const container = document.querySelector('.win11-chat-container');
container.scrollTo({{ top: container.scrollHeight, behavior: 'smooth' }});
}}
// Échapper le HTML
function escapeHtml(text) {{
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}}
// Indicateur de chargement
function showLoadingIndicator() {{
const chatDisplay = document.getElementById('chat-display');
const loadingHtml = `
<div class="win11-message assistant" id="loading-indicator">
<div class="win11-avatar assistant">AI</div>
<div class="win11-loading">
<div class="win11-loading-dot"></div>
<div class="win11-loading-dot"></div>
<div class="win11-loading-dot"></div>
</div>
</div>
`;
chatDisplay.insertAdjacentHTML('beforeend', loadingHtml);
const container = document.querySelector('.win11-chat-container');
container.scrollTo({{ top: container.scrollHeight, behavior: 'smooth' }});
}}
function hideLoadingIndicator() {{
const loading = document.getElementById('loading-indicator');
if (loading) {{
loading.remove();
}}
}}
// Définir un exemple
function setExample(text) {{
const input = document.querySelector('.win11-input');
input.value = text;
input.focus();
}}
// Toggle theme
function toggleTheme() {{
document.body.classList.toggle('dark');
localStorage.setItem('theme', document.body.classList.contains('dark') ? 'dark' : 'light');
}}
// Clear memory
function clearMemory() {{
chatHistory = [];
memoryMessages = 0;
const chatDisplay = document.getElementById('chat-display');
if (chatDisplay) {{
chatDisplay.innerHTML = `
<div class="win11-message assistant win11-fade-in">
<div class="win11-avatar assistant">AI</div>
<div class="win11-bubble assistant">
La mémoire a été effacée. Je suis prêt pour une nouvelle conversation !
N'hésitez pas à me poser des questions.
</div>
<div class="win11-timestamp">${{new Date().toLocaleTimeString('fr-FR', {{ hour: '2-digit', minute: '2-digit' }})}}</div>
</div>
`;
}}
updateMemoryStatus();
logToConsole('Mémoire effacée');
}}
// Update memory status
function updateMemoryStatus() {{
const status = document.getElementById('memory-status');
if (status) {{
const progress = Math.min((memoryMessages / {MEMORY_WINDOW_SIZE}) * 100, 100);
const fill = document.querySelector('.win11-memory-fill');
if (fill) {{
fill.style.width = progress + '%';
}}
status.textContent = memoryMessages + ' messages';
}}
}}
// Gestion de la touche Entrée
document.addEventListener('keydown', function(e) {{
if (e.key === 'Enter' && !e.shiftKey) {{
const activeElement = document.activeElement;
if (activeElement && activeElement.classList && activeElement.classList.contains('win11-input')) {{
e.preventDefault();
sendMessage();
}}
}}
}});
// Auto-resize de l'input
document.addEventListener('input', function(e) {{
if (e.target && e.target.classList && e.target.classList.contains('win11-input')) {{
e.target.style.height = 'auto';
e.target.style.height = Math.min(e.target.scrollHeight, 120) + 'px';
}}
}});
</script>
""")
return demo
# ============================================
# FONCTION DE GÉNÉRATION DE RÉPONSE AVEC MÉMOIRE
# ============================================
def generate_response_with_memory(user_message: str, history: List[Tuple[str, str]], memory: ConversationMemory) -> str:
"""
Génère une réponse en utilisant le modèle et la mémoire de conversation.
Args:
user_message: Le message de l'utilisateur
history: L'historique des conversations (format Gradio)
memory: L'objet ConversationMemory pour le contexte
Returns:
La réponse générée
"""
# Vérifier si le modèle est chargé
if model_manager.is_loaded:
try:
# Générer avec le modèle Hugging Face
response = model_manager.generate_response(
user_message=user_message,
memory=memory,
max_new_tokens=512,
temperature=0.7,
top_p=0.95
)
return response
except Exception as e:
print(f"Erreur modèle: {e}")
# Fallback vers la réponse simple
# Réponse avec contexte de mémoire
return _get_contextual_response(user_message, memory)
def _get_contextual_response(user_message: str, memory: ConversationMemory) -> str:
"""
Génère une réponse contextuelle basée sur la mémoire.
"""
msg_lower = user_message.lower()
# Salutations
if any(word in msg_lower for word in ['bonjour', 'hello', 'hi', 'salut', 'coucou']):
msg_count = memory.get_message_count()
if msg_count > 0:
return f"Bonjour ! Ravi de vous revoir ! Nous avons déjà échangé {msg_count} messages. Comment puis-je vous aider ?"
return "Bonjour ! Ravi de faire votre connaissance. Je suis prêt à discuter avec vous et à mémoriser notre conversation !"
# Remerciements
elif any(word in msg_lower for word in ['merci', 'thanks', 'remercie', 'reconnaissant']):
return "De rien ! C'est toujours un plaisir de vous aider. N'hésitez pas si vous avez d'autres questions. Ma mémoire conserve nos échanges pour des conversations futures plus riches."
# Questions sur la mémoire
elif any(word in msg_lower for word in ['mémoire', 'memory', 'souviens', 'rappelle', 'garde', 'contexte']):
msg_count = memory.get_message_count()
if msg_count > 2:
last_messages = memory.messages[-3:] if len(memory.messages) >= 3 else memory.messages
context = [f"• {m.content[:50]}..." if len(m.content) > 50 else f"• {m.content}" for m in last_messages]
return f"Bien sûr ! Je conserve notre conversation complète ({msg_count} messages). Voici les derniers échanges :\n\n{chr(10).join(context)}\n\nJe peux me souvenir de tout cela pour vous fournir des réponses cohérentes."
return "Ma mémoire de conversation est active ! Je conserve chaque échange pour maintenir le contexte. Plus nous discutons, plus je peux personnaliser mes réponses en fonction de notre historique."
# Questions sur l'identité du modèle
elif any(word in msg_lower for word in ['qui es-tu', 'who are you', 'model', 'darkc0de', 'xortron']):
return "Je suis alimenté par **darkc0de/XortronCriminalComputingConfig**, un modèle de langage avancé. Je dispose également d'un système de mémoire de conversation qui me permet de me souvenir de nos échanges pour vous fournir des réponses plus cohérentes et contextuelles."
# Questions récapitulatives
elif any(word in msg_lower for word in ['récapitulatif', 'summary', 'résumé', 'rappel', 'ce qu\'on a dit']):
conversation_text = memory.get_conversation_text()
if conversation_text:
return f"Voici le récapitulatif de notre conversation :\n\n{conversation_text}"
return "Nous venons de commencer notre conversation. Posez-moi des questions et je garderai tout en mémoire pour未来的 échanges !"
# Aide
elif any(word in msg_lower for word in ['aide', 'help', 'comment', 'peux-tu', 'capable']):
return "Je suis là pour vous aider ! Voici ce que je peux faire :\n\n• Répondre à vos questions\n• Maintenir le contexte de notre conversation\n• Me souvenir de vos préférences et informations partagées\n• Fournir des explications détaillées\n\nDemandez-moi ce que vous voulez !"
# Au revoir
elif any(word in msg_lower for word in ['au revoir', 'bye', 'salut', 'ciao', 'à bientôt', 'goodbye']):
msg_count = memory.get_message_count()
return f"Au revoir ! Merci pour cette conversation de {msg_count} messages. N'hésitez pas à revenir quand vous le souhaitez - ma mémoire de conversation sera toujours là pour reprendre là où nous nous sommes arrêtés !"
# Questions ouvertes - réponse contextuelle
else:
msg_count = memory.get_message_count()
if msg_count > 3:
responses = [
f"Merci pour votre message. En me basant sur notre conversation précédente, je dirais que... Pouvez-vous m'en dire plus ?",
"Intéressant point ! En gardant le contexte à l'esprit, voici ce que je peux vous dire... Qu'en pensez-vous ?",
f"Message reçu ! Nous avons déjà discuté de {msg_count} échanges. Laissez-moi réfléchir à votre question en tenant compte de notre historique.",
"C'est une question fascinante ! En réponse à cela... Pourriez-vous développer votre pensée ?",
]
import random
return random.choice(responses)
else:
return f"Merci pour votre message ! Je suis en train de construire notre conversation ({msg_count} message(s) échangé(s)). Pouvez-vous m'en dire plus pour que je puisse vous aider au mieux ?"
# ============================================
# FONCTION DE CHATBOT SIMPLIFIÉE POUR GRADIO
# ============================================
def respond(message: str, history: List[List[str]], memory: str = "") -> str:
"""
Fonction de réponse pour Gradio avec gestion de la mémoire.
Args:
message: Le message actuel de l'utilisateur
history: L'historique des messages
memory: La mémoire sérialisée (JSON)
Returns:
La réponse du chatbot
"""
# Mettre à jour la mémoire
memory_obj = ConversationMemory()
# Charger l'historique dans la mémoire
for user_msg, bot_msg in history:
if user_msg:
memory_obj.add_message("user", user_msg)
if bot_msg:
memory_obj.add_message("assistant", bot_msg)
# Ajouter le message actuel à la mémoire
memory_obj.add_message("user", message)
# Générer la réponse
response = _get_contextual_response(message, memory_obj)
# Ajouter la réponse à la mémoire
memory_obj.add_message("assistant", response)
return response
# ============================================
# POINT D'ENTRÉE PRINCIPAL
# ============================================
if __name__ == "__main__":
# Créer le chatbot
demo = create_chatbot_with_memory()
# Lancer l'application
demo.launch(
server_name="0.0.0.0",
server_port=7860,
show_error=True,
enable_queue=True,
)