Bienvenue sur CFTPfr.fr › Forums › scripts › chat_ia.py
- Ce sujet est vide.
-
AuteurMessages
-
25.07.2026 à 16h01 #208
Mario Da Conceicao
Maître des clésSauvegarde de travail — version du 25/07/2026 (Windows, avant portage OS-MIRE)
« `python
import tkinter as tk
from tkinter import scrolledtext, simpledialog, messagebox
from pathlib import Path
import json
import os
import re
import webbrowser
import subprocess
import ast
import operator as op
import importlib.util
import sqlite3
import hashlib
import secrets
import random
from datetime import datetime
from difflib import get_close_matchesBASE_DIR = Path(r »D:\MIRA_Project\mira_ai »)
PLUGINS_DIR = BASE_DIR / « plugins »
CONFIG_FILE = BASE_DIR / « config.json »
KNOWLEDGE_FILE = BASE_DIR / « connaissances.json »
HISTORY_FILE = BASE_DIR / « history » / « questions_non_comprises.txt »
DB_FILE = BASE_DIR / « memory.db »SAFE_OPERATORS = {
ast.Add: op.add,
ast.Sub: op.sub,
ast.Mult: op.mul,
ast.Div: op.truediv,
ast.FloorDiv: op.floordiv,
ast.Mod: op.mod,
ast.Pow: op.pow,
ast.UAdd: op.pos,
ast.USub: op.neg,
}def hacher_mot_de_passe(mot_de_passe: str) -> str:
salt = secrets.token_hex(16)
h = hashlib.sha256((mot_de_passe + salt).encode(« utf-8″)).hexdigest()
return f »{salt}:{h} »def verifier_mot_de_passe(mot_de_passe: str, stocke: str) -> bool:
try:
salt, h = stocke.split(« : »)
return hashlib.sha256((mot_de_passe + salt).encode(« utf-8 »)).hexdigest() == h
except Exception:
return Falseclass AutoImprover:
def __init__(self, db_file=DB_FILE):
self.db_file = Path(db_file)
self.db_file.parent.mkdir(parents=True, exist_ok=True)
self.conn = sqlite3.connect(self.db_file)
self.conn.execute(« » »
CREATE TABLE IF NOT EXISTS messages(
id INTEGER PRIMARY KEY AUTOINCREMENT,
ts TEXT,
sender TEXT,
content TEXT
)
« » »)
self.conn.execute(« » »
CREATE TABLE IF NOT EXISTS needs(
id INTEGER PRIMARY KEY AUTOINCREMENT,
ts TEXT,
message TEXT,
domain TEXT,
priority INTEGER,
suggestion TEXT,
done INTEGER DEFAULT 0
)
« » »)
self.conn.execute(« » »
CREATE TABLE IF NOT EXISTS improvements(
id INTEGER PRIMARY KEY AUTOINCREMENT,
ts TEXT,
title TEXT,
description TEXT,
score INTEGER DEFAULT 0,
applied INTEGER DEFAULT 0
)
« » »)
self.conn.commit()def log_message(self, sender, content):
self.conn.execute(
« INSERT INTO messages(ts, sender, content) VALUES(?,?,?) »,
(datetime.now().isoformat(timespec= »seconds »), sender, content)
)
self.conn.commit()def classify_message(self, message):
msg = message.lower()
if any(w in msg for w in [« code », « python », « script », « corrige »]):
return « code », 4, « Ajouter une analyse de code plus poussée »
if any(w in msg for w in [« fichier », « dossier », « ouvrir », « lire »]):
return « fichiers », 3, « Améliorer l’ouverture et la prévisualisation des fichiers »
if any(w in msg for w in [« recherche », « web », « internet », « trouve »]):
return « recherche », 3, « Ajouter un moteur de recherche et de résumé »
if any(w in msg for w in [« plugin », « module », « extension », « ajouter »]):
return « extensions », 5, « Créer un système d’installation automatique de plugins »
if any(w in msg for w in [« erreur », « bug », « panne », « crash », « ne marche pas »]):
return « diagnostic », 4, « Créer un module de diagnostic plus précis »
if any(w in msg for w in [« mémoire », « souviens », « historique »]):
return « memoire », 4, « Ajouter une mémoire conversationnelle plus intelligente »
return « general », 1, « Améliorer la compréhension générale »def analyze_and_learn(self, message):
domain, priority, suggestion = self.classify_message(message)
self.conn.execute(
« INSERT INTO needs(ts, message, domain, priority, suggestion) VALUES(?,?,?,?,?) »,
(datetime.now().isoformat(timespec= »seconds »), message, domain, priority, suggestion)
)
self.conn.execute(
« INSERT INTO improvements(ts, title, description, score, applied) VALUES(?,?,?,?,0) »,
(datetime.now().isoformat(timespec= »seconds »), f »Besoin détecté: {domain} », message, 1)
)
self.conn.commit()
return domaindef top_needs(self, limit=5):
cur = self.conn.execute(« » »
SELECT domain, suggestion, COUNT(*) as cnt, MAX(priority) as maxp
FROM needs
WHERE done=0
GROUP BY domain, suggestion
ORDER BY cnt DESC, maxp DESC
LIMIT ?
« » », (limit,))
return cur.fetchall()def suggest_next_update(self):
rows = self.top_needs()
if not rows:
return « Je n’ai pas encore assez de données pour proposer une amélioration. »
lines = [« Améliorations suggérées : »]
for domain, suggestion, cnt, maxp in rows:
lines.append(f »- {domain} ({cnt} demandes, priorité {maxp}) : {suggestion} »)
return « \n ».join(lines)class PluginManager:
def __init__(self):
self.plugins = {}
PLUGINS_DIR.mkdir(parents=True, exist_ok=True)def charger_plugins(self):
self.plugins = {}
for file in PLUGINS_DIR.glob(« *.py »):
if file.name.startswith(« _ »):
continue
try:
spec = importlib.util.spec_from_file_location(file.stem, file)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
if hasattr(module, « INTENTION ») and hasattr(module, « executer »):
self.plugins[module.INTENTION] = module
except Exception as e:
print(f »Erreur plugin {file.name}: {e} »)def executer(self, intention, message, app):
module = self.plugins.get(intention)
if module:
try:
return module.executer(message, app)
except Exception as e:
return f »Erreur plugin {intention}: {e} »
return Noneclass ChatIA:
def __init__(self):
self.fenetre = tk.Tk()
self.fenetre.title(« MIRA_IA – Assistant Intelligent »)
self.fenetre.geometry(« 900×700 »)self.auto = AutoImprover()
self.plugin_manager = PluginManager()
self.plugin_manager.charger_plugins()
self.historique = []
self.contexte = {« derniere_intention »: None, « derniere_question »: None}self.connaissances = self.charger_connaissances()
self.chat_area = scrolledtext.ScrolledText(
self.fenetre, width=95, height=32, wrap=tk.WORD,
font=(« Segoe UI », 10), bg= »#f8f9fa »
)
self.chat_area.pack(padx=10, pady=10, fill=tk.BOTH, expand=True)self.append_system(« Bonjour ! Je suis MIRA_IA. Je peux apprendre de vos demandes et proposer des améliorations. »)
panel = tk.Frame(self.fenetre)
panel.pack(fill=tk.X, padx=10, pady=6)self.entry = tk.Entry(panel, font=(« Segoe UI », 10))
self.entry.pack(side=tk.LEFT, fill=tk.X, expand=True)
self.entry.bind(«« , self.send) tk.Button(panel, text= »Envoyer », command=self.send, bg= »#4CAF50″, fg= »white »).pack(side=tk.LEFT, padx=5)
tk.Button(panel, text= »Suggestions », command=self.show_suggestions).pack(side=tk.LEFT, padx=5)
tk.Button(panel, text= »Plugins », command=self.reload_plugins).pack(side=tk.LEFT, padx=5)
tk.Button(panel, text= »Historique », command=self.export_history).pack(side=tk.LEFT, padx=5)def charger_connaissances(self):
default = {
« salutations »: {« mots_cles »: [« bonjour », « salut », « hello », « hey », « coucou »], « reponses »: [« Bonjour ! », « Salut ! »]},
« presentation »: {« mots_cles »: [« qui es-tu », « ton nom », « présente toi »], « reponses »: [« Je suis MIRA_IA, un assistant local modulaire. »]},
« remerciements »: {« mots_cles »: [« merci », « super », « génial »], « reponses »: [« Avec plaisir ! », « Je vous en prie ! »]},
« au_revoir »: {« mots_cles »: [« au revoir », « bye », « exit », « quit »], « reponses »: [« Au revoir ! »], « action »: « quitter »},
« aide »: {« mots_cles »: [« aide », « commandes », « que peux-tu faire »], « reponses »: [« Je peux calculer, rechercher, ouvrir des fichiers, diagnostiquer et charger des plugins. »]},
« calcul »: {« mots_cles »: [« calcule », « calcul », « combien font », « + », « -« , « * », « / »], « reponses »: [« Donnez une opération. »], « action »: « calculer »},
« recherche »: {« mots_cles »: [« recherche », « cherche », « trouve », « web », « internet »], « reponses »: [« Quel sujet ? »], « action »: « recherche_web »},
« fichier »: {« mots_cles »: [« ouvre », « lire », « fichier », « dossier »], « reponses »: [« Quel fichier ? »], « action »: « ouvrir_fichier »},
« probleme »: {« mots_cles »: [« erreur », « bug », « problème », « ne marche pas »], « reponses »: [« Décrivez le problème. »], « action »: « resoudre_probleme »},
}
if KNOWLEDGE_FILE.exists():
try:
with open(KNOWLEDGE_FILE, « r », encoding= »utf-8″) as f:
return {**default, **json.load(f)}
except Exception:
pass
KNOWLEDGE_FILE.parent.mkdir(parents=True, exist_ok=True)
with open(KNOWLEDGE_FILE, « w », encoding= »utf-8″) as f:
json.dump(default, f, indent=4, ensure_ascii=False)
return defaultdef append(self, sender, message):
self.chat_area.config(state=tk.NORMAL)
self.chat_area.insert(tk.END, f »{datetime.now().strftime(‘%H:%M’)} | {sender}: {message}\n »)
self.chat_area.config(state=tk.DISABLED)
self.chat_area.see(tk.END)def append_system(self, message):
self.append(« MIRA_IA », message)def normalize(self, text):
return re.sub(r »\s+ », » « , text.lower().strip())def send(self, event=None):
msg = self.entry.get().strip()
if not msg:
return
self.entry.delete(0, tk.END)
self.append(« Toi », msg)
self.historique.append((datetime.now().isoformat(timespec= »seconds »), « Toi », msg))
self.auto.log_message(« Toi », msg)
self.auto.analyze_and_learn(msg)
response = self.generate(msg)
self.append_system(response)
self.historique.append((datetime.now().isoformat(timespec= »seconds »), « MIRA_IA », response))
self.auto.log_message(« MIRA_IA », response)def generate(self, message):
msg = self.normalize(message)if self.contexte[« derniere_intention »] == « recherche » and msg not in [« oui », « non »]:
return self.search_web(message)intent = self.detect_intent(message)
if intent:
info = self.connaissances[intent]
self.contexte[« derniere_intention »] = intent
self.contexte[« derniere_question »] = message
action = info.get(« action »)
if action == « quitter »:
self.fenetre.after(100, self.fenetre.destroy)
return random.choice(info.get(« reponses », [« Au revoir ! »]))
if action:
return self.execute_action(action, message)
return random.choice(info.get(« reponses », [« Je ne sais pas répondre. »]))plugin_intent = self.detect_plugin_intent(message)
if plugin_intent:
rep = self.plugin_manager.executer(plugin_intent, message, self)
if rep:
return repself.save_unknown(message)
return self.fallback_answer(message)def detect_intent(self, message):
msg = self.normalize(message)
best_intent = None
best_score = 0
for intent, info in self.connaissances.items():
score = 0
for kw in info.get(« mots_cles », []):
if kw in msg:
score += 2
if score > best_score:
best_score = score
best_intent = intent
return best_intentdef detect_plugin_intent(self, message):
msg = self.normalize(message)
for intent in self.plugin_manager.plugins:
if intent in msg:
return intent
return Nonedef execute_action(self, action, message):
if action == « calculer »:
return self.calculate(message)
if action == « recherche_web »:
return self.search_web(message)
if action == « ouvrir_fichier »:
return self.open_file(message)
if action == « resoudre_probleme »:
return self.solve_problem(message)
return « Action non reconnue. »def calculate(self, message):
expr = self.extract_expression(message)
if not expr:
expr = simpledialog.askstring(« Calcul », « Entrez une expression: ») or « »
if not expr:
return « Aucune expression fournie. »
try:
node = ast.parse(expr, mode= »eval »)
return f »Résultat : {self.eval_node(node.body)} »
except Exception:
return « Calcul invalide. »def extract_expression(self, message):
msg = self.normalize(message)
msg = re.sub(r »^(calcule|calcul|combien font|résous|resous)\s* », « », msg)
return msg.strip()def eval_node(self, node):
if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)):
return node.value
if isinstance(node, ast.BinOp) and type(node.op) in SAFE_OPERATORS:
return SAFE_OPERATORS[type(node.op)](self.eval_node(node.left), self.eval_node(node.right))
if isinstance(node, ast.UnaryOp) and type(node.op) in SAFE_OPERATORS:
return SAFE_OPERATORS[type(node.op)](self.eval_node(node.operand))
raise ValueError(« Expression interdite »)def search_web(self, message):
msg = self.normalize(message)
for word in [« recherche », « cherche », « trouve », « web », « internet », « google »]:
msg = msg.replace(word, « »)
subject = msg.strip()
if not subject:
subject = simpledialog.askstring(« Recherche web », « Quel sujet ? ») or « »
if not subject:
return « Recherche annulée. »
webbrowser.open(« https://www.google.com/search?q= » + subject.replace( » « , « + »))
self.contexte[« derniere_intention »] = « recherche »
return f »Recherche lancée pour : {subject} »def open_file(self, message):
path_text = re.sub(r »\b(ouvre|lire|fichier|dossier)\b », « », message, flags=re.I).strip().strip(‘ »‘).strip(« ‘ »)
if not path_text:
path_text = simpledialog.askstring(« Fichier », « Chemin complet du fichier: ») or « »
if not path_text:
return « Aucun fichier indiqué. »
p = Path(path_text)
if not p.is_absolute():
p = Path(r »D:\MIRA_Project ») / path_text
if not p.exists():
return f »Le fichier n’existe pas : {p} »
try:
if p.suffix.lower() in [« .txt », « .py », « .json », « .md », « .log », « .csv »]:
return « Voici un aperçu:\n » + « \n ».join(p.read_text(encoding= »utf-8″, errors= »replace »).splitlines()[:30])
if os.name == « nt »:
os.startfile(str(p))
else:
subprocess.run([« xdg-open », str(p)])
return f »Fichier ouvert : {p} »
except Exception as e:
return f »Erreur fichier : {e} »def solve_problem(self, message):
msg = self.normalize(message)
if « python » in msg:
return « Python : envoyez l’erreur exacte, le code et le résultat attendu. »
if « mira_os » in msg:
return « MIRA_OS : vérifiez les chemins, les dépendances et les logs. »
return « Décrivez le contexte, le message d’erreur et ce que vous avez essayé. »def fallback_answer(self, message):
all_keys = list(self.connaissances.keys())
close = get_close_matches(self.normalize(message), all_keys, n=1, cutoff=0.4)
if close:
key = close[0]
info = self.connaissances[key]
return random.choice(info.get(« reponses », [« Je ne sais pas. »]))
return f »Je n’ai pas compris : ‘{message}’. Essayez par exemple ‘Calcule 5+3’ ou ‘Recherche Python’. »def save_unknown(self, message):
HISTORY_FILE.parent.mkdir(parents=True, exist_ok=True)
with open(HISTORY_FILE, « a », encoding= »utf-8″) as f:
f.write(f »{datetime.now().isoformat(timespec=’seconds’)} | {message}\n »)def show_suggestions(self):
msg = self.auto.suggest_next_update()
messagebox.showinfo(« Suggestions d’évolution », msg)def reload_plugins(self):
self.plugin_manager.charger_plugins()
messagebox.showinfo(« Plugins », « Plugins rechargés. »)def export_history(self):
out = BASE_DIR / f »historique_{datetime.now().strftime(‘%Y%m%d_%H%M%S’)}.txt »
with open(out, « w », encoding= »utf-8″) as f:
for ts, sender, msg in self.historique:
f.write(f »{ts} | {sender}: {msg}\n »)
self.append_system(f »Historique exporté : {out.name} »)def run(self):
self.fenetre.mainloop()if __name__ == « __main__ »:
ChatIA().run()
« ` -
AuteurMessages
- Vous devez être connecté pour répondre à ce sujet.