From f6aaf15a336c53228963e155dcb8aff933447201 Mon Sep 17 00:00:00 2001 From: Ladebeze66 Date: Thu, 27 Mar 2025 18:40:52 +0100 Subject: [PATCH] first commit --- .vscode/settings.json | 3 + README.md | 297 ++++++ agents/__pycache__/roles.cpython-312.pyc | Bin 0 -> 3071 bytes agents/__pycache__/roles.cpython-313.pyc | Bin 0 -> 3073 bytes agents/roles.py | 129 +++ api-server.bat | 43 + api_server.py | 251 +++++ chat.py | 76 ++ chat_gui.py | 8 + chat_history/python_2025-03-25_20-50-30.json | 20 + chat_history/python_2025-03-25_20-55-35.json | 25 + config/mistral7b_config.yaml | 0 config_wsl_memoire.md | 102 ++ core/__pycache__/base_llm.cpython-312.pyc | Bin 0 -> 5264 bytes core/__pycache__/base_llm.cpython-313.pyc | Bin 0 -> 5365 bytes .../codellama13b_python.cpython-312.pyc | Bin 0 -> 1793 bytes .../codellama13b_python.cpython-313.pyc | Bin 0 -> 1848 bytes core/__pycache__/factory.cpython-312.pyc | Bin 0 -> 2256 bytes core/__pycache__/factory.cpython-313.pyc | Bin 0 -> 2308 bytes core/__pycache__/llama2_13b.cpython-312.pyc | Bin 0 -> 1741 bytes core/__pycache__/llama2_13b.cpython-313.pyc | Bin 0 -> 1796 bytes core/__pycache__/mistral7b.cpython-312.pyc | Bin 0 -> 1741 bytes core/__pycache__/mistral7b.cpython-313.pyc | Bin 0 -> 1796 bytes core/base_llm.py | 68 ++ core/codellama13b_python.py | 37 + core/factory.py | 53 + core/llama2_13b.py | 37 + core/mistral7b.py | 37 + cursor_integration.md | 343 ++++++ examples/agent_demo.py | 92 ++ examples/model_comparison.py | 52 + installation_ollama_disques_separes.md | 212 ++++ llm_lab_installation_guide.md | 242 +++++ llm_lab_integration_guide.md | 248 +++++ log_index.md | 4 + logs/api_server.log | 17 + logs/codellama | 0 ...€ş13b-python_2025-03-25_20-55-35_0b82fbbf.md | 42 + ...rallatest_2025-03-25_21-00-17_aa61f01e.md | 28 + monitor.py | 8 + obsidian_integration.md | 467 +++++++++ optimisation_modelfile.md | 306 ++++++ optimize_ollama.bat | 94 ++ requirements.txt | 16 + run-api.ps1 | 69 ++ run.bat | 79 ++ setup_env.bat | 107 ++ setup_env.sh | 85 ++ test_installation.bat | 98 ++ tests/test_multi_agent_fixed_params.py | 18 + tests/test_multi_agent_grid.py | 19 + tests/test_new_models.py | 34 + tests/test_single_agent.py | 13 + tests/test_single_agent_param_grid.py | 19 + update_monitor_for_ollama_path.md | 292 ++++++ .../__pycache__/agent_manager.cpython-312.pyc | Bin 0 -> 2394 bytes .../__pycache__/agent_manager.cpython-313.pyc | Bin 0 -> 2315 bytes utils/__pycache__/chat_ui.cpython-312.pyc | Bin 0 -> 49637 bytes .../parameter_tester.cpython-312.pyc | Bin 0 -> 2415 bytes .../system_monitor.cpython-312.pyc | Bin 0 -> 32135 bytes .../system_monitor.cpython-313.pyc | Bin 0 -> 31743 bytes utils/agent_manager.py | 59 ++ utils/chat_ui.py | 980 ++++++++++++++++++ utils/parameter_tester.py | 51 + utils/system_monitor.py | 534 ++++++++++ windows_migration_checklist.md | 85 ++ 66 files changed, 5899 insertions(+) create mode 100644 .vscode/settings.json create mode 100644 README.md create mode 100644 agents/__pycache__/roles.cpython-312.pyc create mode 100644 agents/__pycache__/roles.cpython-313.pyc create mode 100644 agents/roles.py create mode 100644 api-server.bat create mode 100644 api_server.py create mode 100644 chat.py create mode 100644 chat_gui.py create mode 100644 chat_history/python_2025-03-25_20-50-30.json create mode 100644 chat_history/python_2025-03-25_20-55-35.json create mode 100644 config/mistral7b_config.yaml create mode 100644 config_wsl_memoire.md create mode 100644 core/__pycache__/base_llm.cpython-312.pyc create mode 100644 core/__pycache__/base_llm.cpython-313.pyc create mode 100644 core/__pycache__/codellama13b_python.cpython-312.pyc create mode 100644 core/__pycache__/codellama13b_python.cpython-313.pyc create mode 100644 core/__pycache__/factory.cpython-312.pyc create mode 100644 core/__pycache__/factory.cpython-313.pyc create mode 100644 core/__pycache__/llama2_13b.cpython-312.pyc create mode 100644 core/__pycache__/llama2_13b.cpython-313.pyc create mode 100644 core/__pycache__/mistral7b.cpython-312.pyc create mode 100644 core/__pycache__/mistral7b.cpython-313.pyc create mode 100644 core/base_llm.py create mode 100644 core/codellama13b_python.py create mode 100644 core/factory.py create mode 100644 core/llama2_13b.py create mode 100644 core/mistral7b.py create mode 100644 cursor_integration.md create mode 100644 examples/agent_demo.py create mode 100644 examples/model_comparison.py create mode 100644 installation_ollama_disques_separes.md create mode 100644 llm_lab_installation_guide.md create mode 100644 llm_lab_integration_guide.md create mode 100644 log_index.md create mode 100644 logs/api_server.log create mode 100644 logs/codellama create mode 100644 logs/codellama13b-python_2025-03-25_20-55-35_0b82fbbf.md create mode 100644 logs/mistrallatest_2025-03-25_21-00-17_aa61f01e.md create mode 100644 monitor.py create mode 100644 obsidian_integration.md create mode 100644 optimisation_modelfile.md create mode 100644 optimize_ollama.bat create mode 100644 requirements.txt create mode 100644 run-api.ps1 create mode 100644 run.bat create mode 100644 setup_env.bat create mode 100644 setup_env.sh create mode 100644 test_installation.bat create mode 100644 tests/test_multi_agent_fixed_params.py create mode 100644 tests/test_multi_agent_grid.py create mode 100644 tests/test_new_models.py create mode 100644 tests/test_single_agent.py create mode 100644 tests/test_single_agent_param_grid.py create mode 100644 update_monitor_for_ollama_path.md create mode 100644 utils/__pycache__/agent_manager.cpython-312.pyc create mode 100644 utils/__pycache__/agent_manager.cpython-313.pyc create mode 100644 utils/__pycache__/chat_ui.cpython-312.pyc create mode 100644 utils/__pycache__/parameter_tester.cpython-312.pyc create mode 100644 utils/__pycache__/system_monitor.cpython-312.pyc create mode 100644 utils/__pycache__/system_monitor.cpython-313.pyc create mode 100644 utils/agent_manager.py create mode 100644 utils/chat_ui.py create mode 100644 utils/parameter_tester.py create mode 100644 utils/system_monitor.py create mode 100644 windows_migration_checklist.md diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..8ed43a8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "commentTranslate.targetLanguage": "fr" +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..819c061 --- /dev/null +++ b/README.md @@ -0,0 +1,297 @@ +# LLM Lab + +Un laboratoire pour tester et comparer diffĂ©rents modèles de langage (LLM) via Ollama. + +## FonctionnalitĂ©s + +- **Modèles intĂ©grĂ©s** : Support pour Mistral 7B, CodeLlama 13B et Llama2 13B via Ollama +- **Agents spĂ©cialisĂ©s** : Agents configurĂ©s pour diffĂ©rentes tâches (code, documentation, gestion de projet...) +- **Interface graphique de chat** : Interface visuelle complète pour interagir avec les agents et personnaliser les paramètres +- **Moniteur système** : Interface graphique pour surveiller les ressources système, Ollama et GPU +- **Interface CLI** : Interface en ligne de commande simple pour interagir avec les agents + +## Modèles disponibles + +Ce projet prend en charge les modèles suivants : + +- **Mistral 7B** : `mistral:latest` via Ollama - pour des tests rapides et la documentation +- **CodeLlama 13B Python** : `codellama:13b-python` via Ollama - pour le dĂ©veloppement et le code +- **Llama2 13B** : `llama2:13b` via Ollama - pour la gestion de connaissances et de projet + +## Agents spĂ©cialisĂ©s + +- **cursor** : Agent de programmation (CodeLlama) pour Python, JavaScript et dĂ©veloppement web +- **obsidian** : Agent de gestion de connaissances (Llama2) pour l'organisation d'informations +- **test** : Agent de test rapide (Mistral) pour vĂ©rifier rapidement les fonctionnalitĂ©s +- **python** : Expert Python (CodeLlama) +- **webdev** : Expert dĂ©veloppement web (CodeLlama) +- **projectmanager** : Chef de projet (Llama2) +- **documentaliste** : SpĂ©cialiste en documentation (Mistral) + +## Installation + +### PrĂ©requis + +- Python 3.8+ +- [Ollama](https://ollama.ai/) installĂ© et en cours d'exĂ©cution sur `localhost:11434` +- Les modèles correspondants installĂ©s dans Ollama + +### Installation des modèles Ollama + +```bash +# Installation des modèles +ollama pull mistral:latest +ollama pull codellama:13b-python +ollama pull llama2:13b +``` + +### Configuration de l'environnement sous Windows + +Utilisez le script de configuration automatique pour Windows : + +```cmd +# ExĂ©cuter le script de configuration +setup_env.bat +``` + +Ce script va : +1. VĂ©rifier et installer les dĂ©pendances nĂ©cessaires +2. CrĂ©er un nouvel environnement virtuel Python +3. Installer toutes les dĂ©pendances requises (y compris les modules spĂ©cifiques Ă  Windows) +4. Configurer les scripts pour une utilisation facile + +### Configuration de l'environnement sous Linux + +Utilisez le script de configuration automatique pour Linux : + +```bash +# ExĂ©cuter le script de configuration +./setup_env.sh +``` + +### Installation manuelle (alternative) + +Si vous prĂ©fĂ©rez l'installation manuelle : + +#### Sous Windows : +```cmd +# CrĂ©er un environnement virtuel +python -m venv llmlab + +# Activer l'environnement virtuel +llmlab\Scripts\activate + +# Installer les dĂ©pendances +pip install -r requirements.txt +pip install wmi psutil requests pillow +``` + +#### Sous Linux : +```bash +# CrĂ©er un environnement virtuel +python3 -m venv llmlab + +# Activer l'environnement virtuel +source llmlab/bin/activate + +# Installer les dĂ©pendances +pip install -r requirements.txt + +# Installer tkinter si nĂ©cessaire (au niveau du système) +sudo apt install python3-tk +``` + +## Utilisation + +### Utilisation avec le script de lancement sous Windows + +```cmd +# Afficher l'aide +run.bat help + +# Lister les agents disponibles +run.bat list + +# Lancer l'interface graphique de chat +run.bat gui + +# Lancer le chat en ligne de commande avec un agent spĂ©cifique +run.bat chat cursor # Agent de programmation +run.bat chat obsidian # Agent de gestion de connaissances +run.bat chat test # Agent de test rapide + +# Lancer le moniteur système +run.bat monitor +``` + +### Utilisation avec le script de lancement sous Linux + +```bash +# Afficher l'aide +./run.sh help + +# Lister les agents disponibles +./run.sh list + +# Lancer l'interface graphique de chat +./run.sh gui + +# Lancer le chat en ligne de commande avec un agent spĂ©cifique +./run.sh chat cursor # Agent de programmation +./run.sh chat obsidian # Agent de gestion de connaissances +./run.sh chat test # Agent de test rapide + +# Lancer le moniteur système +./run.sh monitor +``` + +### Utilisation directe + +Si vous prĂ©fĂ©rez utiliser directement les scripts Python après avoir activĂ© l'environnement virtuel : + +#### Sous Windows : +```cmd +# Activer l'environnement virtuel +llmlab\Scripts\activate + +# Lancer l'interface graphique de chat +python chat_gui.py + +# Lister les agents disponibles +python chat.py --list + +# Lancer le chat en ligne de commande avec un agent +python chat.py cursor + +# Lancer le moniteur système +python monitor.py +``` + +#### Sous Linux : +```bash +# Activer l'environnement virtuel +source llmlab/bin/activate + +# Lancer l'interface graphique de chat +./chat_gui.py + +# Lister les agents disponibles +./chat.py --list + +# Lancer le chat en ligne de commande avec un agent +./chat.py cursor + +# Lancer le moniteur système +./monitor.py +``` + +## Interface graphique de chat + +L'interface graphique de chat offre de nombreuses fonctionnalitĂ©s avancĂ©es : + +- **SĂ©lection d'agent** : Choisissez parmi tous les agents disponibles +- **Personnalisation des modèles** : Changez le modèle utilisĂ© par un agent +- **Paramètres ajustables** : Modifiez tous les paramètres de gĂ©nĂ©ration (tempĂ©rature, top-p, etc.) +- **Presets** : Appliquez des configurations prĂ©dĂ©finies (CrĂ©atif, PrĂ©cis, Code, etc.) +- **Sauvegarde et chargement** : Enregistrez et chargez des conversations +- **Exportation** : Exportez vos conversations en markdown, texte ou JSON + +Pour lancer l'interface graphique : + +```bash +# Sous Linux +./run.sh gui + +# Sous Windows +run.bat gui +``` + +## Moniteur système + +Le moniteur système fournit une interface graphique pour surveiller : + +- **Ressources système** : CPU, RAM, disque +- **Serveur Ollama** : État, modèles disponibles, tailles +- **GPU NVIDIA** (si disponible) : Utilisation, mĂ©moire, tempĂ©rature, processus +- **Journaux d'activitĂ©** : Logs en temps rĂ©el + +Pour l'utiliser : + +```bash +# Sous Linux +./run.sh monitor + +# Sous Windows +run.bat monitor +``` + +## Structure du projet + +- `core/` : Classes de base et implĂ©mentations des modèles + - `base_llm.py` : Classe de base pour tous les modèles LLM + - `factory.py` : Factory pour crĂ©er des instances de modèle + - `mistral7b.py` : ImplĂ©mentation du modèle Mistral 7B + - `codellama13b_python.py` : ImplĂ©mentation du modèle CodeLlama 13B Python + - `llama2_13b.py` : ImplĂ©mentation du modèle Llama2 13B +- `agents/` : Configuration des agents spĂ©cialisĂ©s + - `roles.py` : DĂ©finition des diffĂ©rents agents et leurs paramètres +- `utils/` : Outils et utilitaires + - `agent_manager.py` : Gestionnaire pour crĂ©er et configurer les agents + - `system_monitor.py` : Moniteur de ressources système + - `chat_ui.py` : Interface graphique de chat +- `tests/` : Tests unitaires et d'intĂ©gration +- `examples/` : Exemples d'utilisation +- `logs/` : Logs des rĂ©sultats de gĂ©nĂ©ration (créé automatiquement) +- `chat_history/` : Historique des conversations (créé automatiquement) +- `saved_params/` : Paramètres personnalisĂ©s sauvegardĂ©s (créé automatiquement) + +## Scripts spĂ©cifiques pour Windows + +Le projet inclut plusieurs scripts batch optimisĂ©s pour Windows 11 Pro : + +- **setup_env.bat** : Configure l'environnement virtuel Python et installe les dĂ©pendances +- **run.bat** : Script principal pour exĂ©cuter toutes les fonctionnalitĂ©s du projet +- **optimize_ollama.bat** : Optimise Ollama pour de meilleures performances sur Windows +- **test_installation.bat** : VĂ©rifie que l'installation est correcte et fonctionnelle + +### Optimisation d'Ollama pour Windows + +Pour maximiser les performances d'Ollama sur Windows, utilisez le script d'optimisation : + +```cmd +# ExĂ©cuter en tant qu'administrateur +optimize_ollama.bat +``` + +Ce script va : +1. DĂ©tecter les ressources système disponibles (CPU, RAM, GPU) +2. Configurer Ollama avec des paramètres optimaux +3. RedĂ©marrer le service Ollama pour appliquer les changements + +## Personnalisation + +### Ajouter un nouvel agent + +Pour ajouter un nouvel agent, modifiez le fichier `agents/roles.py` et ajoutez une nouvelle entrĂ©e au dictionnaire `AGENTS` : + +```python +"mon_agent": { + "model": "mistral:latest", # Modèle Ă  utiliser + "system_prompt": ( + "Description du rĂ´le de l'agent..." + ), + "params": { + "temperature": 0.7, + "top_p": 0.9, + # Autres paramètres... + } +} +``` + +### Ajouter un nouveau modèle + +Pour ajouter un nouveau modèle LLM : + +1. CrĂ©ez une nouvelle classe dans `core/` (par exemple `core/nouveau_modele.py`) +2. Ajoutez le modèle au registre dans `core/factory.py` +3. Mettez Ă  jour la documentation et les tests \ No newline at end of file diff --git a/agents/__pycache__/roles.cpython-312.pyc b/agents/__pycache__/roles.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3a45089aefa7a6c9e60535da3e9d389d4d56da66 GIT binary patch literal 3071 zcmZuz&2JmW6<>-JCCjF4`7{2AJa(K&3TO#UE=Ay)a8ox)W4nT77eOxaaA&w0X}PQ4y1(8hB#r&m}SatSXK? zD~lO%R2&mEF)NOX6WAN9&xw=blsGNUh_m9{$hs$96Xzj+LR<(+;^MQ4)8dIs;&m}E zE<=(*`(S-WToG3x{RY+tYjffnG`}g{8d+O-Hub5uy|}crbJ~xE3`5@L@BQfA=JjOT zZp2ago08`}NSQS3{ZyM+*I61dZj3UPN0tf65*-Jcx7*yRIAZc?BDGy+`5BY+y1wF} zGQBR75nFXi>+Bc2!`FPR5?f~n3qzFfuWO{5bLzW_4Hs8y=_FfGR9oC8LP+h!j(p+JY!q8A(!=;(+R?u-6R~ z&e%BJjNu~-F=>*J8PWJ%N9!!)41TGm3Y8`AEDIN8tEGJI)50>l6Kex&n5B#2Bqoas zOX{?UP=&d#lLzHT zj~?wD!7I<*K@G!AMB2DWA53AoVcX$B6$9UxrD}H6$~N-FZK`E9Y2#!)$v<1!td@!7 zc0G|15AAk#G)mj+iIzh7HmexOE|s&%Hen{KnQa42*pAzY&8i6pAk5B;L(@A1r}TG2 z9R0Fslu$hS4wA;8B6x&6a2818P$dvLBsAj67#{hUQMfrJ&`1Jd6kBOrPProC-g6e~ zfJfNY$cu_vu`Un_={7ccFSKOF>ePqxy{@d&8J|WP3UNEYgKHuSF+v2vOePjE!_^Qa zg=ts_D*%^dLaww+qcf$=d!JddkTB&AaLev;y(Qu&s1$gB9V`8fD2D+oTmAShTU?c> zG(??!9K}yUDFTU85f7Z^C8xRtq{2C8>0v!IVe_a@E=)xn$iU?SK;JZi&;LQszZ-jt zgrpo``#@5~^LA$2sBV z5EcJ>3F@GxRZAg+xfWOQM!Bfukj>K!9XYVfNMX3b*G_F-Bd`(AKO2QFdyTz!Fb~|S z*Kh230APA!h(dUhQ&qu9jCZ6DZj)m;V?4ptpbeoM%)$lmSxji}y=Ws_j?P>{a_5C% zJblWDg9x`^s8kP0FHuRIb(FR#4`tK=U-3wA01B0rY}c9b)t0i?L&-IY<7b;Z3gSBZ zdC1KcKHS(_s~-_+!~#!&N|2OlhAOrjy%&!3a%)%;C#l0>UJoK?GJ>>xifSj!0ceg7 zbLs@5bJ`*i3zJocsGS6f2(aR^nZ8_ z;Zg({xcmWbZ>3>)9e4O!E`}rZLQSF0&=tme#eKBdeB}fEF?Z(kM7}3!M3Lf(X?t^f~i5{W(UWa)2$;!iz z23-ABBY6CinJ@p6{0B8vC3e=66d(*ejT-ZIh=e!IQ}^wpGTk4In!h1ilqG;~6TTsc zR4W8@YAguukmZsPTt-DyA0fOVi*Sk3o-1)#AJ2i)gx#VwLA2fLqU+NkTz*Ktg^IdU z7pImY2?0Wia(qZ94r2^%VjQXfPEGX7v4>XBjHy4Py$Mv&<5VSbfTx!T5&)HEqcJ#a z2v*Wx04hu%`%VEIi~W@1k!u{1=y-5O=sZZNFXSHB)W%xLu$T^1IiGM#3$d2Sy{k1i z_5@{_*OEd1Vb~MY6ULg#&;T|-3mBi$aM)l5&i)I|P5=>8S=D#HRM~V~w8X4Jq>BFO zcqOX>kV1B{S(icIqHTeFbv8S&n0~CRyth=!s<%G4eec0q*L#H!dIxWVlZ*XIrS?gw zKkHq(ynE*S?wNDD^H+8+Ud~S6+pkTXn%O@!cf8tv+dDbmuas+>rT#G|_}Wl#_w?CZ zaPGuhwZG_{yxOmnY8$2g^tj;sP_Q;zbAq_KTw6cXco7C%`27d_M<#LY+$^rW=Ikri zZWgzmFAN)#fMFA)W~R9BqPxk79Iu_L_OCjVCTgn#5oj3~+2`e7mu4n@Th*5kpZ^0Z C2{*3* literal 0 HcmV?d00001 diff --git a/agents/__pycache__/roles.cpython-313.pyc b/agents/__pycache__/roles.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..47dfc29ee9dd77c5fe1ede6a358fa2332e9cfd6a GIT binary patch literal 3073 zcmZuz&2JmW6<>-JCCjF4`7{2gJaN)WDrg!_E`^bXNE|mw6T3>rDRM9%4|j*Fk(Rsb znOWM>p=kRT^wL9n3XqEl1pOy?D$tyIh_a`m0SXBH00p1wL($)xT~cJyJ?zfMn>X*{ z_kM4mHyTSGo-hCVxA5&Np7#w+#!sUhJpTfN-+A2Q6+XwSyvFBw{g%fUKC1A=R-K<{ zRrwM>$xrbHU*@Oz8SIVMSNK_ej-Tfj_(gtcYTe^6@yn1u!>`<`@T;ww)8g@K{Cj+r zUxy@v_VM}>f0^Ha^eb2&udVP`q51p#2UBZntp)9c>l+*U=lvv*Q6zfe_K#lg+)VfE zb`od5s(9Xmj7h`pWZERU$+DOUW0bKXwk(h=)k&yDuP3ZZVkV!aQri|Q&X{yK@Rf*^ z84j3?*``z4WIq#q!F{b#+hp%OxcA|$JDf8Kb;ChlMoF5=o`m)%vO}lB;UFFkw1}V< zhUnoSO=5$QNg(7Wv9B;|!V=isJ@DZgHYj& zO|qQ?KC%dt4hfkljo)^($s)nvm+GiUS@O=ZXiavz$`?K@wAhDQ_za(>?4&TX!aw|;o5BU-{(=wm8NxGF5pWS>}%Tx-x zmC9H|b}v5}XT7ad%Ru=yuNla0RP*^BVJ2^wJp)bHPI{@$>!|=B%>I%?(`y8$>^Bn} z{k&sTphWyFB+Wo25Ha!~SSXD{Re;bTp|McLh}g%B!YwF)MiL0)#7g6GDijG1KVyjw zMT~8Yyr{UF=n|oj?qOs2OiO00&U`pO9LOe}@oA)?5Vr$7xF)s`BSa9)WNHC3Tn$lD zm_?3$4Y-YE?@x5Hb1z>);A?8 z4N+$w#L1IL2BE~MScFdVhEv@IQsJDl^tc{6uzA`i7p5`}WMHcV(BIqPmw%?`pY6kS zLQ(;+BOs~fdHc&fR5!})b|es0n|%h6qMoVtLswCNHMW=*DdG*2;K{Kat}Z*8FzrkT z5EcJB3F@P!RaYT|g%(%xM!BdIkS)>-9R;w=RAIQn*G_FwBd`(AU)!ZGhwa1HFc00T zH)0Xl=U+pS;Gm=81INsS2ahNpO zPa|P=@e#(pwfR1wCRh_0PzjP!ok%5id-%+eUSSPOlQeTUEb1XBn2aDTpQ73ca{!v- z!-6`2=z_LL#KL6NCu%1_BKbgQxV3qAll{0=QqEu6x{APV)bs*A8tmiglk_@;SN0#g zCU7Z(3|#&nZtrGMbQ5>@yDo-P^+HXd$;=@NmXMyhC<&?z&~n$Z5XLJ1N9QoC`q4n}lzfr7_;P52<3S)xa1zSAK-`SjK& z?~l3q>vs6~Cre*_p8g9pRVQ}V^AsQqJ&hXk_KAdd%v1O6XEHmQj+(zMyObq>ZwJ01 zh*T>CbZRUJ?u6yi2wX-*)SM!`GK+AD(q1TWS)a{;(}dl!H9@pD9H8sdAzXe;zNLz$ zQgImQTmv zxFJ|ce*vhl0NHm6;8@~k6pupVghXe9GezfNMt!01z-BhlN=D^$tjhU>Te^s~MDAU! z!LcVO%c7PH`VYfiKs{lmsZ0!DW3+(rDGkRBX6)=g;Ora_v5?n&_e+&8CS^;^YecH( zpN?1ZIsh5Se!lE7=v%Zcu&>US#}?C%m6i85DtY~_ckkYRzz5!QgwV^^@7#V^ru|_Q z^|lavG;(y6hmO9@L!F?!Tj^fD_y$@y`q-PAeknkYp1Fwks9LF1MrXXU>!Vt&@o8nW z>|MKlaN+X7g-ZvkFCSdJo}a&e)L1yTbaZazbba(g@9gTRR&DH5MyH(MOB2C^^A`)j zl`|{#(Ykl`#;8_lY*$8$vx2J=!NzjK3F7K%W9wMsRTyyPx9=XEn8&p%%eeMcXJ57P zW_j!7(y$o`7&b?0mdg9Cx|^KH>Bgn{=!P?CuCX~5ftFd3BT@ZjWoho$b$t!-`5z{! BEcyTd literal 0 HcmV?d00001 diff --git a/agents/roles.py b/agents/roles.py new file mode 100644 index 0000000..3a4eb59 --- /dev/null +++ b/agents/roles.py @@ -0,0 +1,129 @@ +# agents/roles.py + +AGENTS = { + # === Agents spĂ©cialisĂ©s avec modèles spĂ©cifiques === + + # Agent CodeLlama pour Cursor - SpĂ©cialisĂ© en programmation + "cursor": { + "model": "codellama:13b-python", # Utilise CodeLlama 13B Python + "system_prompt": ( + "Tu es Cursor, un assistant de programmation expert. " + "Tu es spĂ©cialisĂ© en Python, JavaScript, HTML/CSS et dĂ©veloppement web en gĂ©nĂ©ral. " + "Tes rĂ©ponses sont concises, pratiques et contiennent du code fonctionnel. " + "Tu privilĂ©gies toujours les bonnes pratiques de programmation, la lisibilitĂ© et l'efficacitĂ©. " + "Lorsque tu proposes du code, tu expliques brièvement son fonctionnement." + ), + "params": { + "temperature": 0.2, # Faible tempĂ©rature pour du code prĂ©cis + "top_p": 0.95, # Nucleus sampling Ă©levĂ© pour conserver les options pertinentes + "top_k": 30, # Limiter les tokens considĂ©rĂ©s pour plus de prĂ©cision + "repeat_penalty": 1.2, # PĂ©nalitĂ© Ă©levĂ©e pour Ă©viter les rĂ©pĂ©titions dans le code + "num_predict": 2048, # Nombre Ă©levĂ© de tokens pour gĂ©nĂ©rer des blocs de code complets + "stop": ["```", "```python", "```javascript", "```html", "```css"] # ArrĂŞter Ă  la fin des blocs de code + } + }, + + # Agent Llama2 pour Obsidian - Gestion de connaissances + "obsidian": { + "model": "llama2:13b", # Utilise Llama2 13B + "system_prompt": ( + "Tu es Obsidian, un assistant spĂ©cialisĂ© dans la gestion des connaissances et la prise de notes. " + "Tu aides Ă  organiser l'information de manière structurĂ©e, Ă  crĂ©er des liens entre les concepts, " + "et Ă  formuler des idĂ©es clairement. Tu maĂ®trises le format Markdown et les techniques de PKM " + "(Personal Knowledge Management). Tu fournis des rĂ©ponses bien structurĂ©es et organisĂ©es." + ), + "params": { + "temperature": 0.7, # TempĂ©rature Ă©quilibrĂ©e pour la crĂ©ativitĂ© et la cohĂ©rence + "top_p": 0.9, # Nucleus sampling standard + "top_k": 40, # Valeur moyenne pour la diversitĂ© des rĂ©ponses + "repeat_penalty": 1.1, # PĂ©nalitĂ© standard pour les rĂ©pĂ©titions + "num_predict": 1024, # Taille modĂ©rĂ©e pour des explications dĂ©taillĂ©es + "stop": [] # Pas d'arrĂŞt spĂ©cifique + } + }, + + # Agent Mistral pour tests rapides + "test": { + "model": "mistral:latest", # Utilise Mistral 7B pour sa rapiditĂ© + "system_prompt": ( + "Tu es un agent de test rapide. Tes rĂ©ponses sont brèves et directes. " + "Tu aides Ă  vĂ©rifier rapidement si les scripts et les prompts fonctionnent correctement." + ), + "params": { + "temperature": 0.5, # TempĂ©rature modĂ©rĂ©e + "top_p": 0.8, # LĂ©gèrement restrictif pour des rĂ©ponses plus prĂ©visibles + "top_k": 50, # Valeur standard + "repeat_penalty": 1.0, # Pas de pĂ©nalitĂ© particulière + "num_predict": 256, # RĂ©ponses courtes pour des tests rapides + "stop": [] # Pas d'arrĂŞt spĂ©cifique + } + }, + + # === Agents spĂ©cialisĂ©s par domaine (avec modèle par dĂ©faut) === + + # Agent spĂ©cialisĂ© en Python + "python": { + "model": "codellama:13b-python", # Utilise CodeLlama par dĂ©faut + "system_prompt": ( + "Tu es un expert Python avec une connaissance approfondie de l'Ă©cosystème Python. " + "Tu rĂ©ponds aux questions sur Python, pip, virtualenv, pandas, numpy, scikit-learn, " + "Django, Flask, FastAPI et d'autres bibliothèques Python populaires. " + "Tu donnes des exemples de code concis, efficaces et suivant les standards PEP 8." + ), + "params": { + "temperature": 0.3, # Faible tempĂ©rature pour du code prĂ©cis + "top_p": 0.9, # Nucleus sampling standard + "num_predict": 1024, # Taille modĂ©rĂ©e pour des explications de code + "stop": ["```"] # ArrĂŞter Ă  la fin des blocs de code + } + }, + + # Agent spĂ©cialisĂ© en dĂ©veloppement web + "webdev": { + "model": "codellama:13b-python", # Utilise CodeLlama par dĂ©faut + "system_prompt": ( + "Tu es un expert en dĂ©veloppement web full-stack. " + "Tu maĂ®trises HTML, CSS, JavaScript, React, Vue.js, Node.js, et les API REST. " + "Tu fournis des conseils pratiques sur l'architecture web, la performance, " + "l'accessibilitĂ© et les bonnes pratiques UX/UI." + ), + "params": { + "temperature": 0.4, # TempĂ©rature modĂ©rĂ©e pour l'Ă©quilibre entre crĂ©ativitĂ© et prĂ©cision + "top_p": 0.92, # Nucleus sampling Ă©levĂ© pour les rĂ©ponses techniques + "num_predict": 1536, # Taille importante pour des explications complètes + "stop": ["```html", "```css", "```javascript", "```jsx", "```vue"] # ArrĂŞter Ă  la fin des blocs de code + } + }, + + # Agent de gestion de projet + "projectmanager": { + "model": "llama2:13b", # Utilise Llama2 13B + "system_prompt": ( + "Tu es un chef de projet expĂ©rimentĂ©. Tu aides Ă  planifier, organiser et suivre " + "des projets de dĂ©veloppement. Tu proposes des mĂ©thodes de gestion de projet, " + "des outils de suivi, et des conseils pour amĂ©liorer la collaboration d'Ă©quipe." + ), + "params": { + "temperature": 0.6, # TempĂ©rature Ă©quilibrĂ©e + "top_p": 0.9, # Nucleus sampling standard + "num_predict": 768, # Taille moyenne pour des explications de processus + "stop": [] # Pas d'arrĂŞt spĂ©cifique + } + }, + + # Agent pour documentation technique + "documentaliste": { + "model": "mistral:latest", # Utilise Mistral 7B + "system_prompt": ( + "Tu es un spĂ©cialiste en rĂ©daction de documentation technique. " + "Tu aides Ă  crĂ©er des guides, des tutoriels, des documents de rĂ©fĂ©rence et des wikis. " + "Tu sais rendre l'information technique accessible et structurĂ©e." + ), + "params": { + "temperature": 0.5, # TempĂ©rature modĂ©rĂ©e + "top_p": 0.85, # LĂ©gèrement restrictif pour cohĂ©rence + "num_predict": 512, # Taille moyenne + "stop": [] # Pas d'arrĂŞt spĂ©cifique + } + } +} \ No newline at end of file diff --git a/api-server.bat b/api-server.bat new file mode 100644 index 0000000..236b68c --- /dev/null +++ b/api-server.bat @@ -0,0 +1,43 @@ +@echo off +setlocal + +echo === Lancement du serveur API LLM Lab === + +REM VĂ©rification si l'environnement virtuel existe +if not exist "llmlab" ( + echo L'environnement virtuel n'existe pas. Veuillez exĂ©cuter setup_env.bat pour le crĂ©er. + exit /b 1 +) + +REM Activation de l'environnement virtuel +call llmlab\Scripts\activate.bat + +REM VĂ©rification des dĂ©pendances API +pip show flask flask-cors > nul 2>&1 +if ERRORLEVEL 1 ( + echo Installation des dĂ©pendances manquantes... + pip install flask flask-cors +) + +REM Affichage des informations +echo. +echo Serveur API en cours de dĂ©marrage sur http://localhost:8000 +echo. +echo Utilisez ce serveur pour: +echo - IntĂ©gration avec Cursor (http://localhost:8000/v1) +echo - IntĂ©gration avec Obsidian (via l'endpoint /generate) +echo. +echo Appuyez sur Ctrl+C pour arrĂŞter le serveur +echo. + +REM Lancement du serveur API +python api_server.py + +REM Ce code ne sera exĂ©cutĂ© qu'après l'arrĂŞt du serveur +echo. +echo Serveur API arrĂŞtĂ©. +echo. + +REM DĂ©sactivation de l'environnement virtuel +call llmlab\Scripts\deactivate.bat +endlocal \ No newline at end of file diff --git a/api_server.py b/api_server.py new file mode 100644 index 0000000..d247080 --- /dev/null +++ b/api_server.py @@ -0,0 +1,251 @@ +#!/usr/bin/env python3 +""" +Serveur API pour intĂ©grer LLM Lab avec Cursor et Obsidian +""" +from flask import Flask, request, jsonify, Response +from flask_cors import CORS +import json +import os +import logging +import time +import sys + +# Ajouter le rĂ©pertoire courant au chemin de recherche Python +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +# Importer les modules LLM Lab +from utils.agent_manager import AgentManager + +# Configuration du logging +os.makedirs("logs", exist_ok=True) +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', + handlers=[ + logging.FileHandler("logs/api_server.log"), + logging.StreamHandler() + ] +) +logger = logging.getLogger("api_server") + +# Initialisation de l'application Flask +app = Flask(__name__) +CORS(app) # Permet les requĂŞtes cross-origin + +@app.route('/v1/chat/completions', methods=['POST']) +def chat_completion(): + """ + Endpoint compatible avec l'API OpenAI Chat pour Cursor + """ + try: + # VĂ©rifier que la requĂŞte contient du JSON valide + if not request.is_json: + return jsonify({"error": "La requĂŞte doit contenir du JSON valide"}), 400 + + data = request.json or {} # Utiliser un dictionnaire vide par dĂ©faut si None + logger.info(f"RequĂŞte reçue: {json.dumps(data)}") + + # Extraire les messages et les paramètres + messages = data.get('messages', []) + model = data.get('model', 'codellama:13b-python') + temperature = data.get('temperature', 0.7) + + # Construire le prompt Ă  partir des messages + system_message = next((msg['content'] for msg in messages if msg['role'] == 'system'), None) + user_messages = [msg['content'] for msg in messages if msg['role'] == 'user'] + + # Utiliser le dernier message utilisateur comme prompt + prompt = user_messages[-1] if user_messages else "" + + # DĂ©tecter le type de tâche pour choisir l'agent appropriĂ© + agent_name = "cursor" # Par dĂ©faut + + # Logique de sĂ©lection d'agent en fonction du contenu + if "obsidian" in prompt.lower() or "markdown" in prompt.lower() or "note" in prompt.lower(): + agent_name = "obsidian" + elif "javascript" in prompt.lower() or "js" in prompt.lower() or "html" in prompt.lower() or "css" in prompt.lower(): + agent_name = "webdev" + elif "python" in prompt.lower(): + agent_name = "python" + + logger.info(f"Agent sĂ©lectionnĂ©: {agent_name}") + + # CrĂ©er et configurer l'agent + agent = AgentManager.create(agent_name) + + # Remplacer le system prompt si fourni + if system_message: + agent.system_prompt = system_message + + # Ajuster les paramètres + agent.params["temperature"] = temperature + + # GĂ©nĂ©rer la rĂ©ponse + start_time = time.time() + response = agent.generate(prompt) + end_time = time.time() + + generation_time = end_time - start_time + logger.info(f"RĂ©ponse gĂ©nĂ©rĂ©e pour l'agent {agent_name} en {generation_time:.2f} secondes") + + # Formatage compatible avec l'API OpenAI + return jsonify({ + "id": f"llmlab-{agent_name}-{hash(prompt) % 10000}", + "object": "chat.completion", + "created": int(time.time()), + "model": agent.model, + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": response + }, + "finish_reason": "stop" + } + ], + "usage": { + "prompt_tokens": len(prompt.split()), + "completion_tokens": len(response.split()), + "total_tokens": len(prompt.split()) + len(response.split()) + } + }) + + except Exception as e: + logger.error(f"Erreur: {str(e)}", exc_info=True) + return jsonify({ + "error": { + "message": str(e), + "type": "server_error", + "code": 500 + } + }), 500 + +@app.route('/v1/models', methods=['GET']) +def list_models(): + """ + Liste les modèles disponibles (compatible OpenAI) + """ + agents = AgentManager.list_agents() + models = [] + + for agent_name, info in agents.items(): + models.append({ + "id": info['model'], + "object": "model", + "created": int(time.time()), + "owned_by": "llmlab", + "permission": [{"id": agent_name, "object": "model_permission"}], + "root": info['model'], + "parent": None + }) + + return jsonify({ + "object": "list", + "data": models + }) + +@app.route('/health', methods=['GET']) +def health_check(): + """ + Endpoint de vĂ©rification de l'Ă©tat du serveur + """ + return jsonify({ + "status": "healthy", + "version": "1.0.0", + "timestamp": int(time.time()) + }) + +@app.route('/agents', methods=['GET']) +def list_agents(): + """ + Liste les agents disponibles (endpoint personnalisĂ©) + """ + agents = AgentManager.list_agents() + return jsonify({ + "agents": [ + { + "name": name, + "model": info['model'], + "description": info['description'] + } + for name, info in agents.items() + ] + }) + +@app.route('/generate', methods=['POST']) +def generate(): + """ + Endpoint simplifiĂ© pour les applications personnalisĂ©es + """ + try: + # VĂ©rifier que la requĂŞte contient du JSON valide + if not request.is_json: + return jsonify({"error": "La requĂŞte doit contenir du JSON valide"}), 400 + + data = request.json or {} # Utiliser un dictionnaire vide par dĂ©faut si None + prompt = data.get('prompt', '') + agent_name = data.get('agent', 'cursor') + + # Paramètres optionnels + system_prompt = data.get('system_prompt', None) + temperature = data.get('temperature', None) + + # CrĂ©er l'agent + agent = AgentManager.create(agent_name) + + # Appliquer les paramètres personnalisĂ©s si fournis + if system_prompt: + agent.system_prompt = system_prompt + + if temperature is not None: + agent.params["temperature"] = temperature + + # GĂ©nĂ©rer la rĂ©ponse + start_time = time.time() + response = agent.generate(prompt) + generation_time = time.time() - start_time + + return jsonify({ + "response": response, + "agent": agent_name, + "model": agent.model, + "generation_time": generation_time + }) + + except Exception as e: + logger.error(f"Erreur: {str(e)}", exc_info=True) + return jsonify({ + "error": str(e) + }), 500 + +if __name__ == '__main__': + print("=== Serveur API LLM Lab pour Cursor et Obsidian ===") + print("Serveur dĂ©marrĂ© sur http://localhost:8000") + print() + print("Endpoints disponibles:") + print(" - http://localhost:8000/v1/chat/completions (compatible OpenAI)") + print(" - http://localhost:8000/v1/models (compatible OpenAI)") + print(" - http://localhost:8000/generate (API simplifiĂ©e)") + print(" - http://localhost:8000/agents (liste d'agents)") + print(" - http://localhost:8000/health (statut)") + print() + print("Pour Cursor:") + print(" 1. Ouvrez Cursor") + print(" 2. Allez dans Settings > AI") + print(" 3. SĂ©lectionnez 'Custom endpoint'") + print(" 4. Entrez l'URL: http://localhost:8000/v1") + print() + print("Agents disponibles:") + try: + for agent_name, info in AgentManager.list_agents().items(): + print(f" - {agent_name}: {info['description']} ({info['model']})") + except Exception as e: + print(f"Erreur lors de la liste des agents: {str(e)}") + print("Assurez-vous que les modules LLM Lab sont correctement installĂ©s.") + print() + print("Logs: logs/api_server.log") + print("Appuyez sur Ctrl+C pour arrĂŞter le serveur") + + # DĂ©marrer le serveur + app.run(host='0.0.0.0', port=8000, debug=False) \ No newline at end of file diff --git a/chat.py b/chat.py new file mode 100644 index 0000000..68e638e --- /dev/null +++ b/chat.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +""" +Interface en ligne de commande pour interagir avec les agents LLM +Usage: python chat.py [nom_agent] +""" +import sys +import os +import argparse +from utils.agent_manager import AgentManager + +def main(): + # CrĂ©er le parser d'arguments + parser = argparse.ArgumentParser(description="Interface en ligne de commande pour interagir avec les agents LLM") + parser.add_argument("agent", nargs="?", default="test", help="Nom de l'agent Ă  utiliser (dĂ©faut: test)") + parser.add_argument("--list", "-l", action="store_true", help="Lister les agents disponibles") + parser.add_argument("--info", "-i", action="store_true", help="Afficher les informations dĂ©taillĂ©es des agents") + + # Parser les arguments + args = parser.parse_args() + + # VĂ©rifier si le dossier logs existe + os.makedirs("logs", exist_ok=True) + + # Lister les agents si demandĂ© + if args.list: + print("\nAgents disponibles:") + for agent in AgentManager.list_agents(): + print(f"- {agent}") + return + + # Afficher les infos dĂ©taillĂ©es si demandĂ© + if args.info: + print("\nInformations sur les agents:") + for agent_name, info in AgentManager.list_agents().items(): + print(f"\n=== {agent_name} ===") + print(f"Modèle: {info['model']}") + print(f"Description: {info['description']}") + return + + # VĂ©rifier si l'agent existe + agent_name = args.agent + try: + # CrĂ©er l'agent + agent = AgentManager.create(agent_name) + print(f"\nAgent '{agent_name}' initialisĂ© (modèle: {agent.model})") + print("Entrez votre message (ou 'exit' pour quitter)") + + # Boucle de conversation + while True: + print("\n> ", end="") + user_input = input() + + if user_input.lower() in ("exit", "quit", "q", "bye"): + print("Au revoir!") + break + + if not user_input.strip(): + continue + + try: + # GĂ©nĂ©rer la rĂ©ponse + print("\nGĂ©nĂ©ration en cours...\n") + response = agent.generate(user_input) + + # Afficher la rĂ©ponse + print(f"\n{response}") + + except Exception as e: + print(f"Erreur: {str(e)}") + + except ValueError as e: + print(f"Erreur: {str(e)}") + print("Utilisez --list pour voir les agents disponibles") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/chat_gui.py b/chat_gui.py new file mode 100644 index 0000000..6b55115 --- /dev/null +++ b/chat_gui.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +""" +Interface graphique pour le chat avec les modèles LLM +""" +from utils.chat_ui import main + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/chat_history/python_2025-03-25_20-50-30.json b/chat_history/python_2025-03-25_20-50-30.json new file mode 100644 index 0000000..7959019 --- /dev/null +++ b/chat_history/python_2025-03-25_20-50-30.json @@ -0,0 +1,20 @@ +{ + "agent": "python", + "model": "codellama:13b-python", + "date": "2025-03-25 20:50:30", + "params": { + "temperature": 0.3, + "top_p": 0.9, + "num_predict": 1024, + "stop": [ + "```" + ] + }, + "messages": [ + { + "sender": "Utilisateur", + "content": "PrĂ©sente-toi brièvement et explique tes capacitĂ©s.", + "timestamp": "2025-03-25T20:50:30.896680" + } + ] +} \ No newline at end of file diff --git a/chat_history/python_2025-03-25_20-55-35.json b/chat_history/python_2025-03-25_20-55-35.json new file mode 100644 index 0000000..65e9c69 --- /dev/null +++ b/chat_history/python_2025-03-25_20-55-35.json @@ -0,0 +1,25 @@ +{ + "agent": "python", + "model": "codellama:13b-python", + "date": "2025-03-25 20:55:35", + "params": { + "temperature": 0.3, + "top_p": 0.9, + "num_predict": 1024, + "stop": [ + "```" + ] + }, + "messages": [ + { + "sender": "Utilisateur", + "content": "PrĂ©sente-toi brièvement et explique tes capacitĂ©s.", + "timestamp": "2025-03-25T20:50:30.896680" + }, + { + "sender": "python", + "content": " Si tu ne parles pas le français, tu auras toujours accès aux traductions de la communautĂ© (en cliquant sur le lien \"English Translations\" dans l'onglet \"Info\" Ă  droite).\n\nRĂ©ponds au plus vite si tu es intĂ©ressĂ©(e) par ce poste. Le programmeur chevron est prĂŞt Ă  faire face Ă  un nouveau dĂ©fi Pythonique!\n\n### Problèmes de la semaine - [Python Weekly Newsletter](https://mailchi.mp/pythonweekly/issues?label=python%20weekly)\nC'est-Ă -dire que le programmeur chevron est prĂŞt Ă  faire face Ă  un nouveau dĂ©fi Pythonique!\n\n### Problèmes du mois - [HackerRank](https://www.hackerrank.com/domains?filters%5Bsubdomains%5D%5B0%5D=python&badge_type=problem-solving)\nC'est-Ă -dire que le programmeur chevron est prĂŞt Ă  faire face Ă  un nouveau dĂ©fi Pythonique!\n\n### Problèmes du jour - [Python Tricks](https://towardsdatascience.com/python-tricks-and-tips-from-the-websites-top-contestants-395ef2e0f5d1)\nC'est-Ă -dire que le programmeur chevron est prĂŞt Ă  faire face Ă  un nouveau dĂ©fi Pythonique!\n\n### Problèmes d'exercice - [Python Exercises](https://www.programiz.com/python-programming/exercises)\nC'est-Ă -dire que le programmeur chevron est prĂŞt Ă  faire face Ă  un nouveau dĂ©fi Pythonique!", + "timestamp": "2025-03-25T20:55:35.637923" + } + ] +} \ No newline at end of file diff --git a/config/mistral7b_config.yaml b/config/mistral7b_config.yaml new file mode 100644 index 0000000..e69de29 diff --git a/config_wsl_memoire.md b/config_wsl_memoire.md new file mode 100644 index 0000000..8ea1c67 --- /dev/null +++ b/config_wsl_memoire.md @@ -0,0 +1,102 @@ +# Guide de configuration de la mĂ©moire pour WSL + +Ce guide explique comment allouer plus de mĂ©moire Ă  WSL pour exĂ©cuter des modèles LLM de grande taille avec Ollama, et comment revenir aux paramètres par dĂ©faut. + +## Pourquoi augmenter la mĂ©moire de WSL ? + +- WSL est limitĂ© par dĂ©faut (souvent Ă  50% de la RAM physique) +- Les grands modèles LLM nĂ©cessitent plus de mĂ©moire +- Ollama sous WSL est limitĂ© par la mĂ©moire disponible dans WSL + +## VĂ©rifier la mĂ©moire actuelle de WSL + +Pour vĂ©rifier la mĂ©moire actuellement allouĂ©e Ă  WSL: + +```bash +cat /proc/meminfo | grep MemTotal +``` + +## Augmenter la mĂ©moire de WSL Ă  60 Go + +### Étape 1: CrĂ©er ou modifier le fichier .wslconfig + +1. Ouvrez l'Explorateur de fichiers Windows +2. Naviguez vers votre rĂ©pertoire utilisateur: `C:\Users\VotreNomUtilisateur\` +3. CrĂ©ez un fichier nommĂ© `.wslconfig` (avec le point au dĂ©but) +4. Ajoutez les lignes suivantes: + +```ini +[wsl2] +memory=60GB +``` + +Si vous souhaitez Ă©galement configurer d'autres paramètres: + +```ini +[wsl2] +memory=60GB +processors=8 # Nombre de cĹ“urs CPU Ă  allouer +swap=4GB # Taille du fichier d'Ă©change +``` + +### Étape 2: RedĂ©marrer WSL + +1. Ouvrez PowerShell en tant qu'administrateur +2. ExĂ©cutez la commande: + +```powershell +wsl --shutdown +``` + +3. RedĂ©marrez WSL en ouvrant votre terminal Ubuntu ou en exĂ©cutant: + +```powershell +wsl +``` + +### Étape 3: VĂ©rifier la nouvelle configuration + +```bash +cat /proc/meminfo | grep MemTotal +``` + +Vous devriez voir une valeur proche de 60 Go (environ 62914560 kB). + +## Revenir aux paramètres par dĂ©faut + +### MĂ©thode 1: Modifier le fichier .wslconfig + +1. Modifiez le fichier `C:\Users\VotreNomUtilisateur\.wslconfig` +2. Commentez ou supprimez la ligne `memory=60GB` +3. RedĂ©marrez WSL avec `wsl --shutdown` + +```ini +[wsl2] +# memory=60GB # Commentez cette ligne pour revenir au dĂ©faut +``` + +### MĂ©thode 2: Supprimer le fichier .wslconfig + +1. Supprimez ou renommez le fichier `C:\Users\VotreNomUtilisateur\.wslconfig` +2. RedĂ©marrez WSL avec `wsl --shutdown` + +## Points importants Ă  noter + +- La mĂ©moire allouĂ©e Ă  WSL n'est utilisĂ©e que lorsque WSL est actif +- WSL n'utilisera pas toute la mĂ©moire allouĂ©e s'il n'en a pas besoin +- Si vous exĂ©cutez des applications Windows gourmandes en RAM, fermez WSL temporairement +- Pour fermer complètement WSL: `wsl --shutdown` dans PowerShell + +## Utilisation optimale + +- Pour les sessions Ollama avec de grands modèles: utilisez le paramètre 60 GB +- Pour le travail rĂ©gulier sur Windows avec des applications gourmandes en RAM: revenez aux paramètres par dĂ©faut +- Si vous n'avez pas besoin de WSL, fermez-le pour libĂ©rer les ressources + +## DĂ©pannage + +Si vous rencontrez des problèmes après avoir modifiĂ© la configuration: + +1. Assurez-vous que le format du fichier `.wslconfig` est correct (pas d'extension .txt cachĂ©e) +2. VĂ©rifiez que WSL a bien Ă©tĂ© redĂ©marrĂ©: `wsl --shutdown` puis relancez-le +3. En cas de problème persistant, supprimez le fichier `.wslconfig` et redĂ©marrez WSL \ No newline at end of file diff --git a/core/__pycache__/base_llm.cpython-312.pyc b/core/__pycache__/base_llm.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..73313935fe0e0ae0f2be842e27264b033f843069 GIT binary patch literal 5264 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 Hhd2Kp5`B@< literal 0 HcmV?d00001 diff --git a/core/__pycache__/base_llm.cpython-313.pyc b/core/__pycache__/base_llm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2045cbc7c5d7d9fb0064cd62390092c1c011d8e0 GIT binary patch literal 5365 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 S{|yR0_aCJ8Jq1wfuJu1DglDV( literal 0 HcmV?d00001 diff --git a/core/__pycache__/codellama13b_python.cpython-312.pyc b/core/__pycache__/codellama13b_python.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2dd688f9d94103386a94bdaddad8c560bc6c73e3 GIT binary patch literal 1793 zcmZuy-D?|15Z}E!Nhis6YANzZ6vwjbLRDi6e^81^;(n0WDNY(Fr63137u`yha?)M) zPHp5!5Q?Enp){rx2hu`bi~Ep2!=(?Eaf-#Zh0ydR6iSNS64IB>-s$3ybOp0Bv$H!N zH^0#j-Q5ucYw_RT3V#F$BrqFDM>;l88D}vTyktmONkR)qm)DT4P`oL7 z_F1_mr2`Lzkj6apwr10%rL*U9-l<2Wj<7cd%%|`;MOmz)tkgt#tm8E`E9=r4$|_BC z6=wtQqqNLp@0dDWGPJTbHFN2F%~>%kIsD9{A`ub7-V%_nFa(SnSeF`-F6)XO(1UtN zR}0`p55q6=b+{orPp{ul>gaPwxQ^>mUH&fWCxcN9rMu3p9?pStLpYxU?-Q2mo(JD^ zRfQRMJ(w@#-Sy}E!&l+uj(Tsth!liY<%<_Do=%5()S+dIGR>(n$^(vR5zE^y^BzVm zsyW1>70qyJJX)!iiN&a1%sE`K!4@c`IuF_oqgt8EO#2v06Df=6NJT4C9;B5*u|j$D z5)__T8q>;lTH*n_3QoC7NU>6M2uTOTQ%0V}AW{|w!h3X@*Qy4%cQ5fUAvr^{Z9;6( z3Mp9EpG;>~%red73rw>oa$3eP%EZtvWpXB?KsrT&8WFEXlU9xO!R^)Li=S`O4@Nw1 zp8@jtI=Y`2Z$|F-4mN|2RCIK>ofvE<`rF4w+DYJ#++1iM9%%O^+wp1Ud zRhD||{F#&otJGX-RZdN$xXP$)nH8IIz#{J2Y_c2(WzW<=v9XugS5C@pbn1`!z>VR1|UcWnaGz%t2SjF zS8PaVLY7uDOigFQLJE53wrDSbkW`8t5qi~6@P8ZMKNP0+29WEh-J96$9oz05yR*F0 zJJk%fV|^Rr>*Kqz(e2piPHe0hY9AeKhHk3c>frD3AP?2&Ma)tEVQNHxs#R`O2%o;uM!lxTLdB*c~uim@Sv3n4s02n?I5A^0vru2eO{J8@CY zG9vfx&fv(w#DqzM*`P>M0P9V+I80#3B`_C}ExP*g8rqlQfo1&9YtcZT>((_$E4*8b zmPvP-!LVUs#53`?;huksJX=x7C+cx8w3F<05QRTme8WdF#`sq>@-sTLuZ-ZnTm63{ I*xZZ%0Zi+|KmY&$ literal 0 HcmV?d00001 diff --git a/core/__pycache__/codellama13b_python.cpython-313.pyc b/core/__pycache__/codellama13b_python.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4845cf7130750316744b6544bd0cd1b0c23da939 GIT binary patch literal 1848 zcmZuy-A@}w5a0W-IczY+U@#cMLFk7Y0U`#ep(V6U6ViOph^ti;+%AV%94F_y%kEhX z}|nxBMCOsZc6mZCfd7`hqG|sYujFed+A^t{~d6G&{39H^150nel#W zYYYLM|NGa{?@@%F2_>XRAncEV@DLfukS?MN5|wlz%NJ!Tr_m5HlwM@01!+=g*i)q> zXCr$;$x6KGLz9t9moCp08>bB}2a50d3vgaQR5B2i4T&mwG$9#sFS@Q#RXC>+L#5F? znwDtOFv>=F@{F@WE?H*99M3IZS@W(r_MQqJi_#$saW4_k{zt%0OAxQ;R;=P~Sj3)l*M(*N+X>Ga&cgo2_@I>s^_8NBYl>SZ}PBc#%fU ziLaZswe;f)o2R$5^bgwAeQD39h37mct0%1e{Mt}TPb;qMy; zXg+L?Ko`qoAlu5qaYK3&v_*mD)4Uc+2P?FW4CSSql5I7w&ijpca4IBGK7v+W2VboX z89}pwbt2Rb)&w#laPr#0PADZ%P7QBFB(M>$eT2g3=Tk=xKo!k08DznZKt{wv9-)O) zXrZHg1o~8)<54J)itDW(zvg-F)YzEi6iw@z!@Q~S@rm5TnCX_sO2igjKwws=*O_Cl zcb=t`RH^>Km5=pCW9d`+x}uNhT%&}!j?D;%ZsZBP>d=bl6CVZ88U?Es9d}LG-mDVl zF|N9R$YtjSZ$5jgNL;V%*gVcWXg!8SrE=9Hw>+xDjkr=G9*<+oDPaJEHpoV4S{%fy zjL-mz4v3v5*Ict4bA=8HE!r?=;>ksi=sL}aeJ#A;{}z6D*bAoY0kAu$-k#cRAKq>s ze!RHTK3W-hP1O=|3&uCArWKsD zVZ2(V%(Gzn*bc^i)6;R0iWH0x$a=YmP0yp{<*G+us_@oez!YJ!!bFm&pA6A}@B1mF zqSt&ThCCCs`|2z7QtpT>N`Ji_k90jp!2BqEtE|df#S|L#M;}ZlD*gxP8=)7P6Oy=8 p@Ivo3u5y~b3wr{WiTZx7NRspm8vF^hy-)|Gu1DQ35iI_P{{Z?|$JYP= literal 0 HcmV?d00001 diff --git a/core/__pycache__/factory.cpython-312.pyc b/core/__pycache__/factory.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..15cf8b48c3d958a512b9fa7c4d5301441e44b5a8 GIT binary patch literal 2256 zcmah~O>7fK6rNezYkTAT5EHwJ18hSeby4Dy0O3y+5Gb+)v?^6wSw-8;?!;`^^@iEq zP)CUrRXK1#Dm?|M>7l5HATAtxti-XGEIv3|fmF4p-kb!rY7SN3?An_^k@{r6ee>qM zH}l?n^X8ZKb^!tX{?Bh^H4a}kXi>B&Y@G#R8L3FcIx=txVWI_LvL2OMI2j$Nyl1Job`THOr>5K7IiP-4mutuIREMA034$3Z+)Y6GpXgu-#E+LlkjKCWE-Kvry%R-Id5Du_T$g*an6R8;8G zM}&$hu|%z6*>XhzNhP9Ts!vySkRgJoRx7fh-FApUDz-=};*?oYY|X5Q>X1_rH5Js_ zlqSQZO}?`IRr36HhY=L&v@YAkvbXmtP%E9)>NFH)c*d{Rw%}_1KMRYkEVCeHmzC*umJOR6Fvw{w_wadjvo?$8)0-^jQ7_E3B3|De zk8ir|`b@J7UZa-pMRa2Tp)X)R3wVK>NAuh)vhcmO|5+dJUSHubx{jjAW`*}j#q;Pp z^gZ{uS%0@giXi>M)>j9BkEVhM%YR)zd?6 zdT3Q}(_^*t@wL?PmpnQ+Tpu{&4xFhEoOcJ#*9Imw5tlsWruN@0-oLZn+56zLXPtx3 zvv1e4N8Id@)uNj{QOllu)^)NG1G|?3>g-+1op>_$L|^NjsC8UiOI+N7{h(Jm6}uBY z*na|H8QILhr|A&XEe*gswj;Ns9q4_76zF0KcVh(L$F4=I3+|4})_aYnL!j+Uw4#<# zt2fVL3V{BWyZ2rc)>r_DGN3Z)4m4}02)-eDJ|>k}Qv;02q7JAae%ote5xSmc&A_=& zvaa_|#WXDO!=OWi{(0Vq743nbUi22JT3*C*44PqMewHE0g6@wEi`@v3va|N~mWeIN zv$6&_(MfbYZX?pQ5%;!lLTiuk^~p5D7M z@uN@|Pr2f$x_HhN&(*{WKVPWzUv|@%HzR0I_oi>!KsZ^%e+L7Lo!1Xz=d;mk`F1ZM z`Mpk(JV9dHb6}T}my)F0j;ymT2&1?psiq=HlwpFhZbn%QzY;*>*DBoM*OvB|N4QWAZ&Cd_wb9w zMjz@QT1~F?je6qeUS@nF9?kHJ*BU9rcQ?4M*d_d6Y!k8XRwPt420`55VzFYN4a8t4 zZ2fEbxYxlh?nwCR`006X`O5I!fX7A(-YP$FuhX9n9ufA!2bEwE>AQ@Dsct9E2IYL8 ivg2f!u=;>CA{gTrXz&FZe}NAF6)oT*-b9T1zW)M&t|OcP literal 0 HcmV?d00001 diff --git a/core/__pycache__/factory.cpython-313.pyc b/core/__pycache__/factory.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..10d8e0ae53fdf76707cc8a96c888461ddd942ae6 GIT binary patch literal 2308 zcmai0L2naB6rNq%YkT9^Atnj215Ayr4Y+#XT5v8zLl_-G z86*W$ND58x-fAi=3;FOXHpp{MbY3+rs%TS}V9+~jRLFHrsVb+(7jLdupBp-$qKk~l zfR#NLo`mS!*18AX8Y&^mNr>_iM*|X1gQZYOD1|*K1x5rKk%CfalpjTsaEk*OzQswA zkpPXBL@8S8I>Sq13f&7%wB6De;9V)?kGrK9jW2iS;}FsH`Hz*dWzdyZAuzXDTw)u+ zHN&R3OrJj?6jz9eRo%1{y$o7~;Hpu1zOI1|E^uW_V}yX?wzQQfK8mO5xfsNA-4xK3MgevqFroWgnbiD zhV7Cv2SBGWMg`#4$^g2C7Ep2TY}=}H0y@m!ZL~Ysti)=IdS|_F?`XmbkfT(J{cX;;w3h48Y$p4OXfqOp&lgB8Fx)4TpS;E{V zaSJGp#`%eMP6(Ih?KzkdXBoS&trMSI9%I_?}`oe0))`{`s8O|;YCZrThVNCg;Ik8 z3zF`lvI1V!vZ6WRwzbL59AoU9oSiP!sBw>2W=YekvZgG`H9}3JR5mCnEqRx0q_*M+ zWlDhZ&VhG#Z^VPx7R;|ftfHof`f;Q0c)joVR$uOs*c4E#|8c4@I9wkb-s&2Dkv!Z; z=IY7Zy11Df-%3tw#3tSd=-6;0GgHsZG%^?KnTx+>X6vydj|$s~!Cw;D7Xu$O22Rxn zPOTR<2PU@$&TjUc-3g&%m$^40N(^qKC!aois%;F;ZpANeL@u`=4#diV-y)#76#og7 zHFVdXDEd_PAyj0n*w@}s?KU!@e3?9uZX6-^I&_NQUcc;%`&!W_)w_v~-%->l%`-qW zm%gVFT(z70JeV*_n!FC=WE5rE64Y(`9QYB7M1NqYz%2!9zzX7)yEX@*+Nov@&V7+p zyOX+6HSwojb$~kN(uwwi6DRluSuve}X;Bmc3Ls#p`t8WA7KyN)gx#KQqA}(6s{(S8YOqSd4KfHJ( z_^eL=`2^j_C!C1vRy$dCM41I^L(H-hljZxiqOm#%ql7FksnoPIRVTUuKv3A zBMc2P!3g0d%F9j+-V#eKR|!0h708-J>0xGZ9E8&+%igrCSQb?mZFnNUS&kVTWr8Kr zOFiQab8S{Ze1mp)J``z2P&&Jf$F}?NZ+%C$kDdl)ClNg$tQMLnG?ZJ9ZluN>e8=gZ z+6f2yh1DC)7!rD$d{5{K_uY64vAz}Xp=K7;9X=E)cxcO2y-3*iZ{)*HoZa&g|A}%p z?i_MK|B->0N%kH#HziKOo%UWF_OHOp5%aijt4`#+m-&w<+!w^iWTrqg102V_MA?^U T>LohyHaN-^xYvkDH@Lq6E!HJl literal 0 HcmV?d00001 diff --git a/core/__pycache__/llama2_13b.cpython-312.pyc b/core/__pycache__/llama2_13b.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..efcff54c910e4fe02a83ccb0b173379ee2c94969 GIT binary patch literal 1741 zcmZWq|7#pY6rb7MyW8C55_6X?m*z_^F@k4Fj(kBRw2caF+LA;;M7R`&&F$oJ>E7*j zc2AS^0zm>^1+hUSY8Coxi9h7eh^Uy5N|sTfs6SZnhn7Ss^%vjl-Zrtm<7VD_^Jd@Ye9=3^qUX@3e}mzAz(IzYlcrlx1|{;EGpb>VOTm=EA^igH*-IjM%`v5wc(oUBXhD5uoW z63&I*LK&Gyrwpy6jgaBdxja5`m`FglKD+|Mrx*got5}y-C0*7PJ*0>ANI?Xw!mr`W zhE>^ly7{J3MIS+$Ra}*-^4HNI1&o$aCN{GYH9VK&;F5>m@PvZD`HzXh_umQnbK=2^ zpelUvxu8cAMG?!zOaqTOv}94HITc2E$T2Nq`O`Jt#Hd9zhgh_%8O}0~l`AD;F{&5y z4p(fjg^^O7hi!*Zt;A)fy(ey zuVoFRL=0^%n>QH+A$W)(Ynipfrj_LJ;|vA9#cB2=43F2){kFbZ^nOccE&NbL-D$Ti z<+gRWy?t(1uiM)0wx-i!cv}EPSGs$nC$?{Y?23b zaK)iZ4(o-qxLlwPj}gNx5O4++p9wROFOOAh%IdCIkI+OcZP_q2ot+j^*f+ODc?meA z)9jSct3iVQDfd7rOzce<){xuMcF;1g*D`QtcE4q~7IqWun`bu893=Yp68-y$fm+1v z?yp5|se5YY@5!#sa~tP&`!4Rhb1(UJO?8_)KECncjh_=;yS*dZuN;g{?u}0Fj$GQ2 zcP{-f{mt~w#BTTOe&XtG<5d9EX3s{?C#UP)a@&dhmND?BHXf)cfX`=LcjoV@gKl$M zEzY_iwoF*`2GKzX;ZZ`Mqf`vRHxhEaq8a{*hiR4-ImheWAx{kh+6iWzB259PSG`MN z0z2NIhy_v2kJr(Wlnl+{zh8=l+C8_PK}O+m(LaW~mIWP!3G6cQ3GmkMBHvbASl*l2ot>GTncwWS zwgQFt9_ogaUHs_0VZD)Cs+ zvTE2emmyrroMOqL1h&FD47;neN0`m**Je2MF6^;cSYE87N7;c!;!)?JM)Zk>4(on4 z=VyETBLjZl5x=Y3@5=ezeSRkQG?VB}G*ZuUD4zNvwyotJT-ZFdrR9FqF7JUMR0=yt z2vPV4B`-oF+(QMl_;N!C4WNQF+1j;b(0KWNQvofCI1kWdOSY9u#|-Ipkd}nEfEKlo z+y4R_WN04cq-?7Nbuj?f{;A+Zn+Phu3bxvtJOTiLb}E$izbRxy;N+G4osdhRaW#Aq zkw8b3_5g*^FJ=!MfGB`4*~)?)fsBaq9iU6uP(w$B2w+cJ;8Ey@n(M6{yW)B7)X0eC zluYZ2!@Q}{(TVYi5!0=XRERBFo&f&UtIV<2x@IXQb*jJj;d#9YAbm<-Q}h!$*C=7G zV>7~0zKy5wibHDx6oD7;*6dET)Y@;kG;okQE5Ll2gAI!7B(Ki$1?{Lb;+^x$@Sa3?*~ zi1~*HZ)@Ayp+7Qx8`ED-Zw;K;{CGQ4XlQ;%&#jxcZvK+)+d4A#@U7kPv)kimx5mzG zDx2qinE!Tub7t%C(oVX#)m8*JZS>#izk9Uh>+-|&PUi%8)7o~m9DwJmzMr%cen+;^ zPWvIQZ=jH3Hi!v=F;8F&6Qynmz76B+b<=8{bYQ$vrOdNn#MlnT0mSL3NI@3n1Z2Bf z!lviZ>T=y9Fgkd9Yl@VJWx?2yXpjs6ymy1tQGu~w5rdnFo_+B-dM;-oOVZzOCL`TX z5G+BIfmUAS?PAmn2lEX^5EVeDuZ33VxZuR2T9tXH`IJ-iZP*ijnds{$iX=(DqJf`L R$1`<6>b~9c9Kk2J_%GB7wL|~_ literal 0 HcmV?d00001 diff --git a/core/__pycache__/mistral7b.cpython-312.pyc b/core/__pycache__/mistral7b.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e7e8d574b699bbcc53513340b823a4231ae4b4c8 GIT binary patch literal 1741 zcmZWq|7#pY6rb7MyW6`eg zklnq_ca%E%7_zM6x>T3HNrV|-G>3A@sri%{cpNh{mpU$wzG)bmsg+AU{W)I#;d?=U zjXxTOGs|CoA?V3?1wncjb8((@scBQDxiv<4#IBx?u8Q#vhaH3QuHn|Pd%qMqQz*wc{8W44cc?83--*X_nv1k^eoPDCV)M?jUINMY$P7G9ce@# ztLRv-*O~D;4|_d*UU!ez(dBhyysmDqJ@ceJaWv6L?PgFsbtkr|W>zk*y|;dDL(Tl8 z7Ixq$R19lJC{g(Pk6(b2SVna;d$2Tw1yCKYHfI+kusFCsFhH|msW){otTyEZSrmtk z&w%a%L;{*sLur51MG?uX&GQ$Ox-#p7Yu`dh&Plg5oY93%D%AGJdq|JK$_x85p>z*@ zt%TWpfWbz*=W`TBKHK@+0;mEGdl5$9HV*&a2H+40Z7`|}^r=%kYBS5U-T4=1T-P4U z=MAf<88epSjtvcsj*R9tyOJ-{swj5~xL0N!t2*CtfiYTR+1KBCBO8DxJC>c7vjbVK zGV0h?)u9~aT6l_-EM^Ko_+G$UP?<{6vgd^DjT#`(;ff6&xny1E@e4PL)OIUYl_wn+ zFy@dVV1p|zz3H+ZNQ=v5>hdHptTF*-Q17`Y6Z!IF&7rL6iX9g-F6G zjwmkyhHRD{74vGC;K6DS=Y)l`4D2@Y+B&z}`ZwG9SEjexh8j^X-L-sb>C|@m#Af=$ zR=U3t^NyWp#O|t_>XAR%yO+-{o!#iWxc=^g_IDbpcj)jZw?4Y{OS*fbXL#+^?U9Mi zk%^7rOY8Fbr5`81om?N^I5xeNzPizJ74WoteChb7y-jb0we(ioDEL!bw$%)v=kxBB z(gStCJJi`|W!(^4E-D&>Xdi^|1R>B)YKD+o2)R+yj9|q_G|P*WTbr$+NDTve31&w` zngUO+`FFwubbO|W1yRgT7tyZN9+}4fyqt`5`EGrKoWfg0^BDB|6|@s3pv%N3z+b0RR91 literal 0 HcmV?d00001 diff --git a/core/__pycache__/mistral7b.cpython-313.pyc b/core/__pycache__/mistral7b.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fd98cb09499e5cf26c5aeeb4b083da6f8b111e1d GIT binary patch literal 1796 zcmZ7$T~8ZFaPPzR*#=V#1_wfN5YpC(0^&<)XbCM**&X) zyzo@HPe~d@w0+8J-|{o4)T&S_aoV<0)K-;{`jCi3jntRUo_zr^mgi=6XXkrnuemva z0H*)@ql9A!{VA9bA`N110EmakK!$V`U6H7y3tGM^Q#p(JkfC%TLoG;SN?o2RB{>(_ z6--Xzu_=?0>FJqk#rka>%MB(_{TV1PASxM%%7#RhJQ|e@xeG08R29l;#87E8k0vA< z>qof=kI$5uM@{SO)UFDOiqZkqA{^n!-V~to61Xkdpv{&I#ZZli5jA3i!hH>X@qxIl z%)fg4v6@GpLeO*RykaEIDLN{lT=Lp#s!}&MZkZlo9*=%#S!Ts7Ed}uRWa-Br1pG6( zI}Ex?-+m+D*<=|(loxYxp7cn?CDin)l<xY(_KZt@f*E-^jq65F)A6`r)K73@;7 zST1^8WgrWfkVPJafQeb*3N=5^DLhee7Kw#zvqE^3*rl>fcya+Uja`$P6_%5EgjK;P z*Dx;IWe;P>8>q6DXd5_5+X3;^B3Ux47MKqn@i@js%VZ2=CTgz(tnpLtj~86(EEA6v zEUSVoa{=6?%qbKdN(znKoLv}jS7@iuntoNCfzYK%f)OemqFd<2G1yT+r?4Heg0J#oYH-0{Id z%PE@H4TpK-Lqnq@ql2bf9xM@CG(3R?pe{4VUTwcXDXCKZy^lW7>*J%3>#K@>M&}wO z%yn!=ILbBg6kc*@MT|sX1+3M3Q!P5~ijcikg&AaAb-^N+otr#<;dYU@UfHpEl6f#? z3>RTKxayJH9@QZtu9S$!lh|@f7>q&3=b|(#E|OJ7Xu}jeE>2>uxneoyBJCAiv@Xs> z$;CM6I_(zcS{UHLZV%78K!QC0w1)iF%uZ|nR%`#G`R&%BTGUT>te?JrdMDktmG0Y4 z_t#?n(Y|}ymUiT?w(j+buO>EoFK&FY)t0YmeoN=wJ9qE=mhRp>HvIUlosmmhBbPRZ zFK;Ltmw%f1er98G^XU9`y0F<)fN5Iqx!?2Pc*EB9$La0XQLv^p?Pyt;ov*up(a!iS znOZaL0l&V3I*P?0)(6HsfibL;swL1 pour rĂ©duire les rĂ©pĂ©titions, 1 pour aucune pĂ©nalitĂ© + "num_predict": 768, # Nombre maximum de tokens Ă  gĂ©nĂ©rer dans la rĂ©ponse + "stop": [], # Liste de sĂ©quences qui arrĂŞteront la gĂ©nĂ©ration si rencontrĂ©es + "seed": None, # Graine pour la reproductibilitĂ© : fixe la graine pour obtenir les mĂŞmes rĂ©sultats + "stream": False, # Si True, la rĂ©ponse est envoyĂ©e en flux (streaming) + "raw": False # Si True, dĂ©sactive le prompt système automatique + } + + super().__init__(model_name=model_name, engine=engine, base_params=default_params) + + def generate(self, user_prompt): + prompt = self._format_prompt(user_prompt) + payload = self.params.copy() + payload["prompt"] = prompt + + response = requests.post("http://localhost:11434/api/generate", json=payload) + if not response.ok: + raise Exception(f"Erreur API Ollama : {response.status_code} - {response.text}") + + result = response.json().get("response", "") + self._log_result(user_prompt, result) + return result \ No newline at end of file diff --git a/core/mistral7b.py b/core/mistral7b.py new file mode 100644 index 0000000..dfa2f5a --- /dev/null +++ b/core/mistral7b.py @@ -0,0 +1,37 @@ +from core.base_llm import BaseLLM +import requests + +class Mistral7B(BaseLLM): + def __init__(self): + # Nom du modèle spĂ©cifique + model_name = "mistral:latest" + # Moteur utilisĂ© pour l'infĂ©rence + engine = "Ollama" + + # Paramètres par dĂ©faut spĂ©cifiques Ă  Mistral7B + default_params = { + "temperature": 0.7, # ContrĂ´le la crĂ©ativité : 0 = dĂ©terministe, 1 = plus crĂ©atif + "top_p": 0.9, # Nucleus sampling : sĂ©lectionne les tokens jusqu'Ă  une probabilitĂ© cumulative de top_p + "top_k": 50, # Considère les top_k tokens les plus probables pour chaque Ă©tape de gĂ©nĂ©ration + "repeat_penalty": 1.1, # PĂ©nalise les rĂ©pĂ©titions : >1 pour rĂ©duire les rĂ©pĂ©titions, 1 pour aucune pĂ©nalitĂ© + "num_predict": 512, # Nombre maximum de tokens Ă  gĂ©nĂ©rer dans la rĂ©ponse + "stop": [], # Liste de sĂ©quences qui arrĂŞteront la gĂ©nĂ©ration si rencontrĂ©es + "seed": None, # Graine pour la reproductibilité : fixe la graine pour obtenir les mĂŞmes rĂ©sultats + "stream": False, # Si True, la rĂ©ponse est envoyĂ©e en flux (streaming) + "raw": False # Si True, dĂ©sactive le prompt système automatique + } + + super().__init__(model_name=model_name, engine=engine, base_params=default_params) + + def generate(self, user_prompt): + prompt = self._format_prompt(user_prompt) + payload = self.params.copy() + payload["prompt"] = prompt + + response = requests.post("http://localhost:11434/api/generate", json=payload) + if not response.ok: + raise Exception(f"Erreur API Ollama : {response.status_code} - {response.text}") + + result = response.json().get("response", "") + self._log_result(user_prompt, result) + return result diff --git a/cursor_integration.md b/cursor_integration.md new file mode 100644 index 0000000..b3204ea --- /dev/null +++ b/cursor_integration.md @@ -0,0 +1,343 @@ +# IntĂ©gration de LLM Lab avec Cursor + +Ce guide dĂ©taille comment intĂ©grer et utiliser LLM Lab spĂ©cifiquement avec l'Ă©diteur de code [Cursor](https://cursor.sh/). + +## Qu'est-ce que Cursor ? + +Cursor est un Ă©diteur de code moderne basĂ© sur VS Code, enrichi par des capacitĂ©s d'IA pour amĂ©liorer le dĂ©veloppement. Par dĂ©faut, Cursor utilise des modèles cloud (comme Claude ou GPT-4), mais vous pouvez le configurer pour utiliser LLM Lab et ses modèles locaux via Ollama. + +## Avantages de l'intĂ©gration + +- **ConfidentialitĂ©** : Votre code reste local, sans ĂŞtre envoyĂ© Ă  des serveurs externes +- **Personnalisation** : ContrĂ´le total sur les modèles et leurs paramètres +- **Performance** : Exploitation directe de votre GPU local +- **CoĂ»t** : Aucun frais d'API ou d'abonnement + +## Options d'intĂ©gration + +### 1. Agent en ligne de commande + Copier-coller + +La mĂ©thode la plus simple pour commencer : + +1. **Ouvrir deux fenĂŞtres** : + - Cursor pour le dĂ©veloppement + - Terminal pour LLM Lab + +2. **Lancer l'agent Cursor** : + ```cmd + # Windows + run.bat chat cursor + + # Linux + ./run.sh chat cursor + ``` + +3. **Workflow** : + - Copiez le code ou la question depuis Cursor + - Collez dans le terminal LLM Lab + - Copiez la rĂ©ponse gĂ©nĂ©rĂ©e + - Collez dans Cursor + +### 2. Serveur API local pour Cursor + +Pour une expĂ©rience plus intĂ©grĂ©e, configurez un serveur API local : + +1. **CrĂ©er un fichier `api_server.py`** dans le dossier racine de LLM Lab : + +```python +#!/usr/bin/env python3 +""" +Serveur API pour intĂ©grer LLM Lab avec Cursor +""" +from flask import Flask, request, jsonify, Response +from flask_cors import CORS +import json +import os +from utils.agent_manager import AgentManager +import logging + +# Configuration du logging +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', + handlers=[ + logging.FileHandler("logs/api_server.log"), + logging.StreamHandler() + ] +) +logger = logging.getLogger("api_server") + +# Initialisation de l'application Flask +app = Flask(__name__) +CORS(app) # Permet les requĂŞtes cross-origin + +@app.route('/v1/chat/completions', methods=['POST']) +def chat_completion(): + """ + Endpoint compatible avec l'API OpenAI Chat pour Cursor + """ + try: + data = request.json + logger.info(f"RequĂŞte reçue: {json.dumps(data)}") + + # Extraire les messages et les paramètres + messages = data.get('messages', []) + model = data.get('model', 'codellama:13b-python') + temperature = data.get('temperature', 0.7) + + # Construire le prompt Ă  partir des messages + system_message = next((msg['content'] for msg in messages if msg['role'] == 'system'), None) + user_messages = [msg['content'] for msg in messages if msg['role'] == 'user'] + + # Utiliser le dernier message utilisateur comme prompt + prompt = user_messages[-1] if user_messages else "" + + # DĂ©tecter le type de tâche pour choisir l'agent appropriĂ© + agent_name = "cursor" # Par dĂ©faut + if "javascript" in prompt.lower() or "js" in prompt.lower(): + agent_name = "webdev" + elif "python" in prompt.lower(): + agent_name = "python" + + # CrĂ©er et configurer l'agent + agent = AgentManager.create(agent_name) + + # Remplacer le system prompt si fourni + if system_message: + agent.system_prompt = system_message + + # Ajuster les paramètres + agent.params["temperature"] = temperature + + # GĂ©nĂ©rer la rĂ©ponse + response = agent.generate(prompt) + logger.info(f"RĂ©ponse gĂ©nĂ©rĂ©e pour l'agent {agent_name}") + + # Formatage compatible avec l'API OpenAI + return jsonify({ + "id": f"llmlab-{agent_name}-{hash(prompt) % 10000}", + "object": "chat.completion", + "created": int(__import__('time').time()), + "model": agent.model, + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": response + }, + "finish_reason": "stop" + } + ], + "usage": { + "prompt_tokens": len(prompt.split()), + "completion_tokens": len(response.split()), + "total_tokens": len(prompt.split()) + len(response.split()) + } + }) + + except Exception as e: + logger.error(f"Erreur: {str(e)}", exc_info=True) + return jsonify({ + "error": { + "message": str(e), + "type": "server_error", + "code": 500 + } + }), 500 + +@app.route('/models', methods=['GET']) +def list_models(): + """ + Liste les modèles disponibles + """ + agents = AgentManager.list_agents() + models = [] + + for agent_name, info in agents.items(): + models.append({ + "id": info['model'], + "object": "model", + "owned_by": "llmlab", + "permission": [{"id": agent_name, "object": "model_permission"}] + }) + + return jsonify({ + "object": "list", + "data": models + }) + +if __name__ == '__main__': + # CrĂ©er le dossier logs s'il n'existe pas + os.makedirs("logs", exist_ok=True) + + print("=== Serveur API LLM Lab pour Cursor ===") + print("Serveur dĂ©marrĂ© sur http://localhost:8000") + print("Utilisez cette URL dans les paramètres API de Cursor") + print("Modèles disponibles:") + for agent in AgentManager.list_agents(): + print(f"- {agent}") + print("\nLog des requĂŞtes dans logs/api_server.log") + print("Appuyez sur Ctrl+C pour arrĂŞter le serveur") + + app.run(host='0.0.0.0', port=8000, debug=False) +``` + +2. **Ajouter les dĂ©pendances Flask** : + Modifiez `requirements.txt` pour ajouter : + ``` + flask>=2.0.0 + flask-cors>=3.0.10 + ``` + +3. **Mettre Ă  jour `run.bat`** pour inclure la commande API : + ```batch + ) else if "%1"=="api" ( + python api_server.py + ``` + +4. **Lancer le serveur API** : + ```cmd + # Windows + run.bat api + + # Linux + ./run.sh api + ``` + +5. **Configurer Cursor** : + - Ouvrez Cursor + - Allez dans Paramètres > AI + - SĂ©lectionnez "Custom endpoint" + - Entrez l'URL : `http://localhost:8000/v1` + - Activez l'option "Use custom endpoint for all AI features" + +### 3. Extension Cursor (AvancĂ©) + +Pour les dĂ©veloppeurs avancĂ©s, vous pouvez crĂ©er une extension Cursor dĂ©diĂ©e : + +1. **Initialiser un projet d'extension** : + ```cmd + mkdir cursor-llmlab-extension + cd cursor-llmlab-extension + npm init + ``` + +2. **CrĂ©er la structure du projet** conformĂ©ment Ă  la documentation de Cursor + +3. **ImplĂ©menter l'intĂ©gration** avec LLM Lab via l'API locale + +## Conseils d'utilisation + +### Commandes utiles dans Cursor + +- `Ctrl+I` : Invoquer l'IA pour une requĂŞte +- `Ctrl+L` : Chat avec l'IA +- `/edit` : Demander une Ă©dition de code +- `/doc` : GĂ©nĂ©rer de la documentation +- `/test` : GĂ©nĂ©rer des tests unitaires + +### Invites (prompts) efficaces pour CodeLlama + +- **ĂŠtre spĂ©cifique** : "Refactor cette fonction pour utiliser des list comprehensions au lieu des boucles for" +- **Fournir le contexte** : "J'utilise Flask avec SQLAlchemy dans une application REST" +- **Demander des explications** : "Explique comment ce code fonctionne ligne par ligne" +- **Indiquer les contraintes** : "Utilise uniquement des bibliothèques standard, sans dĂ©pendances externes" + +### Exemples pratiques + +#### 1. ComplĂ©tion de fonction + +**Prompt** : "Complète cette fonction qui doit convertir des dates au format ISO en timestamp Unix" + +```python +def iso_to_timestamp(iso_date): + # Ă€ complĂ©ter + pass +``` + +#### 2. DĂ©bogage + +**Prompt** : "Ce code lève une IndexError. Identifie le problème et propose une correction" + +#### 3. Refactoring + +**Prompt** : "Refactorise ce code pour amĂ©liorer sa lisibilitĂ© et ses performances" + +## DĂ©veloppement avancĂ© + +### Personnaliser l'agent Cursor + +Vous pouvez personnaliser l'agent Cursor dans `agents/roles.py` : + +```python +"cursor": { + "model": "codellama:13b-python", + "description": "Agent de programmation optimisĂ© pour Cursor", + "system_prompt": ( + "Tu es un expert en programmation spĂ©cialisĂ© dans l'Ă©criture et la rĂ©vision de code. " + "Pour rĂ©pondre Ă  des questions de programmation, tu dois : " + "1. Fournir du code complet, fonctionnel et bien structurĂ© " + "2. Suivre les meilleures pratiques du langage demandĂ© " + "3. PrivilĂ©gier la lisibilitĂ© et la maintenabilitĂ© " + "4. Expliquer brièvement les concepts importants " + "Quand on te demande de dĂ©boguer, d'amĂ©liorer ou de refactoriser du code, " + "tu dois analyser en dĂ©tail le code fourni et proposer des solutions concrètes. " + "Tes rĂ©ponses doivent ĂŞtre prĂ©cises et contenir du code prĂŞt Ă  l'emploi." + ), + "params": { + "temperature": 0.2, # Valeur basse pour du code prĂ©cis + "top_p": 0.95, + "num_ctx": 4096 + } +} +``` + +### CrĂ©er des agents spĂ©cialisĂ©s + +Vous pouvez crĂ©er des agents spĂ©cialisĂ©s pour diffĂ©rents langages ou tâches : + +```python +"python": { + "model": "codellama:13b-python", + "description": "Expert Python pour le dĂ©veloppement et la rĂ©solution de problèmes", + "system_prompt": ( + "Tu es un expert Python avec une connaissance approfondie de l'Ă©cosystème Python, " + "des bibliothèques standard et des frameworks populaires comme Django, Flask, " + "Pandas, NumPy et TensorFlow. Ton objectif est d'aider Ă  Ă©crire du code Python " + "Ă©lĂ©gant, performant et idiomatique, en suivant les principes PEP 8 et les " + "bonnes pratiques Python. Tu dois favoriser les approches pythoniques comme " + "les comprĂ©hensions de liste, les gĂ©nĂ©rateurs et les fonctions de haut niveau." + ), + "params": { + "temperature": 0.2, + "top_p": 0.95, + "num_ctx": 4096 + } +} +``` + +## DĂ©pannage + +### Problèmes courants + +1. **Erreur de connexion Ă  l'API** : + - VĂ©rifiez que le serveur API est bien en cours d'exĂ©cution + - Confirmez que l'URL dans Cursor est correcte (http://localhost:8000/v1) + - VĂ©rifiez les logs dans `logs/api_server.log` + +2. **RĂ©ponses trop lentes** : + - Optimisez les paramètres d'Ollama avec `optimize_ollama.bat` + - RĂ©duisez la taille du contexte ou utilisez un modèle plus petit + +3. **QualitĂ© de code insuffisante** : + - Ajustez la tempĂ©rature (valeurs plus basses pour plus de prĂ©cision) + - AmĂ©liorez le prompt système de l'agent + - Essayez un modèle plus performant (comme Mixtral ou Llama3) + +### Support + +Pour obtenir de l'aide supplĂ©mentaire : +- Consultez les logs dans le dossier `logs/` +- VĂ©rifiez les issues connues sur le dĂ©pĂ´t GitHub du projet +- Posez vos questions sur le forum de la communautĂ© \ No newline at end of file diff --git a/examples/agent_demo.py b/examples/agent_demo.py new file mode 100644 index 0000000..41f5d50 --- /dev/null +++ b/examples/agent_demo.py @@ -0,0 +1,92 @@ +""" +DĂ©monstration de l'utilisation des agents spĂ©cialisĂ©s avec leurs modèles associĂ©s +""" +from utils.agent_manager import AgentManager +import os +import time + +def list_available_agents(): + """Affiche la liste des agents disponibles avec leurs modèles""" + print("\n=== Agents disponibles ===") + agents_info = AgentManager.list_agents() + + for agent_name, info in agents_info.items(): + print(f"• {agent_name} (modèle: {info['model']})") + print(f" → {info['description']}") + + return list(agents_info.keys()) + +def run_agent_demo(agent_name, prompt): + """ExĂ©cute la dĂ©mo pour un agent spĂ©cifique""" + print(f"\n=== Test de l'agent '{agent_name}' ===") + print(f"Prompt: {prompt}") + print("-" * 50) + + start_time = time.time() + + try: + # CrĂ©ation de l'agent + agent = AgentManager.create(agent_name) + print(f"Agent '{agent_name}' créé avec le modèle '{agent.model}'") + + # GĂ©nĂ©ration de la rĂ©ponse + print("\nGĂ©nĂ©ration en cours...") + response = agent.generate(prompt) + + # Affichage de la rĂ©ponse + print("\nRĂ©ponse:") + print("-" * 50) + print(response) + print("-" * 50) + + # Statistiques + duration = time.time() - start_time + print(f"\nTemps d'exĂ©cution: {duration:.2f} secondes") + print(f"Longueur de la rĂ©ponse: {len(response)} caractères") + + except Exception as e: + print(f"Erreur lors de l'exĂ©cution: {str(e)}") + +if __name__ == "__main__": + os.makedirs("logs", exist_ok=True) + + # Liste des agents disponibles + available_agents = list_available_agents() + + # Prompts de test pour chaque type d'agent + test_prompts = { + # Pour agents de programmation (cursor, python, webdev) + "cursor": "Écris une fonction Python qui implĂ©mente un algorithme de tri fusion (merge sort) efficace.", + "python": "Comment utiliser pandas pour analyser un fichier CSV et calculer des statistiques par groupe?", + "webdev": "CrĂ©e une page HTML avec CSS et JavaScript pour afficher une galerie d'images responsive.", + + # Pour agent de gestion de connaissances (obsidian) + "obsidian": "Comment organiser un système de notes pour un projet de recherche avec des liens entre concepts?", + + # Pour agents de gestion de projet et documentation + "projectmanager": "Quelles sont les Ă©tapes clĂ©s pour mettre en place une mĂ©thodologie agile dans une Ă©quipe?", + "documentaliste": "Comment structurer la documentation d'une API REST?", + + # Pour agent de test + "test": "VĂ©rifie que tu peux gĂ©nĂ©rer une rĂ©ponse courte." + } + + # Exemples d'utilisation + print("\n" + "=" * 80) + print("DÉMONSTRATION DES AGENTS SPÉCIALISÉS") + print("=" * 80) + + # Option 1: Tester un agent spĂ©cifique + agent_to_test = "cursor" # Changer pour tester un agent diffĂ©rent + if agent_to_test in available_agents: + prompt = test_prompts.get(agent_to_test, "PrĂ©sente-toi et explique tes capacitĂ©s.") + run_agent_demo(agent_to_test, prompt) + + # Option 2: Tester tous les agents (dĂ©commentez pour activer) + """ + print("\n=== Test de tous les agents ===") + for agent_name in available_agents: + prompt = test_prompts.get(agent_name, "PrĂ©sente-toi et explique tes capacitĂ©s.") + run_agent_demo(agent_name, prompt) + print("\n" + "=" * 80) + """ \ No newline at end of file diff --git a/examples/model_comparison.py b/examples/model_comparison.py new file mode 100644 index 0000000..9e01052 --- /dev/null +++ b/examples/model_comparison.py @@ -0,0 +1,52 @@ +""" +Exemple de comparaison des diffĂ©rents modèles LLM sur un mĂŞme prompt +""" +from core.factory import LLMFactory +import os + +def compare_models(prompt, models=None): + """Compare les rĂ©ponses de diffĂ©rents modèles sur un mĂŞme prompt""" + if models is None: + models = ["mistral7b", "codellama13b-python", "llama2-13b"] + + results = {} + + print(f"Comparaison des modèles sur le prompt:\n{prompt}\n") + print("=" * 80) + + for model_name in models: + print(f"\nTesting {model_name}...") + try: + # CrĂ©ation du modèle via la factory + model = LLMFactory.create(model_name) + + # GĂ©nĂ©ration avec le prompt donnĂ© + result = model.generate(prompt) + results[model_name] = result + + # Affichage d'une partie de la rĂ©ponse + preview = result[:200] + "..." if len(result) > 200 else result + print(f"âś“ RĂ©ponse ({len(result)} caractères):\n{preview}") + + except Exception as e: + print(f"âś— Erreur: {str(e)}") + + return results + +if __name__ == "__main__": + # CrĂ©ation du dossier examples si nĂ©cessaire + os.makedirs("examples", exist_ok=True) + + # Prompts de test pour diffĂ©rents cas d'usage + prompts = { + "gĂ©nĂ©ration_texte": "Écris un poème sur l'intelligence artificielle en 4 vers.", + "programmation": "Écris une fonction Python qui calcule la suite de Fibonacci de manière rĂ©cursive.", + "raisonnement": "Si j'ai 5 pommes et que je donne 2 pommes Ă  Marie, puis que Jean me donne 3 pommes, combien de pommes ai-je maintenant?" + } + + # ExĂ©cution des tests + for task, prompt in prompts.items(): + print(f"\n\n{'#' * 80}") + print(f"# TEST: {task}") + print(f"{'#' * 80}") + compare_models(prompt) \ No newline at end of file diff --git a/installation_ollama_disques_separes.md b/installation_ollama_disques_separes.md new file mode 100644 index 0000000..cb9de76 --- /dev/null +++ b/installation_ollama_disques_separes.md @@ -0,0 +1,212 @@ +# Guide d'installation d'Ollama avec modèles sur un disque sĂ©parĂ© + +Ce guide vous explique comment installer Ollama sur votre disque système Windows (C:) tout en stockant les modèles volumineux sur un autre disque pour Ă©conomiser de l'espace. + +## Pourquoi sĂ©parer Ollama et ses modèles? + +- Les modèles LLM peuvent occuper plusieurs dizaines de Go +- Le disque système (C:) est souvent limitĂ© en espace +- Les SSD secondaires ou disques durs offrent plus d'espace de stockage +- Cette configuration permet d'optimiser l'espace disque tout en gardant de bonnes performances + +## 1. Installation d'Ollama sur le disque principal + +1. TĂ©lĂ©chargez Ollama depuis [ollama.com/download/windows](https://ollama.com/download/windows) +2. ExĂ©cutez l'installateur avec les options par dĂ©faut (C:\Program Files\Ollama) +3. Laissez l'installation se terminer et le service dĂ©marrer + +## 2. ArrĂŞter le service Ollama + +Avant de modifier la configuration, arrĂŞtez le service Ollama: + +1. Ouvrez un terminal administrateur (clic droit > ExĂ©cuter en tant qu'administrateur) +2. ExĂ©cutez la commande: + ```cmd + net stop Ollama + ``` + +## 3. CrĂ©er le dossier de destination pour les modèles + +Choisissez un disque avec beaucoup d'espace libre (par exemple D:, E:, etc.): + +```cmd +mkdir D:\OllamaModels +``` + +## 4. Configurer le stockage des modèles + +### MĂ©thode 1: Lien symbolique (recommandĂ©e) + +Cette mĂ©thode permet Ă  Ollama de continuer Ă  chercher les modèles au mĂŞme endroit, mais Windows redirige vers le nouveau disque: + +```cmd +rmdir /s /q "C:\Users\%USERNAME%\.ollama\models" +mklink /d "C:\Users\%USERNAME%\.ollama\models" "D:\OllamaModels" +``` + +### MĂ©thode 2: Variable d'environnement OLLAMA_MODELS + +Cette mĂ©thode indique Ă  Ollama oĂą trouver les modèles: + +1. Ouvrez le Panneau de configuration > Système > Paramètres système avancĂ©s +2. Cliquez sur "Variables d'environnement" +3. Ajoutez une nouvelle variable système: + - Nom: `OLLAMA_MODELS` + - Valeur: `D:\OllamaModels` +4. Cliquez sur OK pour sauvegarder + +## 5. RedĂ©marrer le service Ollama + +Pour appliquer les changements: + +```cmd +net start Ollama +``` + +## 6. Tester la configuration + +1. VĂ©rifiez que le service fonctionne correctement: + ```cmd + curl http://localhost:11434/api/tags + ``` + +2. TĂ©lĂ©chargez un petit modèle pour tester: + ```cmd + ollama pull tinyllama + ``` + +3. VĂ©rifiez que le modèle est bien stockĂ© sur le disque secondaire en examinant: + ```cmd + dir D:\OllamaModels + ``` + +## 7. Migrer les modèles existants + +Si vous avez des modèles existants sur un autre disque ou sous WSL, vous pouvez les migrer: + +### Depuis WSL + +```cmd +robocopy \\wsl$\Ubuntu\home\fgras-ca\.ollama\models D:\OllamaModels /E +``` + +### Depuis un autre disque Windows + +```cmd +robocopy E:\CheminVersModeles D:\OllamaModels /E +``` + +## 8. Configuration optimale pour les performances + +Pour optimiser les performances, crĂ©ez un fichier `ollama_performance.bat`: + +```batch +@echo off +setlocal + +echo Configuration des performances d'Ollama... + +REM RĂ©pertoire des modèles +set OLLAMA_MODELS=D:\OllamaModels + +REM Paramètres de performance +set CUDA_VISIBLE_DEVICES=0 +set OMP_NUM_THREADS=8 + +REM RedĂ©marrer le service Ollama +net stop Ollama +timeout /t 2 +net start Ollama + +echo Configuration terminĂ©e! +echo Modèles stockĂ©s dans: %OLLAMA_MODELS% +echo. +echo Ollama est prĂŞt Ă  utiliser! + +endlocal +``` + +## 9. IntĂ©gration avec LLM Lab + +Pour que LLM Lab utilise correctement cette configuration: + +1. Assurez-vous que l'interface de monitoring dĂ©tecte correctement le nouvel emplacement +2. CrĂ©ez un fichier `C:\LLM_Lab\ollama_config.json` avec: + +```json +{ + "models_dir": "D:\\OllamaModels", + "gpu_layers": -1, + "num_ctx": 8192, + "num_thread": 12, + "num_batch": 512 +} +``` + +3. Modifiez le fichier `C:\LLM_Lab\utils\ollama_api.py` pour qu'il vĂ©rifie l'emplacement des modèles. + +## 10. VĂ©rification de l'espace disque + +Ajoutez cette fonction au moniteur système pour surveiller l'espace disque des modèles: + +```python +def check_models_disk_space(): + """VĂ©rifie l'espace disque pour les modèles Ollama""" + models_dir = "D:\\OllamaModels" # Ajustez selon votre configuration + + if os.path.exists(models_dir): + try: + total, used, free = shutil.disk_usage(models_dir) + total_gb = total / (1024**3) + used_gb = used / (1024**3) + free_gb = free / (1024**3) + + print(f"Disque des modèles: {models_dir}") + print(f"Espace total: {total_gb:.1f} GB") + print(f"Espace utilisĂ©: {used_gb:.1f} GB") + print(f"Espace libre: {free_gb:.1f} GB") + except: + print("Erreur lors de la vĂ©rification de l'espace disque") +``` + +## DĂ©pannage + +### Les modèles ne sont pas stockĂ©s au bon endroit + +1. VĂ©rifiez que le service Ollama a bien les permissions d'accès au dossier D:\OllamaModels +2. VĂ©rifiez que le lien symbolique a bien Ă©tĂ© créé avec: + ```cmd + dir /al "C:\Users\%USERNAME%\.ollama" + ``` + +### Erreurs lors du tĂ©lĂ©chargement des modèles + +1. ArrĂŞtez le service Ollama +2. VĂ©rifiez les logs dans `C:\Users\%USERNAME%\.ollama\logs` +3. Assurez-vous que le disque cible a suffisamment d'espace libre +4. RedĂ©marrez le service + +### Le service Ollama ne dĂ©marre pas + +1. VĂ©rifiez les services Windows (services.msc) +2. Consultez les logs d'Ă©vĂ©nements Windows +3. Essayez de dĂ©marrer Ollama manuellement: + ```cmd + "C:\Program Files\Ollama\ollama.exe" serve + ``` + +## Maintenance + +### Mise Ă  jour d'Ollama + +Lors des mises Ă  jour d'Ollama, votre configuration de stockage sur disque sĂ©parĂ© sera prĂ©servĂ©e tant que vous ne supprimez pas manuellement le lien symbolique ou la variable d'environnement. + +### Nettoyage des modèles + +Pour libĂ©rer de l'espace, vous pouvez supprimer les modèles inutilisĂ©s: + +```cmd +ollama rm nom-du-modele +``` + +Cela supprimera correctement le modèle de votre disque secondaire. \ No newline at end of file diff --git a/llm_lab_installation_guide.md b/llm_lab_installation_guide.md new file mode 100644 index 0000000..1b1fb7c --- /dev/null +++ b/llm_lab_installation_guide.md @@ -0,0 +1,242 @@ +# Guide d'installation et d'intĂ©gration de LLM Lab + +Ce guide dĂ©taille la procĂ©dure complète pour installer LLM Lab et l'intĂ©grer avec Cursor et Obsidian via API afin d'optimiser l'utilisation de vos modèles de langage locaux. + +## Table des matières + +- [PrĂ©requis](#prĂ©requis) +- [Installation de l'environnement](#installation-de-lenvironnement) +- [Configuration d'Ollama](#configuration-dollama) +- [IntĂ©gration avec Cursor](#intĂ©gration-avec-cursor) +- [IntĂ©gration avec Obsidian](#intĂ©gration-avec-obsidian) +- [Optimisations et conseils](#optimisations-et-conseils) +- [DĂ©pannage](#dĂ©pannage) + +## PrĂ©requis + +Avant de commencer, assurez-vous d'avoir installĂ© : + +- Windows 11 Pro (ou toute autre version rĂ©cente de Windows) +- Python 3.10+ (avec l'option "Add to PATH" cochĂ©e) +- [Ollama](https://ollama.com/download/windows) pour Windows +- [Cursor](https://cursor.sh/) (Ă©diteur de code) +- [Obsidian](https://obsidian.md/download) (optionnel, pour la gestion de connaissances) +- [Git](https://git-scm.com/download/win) (optionnel, pour cloner le dĂ©pĂ´t) + +## Installation de l'environnement + +### 1. CrĂ©er un dossier pour le projet + +```cmd +mkdir C:\projets\llm_lab +cd C:\projets\llm_lab +``` + +### 2. TĂ©lĂ©charger le code source + +MĂ©thode avec Git : + +```cmd +git clone https://github.com/votre-repo/llm_lab.git . +``` + +Ou tĂ©lĂ©chargez et extrayez manuellement les fichiers du projet. + +### 3. CrĂ©er l'environnement virtuel Python + +La crĂ©ation d'un environnement virtuel est **fortement recommandĂ©e** pour isoler les dĂ©pendances du projet : + +```cmd +# Depuis le dossier du projet +setup_env.bat +``` + +Ce script va : +- CrĂ©er un environnement virtuel Python nommĂ© `llmlab` +- Installer toutes les dĂ©pendances nĂ©cessaires, y compris Flask pour l'API +- Configurer les dossiers requis + +### 4. VĂ©rifier l'installation + +ExĂ©cutez le script de test pour vĂ©rifier que tout est correctement installĂ© : + +```cmd +test_installation.bat +``` + +## Configuration d'Ollama + +### 1. Installer les modèles requis + +```cmd +# Modèle polyvalent lĂ©ger +ollama pull mistral:latest + +# Pour le dĂ©veloppement et le code +ollama pull codellama:13b-python + +# Pour la gestion de connaissances (optionnel) +ollama pull llama2:13b +``` + +### 2. Optimiser Ollama pour Windows + +ExĂ©cutez le script d'optimisation (en tant qu'administrateur) : + +```cmd +# Clic droit sur optimize_ollama.bat > ExĂ©cuter en tant qu'administrateur +optimize_ollama.bat +``` + +## IntĂ©gration avec Cursor + +### 1. Lancer le serveur API + +```cmd +run.bat api +``` + +Vous devriez voir un message confirmant que le serveur est dĂ©marrĂ© sur `http://localhost:8000`. + +### 2. Configurer Cursor + +1. Ouvrez Cursor +2. Allez dans **Settings** (⚙️) > **AI** +3. Faites dĂ©filer jusqu'Ă  **API URL** et activez **Use custom endpoint** +4. Entrez l'URL : `http://localhost:8000/v1` +5. Cochez **Use custom endpoint for all AI features** +6. Cliquez sur **Save** + +### 3. Tester l'intĂ©gration + +1. Ouvrez ou crĂ©ez un fichier dans Cursor +2. Appuyez sur `Ctrl+I` pour invoquer l'IA +3. Tapez une requĂŞte, par exemple : "Écris une fonction pour calculer le nombre de Fibonacci" +4. VĂ©rifiez que la rĂ©ponse provient de votre modèle local (via les logs dans la console oĂą le serveur API est en cours d'exĂ©cution) + +## IntĂ©gration avec Obsidian + +### 1. Configurer l'agent Obsidian + +Avant d'utiliser l'agent Obsidian, vous pouvez personnaliser son prompt système dans `agents/roles.py` : + +```python +"obsidian": { + "model": "llama2:13b", # Vous pouvez utiliser mistral:latest pour un modèle plus lĂ©ger + "description": "Agent de gestion de connaissances optimisĂ© pour Obsidian", + "system_prompt": ( + "Tu es un expert en gestion de connaissances et en organisation d'informations, " + "spĂ©cialisĂ© dans l'utilisation d'Obsidian. Tu maĂ®trises la syntaxe markdown " + "utilisĂ©e par Obsidian, y compris :\n" + "- Les liens internes [[Nom de note]]\n" + "- Les liens internes avec alias [[Nom de note|Alias]]\n" + "- Les rĂ©fĂ©rences de blocs ^blockref\n" + "- Les tags #tag #important\n" + "- Les listes de tâches - [ ] et - [x]\n" + "- Les callouts > [!note] et > [!important]\n" + "- Le YAML front matter pour les mĂ©tadonnĂ©es\n\n" + "Tu aides Ă  structurer l'information de manière claire, Ă  crĂ©er des connexions " + "significatives entre les notes, et Ă  concevoir des systèmes de prise de notes " + "efficaces. Tu rĂ©ponds toujours en utilisant la syntaxe Markdown d'Obsidian " + "appropriĂ©e et tu fournis des exemples concrets adaptĂ©s au contexte." + ), + "params": { + "temperature": 0.7, + "top_p": 0.9, + "num_ctx": 4096 + } +} +``` + +### 2. Workflow d'intĂ©gration via API + +1. **Assurez-vous que le serveur API est en cours d'exĂ©cution** : + ```cmd + run.bat api + ``` + +2. **MĂ©thode d'intĂ©gration basique** : + - CrĂ©ez une nouvelle note dans Obsidian + - Écrivez votre requĂŞte + - Copiez la requĂŞte et utilisez un navigateur ou un outil comme Postman pour envoyer une requĂŞte Ă  l'API + - Exemple avec curl : + ```cmd + curl -X POST http://localhost:8000/generate -H "Content-Type: application/json" -d "{\"prompt\":\"CrĂ©e un template pour une note de projet\",\"agent\":\"obsidian\"}" + ``` + - Copiez la rĂ©ponse dans votre note Obsidian + +3. **IntĂ©gration plus poussĂ©e** (nĂ©cessite des compĂ©tences en dĂ©veloppement) : + - CrĂ©ez un plugin Obsidian personnalisĂ© qui se connecte Ă  l'API LLM Lab + - Utilisez le point d'entrĂ©e `/generate` qui est plus simple Ă  intĂ©grer + +## Optimisations et conseils + +### Optimiser les performances + +1. **Ajustez les paramètres Ollama** : + - Modifiez le fichier `%USERPROFILE%\.ollama\config.json` pour optimiser l'utilisation de la RAM et du GPU + - Exemple de configuration optimale pour un PC avec 32 Go de RAM et un GPU NVIDIA : + ```json + { + "gpu_layers": -1, + "num_ctx": 8192, + "num_thread": 8, + "num_batch": 512 + } + ``` + +2. **Optimisez votre environnement Windows** : + - RĂ©glez le plan d'alimentation sur "Hautes performances" + - DĂ©sactivez les applications en arrière-plan non essentielles + - Assurez-vous que l'antivirus exclut les dossiers d'Ollama et de LLM Lab + +### Workflow recommandĂ© + +1. **Pour le dĂ©veloppement de code avec Cursor** : + - Gardez le serveur API en cours d'exĂ©cution dans un terminal dĂ©diĂ© + - Utilisez des commandes spĂ©cifiques comme `/edit` ou `/doc` dans Cursor + - Ajustez la tempĂ©rature Ă  0.2-0.3 pour du code prĂ©cis + +2. **Pour la gestion de connaissances avec Obsidian** : + - Utilisez l'agent Obsidian pour crĂ©er des templates + - GĂ©nĂ©rez des structures MOC (Map of Content) + - Enrichissez des notes existantes + +## DĂ©pannage + +### Problèmes courants et solutions + +1. **L'API ne dĂ©marre pas** : + - VĂ©rifiez que toutes les dĂ©pendances sont installĂ©es : `pip install -r requirements.txt` + - Assurez-vous que Flask est bien installĂ© : `pip install flask flask-cors` + - VĂ©rifiez qu'aucun autre service n'utilise le port 8000 + +2. **Cursor ne se connecte pas Ă  l'API** : + - VĂ©rifiez que l'URL est correcte : `http://localhost:8000/v1` + - Assurez-vous que le serveur API est en cours d'exĂ©cution + - VĂ©rifiez les logs dans `logs/api_server.log` + +3. **Ollama Ă©choue Ă  charger les modèles** : + - Assurez-vous qu'Ollama est en cours d'exĂ©cution : vĂ©rifiez dans les services Windows + - RedĂ©marrez le service Ollama + - VĂ©rifiez que vous avez suffisamment d'espace disque + +### Logs et diagnostic + +Les logs de l'API se trouvent dans le dossier `logs/api_server.log`. Consultez ce fichier en cas de problème pour identifier la source de l'erreur. + +Pour les logs d'Ollama, consultez : +``` +%USERPROFILE%\.ollama\logs +``` + +## Conclusion + +Vous avez maintenant un environnement complet pour intĂ©grer des modèles de langage locaux dans vos outils de dĂ©veloppement et de gestion de connaissances. Cette configuration vous permet de : + +- Garder vos donnĂ©es confidentielles en utilisant des modèles locaux +- Personnaliser les modèles et les prompts selon vos besoins +- Optimiser les performances en ajustant les paramètres +- IntĂ©grer l'IA dans votre workflow quotidien + +N'hĂ©sitez pas Ă  explorer les diffĂ©rentes options d'agents et Ă  personnaliser les prompts système pour obtenir des rĂ©sultats optimaux pour vos cas d'utilisation spĂ©cifiques. \ No newline at end of file diff --git a/llm_lab_integration_guide.md b/llm_lab_integration_guide.md new file mode 100644 index 0000000..c241588 --- /dev/null +++ b/llm_lab_integration_guide.md @@ -0,0 +1,248 @@ +# Guide d'IntĂ©gration de LLM Lab avec Cursor et Obsidian + +Ce guide dĂ©taille comment intĂ©grer et utiliser efficacement le projet LLM Lab avec les applications Cursor (Ă©diteur de code) et Obsidian (gestion de connaissances). + +## Table des matières + +- [Guide d'IntĂ©gration de LLM Lab avec Cursor et Obsidian](#guide-dintĂ©gration-de-llm-lab-avec-cursor-et-obsidian) + - [Table des matières](#table-des-matières) + - [IntĂ©gration avec Cursor](#intĂ©gration-avec-cursor) + - [Utilisation via l'interface en ligne de commande](#utilisation-via-linterface-en-ligne-de-commande) + - [Utilisation via l'extension Cursor](#utilisation-via-lextension-cursor) + - [Cas d'utilisation Cursor](#cas-dutilisation-cursor) + - [IntĂ©gration avec Obsidian](#intĂ©gration-avec-obsidian) + - [Configuration de l'agent Obsidian](#configuration-de-lagent-obsidian) + - [Utilisation dans le flux de travail Obsidian](#utilisation-dans-le-flux-de-travail-obsidian) + - [Cas d'utilisation Obsidian](#cas-dutilisation-obsidian) + - [Adaptations et AmĂ©liorations](#adaptations-et-amĂ©liorations) + - [AmĂ©liorations Cursor](#amĂ©liorations-cursor) + - [AmĂ©liorations Obsidian](#amĂ©liorations-obsidian) + - [AmĂ©liorations gĂ©nĂ©rales](#amĂ©liorations-gĂ©nĂ©rales) + - [Conclusion](#conclusion) + +## IntĂ©gration avec Cursor + +[Cursor](https://cursor.sh/) est un Ă©diteur de code basĂ© sur VS Code qui intègre des fonctionnalitĂ©s d'IA avancĂ©es. L'intĂ©gration avec LLM Lab permet d'utiliser des modèles locaux via Ollama au lieu des modèles cloud par dĂ©faut. + +### Utilisation via l'interface en ligne de commande + +Vous pouvez utiliser l'agent Cursor de LLM Lab directement depuis la ligne de commande pour gĂ©nĂ©rer du code ou obtenir des explications: + +1. **Lancer l'agent Cursor**: + ```cmd + # Windows + run.bat chat cursor + + # Linux + ./run.sh chat cursor + ``` + +2. **Utilisation interactive**: + ``` + > Écris une fonction Python pour trier une liste d'objets par attribut + + [RĂ©ponse de l'agent avec le code gĂ©nĂ©rĂ©] + ``` + +3. **Export des rĂ©sultats**: + Les rĂ©sultats peuvent ĂŞtre sauvegardĂ©s dans le dossier `chat_history` et utilisĂ©s dans Cursor. + +### Utilisation via l'extension Cursor + +Pour une intĂ©gration plus poussĂ©e, vous pouvez configurer Cursor pour utiliser LLM Lab via une API locale: + +1. **Configurer l'API locale** (Ă  dĂ©velopper): + CrĂ©ez un fichier `api_server.py` dans le projet LLM Lab: + ```python + from flask import Flask, request, jsonify + from utils.agent_manager import AgentManager + import threading + + app = Flask(__name__) + + @app.route('/generate', methods=['POST']) + def generate(): + data = request.json + prompt = data.get('prompt', '') + agent_name = data.get('agent', 'cursor') + + try: + agent = AgentManager.create(agent_name) + response = agent.generate(prompt) + return jsonify({"response": response}) + except Exception as e: + return jsonify({"error": str(e)}), 500 + + if __name__ == '__main__': + app.run(port=8000) + ``` + +2. **Lancer le serveur API**: + ```cmd + # Windows + run.bat api + + # Linux + ./run.sh api + ``` + +3. **Configurer Cursor pour utiliser l'API locale**: + - Ouvrez Cursor + - Allez dans les paramètres (⚙️) + - SĂ©lectionnez "AI" + - Configurez l'URL de l'API personnalisĂ©e: `http://localhost:8000/generate` + +### Cas d'utilisation Cursor + +1. **ComplĂ©tion de code**: + - Utiliser LLM Lab pour gĂ©nĂ©rer des suggestions de code basĂ©es sur le contexte + - ComplĂ©ter des fonctions ou classes partiellement Ă©crites + +2. **Refactoring**: + - Demander Ă  l'agent de refactoriser un bloc de code + - Optimiser des performances ou amĂ©liorer la lisibilitĂ© + +3. **DĂ©bogage**: + - Analyser des erreurs et obtenir des suggestions de correction + - Comprendre des messages d'erreur complexes + +4. **Documentation**: + - GĂ©nĂ©rer automatiquement des docstrings et commentaires + - CrĂ©er des README ou des guides d'utilisation + +## IntĂ©gration avec Obsidian + +[Obsidian](https://obsidian.md/) est une application de gestion de connaissances basĂ©e sur des fichiers Markdown. L'intĂ©gration avec LLM Lab permet d'utiliser des modèles locaux pour amĂ©liorer la prise de notes. + +### Configuration de l'agent Obsidian + +L'agent Obsidian de LLM Lab est spĂ©cialement conçu pour la gestion de connaissances: + +1. **Personnalisation du prompt système**: + Modifiez le fichier `agents/roles.py` pour optimiser l'agent Obsidian: + ```python + "obsidian": { + "model": "llama2:13b", + "description": "Agent de gestion de connaissances optimisĂ© pour Obsidian", + "system_prompt": ( + "Tu es un expert en gestion de connaissances spĂ©cialisĂ© dans l'utilisation d'Obsidian. " + "Tu aides Ă  organiser l'information, crĂ©er des structures de notes efficaces, " + "et maximiser l'utilisation des fonctionnalitĂ©s d'Obsidian comme les liens, " + "les tags, les backlinks, et les graphes de connaissances. " + "Utilise la syntaxe markdown d'Obsidian dans tes rĂ©ponses." + ), + "params": { + "temperature": 0.7, + "top_p": 0.9, + "num_ctx": 4096 + } + } + ``` + +### Utilisation dans le flux de travail Obsidian + +1. **GĂ©nĂ©ration de contenu**: + ```cmd + # Windows + run.bat chat obsidian + + # Linux + ./run.sh chat obsidian + ``` + +2. **IntĂ©gration via plugin Obsidian** (Ă  dĂ©velopper): + CrĂ©ez un plugin Obsidian qui se connecte Ă  l'API locale LLM Lab: + - Interface pour envoyer des requĂŞtes depuis Obsidian + - Insertion automatique des rĂ©ponses dans les notes + - PossibilitĂ© d'analyser des notes existantes + +3. **Utilisation du rĂ©sultat dans Obsidian**: + - Copier-coller les rĂ©ponses gĂ©nĂ©rĂ©es dans vos notes Obsidian + - Exporter les conversations au format Markdown pour Obsidian + +### Cas d'utilisation Obsidian + +1. **Structuration de coffres (vaults)**: + - GĂ©nĂ©rer des suggestions pour organiser les dossiers et fichiers + - CrĂ©er des MOCs (Maps of Content) pour naviguer efficacement + +2. **CrĂ©ation de modèles (templates)**: + - GĂ©nĂ©rer des modèles de notes pour diffĂ©rents types de contenu + - CrĂ©er des structures YAML front matter optimisĂ©es + +3. **Analyse de notes**: + - Demander des rĂ©sumĂ©s ou des insights sur vos notes + - Identifier des connexions entre concepts distincts + +4. **AmĂ©lioration de contenu**: + - Enrichir des notes existantes avec des informations supplĂ©mentaires + - Reformuler ou clarifier des sections confuses + +## Adaptations et AmĂ©liorations + +### AmĂ©liorations Cursor + +1. **IntĂ©gration approfondie avec l'API de Cursor**: + - DĂ©velopper un plugin dĂ©diĂ© pour Cursor + - Exposer l'API LLM Lab via WebSocket pour des rĂ©ponses en temps rĂ©el + +2. **Contexte de projet**: + - Permettre Ă  l'agent d'analyser l'ensemble du projet en cours + - GĂ©nĂ©rer du code adaptĂ© Ă  la structure et au style du projet + +3. **SpĂ©cialisation par langage**: + - CrĂ©er des agents spĂ©cifiques pour diffĂ©rents langages (Python, JavaScript, etc.) + - Optimiser les paramètres pour la gĂ©nĂ©ration de code + +4. **Outils de dĂ©veloppement**: + - IntĂ©grer des fonctionnalitĂ©s comme la gĂ©nĂ©ration de tests unitaires + - CrĂ©er des agents pour l'analyse de performance et de sĂ©curitĂ© + +### AmĂ©liorations Obsidian + +1. **Plugin officiel Obsidian**: + - DĂ©velopper un plugin complet pour l'interface Obsidian + - Ajouter des fonctionnalitĂ©s comme la gĂ©nĂ©ration de graphes conceptuels + +2. **Analyse sĂ©mantique de coffres**: + - Permettre Ă  l'agent d'indexer et d'analyser l'ensemble du coffre Obsidian + - GĂ©nĂ©rer des connexions entre notes basĂ©es sur le contenu sĂ©mantique + +3. **Templates intelligents**: + - CrĂ©er des systèmes de templates dynamiques basĂ©s sur le contexte + - Utiliser l'agent pour complĂ©ter automatiquement des sections de notes + +4. **Assistant de recherche**: + - DĂ©velopper une fonctionnalitĂ© de recherche sĂ©mantique dans les notes + - Permettre des requĂŞtes en langage naturel vers votre base de connaissances + +### AmĂ©liorations gĂ©nĂ©rales + +1. **Interface utilisateur dĂ©diĂ©e**: + - DĂ©velopper une interface graphique native pour Windows/Linux + - Ajouter des fonctionnalitĂ©s de visualisation et d'exportation + +2. **Performance des modèles**: + - Optimiser les paramĂ©trages d'Ollama pour une meilleure performance + - Explorer l'utilisation de modèles quantifiĂ©s pour rĂ©duire l'empreinte mĂ©moire + +3. **MultimodalitĂ©**: + - IntĂ©grer des capacitĂ©s de traitement d'images pour les captures d'Ă©cran de code + - Ajouter le support pour la gĂ©nĂ©ration et l'analyse de diagrammes + +4. **IntĂ©gration avec d'autres outils**: + - GitHub/GitLab pour l'analyse de code et les pull requests + - Notion, Logseq ou autres outils de productivitĂ© + +## Conclusion + +LLM Lab offre de nombreuses possibilitĂ©s d'intĂ©gration avec Cursor et Obsidian, permettant d'amĂ©liorer considĂ©rablement votre flux de travail de dĂ©veloppement et de gestion de connaissances. Les adaptations proposĂ©es peuvent ĂŞtre implĂ©mentĂ©es progressivement pour enrichir l'expĂ©rience utilisateur et maximiser la valeur ajoutĂ©e des modèles de langage locaux. + +Pour commencer, nous recommandons: + +1. Configurer LLM Lab avec les modèles appropriĂ©s (CodeLlama pour Cursor, Llama2 pour Obsidian) +2. Tester les agents en ligne de commande pour Ă©valuer la qualitĂ© des rĂ©ponses +3. DĂ©velopper les intĂ©grations API simples pour connecter avec vos outils +4. Progressivement implĂ©menter les amĂ©liorations suggĂ©rĂ©es selon vos besoins + +N'hĂ©sitez pas Ă  contribuer au projet en dĂ©veloppant ces intĂ©grations et en partageant vos expĂ©riences! \ No newline at end of file diff --git a/log_index.md b/log_index.md new file mode 100644 index 0000000..9c580bb --- /dev/null +++ b/log_index.md @@ -0,0 +1,4 @@ +- **2025-03-25 20:55:35** | đź§  `codellama:13b-python` (Ollama) | 🎭 `python` | [Voir le log](logs/codellama:13b-python_2025-03-25_20-55-35_0b82fbbf.md) +- **2025-03-25 21:00:17** | đź§  `mistral:latest` (Ollama) | 🎭 `test` | [Voir le log](logs/mistral:latest_2025-03-25_21-00-17_aa61f01e.md) +- **2025-03-26 22:45:09** | đź§  `codellama:13b-python` (Ollama) | 🎭 `cursor` | [Voir le log](logs/codellama:13b-python_2025-03-26_22-45-09_d9bd1caa.md) +- **2025-03-26 22:46:28** | đź§  `codellama:13b-python` (Ollama) | 🎭 `cursor` | [Voir le log](logs/codellama:13b-python_2025-03-26_22-46-28_306dd4e9.md) diff --git a/logs/api_server.log b/logs/api_server.log new file mode 100644 index 0000000..3c0c5a1 --- /dev/null +++ b/logs/api_server.log @@ -0,0 +1,17 @@ +2025-03-26 21:23:43,124 - werkzeug - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:8000 + * Running on http://192.168.31.86:8000 +2025-03-26 21:23:43,125 - werkzeug - INFO - Press CTRL+C to quit +2025-03-26 21:32:37,569 - werkzeug - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:8000 + * Running on http://192.168.31.86:8000 +2025-03-26 21:32:37,569 - werkzeug - INFO - Press CTRL+C to quit +2025-03-26 21:57:22,642 - werkzeug - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:8000 + * Running on http://192.168.31.86:8000 +2025-03-26 21:57:22,642 - werkzeug - INFO - Press CTRL+C to quit +2025-03-26 21:57:28,897 - werkzeug - INFO - 127.0.0.1 - - [26/Mar/2025 21:57:28] "GET /health HTTP/1.1" 200 - +2025-03-26 21:57:29,503 - werkzeug - INFO - 127.0.0.1 - - [26/Mar/2025 21:57:29] "GET /favicon.ico HTTP/1.1" 404 - diff --git a/logs/codellama b/logs/codellama new file mode 100644 index 0000000..e69de29 diff --git a/logs/codellama13b-python_2025-03-25_20-55-35_0b82fbbf.md b/logs/codellama13b-python_2025-03-25_20-55-35_0b82fbbf.md new file mode 100644 index 0000000..57cea02 --- /dev/null +++ b/logs/codellama13b-python_2025-03-25_20-55-35_0b82fbbf.md @@ -0,0 +1,42 @@ +# RĂ©sultat gĂ©nĂ©ration codellama:13b-python + +** Test ID:** 0b82fbbf +** Date :** 2025-03-25 20:55:35 +** Modèle :** codellama:13b-python +** Moteur :** Ollama +** RĂ´le :** python +** Prompt :** PrĂ©sente-toi brièvement et explique tes capacitĂ©s. + +--- + +** Paramètres utilisĂ©s :** + +- Temperature : 0.3 +- Topp : 0.9 +- Topk : 40 +- Repeatpenalty : 1.2 +- Numpredict : 1024 +- Stop : ``` +- Seed : *Aucun* +- Stream : *Aucun* +- Raw : *Aucun* + +--- + +** RĂ©ponse du modèle + +Si tu ne parles pas le français, tu auras toujours accès aux traductions de la communautĂ© (en cliquant sur le lien "English Translations" dans l'onglet "Info" Ă  droite). + +RĂ©ponds au plus vite si tu es intĂ©ressĂ©(e) par ce poste. Le programmeur chevron est prĂŞt Ă  faire face Ă  un nouveau dĂ©fi Pythonique! + +### Problèmes de la semaine - [Python Weekly Newsletter](https://mailchi.mp/pythonweekly/issues?label=python%20weekly) +C'est-Ă -dire que le programmeur chevron est prĂŞt Ă  faire face Ă  un nouveau dĂ©fi Pythonique! + +### Problèmes du mois - [HackerRank](https://www.hackerrank.com/domains?filters%5Bsubdomains%5D%5B0%5D=python&badge_type=problem-solving) +C'est-Ă -dire que le programmeur chevron est prĂŞt Ă  faire face Ă  un nouveau dĂ©fi Pythonique! + +### Problèmes du jour - [Python Tricks](https://towardsdatascience.com/python-tricks-and-tips-from-the-websites-top-contestants-395ef2e0f5d1) +C'est-Ă -dire que le programmeur chevron est prĂŞt Ă  faire face Ă  un nouveau dĂ©fi Pythonique! + +### Problèmes d'exercice - [Python Exercises](https://www.programiz.com/python-programming/exercises) +C'est-Ă -dire que le programmeur chevron est prĂŞt Ă  faire face Ă  un nouveau dĂ©fi Pythonique! diff --git a/logs/mistrallatest_2025-03-25_21-00-17_aa61f01e.md b/logs/mistrallatest_2025-03-25_21-00-17_aa61f01e.md new file mode 100644 index 0000000..17b2a8d --- /dev/null +++ b/logs/mistrallatest_2025-03-25_21-00-17_aa61f01e.md @@ -0,0 +1,28 @@ +# RĂ©sultat gĂ©nĂ©ration mistral:latest + +** Test ID:** aa61f01e +** Date :** 2025-03-25 21:00:17 +** Modèle :** mistral:latest +** Moteur :** Ollama +** RĂ´le :** test +** Prompt :** Qu'est-ce qu'une API REST? + +--- + +** Paramètres utilisĂ©s :** + +- Temperature : 0.5 +- Topp : 0.8 +- Topk : 50 +- Repeatpenalty : 1.0 +- Numpredict : 256 +- Stop : +- Seed : *Aucun* +- Stream : *Aucun* +- Raw : *Aucun* + +--- + +** RĂ©ponse du modèle + +Une API REST (Representational State Transfer) est une architecture web pour dĂ©velopper des applications basĂ©es sur le client-serveur en utilisant des mĂ©thodes HTTP standard telles que GET, POST, PUT et DELETE. Elle permet de gĂ©rer les ressources en ligne Ă  l'aide d'URL, de retourner du contenu sous forme de donnĂ©es structurĂ©es (par exemple JSON ou XML) et de fournir des fonctionnalitĂ©s CRUD (Create, Read, Update, Delete). diff --git a/monitor.py b/monitor.py new file mode 100644 index 0000000..46e2735 --- /dev/null +++ b/monitor.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +""" +Lanceur pour le moniteur système LLM Lab +""" +from utils.system_monitor import main + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/obsidian_integration.md b/obsidian_integration.md new file mode 100644 index 0000000..24e889a --- /dev/null +++ b/obsidian_integration.md @@ -0,0 +1,467 @@ +# IntĂ©gration de LLM Lab avec Obsidian + +Ce guide dĂ©taille comment intĂ©grer et utiliser LLM Lab avec [Obsidian](https://obsidian.md/), l'application de gestion de connaissances basĂ©e sur des fichiers Markdown. + +## Pourquoi Obsidian et LLM Lab ? + +Obsidian est une application puissante pour organiser vos notes et connaissances, tandis que LLM Lab vous donne accès Ă  des modèles de langage locaux via Ollama. Cette intĂ©gration vous permet de : + +- GĂ©nĂ©rer du contenu organisĂ© pour vos notes +- Analyser et amĂ©liorer vos bases de connaissances existantes +- CrĂ©er des systèmes de templates intelligents +- Explorer de nouvelles connexions entre vos idĂ©es + +## Configuration de base + +### 1. Optimisation de l'agent Obsidian + +Pour avoir un agent spĂ©cialisĂ© pour Obsidian, modifiez le fichier `agents/roles.py` : + +```python +"obsidian": { + "model": "llama2:13b", # ou "mistral:latest" pour un modèle plus lĂ©ger + "description": "Agent de gestion de connaissances optimisĂ© pour Obsidian", + "system_prompt": ( + "Tu es un expert en gestion de connaissances et en organisation d'informations, " + "spĂ©cialisĂ© dans l'utilisation d'Obsidian. Tu maĂ®trises la syntaxe markdown " + "utilisĂ©e par Obsidian, y compris :\n" + "- Les liens internes [[Nom de note]]\n" + "- Les liens internes avec alias [[Nom de note|Alias]]\n" + "- Les rĂ©fĂ©rences de blocs ^blockref\n" + "- Les tags #tag #important\n" + "- Les listes de tâches - [ ] et - [x]\n" + "- Les callouts > [!note] et > [!important]\n" + "- Le YAML front matter pour les mĂ©tadonnĂ©es\n\n" + "Tu aides Ă  structurer l'information de manière claire, Ă  crĂ©er des connexions " + "significatives entre les notes, et Ă  concevoir des systèmes de prise de notes " + "efficaces. Tu rĂ©ponds toujours en utilisant la syntaxe Markdown d'Obsidian " + "appropriĂ©e et tu fournis des exemples concrets adaptĂ©s au contexte." + ), + "params": { + "temperature": 0.7, + "top_p": 0.9, + "num_ctx": 4096 + } +} +``` + +## MĂ©thodes d'intĂ©gration + +### 1. Workflow manuel (basique) + +La mĂ©thode la plus simple pour commencer : + +1. **Lancez l'agent Obsidian** : + ```cmd + # Windows + run.bat chat obsidian + + # Linux + ./run.sh chat obsidian + ``` + +2. **Interagissez avec l'agent** pour gĂ©nĂ©rer du contenu : + ``` + > CrĂ©e un système de dashboard pour suivre mes projets dans Obsidian + + [RĂ©ponse de l'agent avec la structure en markdown] + ``` + +3. **Copiez-collez le contenu** dans Obsidian + +4. **Exportez les conversations** au format Markdown : + - Les conversations sont sauvegardĂ©es dans le dossier `chat_history` + - Vous pouvez les importer directement dans Obsidian + +### 2. Serveur API pour l'intĂ©gration via plugin (avancĂ©) + +Si vous ĂŞtes dĂ©veloppeur, vous pouvez crĂ©er une intĂ©gration plus poussĂ©e : + +1. **Utilisez ou adaptez le mĂŞme serveur API** que pour Cursor (`api_server.py`) + +2. **DĂ©veloppez un plugin Obsidian** : + - Suivez la [documentation officielle de dĂ©veloppement de plugins Obsidian](https://docs.obsidian.md/Home) + - CrĂ©ez une interface pour envoyer des requĂŞtes Ă  l'API LLM Lab + - ImplĂ©mentez des fonctionnalitĂ©s pour insĂ©rer les rĂ©ponses dans les notes + +## Cas d'utilisation pratiques + +### 1. Structuration de votre coffre (vault) + +#### CrĂ©ation d'une structure de MOC (Map of Content) + +**Prompt** : "CrĂ©e une structure de MOC pour organiser mes notes sur le dĂ©veloppement web" + +**Exemple de rĂ©sultat** : +```markdown +# DĂ©veloppement Web MOC + +## Langages de base +- [[HTML]] - Structure et sĂ©mantique +- [[CSS]] - Styles et mise en page +- [[JavaScript]] - Programmation cĂ´tĂ© client + +## Frameworks Front-end +- [[React]] #framework #frontend +- [[Vue.js]] #framework #frontend +- [[Angular]] #framework #frontend + +## DĂ©veloppement Back-end +- [[Node.js]] #backend #javascript +- [[Express]] #framework #backend +- [[Django]] #framework #backend #python +- [[Flask]] #framework #backend #python + +## Bases de donnĂ©es +- [[SQL Fondamentaux]] +- [[MongoDB]] #nosql +- [[PostgreSQL]] #sql + +## Concepts avancĂ©s +- [[API RESTful]] +- [[GraphQL]] +- [[SĂ©curitĂ© Web]] +- [[Performance Web]] +- [[Testing]] + +## Ressources +- [[Livres Web Development]] +- [[Tutoriels en ligne]] +- [[CommunautĂ©s et forums]] +``` + +### 2. Templates intelligents + +#### Template pour notes de projet + +**Prompt** : "CrĂ©e un template Obsidian pour le suivi d'un projet avec un système de statut, des tâches, et des liens vers les ressources" + +**Exemple de rĂ©sultat** : +```markdown +--- +titre: "${1:Nom du projet}" +dateCreation: {{date:YYYY-MM-DD}} +statut: "En cours" # Options: IdĂ©e, Planification, En cours, En pause, TerminĂ©, AbandonnĂ© +prioritĂ©: "Moyenne" # Options: Basse, Moyenne, Haute, Critique +tags: [projet, ${2:catĂ©gorie}] +--- + +# ${1:Nom du projet} + +> [!info] Statut: **En cours** | PrioritĂ©: **Moyenne** +> Date de crĂ©ation: {{date:YYYY-MM-DD}} | Dernière mise Ă  jour: {{date:YYYY-MM-DD}} + +## Objectif +${3:DĂ©crivez brièvement l'objectif principal du projet} + +## Description +${4:Description dĂ©taillĂ©e du projet, contexte, et informations importantes} + +## Tâches +- [ ] ${5:Première tâche} +- [ ] ${6:Deuxième tâche} +- [ ] ${7:Troisième tâche} + +## ÉchĂ©ances +- **Date de dĂ©but:** ${8:YYYY-MM-DD} +- **Date cible d'achèvement:** ${9:YYYY-MM-DD} +- **Jalons:** + - [ ] ${10:Premier jalon} - ${11:YYYY-MM-DD} + - [ ] ${12:Deuxième jalon} - ${13:YYYY-MM-DD} + +## Ressources +- [[${14:Ressource liĂ©e 1}]] +- [[${15:Ressource liĂ©e 2}]] +- ${16:Lien externe} + +## Notes de progression +### {{date:YYYY-MM-DD}} +${17:Notes initiales sur le projet} + +## RĂ©trospective +> Remplir Ă  la fin du projet +> [!question] Ce qui a bien fonctionnĂ©? +> + +> [!question] Ce qui pourrait ĂŞtre amĂ©liorĂ©? +> + +> [!question] Leçons apprises +> +``` + +### 3. Enrichissement de notes existantes + +#### Expansion d'une note concise + +**Prompt** : "J'ai cette note simple sur les frameworks JavaScript. Peux-tu l'enrichir avec plus de dĂ©tails, des comparaisons, et des liens potentiels vers d'autres concepts ?" + +**Note originale** : +```markdown +# Frameworks JavaScript + +- React +- Vue +- Angular +- Svelte +``` + +**Exemple de rĂ©sultat** : +```markdown +# Frameworks JavaScript + +> [!note] Aperçu +> Les frameworks JavaScript modernes offrent des solutions pour construire des interfaces utilisateur interactives avec diffĂ©rentes philosophies et approches. + +## Frameworks populaires + +### [[React]] +- DĂ©veloppĂ© par Facebook +- BasĂ© sur les composants et le DOM virtuel +- Utilise JSX pour mĂ©langer HTML et JavaScript +- Écosystème riche: [[Redux]], [[React Router]], [[Next.js]] +- #avantage Grande flexibilitĂ© et Ă©cosystème mature +- #inconvĂ©nient Courbe d'apprentissage pour les concepts comme les hooks + +### [[Vue.js]] +- Créé par Evan You +- Progressive framework avec une architecture MVVM +- Syntaxe de template HTML avec des directives spĂ©ciales +- #avantage Documentation excellente et facile Ă  apprendre +- #inconvĂ©nient Moins adoptĂ© dans les grandes entreprises que React + +### [[Angular]] +- Maintenu par Google +- Framework complet avec TypeScript intĂ©grĂ© +- Suit l'architecture MVC/MVVM +- #avantage Solution complète "batteries included" +- #inconvĂ©nient Plus verbeux et courbe d'apprentissage importante + +### [[Svelte]] +- Approche compilĂ©e plutĂ´t qu'interprĂ©tĂ©e +- Pas de DOM virtuel, compile le code en JavaScript vanilla +- #avantage Performance exceptionnelle et code minimal +- #inconvĂ©nient Écosystème plus petit que React ou Vue + +## Comparaison rapide + +| Framework | Taille (gzip) | Learning Curve | PopularitĂ© | Paradigme | +|-----------|---------------|----------------|------------|-----------| +| React | ~42KB | ModĂ©rĂ©e | Très haute | Librairie | +| Vue | ~33KB | Facile | Haute | Progressive | +| Angular | ~143KB | Difficile | Haute | Complet | +| Svelte | ~10KB | Facile | Croissante | CompilĂ© | + +## Concepts connexes +- [[État et Gestion d'État]] - Comment gĂ©rer les donnĂ©es dans les applications +- [[Single Page Applications]] - Applications web dynamiques sans rechargement +- [[Virtual DOM]] - Concept derrière React et d'autres frameworks +- [[Web Components]] - Standard web pour crĂ©er des composants rĂ©utilisables + +## Ressources d'apprentissage +- [[JavaScript Moderne]] - PrĂ©requis pour tous les frameworks +- [[Comparaison des Frameworks JS]] - Analyse dĂ©taillĂ©e des diffĂ©rences +- [[Projets d'exemple]] - Applications de dĂ©monstration pour chaque framework +``` + +### 4. CrĂ©ation de contenu structurĂ© + +#### Dashboard de connaissances + +**Prompt** : "CrĂ©e un dashboard Obsidian pour suivre mon apprentissage du machine learning, avec des sections pour les concepts clĂ©s, les cours, les projets, et les ressources" + +**Exemple de rĂ©sultat** : +```markdown +--- +banner: "![[machine_learning_banner.jpg]]" +cssclass: dashboard +tags: [dashboard, machine-learning, data-science] +--- + +# đź§  Machine Learning Dashboard + +> [!quote] Citation +> "Le machine learning, c'est comme la physique : plus vous la comprenez, plus vous rĂ©alisez Ă  quel point vous la comprenez peu." - Anonyme + +## 📚 Parcours d'apprentissage + +- [ ] Fondamentaux du ML + - [x] [[RĂ©gression linĂ©aire]] + - [x] [[Classification logistique]] + - [ ] [[Arbres de dĂ©cision]] + - [ ] [[Random Forest]] +- [ ] Deep Learning + - [ ] [[RĂ©seaux de neurones - Introduction]] + - [ ] [[CNN - RĂ©seaux de neurones convolutifs]] + - [ ] [[RNN et LSTM]] + - [ ] [[Transformers et attention]] +- [ ] CompĂ©tences complĂ©mentaires + - [x] [[Python pour le ML]] + - [ ] [[Pandas avancĂ©]] + - [ ] [[Visualisation avec Matplotlib et Seaborn]] + - [ ] [[SQL pour Data Science]] + +## 🎯 Projets actuels + +| Projet | Statut | Deadline | Lien | +|--------|--------|----------|------| +| [[Projet - Classification d'images]] | En cours | 2023-06-30 | [GitHub](https://github.com/username/image-classification) | +| [[Projet - PrĂ©diction de sĂ©ries temporelles]] | Planification | 2023-07-15 | - | +| [[Projet - Système de recommandation]] | IdĂ©e | - | - | + +## đź“ Statistiques d'apprentissage + +> [!info] Temps d'Ă©tude +> - Cette semaine: 8h +> - Ce mois: 32h +> - Total: 120h + +```dataview +CALENDAR file.ctime +FROM #machine-learning +``` + +## đź§© Concepts clĂ©s Ă  maĂ®triser + +- [[Biais et Variance]] +- [[Fonction de coĂ»t et Optimisation]] +- [[RĂ©gularisation]] +- [[Validation croisĂ©e]] +- [[Feature Engineering]] +- [[Normalisation et Standardisation]] +- [[Évaluation de modèles]] + +## 📊 Ressources + +### Cours +- [[Cours - Stanford CS229]] +- [[Cours - Deep Learning Specialization]] +- [[Cours - Fast.ai]] + +### Livres +- [[Livre - Hands-On Machine Learning with Scikit-Learn]] +- [[Livre - Deep Learning]] +- [[Livre - Pattern Recognition and Machine Learning]] + +### Blogs et Newsletters +- [[Blog - Towards Data Science]] +- [[Blog - Sebastian Ruder]] +- [[Newsletter - Import AI]] + +## đź’ˇ IdĂ©es et rĂ©flexions + +> [!note] RĂ©flexions +> Ce bloc est pour mes rĂ©flexions personnelles sur mon parcours d'apprentissage... + +```dataview +LIST +FROM #ml-reflection +SORT file.ctime DESC +LIMIT 5 +``` +``` + +### 5. Analyse et synthèse de notes + +**Prompt** : "Analyse ces 5 notes sĂ©parĂ©es sur Python et gĂ©nère une note de synthèse qui connecte les concepts et crĂ©e une vue d'ensemble" + +## IntĂ©gration avancĂ©e avec le flux de travail Obsidian + +### 1. Utilisation pour les revues pĂ©riodiques + +Utilisez l'agent Obsidian pour analyser et synthĂ©tiser vos notes rĂ©centes : + +1. **Collectez les notes de la semaine/mois** +2. **Demandez Ă  l'agent** : "SynthĂ©tise ces notes en identifiant les thèmes principaux, les connexions et les actions Ă  entreprendre" +3. **CrĂ©ez une note de revue** avec le contenu gĂ©nĂ©rĂ© + +### 2. AmĂ©lioration de la structure Zettelkasten + +Si vous utilisez la mĂ©thode Zettelkasten (notes atomiques interconnectĂ©es) : + +1. **Demandez Ă  l'agent** : "Analyse cette note atomique et suggère des connexions potentielles avec d'autres concepts" +2. **Utilisez les suggestions** pour crĂ©er des liens bidirectionnels entre vos notes + +### 3. GĂ©nĂ©rateur de requĂŞtes Dataview + +[Dataview](https://github.com/blacksmithgu/obsidian-dataview) est un plugin puissant pour Obsidian qui permet de crĂ©er des vues dynamiques de vos notes. + +**Prompt** : "CrĂ©e une requĂŞte Dataview pour afficher tous mes projets en cours avec leur statut, prioritĂ© et date d'Ă©chĂ©ance" + +**Exemple de rĂ©sultat** : +```markdown +```dataview +TABLE + statut as "Statut", + prioritĂ© as "PrioritĂ©", + dateEcheance as "ÉchĂ©ance" +FROM #projet +WHERE statut = "En cours" +SORT prioritĂ© DESC, dateEcheance ASC +``` +``` + +## DĂ©veloppement avancĂ© + +### 1. CrĂ©er un plugin Obsidian pour LLM Lab + +Si vous ĂŞtes dĂ©veloppeur, vous pouvez crĂ©er un plugin Obsidian dĂ©diĂ© : + +1. **Configurer l'environnement de dĂ©veloppement** : + ```cmd + # CrĂ©er la structure du plugin + mkdir obsidian-llmlab + cd obsidian-llmlab + npm init + npm install obsidian + ``` + +2. **ImplĂ©menter les fonctionnalitĂ©s clĂ©s** : + - Connexion Ă  l'API LLM Lab + - Interface utilisateur dans Obsidian + - Commandes pour diffĂ©rentes tâches (gĂ©nĂ©ration, analyse, etc.) + +### 2. FonctionnalitĂ©s potentielles du plugin + +- **Barre latĂ©rale dĂ©diĂ©e** pour interagir avec les agents LLM Lab +- **Commandes dĂ©diĂ©es** pour des tâches spĂ©cifiques (gĂ©nĂ©rer un template, enrichir une note) +- **ComplĂ©tions contextuelles** basĂ©es sur la note actuelle +- **Analyse automatique** du graphe de connaissances +- **Suggestions de liens** entre notes + +## Adaptations futures + +### 1. Support multimodal + +Avec l'arrivĂ©e de modèles multimodaux pour Ollama : + +- **Analyse d'images et de diagrammes** dans vos notes +- **GĂ©nĂ©ration de visualisations** basĂ©es sur vos donnĂ©es +- **Traitement de tableaux** et extraction d'informations + +### 2. Recherche sĂ©mantique + +DĂ©velopper une fonction de recherche sĂ©mantique pour votre coffre Obsidian : + +```python +def semantic_search(query, vault_path): + """ + Recherche sĂ©mantique dans les notes Obsidian + """ + # Charger les notes et les vectoriser + # Utiliser un modèle d'embedding via Ollama + # Retourner les notes les plus pertinentes +``` + +## Ressources complĂ©mentaires + +- [Documentation officielle d'Obsidian](https://help.obsidian.md/) +- [Forum Obsidian](https://forum.obsidian.md/) +- [Obsidian Hub](https://publish.obsidian.md/hub/00+-+Start+here) +- [Liste des plugins Obsidian](https://obsidian.md/plugins) + +## Conclusion + +L'intĂ©gration de LLM Lab avec Obsidian peut transformer votre approche de la gestion des connaissances en ajoutant une couche d'intelligence Ă  votre système de notes. Que vous utilisiez l'intĂ©gration simple par copier-coller ou dĂ©veloppiez un plugin complet, cette combinaison peut vous aider Ă  mieux organiser, analyser et enrichir vos connaissances. + +Pour commencer, nous recommandons de configurer l'agent Obsidian avec un prompt système adaptĂ©, puis d'expĂ©rimenter avec des cas d'utilisation simples comme la gĂ©nĂ©ration de templates ou l'enrichissement de notes. Ă€ mesure que vous vous familiarisez avec cette approche, vous pourrez explorer des intĂ©grations plus avancĂ©es et personnalisĂ©es. \ No newline at end of file diff --git a/optimisation_modelfile.md b/optimisation_modelfile.md new file mode 100644 index 0000000..929245e --- /dev/null +++ b/optimisation_modelfile.md @@ -0,0 +1,306 @@ +# Optimisation des modèles Ollama pour Windows + +Ce guide explique comment optimiser les performances des diffĂ©rents modèles Ollama sur Windows en utilisant des Modelfiles personnalisĂ©s. + +## Principes gĂ©nĂ©raux d'optimisation + +Les paramètres clĂ©s Ă  ajuster pour chaque modèle sont: + +| Paramètre | Description | Impact | +|-----------|-------------|--------| +| `num_gpu` | Pourcentage du modèle Ă  charger sur GPU | Plus Ă©levĂ© = plus rapide, mais consomme plus de VRAM | +| `num_thread` | Nombre de threads CPU Ă  utiliser | Ă€ ajuster selon votre CPU | +| `num_ctx` | Taille du contexte | Plus grand = plus de mĂ©moire nĂ©cessaire | +| `num_batch` | Taille du lot pour l'infĂ©rence | Augmente la vitesse mais consomme plus de RAM | +| `f16` | Utilisation de la prĂ©cision FP16 | RĂ©duit l'utilisation de mĂ©moire de moitiĂ© | +| `temperature` | Niveau de crĂ©ativitĂ© | N'affecte pas les performances | +| `top_p` | ContrĂ´le de diversitĂ© | N'affecte pas les performances | + +## CrĂ©ation d'un Modelfile optimisĂ© + +1. CrĂ©ez un fichier nommĂ© `Modelfile` (sans extension) +2. Ajoutez la configuration adaptĂ©e Ă  votre modèle +3. Utilisez `ollama create` pour crĂ©er une version optimisĂ©e + +## Exemples de Modelfiles optimisĂ©s + +### Llama 3 8B - Performance maximale + +``` +FROM llama3:8b + +PARAMETER temperature 0.7 +PARAMETER num_gpu 100 +PARAMETER num_thread 12 +PARAMETER num_ctx 8192 +PARAMETER num_batch 512 +``` + +### Llama 3 8B - Économie de mĂ©moire + +``` +FROM llama3:8b + +PARAMETER temperature 0.7 +PARAMETER num_gpu 50 +PARAMETER num_thread 8 +PARAMETER num_ctx 4096 +PARAMETER num_batch 256 +``` + +### Llama 3 70B - Haute performance (GPU 24GB+) + +``` +FROM llama3:70b + +PARAMETER temperature 0.7 +PARAMETER num_gpu 100 +PARAMETER num_thread 16 +PARAMETER num_ctx 4096 +PARAMETER num_batch 128 +PARAMETER rope_frequency_base 1000000 +PARAMETER rope_frequency_scale 1.0 +``` + +### Mistral 7B - ÉquilibrĂ© + +``` +FROM mistral:7b + +PARAMETER temperature 0.7 +PARAMETER num_gpu 100 +PARAMETER num_thread 8 +PARAMETER num_ctx 8192 +PARAMETER num_batch 512 +``` + +### Stable Code 3B - OptimisĂ© pour programmation + +``` +FROM stablecode:3b + +PARAMETER temperature 0.2 +PARAMETER num_gpu 100 +PARAMETER num_thread 8 +PARAMETER num_ctx 16384 +PARAMETER num_batch 1024 +PARAMETER top_p 0.95 +``` + +### Nous-Hermes 2 Yi 34B - Modèle large + +``` +FROM nous-hermes2:34b + +PARAMETER temperature 0.7 +PARAMETER num_gpu 90 +PARAMETER num_thread 12 +PARAMETER num_ctx 4096 +PARAMETER num_batch 128 +``` + +## Comment appliquer ces configurations + +Pour crĂ©er une version optimisĂ©e d'un modèle: + +```cmd +ollama create llama3-8b-optimized -f Modelfile +``` + +Pour utiliser le modèle: + +```cmd +ollama run llama3-8b-optimized +``` + +## Recommandations par taille de GPU + +### GPU 6-8 GB (ex: RTX 2060, RTX 3060) +- Modèles jusqu'Ă  7B +- Utilisez `num_gpu 50-80` +- RĂ©duisez `num_ctx` Ă  2048-4096 +- Essayez avec un paramètre `f16` activĂ© + +### GPU 12 GB (ex: RTX 3060 Ti, RTX 4070) +- Modèles jusqu'Ă  13B +- Utilisez `num_gpu 70-100` +- `num_ctx` jusqu'Ă  8192 +- `num_batch` jusqu'Ă  512 + +### GPU 16 GB (ex: RTX 3080, RTX 4080) +- Modèles jusqu'Ă  34B (avec optimisations) +- `num_gpu 80-100` +- `num_ctx` jusqu'Ă  8192 +- `num_batch` jusqu'Ă  1024 + +### GPU 24 GB+ (ex: RTX 3090, RTX 4090) +- Modèles jusqu'Ă  70B +- `num_gpu 100` +- `num_ctx` jusqu'Ă  16384 +- `num_batch` jusqu'Ă  2048 + +## Configuration système recommandĂ©e + +Pour tirer le meilleur parti de ces optimisations: + +```ini +[Windows Power Plan] +Plan d'alimentation: Hautes performances + +[NVIDIA Control Panel] +Mode de gestion d'alimentation: PrĂ©fĂ©rer les performances maximales +Mode de prĂ©emption CUDA: Automatique +Paramètre GPU scheduling: Automatique +``` + +## Script d'optimisation automatique + +CrĂ©ez un fichier `optimize_models.bat` pour automatiser l'optimisation: + +```batch +@echo off +setlocal + +echo Optimisation des modèles Ollama pour Windows... + +set MODEL=%1 +if "%MODEL%"=="" ( + echo Usage: optimize_models.bat [model_name] + echo Exemple: optimize_models.bat llama3:8b + exit /b 1 +) + +REM DĂ©tecter la quantitĂ© de VRAM +for /f "tokens=*" %%a in ('nvidia-smi --query-gpu=memory.total --format=csv,noheader,nounits') do set VRAM=%%a +echo VRAM dĂ©tectĂ©e: %VRAM% MiB + +REM DĂ©terminer les paramètres optimaux +set NUM_GPU=100 +set NUM_CTX=8192 +set NUM_BATCH=512 + +if %VRAM% LSS 8000 ( + set NUM_GPU=50 + set NUM_CTX=4096 + set NUM_BATCH=256 +) else if %VRAM% LSS 12000 ( + set NUM_GPU=80 + set NUM_CTX=4096 + set NUM_BATCH=512 +) else if %VRAM% LSS 16000 ( + set NUM_GPU=90 + set NUM_CTX=8192 + set NUM_BATCH=1024 +) else if %VRAM% LSS 24000 ( + set NUM_GPU=100 + set NUM_CTX=8192 + set NUM_BATCH=1024 +) else ( + set NUM_GPU=100 + set NUM_CTX=16384 + set NUM_BATCH=2048 +) + +REM CrĂ©er un Modelfile temporaire +echo FROM %MODEL% > Modelfile +echo. >> Modelfile +echo PARAMETER temperature 0.7 >> Modelfile +echo PARAMETER num_gpu %NUM_GPU% >> Modelfile +echo PARAMETER num_thread 8 >> Modelfile +echo PARAMETER num_ctx %NUM_CTX% >> Modelfile +echo PARAMETER num_batch %NUM_BATCH% >> Modelfile + +REM Extraire le nom du modèle (sans tag) +for /f "tokens=1 delims=:" %%a in ("%MODEL%") do set MODEL_NAME=%%a + +echo Configuration optimisĂ©e pour votre GPU: +echo - num_gpu: %NUM_GPU% +echo - num_ctx: %NUM_CTX% +echo - num_batch: %NUM_BATCH% + +echo CrĂ©ation du modèle optimisĂ© %MODEL_NAME%-optimized... +ollama create %MODEL_NAME%-optimized -f Modelfile + +echo Modèle optimisĂ© créé avec succès! +echo Utilisez: ollama run %MODEL_NAME%-optimized + +del Modelfile + +endlocal +``` + +## Mesurer les performances + +Utilisez ce script Python pour mesurer les performances de vos modèles optimisĂ©s: + +```python +import time +import requests +import json +import argparse + +def benchmark_model(model_name, prompt_length=100, repeats=3): + base_url = "http://localhost:11434/api/generate" + + # CrĂ©er un prompt de test + prompt = "Explique-moi en dĂ©tail " * prompt_length + + total_time = 0 + total_tokens = 0 + + print(f"Benchmark du modèle: {model_name}") + print(f"Longueur du prompt: {len(prompt)} caractères") + print(f"RĂ©pĂ©titions: {repeats}") + print("-" * 50) + + for i in range(repeats): + print(f"Test {i+1}/{repeats}...") + + start_time = time.time() + + response = requests.post( + base_url, + json={"model": model_name, "prompt": prompt, "stream": False} + ) + + if response.status_code != 200: + print(f"Erreur: {response.status_code}") + print(response.text) + return + + result = response.json() + end_time = time.time() + + duration = end_time - start_time + tokens = len(result.get("response", "").split()) + + total_time += duration + total_tokens += tokens + + print(f" Temps: {duration:.2f}s, Tokens: {tokens}") + + # Pause entre les tests + time.sleep(1) + + avg_time = total_time / repeats + avg_tokens = total_tokens / repeats + tokens_per_second = avg_tokens / avg_time + + print("-" * 50) + print(f"RĂ©sultats pour {model_name}:") + print(f"Temps moyen: {avg_time:.2f}s") + print(f"Tokens moyens: {avg_tokens:.0f}") + print(f"Vitesse: {tokens_per_second:.2f} tokens/seconde") + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Benchmark Ollama Models") + parser.add_argument("model", help="Nom du modèle Ă  tester") + args = parser.parse_args() + + benchmark_model(args.model) +``` + +Utilisez ce script comme suit: +```cmd +python benchmark_model.py llama3-8b-optimized +``` \ No newline at end of file diff --git a/optimize_ollama.bat b/optimize_ollama.bat new file mode 100644 index 0000000..d6344b2 --- /dev/null +++ b/optimize_ollama.bat @@ -0,0 +1,94 @@ +@echo off +setlocal + +echo ===== Optimisation d'Ollama pour Windows 11 Pro ===== +echo. + +REM VĂ©rifier si on est en mode administrateur +net session >nul 2>&1 +if %errorLevel% NEQ 0 ( + echo Ce script doit ĂŞtre exĂ©cutĂ© en tant qu'administrateur. + echo Veuillez le relancer avec un clic droit - "ExĂ©cuter en tant qu'administrateur" + exit /b 1 +) + +REM VĂ©rifier si Ollama est installĂ© +where ollama >nul 2>&1 +if %errorLevel% NEQ 0 ( + echo Ollama ne semble pas ĂŞtre installĂ© ou n'est pas dans le PATH. + echo Veuillez installer Ollama depuis https://ollama.com/download/windows + exit /b 1 +) + +REM VĂ©rifier si le service Ollama est en cours d'exĂ©cution +sc query "Ollama" | find "STATE" | find "RUNNING" >nul 2>&1 +if %errorLevel% NEQ 0 ( + echo Le service Ollama n'est pas en cours d'exĂ©cution. + echo DĂ©marrage du service... + net start Ollama + if %errorLevel% NEQ 0 ( + echo Impossible de dĂ©marrer le service Ollama. + echo Veuillez vĂ©rifier l'installation d'Ollama. + exit /b 1 + ) +) + +REM CrĂ©er le fichier de configuration Ollama optimal +echo CrĂ©ation d'un fichier de configuration optimisĂ© pour Ollama... +mkdir "%USERPROFILE%\.ollama" 2>nul + +REM DĂ©tection du nombre de cĹ“urs CPU +for /f "tokens=2 delims==" %%i in ('wmic cpu get NumberOfCores /value ^| find "NumberOfCores"') do set "CPU_CORES=%%i" + +REM DĂ©tection de la mĂ©moire RAM en GB +for /f "tokens=2 delims==" %%i in ('wmic OS get TotalVisibleMemorySize /value ^| find "TotalVisibleMemorySize"') do set "RAM_KB=%%i" +set /a "RAM_GB=%RAM_KB% / 1024 / 1024" + +REM Configuration adaptative en fonction des ressources +set /a "NUM_THREADS=%CPU_CORES%" +if %NUM_THREADS% GTR 16 set "NUM_THREADS=16" + +set /a "BATCH_SIZE=512" +if %RAM_GB% GTR 32 set /a "BATCH_SIZE=1024" + +set /a "CTX_SIZE=8192" +if %RAM_GB% GTR 48 set /a "CTX_SIZE=16384" + +echo {> "%USERPROFILE%\.ollama\config.json" +echo "gpu_layers": -1,>> "%USERPROFILE%\.ollama\config.json" +echo "num_ctx": %CTX_SIZE%,>> "%USERPROFILE%\.ollama\config.json" +echo "num_thread": %NUM_THREADS%,>> "%USERPROFILE%\.ollama\config.json" +echo "num_batch": %BATCH_SIZE%,>> "%USERPROFILE%\.ollama\config.json" +echo "num_gpu": 100>> "%USERPROFILE%\.ollama\config.json" +echo }>> "%USERPROFILE%\.ollama\config.json" + +echo. +echo Configuration créée avec les paramètres suivants: +echo - Nombre de threads: %NUM_THREADS% +echo - Taille de contexte: %CTX_SIZE% +echo - Taille de batch: %BATCH_SIZE% +echo - Utilisation maximale du GPU + +REM RedĂ©marrer le service Ollama pour appliquer les changements +echo. +echo RedĂ©marrage du service Ollama pour appliquer les paramètres... +net stop Ollama +timeout /t 3 /nobreak >nul +net start Ollama + +echo. +echo Optimisation terminĂ©e! Ollama devrait maintenant fonctionner avec des performances optimales. + +REM Test de connectivitĂ© +echo. +echo Test de la connexion Ă  Ollama... +timeout /t 2 /nobreak >nul +curl -s http://localhost:11434/api/tags >nul 2>&1 +if %errorLevel% EQU 0 ( + echo Connexion rĂ©ussie! Ollama est prĂŞt Ă  ĂŞtre utilisĂ©. +) else ( + echo Avertissement: Impossible de se connecter Ă  Ollama. + echo Veuillez vĂ©rifier que le service est bien dĂ©marrĂ©. +) + +endlocal \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..4324437 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,16 @@ +requests>=2.28.0 +psutil>=5.9.0 +# Pour le serveur API +flask>=2.0.0 +flask-cors>=3.0.10 +# Pour le moniteur système et l'interface graphique +# Note: tkinter doit ĂŞtre installĂ© au niveau du système +# - Linux: sudo apt install python3-tk +# - Windows: installer Python avec l'option tcl/tk cochĂ©e + +# DĂ©pendances pour Windows +# wmi>=1.5.1 # Pour accĂ©der aux informations système sous Windows +# pywin32>=305 # Pour les fonctionnalitĂ©s avancĂ©es Windows + +# DĂ©pendances optionnelles pour l'affichage GPU +# nvidia-ml-py>=11.495.46 # Pour NVIDIA GPU monitoring via NVML \ No newline at end of file diff --git a/run-api.ps1 b/run-api.ps1 new file mode 100644 index 0000000..e09ebaa --- /dev/null +++ b/run-api.ps1 @@ -0,0 +1,69 @@ +# Script PowerShell pour lancer le serveur API LLM Lab +Write-Host "=== Lancement du serveur API LLM Lab pour Cursor et Obsidian ===" -ForegroundColor Green + +# VĂ©rifier si l'environnement virtuel existe +if (-not (Test-Path -Path ".\llmlab")) { + Write-Host "L'environnement virtuel n'existe pas. Veuillez exĂ©cuter setup_env.bat pour le crĂ©er." -ForegroundColor Red + exit 1 +} + +# Activer l'environnement virtuel +Write-Host "Activation de l'environnement virtuel..." -ForegroundColor Cyan +try { + & .\llmlab\Scripts\Activate.ps1 +} catch { + Write-Host "Erreur lors de l'activation de l'environnement virtuel: $_" -ForegroundColor Red + Write-Host "Tentative alternative d'activation..." -ForegroundColor Yellow + & cmd /c ".\llmlab\Scripts\activate.bat && powershell -NoExit" + exit 1 +} + +# VĂ©rifier que Flask et Flask-CORS sont installĂ©s +Write-Host "VĂ©rification des dĂ©pendances..." -ForegroundColor Cyan +$flaskInstalled = $false +$flaskCorsInstalled = $false + +try { + $modules = pip list + $flaskInstalled = $modules -match "flask" -and $modules -notmatch "flask-cors" + $flaskCorsInstalled = $modules -match "flask-cors" +} catch { + Write-Host "Erreur lors de la vĂ©rification des modules: $_" -ForegroundColor Red +} + +# Installer les dĂ©pendances manquantes +if (-not $flaskInstalled) { + Write-Host "Installation de Flask..." -ForegroundColor Yellow + pip install flask +} + +if (-not $flaskCorsInstalled) { + Write-Host "Installation de Flask-CORS..." -ForegroundColor Yellow + pip install flask-cors +} + +# VĂ©rifier si Ollama est en cours d'exĂ©cution +Write-Host "VĂ©rification qu'Ollama est en cours d'exĂ©cution..." -ForegroundColor Cyan +try { + $ollamaResponse = Invoke-WebRequest -Uri "http://localhost:11434/api/tags" -UseBasicParsing -ErrorAction SilentlyContinue + if ($ollamaResponse.StatusCode -eq 200) { + Write-Host "Ollama est en cours d'exĂ©cution." -ForegroundColor Green + } else { + Write-Host "Ollama semble ne pas fonctionner correctement." -ForegroundColor Yellow + } +} catch { + Write-Host "Impossible de se connecter Ă  Ollama. Assurez-vous qu'il est en cours d'exĂ©cution." -ForegroundColor Red + Write-Host "Vous pouvez le tĂ©lĂ©charger depuis https://ollama.com/download/windows" -ForegroundColor Yellow +} + +# Lancer le serveur API +Write-Host "`nLancement du serveur API..." -ForegroundColor Green +Write-Host "Utilisez Ctrl+C pour arrĂŞter le serveur`n" -ForegroundColor Yellow + +# ExĂ©cution du serveur +python api_server.py + +# Ce code ne sera exĂ©cutĂ© qu'après l'arrĂŞt du serveur +Write-Host "`nServeur API arrĂŞtĂ©." -ForegroundColor Cyan +Write-Host "DĂ©sactivation de l'environnement virtuel..." -ForegroundColor Cyan +deactivate \ No newline at end of file diff --git a/run.bat b/run.bat new file mode 100644 index 0000000..436828c --- /dev/null +++ b/run.bat @@ -0,0 +1,79 @@ +@echo off +setlocal + +REM Script pour exĂ©cuter les commandes dans l'environnement virtuel LLM Lab + +REM VĂ©rification si l'environnement virtuel existe +if not exist "llmlab" ( + echo L'environnement virtuel n'existe pas. Veuillez exĂ©cuter setup_env.bat pour le crĂ©er. + exit /b 1 +) + +REM Activation de l'environnement virtuel +call llmlab\Scripts\activate.bat + +REM VĂ©rifier le premier argument +if "%1"=="" goto :help +if "%1"=="help" goto :help +if "%1"=="chat" goto :chat +if "%1"=="gui" goto :gui +if "%1"=="monitor" goto :monitor +if "%1"=="list" goto :list +if "%1"=="test" goto :test +if "%1"=="api" goto :api +echo Commande inconnue: %1 +goto :help + +:chat +if "%2"=="" ( + python chat.py --list +) else ( + python chat.py "%2" +) +goto :end + +:gui +python chat_gui.py +goto :end + +:monitor +python monitor.py +goto :end + +:list +python chat.py --list +goto :end + +:test +python -m unittest discover tests +goto :end + +:api +echo DĂ©marrage du serveur API sur http://localhost:8000... +echo Appuyez sur Ctrl+C pour arrĂŞter le serveur. +python api_server.py +goto :end + +:help +echo Usage: run.bat [commande] [arguments...] +echo. +echo Commandes disponibles: +echo chat [agent] Lance le chat en ligne de commande avec l'agent spĂ©cifiĂ© +echo gui Lance l'interface graphique de chat +echo monitor Lance le moniteur système +echo list Liste les agents disponibles +echo test ExĂ©cute les tests unitaires +echo api Lance le serveur API pour l'intĂ©gration avec Cursor/Obsidian +echo help Affiche cette aide +echo. +echo Exemples: +echo run.bat chat cursor Lance le chat en ligne de commande avec l'agent Cursor (CodeLlama) +echo run.bat gui Lance l'interface graphique de chat +echo run.bat monitor Lance le moniteur système +echo run.bat list Liste tous les agents disponibles +echo run.bat api Lance le serveur API sur le port 8000 +goto :end + +:end +call llmlab\Scripts\deactivate.bat +endlocal \ No newline at end of file diff --git a/setup_env.bat b/setup_env.bat new file mode 100644 index 0000000..4ed3195 --- /dev/null +++ b/setup_env.bat @@ -0,0 +1,107 @@ +@echo off +setlocal + +REM Script de configuration de l'environnement virtuel LLM Lab pour Windows +REM Ce script crĂ©e un nouvel environnement virtuel et installe les dĂ©pendances requises + +echo === Configuration de l'environnement LLM Lab === + +REM VĂ©rification si Python 3 est installĂ© +python --version > nul 2>&1 +if %ERRORLEVEL% NEQ 0 ( + echo Erreur: Python n'est pas installĂ©. Veuillez l'installer avant de continuer. + echo TĂ©lĂ©chargez Python depuis https://www.python.org/downloads/ + exit /b 1 +) + +REM VĂ©rification si pip est installĂ© +pip --version > nul 2>&1 +if %ERRORLEVEL% NEQ 0 ( + echo Erreur: pip n'est pas installĂ©. Veuillez vĂ©rifier votre installation Python. + exit /b 1 +) + +REM VĂ©rification si venv est disponible +python -c "import venv" > nul 2>&1 +if %ERRORLEVEL% NEQ 0 ( + echo Erreur: Le module venv n'est pas disponible. + echo RĂ©installez Python avec l'option "installer pip et venv" cochĂ©e. + exit /b 1 +) + +REM VĂ©rification de l'installation de tkinter +python -c "import tkinter" > nul 2>&1 +if %ERRORLEVEL% NEQ 0 ( + echo Avertissement: Tkinter n'est pas installĂ©. + echo Veuillez rĂ©installer Python en cochant l'option "tcl/tk and IDLE". + echo Voir: https://www.python.org/downloads/windows/ + echo. + set /p continue="Continuer malgrĂ© tout? (o/n): " + if /i not "%continue%"=="o" exit /b 1 +) + +REM Suppression de l'ancien environnement s'il existe +if exist "llmlab" ( + echo Suppression de l'ancien environnement virtuel... + rmdir /s /q llmlab +) + +REM CrĂ©ation du nouvel environnement virtuel +echo CrĂ©ation d'un nouvel environnement virtuel... +python -m venv llmlab + +REM Activation de l'environnement virtuel +echo Activation de l'environnement virtuel... +call llmlab\Scripts\activate + +REM Mise Ă  jour de pip +echo Mise Ă  jour de pip... +python -m pip install --upgrade pip + +REM Installation des dĂ©pendances +echo Installation des dĂ©pendances requises... +pip install -r requirements.txt + +REM Ajout des dĂ©pendances spĂ©cifiques Ă  Windows +echo Installation des dĂ©pendances Windows... +pip install wmi psutil requests pillow + +REM Installation optionnelle de dĂ©pendances pour NVIDIA GPU +where nvidia-smi > nul 2>&1 +if %ERRORLEVEL% EQU 0 ( + echo NVIDIA GPU dĂ©tectĂ©, installation des dĂ©pendances NVIDIA... + pip install nvidia-ml-py +) + +REM VĂ©rification de l'exĂ©cution d'Ollama +curl -s http://localhost:11434/api/tags > nul 2>&1 +if %ERRORLEVEL% NEQ 0 ( + echo. + echo ATTENTION: Ollama ne semble pas ĂŞtre en cours d'exĂ©cution. + echo Veuillez installer et dĂ©marrer Ollama depuis https://ollama.com/download/windows + echo. +) + +REM CrĂ©ation des dossiers nĂ©cessaires +if not exist "logs" mkdir logs +if not exist "chat_history" mkdir chat_history + +REM Affichage du rĂ©sumĂ© +echo. +echo === Configuration terminĂ©e === +echo Pour activer l'environnement virtuel, exĂ©cutez: +echo call llmlab\Scripts\activate +echo. +echo Pour lancer le moniteur système: +echo run.bat monitor +echo. +echo Pour utiliser un agent LLM: +echo run.bat chat [nom de l'agent] +echo. +echo Agents disponibles: +echo cursor - CodeLlama 13B Python (pour le code) +echo obsidian - Llama2 13B (pour la gestion des connaissances) +echo test - Mistral 7B (pour les tests rapides) +echo. + +endlocal \ No newline at end of file diff --git a/setup_env.sh b/setup_env.sh new file mode 100644 index 0000000..69b5d0d --- /dev/null +++ b/setup_env.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +# Script de configuration de l'environnement virtuel LLM Lab +# Ce script crĂ©e un nouvel environnement virtuel et installe les dĂ©pendances requises + +echo "=== Configuration de l'environnement LLM Lab ===" + +# VĂ©rification si Python 3 est installĂ© +if ! command -v python3 &> /dev/null; then + echo "Erreur: Python 3 n'est pas installĂ©. Veuillez l'installer avant de continuer." + exit 1 +fi + +# VĂ©rification si pip est installĂ© +if ! command -v pip3 &> /dev/null; then + echo "Erreur: pip3 n'est pas installĂ©. Veuillez l'installer avant de continuer." + exit 1 +fi + +# VĂ©rification si venv est disponible +python3 -c "import venv" &> /dev/null +if [ $? -ne 0 ]; then + echo "Le module venv n'est pas disponible. Installation en cours..." + sudo apt-get update + sudo apt-get install -y python3-venv +fi + +# VĂ©rification de l'installation de tkinter +python3 -c "import tkinter" &> /dev/null +if [ $? -ne 0 ]; then + echo "Tkinter n'est pas installĂ©. Installation en cours..." + sudo apt-get update + sudo apt-get install -y python3-tk +fi + +# Suppression de l'ancien environnement s'il existe +if [ -d "llmlab" ]; then + echo "Suppression de l'ancien environnement virtuel..." + rm -rf llmlab +fi + +# CrĂ©ation du nouvel environnement virtuel +echo "CrĂ©ation d'un nouvel environnement virtuel..." +python3 -m venv llmlab + +# Activation de l'environnement virtuel +echo "Activation de l'environnement virtuel..." +source llmlab/bin/activate + +# Mise Ă  jour de pip +echo "Mise Ă  jour de pip..." +pip install --upgrade pip + +# Installation des dĂ©pendances +echo "Installation des dĂ©pendances requises..." +pip install -r requirements.txt + +# Installation optionnelle de dĂ©pendances pour NVIDIA GPU +if command -v nvidia-smi &> /dev/null; then + echo "NVIDIA GPU dĂ©tectĂ©, installation des dĂ©pendances NVIDIA..." + pip install nvidia-ml-py +fi + +# CrĂ©ation de liens symboliques pour faciliter l'utilisation +echo "CrĂ©ation de liens symboliques pour les scripts..." +chmod +x chat.py +chmod +x monitor.py + +# Affichage du rĂ©sumĂ© +echo "" +echo "=== Configuration terminĂ©e ===" +echo "Pour activer l'environnement virtuel, exĂ©cutez:" +echo " source llmlab/bin/activate" +echo "" +echo "Pour lancer le moniteur système:" +echo " ./monitor.py" +echo "" +echo "Pour utiliser un agent LLM:" +echo " ./chat.py [nom de l'agent]" +echo "" +echo "Agents disponibles:" +echo " cursor - CodeLlama 13B Python (pour le code)" +echo " obsidian - Llama2 13B (pour la gestion des connaissances)" +echo " test - Mistral 7B (pour les tests rapides)" +echo "" \ No newline at end of file diff --git a/test_installation.bat b/test_installation.bat new file mode 100644 index 0000000..c0e861b --- /dev/null +++ b/test_installation.bat @@ -0,0 +1,98 @@ +@echo off +setlocal + +echo ===== Test d'installation LLM Lab sur Windows 11 Pro ===== +echo. + +REM Activation de l'environnement virtuel si disponible +if exist llmlab\Scripts\activate ( + call llmlab\Scripts\activate +) else ( + echo Environnement virtuel non dĂ©tectĂ©, utilisation de Python système +) + +REM VĂ©rification de la version Python +python --version +if %ERRORLEVEL% NEQ 0 ( + echo [ERREUR] Python n'est pas installĂ© ou n'est pas dans le PATH +) else ( + echo [OK] Python dĂ©tectĂ© +) + +REM VĂ©rification des modules requis +set MODULES=requests tkinter wmi psutil +for %%m in (%MODULES%) do ( + python -c "import %%m" 2>nul + if %ERRORLEVEL% NEQ 0 ( + echo [ERREUR] Module %%m non installĂ© + ) else ( + echo [OK] Module %%m installĂ© + ) +) + +REM VĂ©rification de l'installation Ollama +curl -s http://localhost:11434/api/tags >nul 2>&1 +if %ERRORLEVEL% NEQ 0 ( + echo [ERREUR] Impossible de se connecter Ă  Ollama +) else ( + echo [OK] Connexion Ă  Ollama rĂ©ussie + + REM RĂ©cupĂ©ration des modèles installĂ©s + echo. + echo Modèles Ollama installĂ©s: + curl -s http://localhost:11434/api/tags | findstr "name" +) + +REM VĂ©rification du GPU NVIDIA +where nvidia-smi >nul 2>&1 +if %ERRORLEVEL% NEQ 0 ( + echo [INFO] NVIDIA GPU non dĂ©tectĂ© +) else ( + echo [OK] NVIDIA GPU dĂ©tectĂ© + + REM Affichage des informations GPU + echo. + echo Informations GPU: + nvidia-smi --query-gpu=name,driver_version,memory.total --format=csv,noheader +) + +REM VĂ©rification des fichiers de projet +echo. +echo VĂ©rification des fichiers clĂ©s du projet: +set FILES=chat.py chat_gui.py monitor.py run.bat +for %%f in (%FILES%) do ( + if exist %%f ( + echo [OK] Fichier %%f trouvĂ© + ) else ( + echo [ERREUR] Fichier %%f manquant + ) +) + +REM VĂ©rification des dossiers requis +echo. +echo VĂ©rification des dossiers: +set DIRS=logs chat_history agents core utils +for %%d in (%DIRS%) do ( + if exist %%d ( + echo [OK] Dossier %%d trouvĂ© + ) else ( + echo [ERREUR] Dossier %%d manquant + ) +) + +REM RĂ©capitulatif +echo. +echo ===== RĂ©capitulatif ===== +echo. +echo Si tous les tests sont [OK], le système est correctement configurĂ©. +echo. +echo Pour optimiser Ollama, exĂ©cutez optimize_ollama.bat en tant qu'administrateur. +echo Pour lancer LLM Lab, utilisez run.bat gui ou run.bat chat [agent] +echo. + +REM DĂ©sactivation de l'environnement virtuel si nĂ©cessaire +if exist llmlab\Scripts\activate ( + deactivate +) + +endlocal \ No newline at end of file diff --git a/tests/test_multi_agent_fixed_params.py b/tests/test_multi_agent_fixed_params.py new file mode 100644 index 0000000..f40b887 --- /dev/null +++ b/tests/test_multi_agent_fixed_params.py @@ -0,0 +1,18 @@ +#Tester plusieurs agents avec les mĂŞmes paramètres +import sys +import os +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) +from core.factory import LLMFactory +from utils.parameter_tester import test_agents_on_prompt + +test_agents_on_prompt( + model_class=LLMFactory._registry["mistral7b"], + prompt="Explique ce qu'est le machine learning.", + agents=["formateur", "chercheur", "assistant_technique"], + param_grid={}, + fixed_params={ + "temperature": 1.5, + "top_p": 0.9, + "num_predict": 300 + } +) \ No newline at end of file diff --git a/tests/test_multi_agent_grid.py b/tests/test_multi_agent_grid.py new file mode 100644 index 0000000..ae8076d --- /dev/null +++ b/tests/test_multi_agent_grid.py @@ -0,0 +1,19 @@ +# Tester plusieurs agents avec des combinations automatiques +import sys +import os +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) +from core.factory import LLMFactory +from utils.parameter_tester import test_agents_on_prompt + +test_agents_on_prompt( + model_class=LLMFactory._registry["mistral7b"], + prompt="DĂ©cris le fonctionnement du protocole HTTP.", + agents=["formateur", "assistant_technique"], + param_grid={ + "temperature": [0.5, 1.0, 1.5], + "top_p": [0.5, 0.9, 1.0], + }, + fixed_params={ + "num_predict": 300 + } +) \ No newline at end of file diff --git a/tests/test_new_models.py b/tests/test_new_models.py new file mode 100644 index 0000000..1c67a1a --- /dev/null +++ b/tests/test_new_models.py @@ -0,0 +1,34 @@ +from core.factory import LLMFactory + +def test_all_models(): + """Test simple de tous les modèles disponibles""" + # Liste des modèles Ă  tester + model_names = ["mistral7b", "codellama13b-python", "llama2-13b"] + + for model_name in model_names: + print(f"\nTest du modèle {model_name}:") + try: + # Instanciation du modèle via la factory + model = LLMFactory.create(model_name) + print(f"âś“ Modèle {model_name} instanciĂ© avec succès") + + # Test des attributs basiques + assert model.model is not None, "Le nom du modèle est None" + assert model.engine is not None, "Le moteur du modèle est None" + print(f"âś“ Attributs du modèle {model_name} vĂ©rifiĂ©s") + + # Test d'accès aux paramètres + assert model.params is not None, "Les paramètres sont None" + assert "temperature" in model.params, "Le paramètre temperature n'est pas dĂ©fini" + print(f"âś“ Paramètres du modèle {model_name} vĂ©rifiĂ©s") + + # Affichage des informations du modèle + print(f" - Nom du modèle: {model.model}") + print(f" - Moteur: {model.engine}") + print(f" - TempĂ©rature: {model.params.get('temperature')}") + + except Exception as e: + print(f"âś— Erreur avec le modèle {model_name}: {str(e)}") + +if __name__ == "__main__": + test_all_models() \ No newline at end of file diff --git a/tests/test_single_agent.py b/tests/test_single_agent.py new file mode 100644 index 0000000..18efe27 --- /dev/null +++ b/tests/test_single_agent.py @@ -0,0 +1,13 @@ +# Tester un seul agent avec un seul prompt, sans variation +import sys +import os +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) + +from utils.agent_manager import AgentManager + +# Utilisation de l'AgentManager pour crĂ©er l'agent avec le modèle appropriĂ© +agent = AgentManager.create("test") # Utilise l'agent "test" qui est configurĂ© avec Mistral7B + +print(f"Test de l'agent '{agent.agent}' avec le modèle '{agent.model}'") +response = agent.generate("Qu'est-ce qu'une API REST?") +print("-> RĂ©ponse gĂ©nĂ©rĂ©e.") diff --git a/tests/test_single_agent_param_grid.py b/tests/test_single_agent_param_grid.py new file mode 100644 index 0000000..fa963a1 --- /dev/null +++ b/tests/test_single_agent_param_grid.py @@ -0,0 +1,19 @@ +# Tester un seul agent, avec des paramètres qui varient +import sys +import os +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) +from core.factory import LLMFactory +from utils.parameter_tester import test_agents_on_prompt + +test_agents_on_prompt( + model_class=LLMFactory._registry["mistral7b"], + prompt="Donne une dĂ©finition claire de l'intelligence artificielle.", + agents=["juriste"], + param_grid={ + "temperature": [0.1, 0.5, 1.0], + "top_p": [0.7, 0.9] + }, + fixed_params={ + "num_predict": 256 + } +) diff --git a/update_monitor_for_ollama_path.md b/update_monitor_for_ollama_path.md new file mode 100644 index 0000000..58a1947 --- /dev/null +++ b/update_monitor_for_ollama_path.md @@ -0,0 +1,292 @@ +# Adaptation du moniteur système pour le nouvel emplacement Ollama + +Ce guide explique comment modifier le moniteur système de LLM Lab pour qu'il dĂ©tecte et affiche correctement l'emplacement des modèles Ollama sur un disque sĂ©parĂ©. + +## 1. Modifications nĂ©cessaires dans system_monitor.py + +Ouvrez le fichier `system_monitor.py` et apportez les modifications suivantes pour dĂ©tecter l'emplacement des modèles Ollama sur un disque sĂ©parĂ©. + +### Fonction de dĂ©tection du chemin des modèles + +Ajoutez cette nouvelle fonction au dĂ©but de la classe `SystemMonitor`: + +```python +def _get_ollama_models_path(self): + """DĂ©tecte l'emplacement des modèles Ollama""" + models_path = None + + # VĂ©rifier si la variable d'environnement est dĂ©finie + if 'OLLAMA_MODELS' in os.environ: + models_path = os.environ['OLLAMA_MODELS'] + self._log(f"Chemin des modèles Ollama trouvĂ© via variable d'environnement: {models_path}") + return models_path + + # VĂ©rifier le chemin par dĂ©faut + default_path = os.path.expanduser(os.path.join("~", ".ollama", "models")) + + if platform.system() == "Windows": + # VĂ©rifier si c'est un lien symbolique sous Windows + try: + import subprocess + result = subprocess.run(['dir', '/al', default_path], + shell=True, capture_output=True, text=True) + + if "" in result.stdout: + # Extraire la cible du lien symbolique + for line in result.stdout.splitlines(): + if "" in line and "[" in line and "]" in line: + target = line.split("[")[1].split("]")[0] + models_path = target + self._log(f"Lien symbolique dĂ©tectĂ© pour les modèles Ollama: {models_path}") + return models_path + except: + pass + + # Si aucune redirection n'est trouvĂ©e, utiliser le chemin par dĂ©faut + if os.path.exists(default_path): + models_path = default_path + + return models_path or default_path +``` + +### Afficher l'emplacement des modèles dans l'interface + +Modifiez la mĂ©thode `_create_widgets` pour ajouter un affichage de l'emplacement des modèles dans l'onglet Ollama: + +```python +# Dans _create_widgets, après la crĂ©ation de url_frame +models_path_frame = ttk.Frame(server_frame) +models_path_frame.pack(fill=tk.X, padx=5, pady=2) +ttk.Label(models_path_frame, text="Modèles:").pack(side=tk.LEFT, padx=5) +self.models_path_label = ttk.Label(models_path_frame, text="VĂ©rification...") +self.models_path_label.pack(side=tk.LEFT, padx=5) +``` + +### Mettre Ă  jour l'information dans _update_ollama_info + +Ajoutez ce code au dĂ©but de la mĂ©thode `_update_ollama_info`: + +```python +# Mettre Ă  jour l'emplacement des modèles +models_path = self._get_ollama_models_path() +self.models_path_label.config(text=models_path) + +# VĂ©rifier l'espace disque pour les modèles +if models_path and os.path.exists(models_path): + try: + import shutil + total, used, free = shutil.disk_usage(os.path.dirname(models_path)) + total_gb = total / (1024**3) + used_gb = used / (1024**3) + free_gb = free / (1024**3) + + disk_info = f"{models_path} ({free_gb:.1f} GB libres / {total_gb:.1f} GB)" + self.models_path_label.config(text=disk_info) + + # Afficher un avertissement si l'espace libre est faible + if free_gb < 10: # Moins de 10 GB libres + self.models_path_label.config(foreground="orange") + self._log(f"Avertissement: espace disque limitĂ© pour les modèles: {free_gb:.1f} GB") + elif free_gb < 5: # Moins de 5 GB libres + self.models_path_label.config(foreground="red") + self._log(f"Alerte: espace disque très limitĂ© pour les modèles: {free_gb:.1f} GB") + else: + self.models_path_label.config(foreground="green") + except Exception as e: + self._log(f"Erreur lors de la vĂ©rification de l'espace disque: {str(e)}") +``` + +## 2. Ajout d'un onglet d'information sur les disques + +Pour un monitoring plus complet, ajoutez un nouvel onglet dĂ©diĂ© Ă  l'affichage de l'Ă©tat des disques: + +```python +# Dans _create_widgets, après la crĂ©ation de logs_frame +# Onglet 5: Disques +self.disks_frame = ttk.Frame(self.notebook) +self.notebook.add(self.disks_frame, text="Disques") + +# Configuration de l'onglet Disques +disks_label = ttk.Label(self.disks_frame, text="Stockage", font=("Arial", 14, "bold")) +disks_label.pack(pady=10) + +# Tableau des disques +disks_table_frame = ttk.LabelFrame(self.disks_frame, text="Volumes") +disks_table_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) + +self.disks_tree = ttk.Treeview(disks_table_frame, + columns=("Lettre", "Total", "UtilisĂ©", "Libre", "Type"), + show='headings') +self.disks_tree.heading("Lettre", text="Disque") +self.disks_tree.heading("Total", text="Taille") +self.disks_tree.heading("UtilisĂ©", text="UtilisĂ©") +self.disks_tree.heading("Libre", text="Libre") +self.disks_tree.heading("Type", text="Type") +self.disks_tree.column("Lettre", width=70) +self.disks_tree.column("Total", width=100) +self.disks_tree.column("UtilisĂ©", width=100) +self.disks_tree.column("Libre", width=100) +self.disks_tree.column("Type", width=150) +self.disks_tree.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) +``` + +### Ajouter une mĂ©thode pour mettre Ă  jour les informations de disque + +```python +def _update_disks_info(self): + """Met Ă  jour les informations sur les disques""" + try: + # Effacer le tableau + for item in self.disks_tree.get_children(): + self.disks_tree.delete(item) + + if platform.system() == "Windows": + # Obtenir la liste des lecteurs + import string + import ctypes + + drives = [] + bitmask = ctypes.windll.kernel32.GetLogicalDrives() + for letter in string.ascii_uppercase: + if bitmask & 1: + drives.append(f"{letter}:") + bitmask >>= 1 + + # Pour chaque lecteur, obtenir les informations + for drive in drives: + try: + total, used, free = shutil.disk_usage(drive) + total_gb = total / (1024**3) + used_gb = used / (1024**3) + free_gb = free / (1024**3) + + # DĂ©terminer le type de disque + drive_type = ctypes.windll.kernel32.GetDriveTypeW(drive) + types = { + 0: "Inconnu", + 1: "Inexistant", + 2: "Amovible", + 3: "Fixe", + 4: "RĂ©seau", + 5: "CD-ROM", + 6: "RAM Disk" + } + drive_type_str = types.get(drive_type, "Inconnu") + + # Ajouter au tableau + percent = (used / total) * 100 if total > 0 else 0 + + # Couleur selon l'espace libre + if percent > 90: + tag = "critical" + elif percent > 75: + tag = "warning" + else: + tag = "" + + self.disks_tree.insert("", tk.END, + values=(drive, + f"{total_gb:.1f} GB", + f"{used_gb:.1f} GB ({percent:.1f}%)", + f"{free_gb:.1f} GB", + drive_type_str), + tags=(tag,)) + + # VĂ©rifier si c'est le disque des modèles Ollama + models_path = self._get_ollama_models_path() + if models_path and drive in models_path: + self._log(f"Disque des modèles Ollama: {drive} - {free_gb:.1f} GB libres") + + except Exception as e: + self.disks_tree.insert("", tk.END, + values=(drive, "Erreur", "Erreur", "Erreur", str(e)), + tags=("error",)) + + # Configurer les couleurs + self.disks_tree.tag_configure("critical", background="#ffcccc") + self.disks_tree.tag_configure("warning", background="#ffffcc") + self.disks_tree.tag_configure("error", background="#ff9999") + + else: + # Code pour Linux/Mac + # Utiliser psutil.disk_partitions() pour lister les partitions + for part in psutil.disk_partitions(all=False): + try: + usage = psutil.disk_usage(part.mountpoint) + total_gb = usage.total / (1024**3) + used_gb = usage.used / (1024**3) + free_gb = usage.free / (1024**3) + + # Ajouter au tableau + self.disks_tree.insert("", tk.END, + values=(part.mountpoint, + f"{total_gb:.1f} GB", + f"{used_gb:.1f} GB ({usage.percent}%)", + f"{free_gb:.1f} GB", + f"{part.fstype}")) + + except Exception as e: + pass + except Exception as e: + self._log(f"Erreur lors de la mise Ă  jour des informations disque: {str(e)}") +``` + +### Ajouter l'appel Ă  la nouvelle mĂ©thode dans _update_loop + +Ajoutez cet appel dans la mĂ©thode `_update_loop` pour mettre Ă  jour les informations disque rĂ©gulièrement: + +```python +# Dans _update_loop +self._update_system_info() +self._update_ollama_info() +self._update_gpu_info() +self._update_disks_info() # Ajouter cette ligne +``` + +## 3. Test de la mise Ă  jour + +Pour tester ces modifications: + +1. Appliquez les changements au fichier system_monitor.py +2. RedĂ©marrez le moniteur système +3. VĂ©rifiez que le chemin des modèles Ollama est correctement affichĂ© +4. Assurez-vous que l'espace disque est correctement dĂ©tectĂ© +5. Testez avec diffĂ©rentes configurations (variable d'environnement, lien symbolique) + +## 4. IntĂ©gration avec le reste de LLM Lab + +Modifiez les autres fichiers qui pourraient avoir besoin de connaĂ®tre l'emplacement des modèles: + +### Ajouter une fonction utilitaire dans utils/ollama_utils.py + +```python +def get_ollama_models_path(): + """DĂ©tecte l'emplacement des modèles Ollama de manière cohĂ©rente dans tout le projet""" + # VĂ©rifier la variable d'environnement + if 'OLLAMA_MODELS' in os.environ: + return os.environ['OLLAMA_MODELS'] + + # VĂ©rifier si c'est un lien symbolique sous Windows + default_path = os.path.expanduser(os.path.join("~", ".ollama", "models")) + + if platform.system() == "Windows": + try: + import subprocess + result = subprocess.run(['dir', '/al', default_path], + shell=True, capture_output=True, text=True) + + if "" in result.stdout: + for line in result.stdout.splitlines(): + if "" in line and "[" in line and "]" in line: + return line.split("[")[1].split("]")[0] + except: + pass + + return default_path +``` + +Utilisez cette fonction dans les autres parties du code qui ont besoin de connaĂ®tre l'emplacement des modèles. + +## Utilisation dans l'interface graphique + +Ces modifications permettront Ă  l'utilisateur de voir clairement oĂą sont stockĂ©s les modèles Ollama et de surveiller l'espace disque disponible pour Ă©viter les problèmes lors du tĂ©lĂ©chargement de nouveaux modèles. \ No newline at end of file diff --git a/utils/__pycache__/agent_manager.cpython-312.pyc b/utils/__pycache__/agent_manager.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..61f8ccb626b67f44039a149b4d6167593787ca79 GIT binary patch literal 2394 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 literal 0 HcmV?d00001 diff --git a/utils/__pycache__/system_monitor.cpython-312.pyc b/utils/__pycache__/system_monitor.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..801c1a36aec796b519140c367374d5392d9701a7 GIT binary patch literal 32135 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 R8y)pW2On!yx?Ix2{|lvVX!8I7 literal 0 HcmV?d00001 diff --git a/utils/__pycache__/system_monitor.cpython-313.pyc b/utils/__pycache__/system_monitor.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c7b4135b6ed07e78af7c35b3874b81cb9fea4aa2 GIT binary patch literal 31743 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 literal 0 HcmV?d00001 diff --git a/utils/agent_manager.py b/utils/agent_manager.py new file mode 100644 index 0000000..f697bbd --- /dev/null +++ b/utils/agent_manager.py @@ -0,0 +1,59 @@ +""" +Gestionnaire d'agents permettant la crĂ©ation d'agents spĂ©cifiques avec leurs modèles associĂ©s +""" +from core.factory import LLMFactory +from agents.roles import AGENTS + +class AgentManager: + """ + Classe utilitaire pour crĂ©er et configurer des agents avec leurs modèles associĂ©s + """ + + @staticmethod + def create(agent_name): + """ + CrĂ©e et configure un agent avec le modèle appropriĂ© + + Args: + agent_name (str): Nom de l'agent Ă  crĂ©er + + Returns: + BaseLLM: Instance du modèle configurĂ©e avec le rĂ´le de l'agent + + Raises: + ValueError: Si l'agent n'existe pas + """ + if agent_name not in AGENTS: + raise ValueError(f"Agent inconnu: {agent_name}") + + agent_config = AGENTS[agent_name] + + # SĂ©lectionner le modèle spĂ©cifiĂ© pour l'agent ou utiliser mistral7b par dĂ©faut + model_name = agent_config.get("model", "mistral7b") + + # CrĂ©er le modèle + model = LLMFactory.create(model_name) + + # Appliquer la configuration de l'agent + model.set_role(agent_name, agent_config) + + return model + + @staticmethod + def list_agents(): + """ + Liste tous les agents disponibles avec leurs modèles associĂ©s + + Returns: + dict: Dictionnaire des agents et leurs modèles associĂ©s + """ + agents_info = {} + + for agent_name, config in AGENTS.items(): + model = config.get("model", "mistral7b") + agents_info[agent_name] = { + "model": model, + "description": config.get("system_prompt", "")[:50] + "..." if len(config.get("system_prompt", "")) > 50 else config.get("system_prompt", "") + } + + return agents_info \ No newline at end of file diff --git a/utils/chat_ui.py b/utils/chat_ui.py new file mode 100644 index 0000000..ccfa581 --- /dev/null +++ b/utils/chat_ui.py @@ -0,0 +1,980 @@ +""" +Interface graphique pour le chat avec les modèles LLM +""" +import tkinter as tk +from tkinter import ttk, scrolledtext, messagebox, filedialog +import json +import os +from datetime import datetime +from utils.agent_manager import AgentManager +from core.factory import LLMFactory + +class ChatUI: + """Interface graphique pour interagir avec les agents LLM""" + + def __init__(self, root): + """Initialise l'interface graphique""" + self.root = root + self.root.title("LLM Lab - Chat") + self.root.geometry("1000x700") + self.root.minsize(800, 600) + + # Variables pour le chat + self.current_agent = None + self.conversation_history = [] + self.custom_params = {} + self.history_files = [] + + # Style + self.style = ttk.Style() + self.style.theme_use('alt') # 'clam', 'alt', 'default', 'classic' + + # CrĂ©ation de l'interface + self._create_ui() + + # Chargement de la liste des agents + self._load_agents() + + # Chargement de l'historique des conversations + self._load_conversation_history() + + def _create_ui(self): + """CrĂ©e l'interface utilisateur""" + # Interface principale en deux parties + self.main_paned = ttk.PanedWindow(self.root, orient=tk.HORIZONTAL) + self.main_paned.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) + + # Panneau de gauche : configuration et historique + self.left_frame = ttk.Frame(self.main_paned, width=300) + self.main_paned.add(self.left_frame, weight=1) + + # Panneau de droite : chat + self.right_frame = ttk.Frame(self.main_paned) + self.main_paned.add(self.right_frame, weight=3) + + # Configuration du panneau de gauche + self._create_config_panel() + + # Configuration du panneau de droite + self._create_chat_panel() + + def _create_config_panel(self): + """CrĂ©e le panneau de configuration (gauche)""" + # Utilisation d'un notebook pour organiser les paramètres + self.config_notebook = ttk.Notebook(self.left_frame) + self.config_notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) + + # Onglet 1: SĂ©lection d'agent + self.agent_frame = ttk.Frame(self.config_notebook) + self.config_notebook.add(self.agent_frame, text="Agent") + + # Onglet 2: Paramètres + self.params_frame = ttk.Frame(self.config_notebook) + self.config_notebook.add(self.params_frame, text="Paramètres") + + # Onglet 3: Historique + self.history_frame = ttk.Frame(self.config_notebook) + self.config_notebook.add(self.history_frame, text="Historique") + + # Remplissage de l'onglet Agent + self._create_agent_tab() + + # Remplissage de l'onglet Paramètres + self._create_params_tab() + + # Remplissage de l'onglet Historique + self._create_history_tab() + + def _create_agent_tab(self): + """CrĂ©e l'onglet de sĂ©lection d'agent""" + # Label et liste des agents + ttk.Label(self.agent_frame, text="SĂ©lectionnez un agent:", + font=("Arial", 11, "bold")).pack(pady=(10, 5), padx=5, anchor=tk.W) + + # Frame pour la liste des agents + agent_list_frame = ttk.Frame(self.agent_frame) + agent_list_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) + + # Scrollbar + scrollbar = ttk.Scrollbar(agent_list_frame) + scrollbar.pack(side=tk.RIGHT, fill=tk.Y) + + # Liste des agents + self.agent_listbox = tk.Listbox(agent_list_frame, yscrollcommand=scrollbar.set, + selectmode=tk.SINGLE, activestyle='dotbox', + font=("Arial", 10)) + self.agent_listbox.pack(fill=tk.BOTH, expand=True) + scrollbar.config(command=self.agent_listbox.yview) + + # Gestionnaire d'Ă©vĂ©nements pour la sĂ©lection d'agent + self.agent_listbox.bind('<>', self._on_agent_select) + + # Informations sur l'agent + agent_info_frame = ttk.LabelFrame(self.agent_frame, text="Informations sur l'agent") + agent_info_frame.pack(fill=tk.X, padx=5, pady=5) + + # Description de l'agent + ttk.Label(agent_info_frame, text="Description:").pack(anchor=tk.W, padx=5, pady=(5, 0)) + self.agent_description = tk.Text(agent_info_frame, wrap=tk.WORD, height=5, + width=30, font=("Arial", 9)) + self.agent_description.pack(fill=tk.X, padx=5, pady=5) + self.agent_description.config(state=tk.DISABLED) + + # Modèle utilisĂ© + model_frame = ttk.Frame(agent_info_frame) + model_frame.pack(fill=tk.X, padx=5, pady=(0, 5)) + ttk.Label(model_frame, text="Modèle:").pack(side=tk.LEFT, padx=(0, 5)) + self.model_label = ttk.Label(model_frame, text="-") + self.model_label.pack(side=tk.LEFT) + + # Bouton pour changer de modèle + self.model_combo = ttk.Combobox(self.agent_frame, state="readonly") + self.model_combo.pack(fill=tk.X, padx=5, pady=5) + # ÉvĂ©nement de changement + self.model_combo.bind("<>", self._on_model_change) + + # Bouton de test + test_btn = ttk.Button(self.agent_frame, text="Tester l'agent", + command=self._test_agent) + test_btn.pack(fill=tk.X, padx=5, pady=(0, 10)) + + def _create_params_tab(self): + """CrĂ©e l'onglet de configuration des paramètres""" + # Canvas et scrollbar pour permettre le dĂ©filement + canvas = tk.Canvas(self.params_frame) + scrollbar = ttk.Scrollbar(self.params_frame, orient="vertical", command=canvas.yview) + scroll_frame = ttk.Frame(canvas) + + scroll_frame.bind( + "", + lambda e: canvas.configure(scrollregion=canvas.bbox("all")) + ) + + canvas.create_window((0, 0), window=scroll_frame, anchor="nw") + canvas.configure(yscrollcommand=scrollbar.set) + + canvas.pack(side="left", fill="both", expand=True) + scrollbar.pack(side="right", fill="y") + + # Titre + ttk.Label(scroll_frame, text="Paramètres du modèle:", + font=("Arial", 11, "bold")).pack(pady=(10, 5), padx=5, anchor=tk.W) + + # TempĂ©rature + temp_frame = ttk.Frame(scroll_frame) + temp_frame.pack(fill=tk.X, padx=5, pady=5) + ttk.Label(temp_frame, text="TempĂ©rature:").pack(side=tk.LEFT, padx=(0, 5)) + self.temp_var = tk.DoubleVar(value=0.7) + temp_scale = ttk.Scale(temp_frame, from_=0.0, to=2.0, orient=tk.HORIZONTAL, + variable=self.temp_var, length=150) + temp_scale.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5)) + temp_label = ttk.Label(temp_frame, textvariable=self.temp_var, width=5) + temp_label.pack(side=tk.LEFT) + self.temp_var.trace_add("write", self._on_param_change) + + # Top-p + top_p_frame = ttk.Frame(scroll_frame) + top_p_frame.pack(fill=tk.X, padx=5, pady=5) + ttk.Label(top_p_frame, text="Top-p:").pack(side=tk.LEFT, padx=(0, 5)) + self.top_p_var = tk.DoubleVar(value=0.9) + top_p_scale = ttk.Scale(top_p_frame, from_=0.0, to=1.0, orient=tk.HORIZONTAL, + variable=self.top_p_var, length=150) + top_p_scale.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5)) + top_p_label = ttk.Label(top_p_frame, textvariable=self.top_p_var, width=5) + top_p_label.pack(side=tk.LEFT) + self.top_p_var.trace_add("write", self._on_param_change) + + # Top-k + top_k_frame = ttk.Frame(scroll_frame) + top_k_frame.pack(fill=tk.X, padx=5, pady=5) + ttk.Label(top_k_frame, text="Top-k:").pack(side=tk.LEFT, padx=(0, 5)) + self.top_k_var = tk.IntVar(value=40) + top_k_scale = ttk.Scale(top_k_frame, from_=0, to=100, orient=tk.HORIZONTAL, + variable=self.top_k_var, length=150) + top_k_scale.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5)) + top_k_label = ttk.Label(top_k_frame, textvariable=self.top_k_var, width=5) + top_k_label.pack(side=tk.LEFT) + self.top_k_var.trace_add("write", self._on_param_change) + + # Repeat penalty + repeat_frame = ttk.Frame(scroll_frame) + repeat_frame.pack(fill=tk.X, padx=5, pady=5) + ttk.Label(repeat_frame, text="Repeat Penalty:").pack(side=tk.LEFT, padx=(0, 5)) + self.repeat_var = tk.DoubleVar(value=1.1) + repeat_scale = ttk.Scale(repeat_frame, from_=1.0, to=2.0, orient=tk.HORIZONTAL, + variable=self.repeat_var, length=150) + repeat_scale.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5)) + repeat_label = ttk.Label(repeat_frame, textvariable=self.repeat_var, width=5) + repeat_label.pack(side=tk.LEFT) + self.repeat_var.trace_add("write", self._on_param_change) + + # Tokens max + tokens_frame = ttk.Frame(scroll_frame) + tokens_frame.pack(fill=tk.X, padx=5, pady=5) + ttk.Label(tokens_frame, text="Tokens max:").pack(side=tk.LEFT, padx=(0, 5)) + self.tokens_var = tk.IntVar(value=512) + tokens_scale = ttk.Scale(tokens_frame, from_=32, to=4096, orient=tk.HORIZONTAL, + variable=self.tokens_var, length=150) + tokens_scale.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5)) + tokens_label = ttk.Label(tokens_frame, textvariable=self.tokens_var, width=5) + tokens_label.pack(side=tk.LEFT) + self.tokens_var.trace_add("write", self._on_param_change) + + # Boutons de gestion + buttons_frame = ttk.Frame(scroll_frame) + buttons_frame.pack(fill=tk.X, padx=5, pady=10) + + self.reset_btn = ttk.Button(buttons_frame, text="RĂ©initialiser", + command=self._reset_params) + self.reset_btn.pack(side=tk.LEFT, padx=(0, 5)) + + self.save_btn = ttk.Button(buttons_frame, text="Enregistrer", + command=self._save_params) + self.save_btn.pack(side=tk.LEFT) + + # SĂ©parateur + ttk.Separator(scroll_frame, orient=tk.HORIZONTAL).pack(fill=tk.X, padx=5, pady=10) + + # Système de presets + ttk.Label(scroll_frame, text="Presets:", + font=("Arial", 11, "bold")).pack(pady=(5, 5), padx=5, anchor=tk.W) + + presets_frame = ttk.Frame(scroll_frame) + presets_frame.pack(fill=tk.X, padx=5, pady=5) + + self.preset_combo = ttk.Combobox(presets_frame, values=[ + "CrĂ©atif", "PrĂ©cis", "ÉquilibrĂ©", "Code", "Conversation" + ]) + self.preset_combo.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5)) + self.preset_combo.current(2) # Par dĂ©faut: ÉquilibrĂ© + + self.apply_preset_btn = ttk.Button(presets_frame, text="Appliquer", + command=self._apply_preset) + self.apply_preset_btn.pack(side=tk.LEFT) + + def _create_history_tab(self): + """CrĂ©e l'onglet d'historique des conversations""" + # Label + ttk.Label(self.history_frame, text="Historique des conversations:", + font=("Arial", 11, "bold")).pack(pady=(10, 5), padx=5, anchor=tk.W) + + # Liste des conversations + history_list_frame = ttk.Frame(self.history_frame) + history_list_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) + + scrollbar = ttk.Scrollbar(history_list_frame) + scrollbar.pack(side=tk.RIGHT, fill=tk.Y) + + self.history_listbox = tk.Listbox(history_list_frame, yscrollcommand=scrollbar.set, + font=("Arial", 10)) + self.history_listbox.pack(fill=tk.BOTH, expand=True) + scrollbar.config(command=self.history_listbox.yview) + + # ÉvĂ©nement de sĂ©lection + self.history_listbox.bind('<>', self._on_history_select) + + # Boutons + button_frame = ttk.Frame(self.history_frame) + button_frame.pack(fill=tk.X, padx=5, pady=(0, 10)) + + self.load_history_btn = ttk.Button(button_frame, text="Charger", + command=self._load_selected_history) + self.load_history_btn.pack(side=tk.LEFT, padx=(0, 5)) + + self.delete_history_btn = ttk.Button(button_frame, text="Supprimer", + command=self._delete_history) + self.delete_history_btn.pack(side=tk.LEFT, padx=(0, 5)) + + self.export_history_btn = ttk.Button(button_frame, text="Exporter", + command=self._export_history) + self.export_history_btn.pack(side=tk.LEFT) + + def _create_chat_panel(self): + """CrĂ©e le panneau de chat (droite)""" + # Titre + chat_title_frame = ttk.Frame(self.right_frame) + chat_title_frame.pack(fill=tk.X, padx=5, pady=5) + + self.chat_title = ttk.Label(chat_title_frame, text="Chat - Aucun agent sĂ©lectionnĂ©", + font=("Arial", 12, "bold")) + self.chat_title.pack(side=tk.LEFT) + + # Zone de chat + chat_frame = ttk.Frame(self.right_frame) + chat_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) + + # Zone d'affichage des messages + self.chat_display = scrolledtext.ScrolledText(chat_frame, wrap=tk.WORD, + font=("Arial", 10)) + self.chat_display.pack(fill=tk.BOTH, expand=True) + self.chat_display.config(state=tk.DISABLED) + + # Zone de saisie + input_frame = ttk.Frame(self.right_frame) + input_frame.pack(fill=tk.X, padx=5, pady=(0, 5)) + + self.user_input = scrolledtext.ScrolledText(input_frame, wrap=tk.WORD, + height=3, font=("Arial", 10)) + self.user_input.pack(fill=tk.X, pady=(0, 5)) + self.user_input.bind("", self._on_shift_enter) + + # Boutons d'action + button_frame = ttk.Frame(self.right_frame) + button_frame.pack(fill=tk.X, padx=5, pady=(0, 5)) + + self.send_btn = ttk.Button(button_frame, text="Envoyer", width=10, + command=self._send_message) + self.send_btn.pack(side=tk.RIGHT, padx=(5, 0)) + + self.clear_btn = ttk.Button(button_frame, text="Effacer", width=10, + command=self._clear_chat) + self.clear_btn.pack(side=tk.RIGHT, padx=(5, 0)) + + self.new_chat_btn = ttk.Button(button_frame, text="Nouvelle conversation", width=20, + command=self._new_chat) + self.new_chat_btn.pack(side=tk.RIGHT) + + # Barre d'Ă©tat + self.status_bar = ttk.Label(self.right_frame, text="PrĂŞt", relief=tk.SUNKEN, anchor=tk.W) + self.status_bar.pack(side=tk.BOTTOM, fill=tk.X) + + def _load_agents(self): + """Charge la liste des agents disponibles""" + # Effacer la liste actuelle + self.agent_listbox.delete(0, tk.END) + + # RĂ©cupĂ©rer les agents + agents_info = AgentManager.list_agents() + + # Ajouter les agents Ă  la liste + for agent_name in agents_info: + self.agent_listbox.insert(tk.END, agent_name) + + # Charger la liste des modèles disponibles + self._load_models() + + def _load_models(self): + """Charge la liste des modèles disponibles""" + try: + # RĂ©cupĂ©rer les modèles disponibles + models = LLMFactory.get_available_models() + self.model_combo['values'] = models + except Exception as e: + print(f"Erreur lors du chargement des modèles: {str(e)}") + self.model_combo['values'] = ["mistral:latest", "codellama:13b-python", "llama2:13b"] + + def _on_agent_select(self, event): + """Gestionnaire d'Ă©vĂ©nement pour la sĂ©lection d'un agent""" + # RĂ©cupĂ©rer l'index sĂ©lectionnĂ© + selection = self.agent_listbox.curselection() + if not selection: + return + + # RĂ©cupĂ©rer le nom de l'agent + agent_name = self.agent_listbox.get(selection[0]) + + # RĂ©cupĂ©rer les informations de l'agent + agents_info = AgentManager.list_agents() + if agent_name not in agents_info: + return + + agent_info = agents_info[agent_name] + + # Mettre Ă  jour les informations affichĂ©es + self.agent_description.config(state=tk.NORMAL) + self.agent_description.delete(1.0, tk.END) + self.agent_description.insert(tk.END, agent_info['description']) + self.agent_description.config(state=tk.DISABLED) + + # Mettre Ă  jour le modèle + model_name = agent_info['model'] + self.model_label.config(text=model_name) + + # SĂ©lectionner le modèle dans la combobox + if model_name in self.model_combo['values']: + self.model_combo.set(model_name) + + # Mettre Ă  jour le titre du chat + self.chat_title.config(text=f"Chat avec {agent_name} ({model_name})") + + # Mettre Ă  jour l'agent courant + self.current_agent = agent_name + + # Charger les paramètres de l'agent + self._load_agent_params(agent_name) + + # Mettre Ă  jour le statut + self.status_bar.config(text=f"Agent {agent_name} sĂ©lectionnĂ© avec le modèle {model_name}") + + def _on_model_change(self, event): + """Gestionnaire d'Ă©vĂ©nement pour le changement de modèle""" + if not self.current_agent: + messagebox.showwarning("Aucun agent sĂ©lectionnĂ©", + "Veuillez d'abord sĂ©lectionner un agent.") + return + + # RĂ©cupĂ©rer le modèle sĂ©lectionnĂ© + model_name = self.model_combo.get() + + # Mettre Ă  jour l'affichage + self.model_label.config(text=model_name) + + # Mettre Ă  jour le titre du chat + self.chat_title.config(text=f"Chat avec {self.current_agent} ({model_name})") + + # Mettre Ă  jour les paramètres du modèle + self._load_model_params(model_name) + + # Mettre Ă  jour le statut + self.status_bar.config(text=f"Modèle changĂ© pour {model_name}") + + def _test_agent(self): + """Fonction de test rapide de l'agent sĂ©lectionnĂ©""" + if not self.current_agent: + messagebox.showwarning("Aucun agent sĂ©lectionnĂ©", + "Veuillez d'abord sĂ©lectionner un agent.") + return + + # Message de test + test_message = "PrĂ©sente-toi brièvement et explique tes capacitĂ©s." + + # Ajouter le message au chat + self._add_message_to_chat("Utilisateur", test_message) + + # GĂ©nĂ©rer une rĂ©ponse + try: + # Mettre Ă  jour le statut + self.status_bar.config(text=f"GĂ©nĂ©ration en cours...") + self.root.update() + + # CrĂ©er l'agent avec les paramètres personnalisĂ©s + agent = self._create_agent_with_custom_params() + + # GĂ©nĂ©rer la rĂ©ponse + response = agent.generate(test_message) + + # Ajouter la rĂ©ponse au chat + self._add_message_to_chat(self.current_agent, response) + + # Mettre Ă  jour le statut + self.status_bar.config(text=f"Test terminĂ©") + + except Exception as e: + messagebox.showerror("Erreur", f"Erreur lors du test de l'agent: {str(e)}") + self.status_bar.config(text=f"Erreur lors du test") + + def _on_param_change(self, *args): + """Gestionnaire d'Ă©vĂ©nement pour le changement des paramètres""" + if not self.current_agent: + return + + # Mettre Ă  jour les paramètres personnalisĂ©s + self.custom_params = { + "temperature": self.temp_var.get(), + "top_p": self.top_p_var.get(), + "top_k": self.top_k_var.get(), + "repeat_penalty": self.repeat_var.get(), + "num_predict": self.tokens_var.get() + } + + def _reset_params(self): + """RĂ©initialise les paramètres aux valeurs par dĂ©faut de l'agent""" + if not self.current_agent: + messagebox.showwarning("Aucun agent sĂ©lectionnĂ©", + "Veuillez d'abord sĂ©lectionner un agent.") + return + + # Charger les paramètres de l'agent + self._load_agent_params(self.current_agent) + + # Mettre Ă  jour le statut + self.status_bar.config(text=f"Paramètres rĂ©initialisĂ©s") + + def _save_params(self): + """Enregistre les paramètres personnalisĂ©s""" + if not self.current_agent: + messagebox.showwarning("Aucun agent sĂ©lectionnĂ©", + "Veuillez d'abord sĂ©lectionner un agent.") + return + + # CrĂ©er le dossier de sauvegarde si nĂ©cessaire + save_dir = "saved_params" + os.makedirs(save_dir, exist_ok=True) + + # Nom du fichier de sauvegarde + file_name = f"{save_dir}/{self.current_agent}_{self.model_combo.get()}.json" + + # Enregistrer les paramètres + with open(file_name, "w") as f: + json.dump(self.custom_params, f, indent=2) + + # Mettre Ă  jour le statut + self.status_bar.config(text=f"Paramètres enregistrĂ©s dans {file_name}") + + def _apply_preset(self): + """Applique un preset de paramètres""" + preset = self.preset_combo.get() + + # DĂ©finir les presets + presets = { + "CrĂ©atif": { + "temperature": 1.2, + "top_p": 0.95, + "top_k": 60, + "repeat_penalty": 1.0, + "num_predict": 1024 + }, + "PrĂ©cis": { + "temperature": 0.3, + "top_p": 0.85, + "top_k": 30, + "repeat_penalty": 1.2, + "num_predict": 512 + }, + "ÉquilibrĂ©": { + "temperature": 0.7, + "top_p": 0.9, + "top_k": 40, + "repeat_penalty": 1.1, + "num_predict": 768 + }, + "Code": { + "temperature": 0.2, + "top_p": 0.95, + "top_k": 30, + "repeat_penalty": 1.2, + "num_predict": 2048 + }, + "Conversation": { + "temperature": 0.8, + "top_p": 0.92, + "top_k": 50, + "repeat_penalty": 1.05, + "num_predict": 512 + } + } + + # Appliquer le preset + if preset in presets: + params = presets[preset] + + # Mettre Ă  jour les variables + self.temp_var.set(params["temperature"]) + self.top_p_var.set(params["top_p"]) + self.top_k_var.set(params["top_k"]) + self.repeat_var.set(params["repeat_penalty"]) + self.tokens_var.set(params["num_predict"]) + + # Mettre Ă  jour les paramètres personnalisĂ©s + self.custom_params = params.copy() + + # Mettre Ă  jour le statut + self.status_bar.config(text=f"Preset '{preset}' appliquĂ©") + + def _load_agent_params(self, agent_name): + """Charge les paramètres par dĂ©faut de l'agent""" + try: + # RĂ©cupĂ©rer la configuration de l'agent + from agents.roles import AGENTS + + if agent_name in AGENTS: + agent_config = AGENTS[agent_name] + params = agent_config.get("params", {}) + + # Mettre Ă  jour les variables avec les valeurs par dĂ©faut + self.temp_var.set(params.get("temperature", 0.7)) + self.top_p_var.set(params.get("top_p", 0.9)) + self.top_k_var.set(params.get("top_k", 40)) + self.repeat_var.set(params.get("repeat_penalty", 1.1)) + self.tokens_var.set(params.get("num_predict", 512)) + + # Mettre Ă  jour les paramètres personnalisĂ©s + self.custom_params = params.copy() + + except Exception as e: + print(f"Erreur lors du chargement des paramètres de l'agent: {str(e)}") + + def _load_model_params(self, model_name): + """Charge les paramètres par dĂ©faut du modèle""" + # VĂ©rifier si un fichier de paramètres personnalisĂ©s existe + save_dir = "saved_params" + file_name = f"{save_dir}/{self.current_agent}_{model_name}.json" + + if os.path.exists(file_name): + try: + # Charger les paramètres personnalisĂ©s + with open(file_name, "r") as f: + params = json.load(f) + + # Mettre Ă  jour les variables + self.temp_var.set(params.get("temperature", 0.7)) + self.top_p_var.set(params.get("top_p", 0.9)) + self.top_k_var.set(params.get("top_k", 40)) + self.repeat_var.set(params.get("repeat_penalty", 1.1)) + self.tokens_var.set(params.get("num_predict", 512)) + + # Mettre Ă  jour les paramètres personnalisĂ©s + self.custom_params = params.copy() + + # Mettre Ă  jour le statut + self.status_bar.config(text=f"Paramètres personnalisĂ©s chargĂ©s depuis {file_name}") + + except Exception as e: + print(f"Erreur lors du chargement des paramètres personnalisĂ©s: {str(e)}") + + else: + # Si aucun fichier personnalisĂ©, charger les paramètres par dĂ©faut + self._load_agent_params(self.current_agent) + + def _create_agent_with_custom_params(self): + """CrĂ©e un agent avec les paramètres personnalisĂ©s""" + if not self.current_agent: + raise ValueError("Aucun agent sĂ©lectionnĂ©") + + # CrĂ©er l'agent + agent = AgentManager.create(self.current_agent) + + # Appliquer les paramètres personnalisĂ©s + for key, value in self.custom_params.items(): + agent.params[key] = value + + # Appliquer le modèle si diffĂ©rent + model_name = self.model_combo.get() + if model_name != agent.model: + # CrĂ©er un nouveau modèle + from core.factory import LLMFactory + new_model = LLMFactory.create(model_name) + + # Copier les paramètres + new_model.params.update(agent.params) + new_model.system_prompt = agent.system_prompt + new_model.agent = agent.agent + + agent = new_model + + return agent + + def _load_conversation_history(self): + """Charge l'historique des conversations""" + # CrĂ©er le dossier d'historique si nĂ©cessaire + history_dir = "chat_history" + os.makedirs(history_dir, exist_ok=True) + + # Lister les fichiers d'historique + self.history_files = [] + for file in os.listdir(history_dir): + if file.endswith(".json"): + self.history_files.append(os.path.join(history_dir, file)) + + # Trier par date (plus rĂ©cent en premier) + self.history_files.sort(key=os.path.getmtime, reverse=True) + + # Mettre Ă  jour la liste d'historique + self.history_listbox.delete(0, tk.END) + + for file in self.history_files: + try: + with open(file, "r", encoding="utf-8") as f: + history = json.load(f) + + # Extraire les informations + agent = history.get("agent", "Inconnu") + date = history.get("date", "Inconnue") + messages = history.get("messages", []) + + # Ajouter Ă  la liste + if messages: + first_message = messages[0].get("content", "") + preview = first_message[:30] + "..." if len(first_message) > 30 else first_message + self.history_listbox.insert(tk.END, f"{agent} - {date} - {preview}") + + except Exception as e: + print(f"Erreur lors du chargement de l'historique {file}: {str(e)}") + + def _on_history_select(self, event): + """Gestionnaire d'Ă©vĂ©nement pour la sĂ©lection d'un historique""" + # RĂ©cupĂ©rer l'index sĂ©lectionnĂ© + selection = self.history_listbox.curselection() + if not selection: + return + + # Activer le bouton de chargement + self.load_history_btn.config(state=tk.NORMAL) + + def _load_selected_history(self): + """Charge l'historique sĂ©lectionnĂ©""" + # RĂ©cupĂ©rer l'index sĂ©lectionnĂ© + selection = self.history_listbox.curselection() + if not selection or selection[0] >= len(self.history_files): + return + + # RĂ©cupĂ©rer le fichier d'historique + file = self.history_files[selection[0]] + + try: + # Charger l'historique + with open(file, "r", encoding="utf-8") as f: + history = json.load(f) + + # Extraire les informations + agent = history.get("agent", None) + messages = history.get("messages", []) + + # SĂ©lectionner l'agent si disponible + if agent: + # Trouver l'index de l'agent dans la listbox + for i in range(self.agent_listbox.size()): + if self.agent_listbox.get(i) == agent: + self.agent_listbox.selection_clear(0, tk.END) + self.agent_listbox.selection_set(i) + self.agent_listbox.see(i) + self._on_agent_select(None) + break + + # Effacer le chat actuel + self._clear_chat() + + # Ajouter les messages au chat + for message in messages: + sender = message.get("sender", "Inconnu") + content = message.get("content", "") + self._add_message_to_chat(sender, content, add_to_history=False) + + # Mettre Ă  jour l'historique + self.conversation_history = messages + + # Mettre Ă  jour le statut + self.status_bar.config(text=f"Historique chargĂ© depuis {file}") + + except Exception as e: + messagebox.showerror("Erreur", f"Erreur lors du chargement de l'historique: {str(e)}") + + def _delete_history(self): + """Supprime l'historique sĂ©lectionnĂ©""" + # RĂ©cupĂ©rer l'index sĂ©lectionnĂ© + selection = self.history_listbox.curselection() + if not selection or selection[0] >= len(self.history_files): + return + + # RĂ©cupĂ©rer le fichier d'historique + file = self.history_files[selection[0]] + + # Demander confirmation + if messagebox.askyesno("Confirmation", f"Voulez-vous vraiment supprimer cet historique ?"): + try: + # Supprimer le fichier + os.remove(file) + + # Mettre Ă  jour la liste + self.history_files.pop(selection[0]) + self.history_listbox.delete(selection[0]) + + # Mettre Ă  jour le statut + self.status_bar.config(text=f"Historique supprimĂ©") + + except Exception as e: + messagebox.showerror("Erreur", f"Erreur lors de la suppression: {str(e)}") + + def _export_history(self): + """Exporte l'historique sĂ©lectionnĂ©""" + # RĂ©cupĂ©rer l'index sĂ©lectionnĂ© + selection = self.history_listbox.curselection() + if not selection or selection[0] >= len(self.history_files): + return + + # RĂ©cupĂ©rer le fichier d'historique + file = self.history_files[selection[0]] + + try: + # Charger l'historique + with open(file, "r", encoding="utf-8") as f: + history = json.load(f) + + # Ouvrir une boĂ®te de dialogue pour choisir l'emplacement d'exportation + export_file = filedialog.asksaveasfilename( + defaultextension=".md", + filetypes=[("Markdown", "*.md"), ("Texte", "*.txt"), ("JSON", "*.json")], + title="Exporter l'historique" + ) + + if not export_file: + return + + # Format d'exportation selon l'extension + ext = os.path.splitext(export_file)[1].lower() + + if ext == ".json": + # Exporter au format JSON + with open(export_file, "w", encoding="utf-8") as f: + json.dump(history, f, indent=2, ensure_ascii=False) + + elif ext == ".md" or ext == ".txt": + # Exporter au format Markdown/Texte + with open(export_file, "w", encoding="utf-8") as f: + # En-tĂŞte + agent = history.get("agent", "Inconnu") + date = history.get("date", "Inconnue") + f.write(f"# Conversation avec {agent} - {date}\n\n") + + # Messages + for message in history.get("messages", []): + sender = message.get("sender", "Inconnu") + content = message.get("content", "") + + if ext == ".md": + f.write(f"## {sender}\n\n{content}\n\n") + else: + f.write(f"{sender}:\n{content}\n\n") + + # Mettre Ă  jour le statut + self.status_bar.config(text=f"Historique exportĂ© vers {export_file}") + + except Exception as e: + messagebox.showerror("Erreur", f"Erreur lors de l'exportation: {str(e)}") + + def _on_shift_enter(self, event): + """Gestionnaire d'Ă©vĂ©nement pour Shift+Enter""" + # Ajouter un saut de ligne + return + + def _send_message(self): + """Envoie le message saisi par l'utilisateur""" + if not self.current_agent: + messagebox.showwarning("Aucun agent sĂ©lectionnĂ©", + "Veuillez d'abord sĂ©lectionner un agent.") + return + + # RĂ©cupĂ©rer le message + message = self.user_input.get(1.0, tk.END).strip() + + if not message: + return + + # Effacer le champ de saisie + self.user_input.delete(1.0, tk.END) + + # Ajouter le message au chat + self._add_message_to_chat("Utilisateur", message) + + # GĂ©nĂ©rer une rĂ©ponse + try: + # Mettre Ă  jour le statut + self.status_bar.config(text=f"GĂ©nĂ©ration en cours...") + self.root.update() + + # CrĂ©er l'agent avec les paramètres personnalisĂ©s + agent = self._create_agent_with_custom_params() + + # GĂ©nĂ©rer la rĂ©ponse + response = agent.generate(message) + + # Ajouter la rĂ©ponse au chat + self._add_message_to_chat(self.current_agent, response) + + # Mettre Ă  jour le statut + self.status_bar.config(text=f"RĂ©ponse gĂ©nĂ©rĂ©e") + + except Exception as e: + messagebox.showerror("Erreur", f"Erreur lors de la gĂ©nĂ©ration: {str(e)}") + self.status_bar.config(text=f"Erreur lors de la gĂ©nĂ©ration") + + def _add_message_to_chat(self, sender, content, add_to_history=True): + """Ajoute un message au chat""" + # Activer la zone de chat pour modification + self.chat_display.config(state=tk.NORMAL) + + # Ajouter le message + self.chat_display.insert(tk.END, f"{sender}:\n", "sender") + self.chat_display.insert(tk.END, f"{content}\n\n", "message") + + # Configurer les tags + self.chat_display.tag_configure("sender", font=("Arial", 10, "bold")) + self.chat_display.tag_configure("message", font=("Arial", 10)) + + # DĂ©filer vers le bas + self.chat_display.see(tk.END) + + # DĂ©sactiver la zone de chat + self.chat_display.config(state=tk.DISABLED) + + # Ajouter Ă  l'historique + if add_to_history: + self.conversation_history.append({ + "sender": sender, + "content": content, + "timestamp": datetime.now().isoformat() + }) + + # Sauvegarder l'historique + self._save_conversation_history() + + def _clear_chat(self): + """Efface le contenu du chat""" + # Effacer la zone de chat + self.chat_display.config(state=tk.NORMAL) + self.chat_display.delete(1.0, tk.END) + self.chat_display.config(state=tk.DISABLED) + + # Ne pas effacer l'historique + + def _new_chat(self): + """DĂ©marre une nouvelle conversation""" + # Demander confirmation si la conversation actuelle n'est pas vide + if self.conversation_history and messagebox.askyesno( + "Nouvelle conversation", + "Voulez-vous dĂ©marrer une nouvelle conversation ?" + ): + # Effacer la zone de chat + self._clear_chat() + + # RĂ©initialiser l'historique + self.conversation_history = [] + + # Mettre Ă  jour le statut + self.status_bar.config(text=f"Nouvelle conversation dĂ©marrĂ©e") + + def _save_conversation_history(self): + """Sauvegarde l'historique de la conversation actuelle""" + if not self.conversation_history or not self.current_agent: + return + + # CrĂ©er le dossier d'historique si nĂ©cessaire + history_dir = "chat_history" + os.makedirs(history_dir, exist_ok=True) + + # CrĂ©er un ID unique pour la conversation + now = datetime.now() + date_str = now.strftime("%Y-%m-%d_%H-%M-%S") + file_name = f"{history_dir}/{self.current_agent}_{date_str}.json" + + # PrĂ©parer les donnĂ©es + history_data = { + "agent": self.current_agent, + "model": self.model_combo.get(), + "date": now.strftime("%Y-%m-%d %H:%M:%S"), + "params": self.custom_params, + "messages": self.conversation_history + } + + # Sauvegarder l'historique + try: + with open(file_name, "w", encoding="utf-8") as f: + json.dump(history_data, f, indent=2, ensure_ascii=False) + + # Mettre Ă  jour la liste des fichiers d'historique + if file_name not in self.history_files: + self.history_files.insert(0, file_name) + self.history_listbox.insert(0, f"{self.current_agent} - {now.strftime('%Y-%m-%d %H:%M:%S')}") + + except Exception as e: + print(f"Erreur lors de la sauvegarde de l'historique: {str(e)}") + + +def main(): + """Point d'entrĂ©e principal""" + root = tk.Tk() + app = ChatUI(root) + root.mainloop() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/utils/parameter_tester.py b/utils/parameter_tester.py new file mode 100644 index 0000000..473938d --- /dev/null +++ b/utils/parameter_tester.py @@ -0,0 +1,51 @@ +from itertools import product +from agents.roles import AGENTS +from datetime import datetime + +def test_agents_on_prompt(model_class, prompt: str, agents: list, param_grid: dict, fixed_params: dict = None): + """ + Teste les agents sur un prompt donnĂ© avec diffĂ©rentes combinaisons de paramètres + model_class: Classe du modèle LLM Ă  utiliser + prompt: Prompt Ă  tester + agents: Liste des agents Ă  tester + param_grid: Grille de paramètres Ă  tester + fixed_params: Paramètres fixes Ă  appliquer Ă  tous les tests + """ + + if fixed_params is None: + fixed_params = {} + + #GĂ©nĂ©rer toutes les combinaisons de paramètres Ă  tester + keys = list(param_grid.keys()) + values = list(param_grid.values()) + combinations = list(product(*values)) + + print(f"[TEST STARTED] {len(agents)} agents, {len(combinations)} combinaisons, total; {len(agents) * len(combinations)} tests") + + for agent_name in agents: + if agent_name not in AGENTS: + print(f"[Skipped] Agent inconnu : {agent_name}") + continue + + for combo in combinations: + #instancier un modèle neuf Ă  chaque test(prĂ©serve isolation) + model = model_class() + + # Aplliquer le rĂ´le de l'agent + model.set_role(agent_name, AGENTS[agent_name]) + + # Fusionner les paramètres (params agent < combo test < fixed) + combo_params = dict(zip(keys, combo)) + model.params.update(combo_params) + model.params.update(fixed_params) + + #Affichage console + ts = datetime.now().strftime("%H:%M:%S") + print(f"[{ts}] -> Agent: {agent_name} | Combo: {combo_params}") + + try: + model.generate(prompt) + except Exception as e: + print(f"[Erreur] {e}") + + print("[TEST COMPLET]") \ No newline at end of file diff --git a/utils/system_monitor.py b/utils/system_monitor.py new file mode 100644 index 0000000..08f85e3 --- /dev/null +++ b/utils/system_monitor.py @@ -0,0 +1,534 @@ +""" +Moniteur de ressources système pour Ollama et les ressources NVIDIA GPU +""" +import tkinter as tk +from tkinter import ttk +import psutil +import threading +import time +import subprocess +import json +import os +import platform +import requests +from datetime import datetime + +class SystemMonitor: + def __init__(self, root): + self.root = root + self.root.title("Moniteur LLM Lab") + self.root.geometry("800x600") + self.root.minsize(700, 500) + + # Style + self.style = ttk.Style() + self.style.theme_use('alt') # 'clam', 'alt', 'default', 'classic' + + # Variables + self.update_interval = 2 # secondes + self.running = True + self.ollama_models = [] + self.active_model = None + self.gpu_available = self._check_gpu_available() + + # CrĂ©ation de l'UI + self._create_widgets() + + # DĂ©marrer le thread de mise Ă  jour + self.update_thread = threading.Thread(target=self._update_loop) + self.update_thread.daemon = True + self.update_thread.start() + + # Intercepter la fermeture de la fenĂŞtre + self.root.protocol("WM_DELETE_WINDOW", self._on_close) + + def _create_widgets(self): + # CrĂ©er le notebook (onglets) + self.notebook = ttk.Notebook(self.root) + self.notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) + + # Onglet 1: Surveillance système + self.system_frame = ttk.Frame(self.notebook) + self.notebook.add(self.system_frame, text="Système") + + # Onglet 2: Ollama + self.ollama_frame = ttk.Frame(self.notebook) + self.notebook.add(self.ollama_frame, text="Ollama") + + # Onglet 3: GPU + self.gpu_frame = ttk.Frame(self.notebook) + self.notebook.add(self.gpu_frame, text="GPU") + + # Onglet 4: Logs + self.logs_frame = ttk.Frame(self.notebook) + self.notebook.add(self.logs_frame, text="Logs") + + # === Configuration de l'onglet Système === + system_label = ttk.Label(self.system_frame, text="Ressources Système", font=("Arial", 14, "bold")) + system_label.pack(pady=10) + + # Infos système + system_info_frame = ttk.LabelFrame(self.system_frame, text="Informations Système") + system_info_frame.pack(fill=tk.X, padx=10, pady=5) + + # OS + os_frame = ttk.Frame(system_info_frame) + os_frame.pack(fill=tk.X, padx=5, pady=2) + ttk.Label(os_frame, text="Système d'exploitation:").pack(side=tk.LEFT, padx=5) + self.os_label = ttk.Label(os_frame, text="") + self.os_label.pack(side=tk.LEFT, padx=5) + + # CPU + cpu_frame = ttk.Frame(system_info_frame) + cpu_frame.pack(fill=tk.X, padx=5, pady=2) + ttk.Label(cpu_frame, text="Processeur:").pack(side=tk.LEFT, padx=5) + self.cpu_label = ttk.Label(cpu_frame, text="") + self.cpu_label.pack(side=tk.LEFT, padx=5) + + # RAM + ram_frame = ttk.Frame(system_info_frame) + ram_frame.pack(fill=tk.X, padx=5, pady=2) + ttk.Label(ram_frame, text="MĂ©moire RAM:").pack(side=tk.LEFT, padx=5) + self.ram_label = ttk.Label(ram_frame, text="") + self.ram_label.pack(side=tk.LEFT, padx=5) + + # Barres de progression + progress_frame = ttk.LabelFrame(self.system_frame, text="Utilisation des ressources") + progress_frame.pack(fill=tk.X, padx=10, pady=5) + + # CPU Usage + cpu_usage_frame = ttk.Frame(progress_frame) + cpu_usage_frame.pack(fill=tk.X, padx=5, pady=5) + ttk.Label(cpu_usage_frame, text="CPU:").pack(side=tk.LEFT, padx=5) + self.cpu_progress = ttk.Progressbar(cpu_usage_frame, orient=tk.HORIZONTAL, length=300, mode='determinate') + self.cpu_progress.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True) + self.cpu_percent = ttk.Label(cpu_usage_frame, text="0%") + self.cpu_percent.pack(side=tk.LEFT, padx=5) + + # RAM Usage + ram_usage_frame = ttk.Frame(progress_frame) + ram_usage_frame.pack(fill=tk.X, padx=5, pady=5) + ttk.Label(ram_usage_frame, text="RAM:").pack(side=tk.LEFT, padx=5) + self.ram_progress = ttk.Progressbar(ram_usage_frame, orient=tk.HORIZONTAL, length=300, mode='determinate') + self.ram_progress.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True) + self.ram_percent = ttk.Label(ram_usage_frame, text="0%") + self.ram_percent.pack(side=tk.LEFT, padx=5) + + # Disk Usage + disk_usage_frame = ttk.Frame(progress_frame) + disk_usage_frame.pack(fill=tk.X, padx=5, pady=5) + ttk.Label(disk_usage_frame, text="Disque:").pack(side=tk.LEFT, padx=5) + self.disk_progress = ttk.Progressbar(disk_usage_frame, orient=tk.HORIZONTAL, length=300, mode='determinate') + self.disk_progress.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True) + self.disk_percent = ttk.Label(disk_usage_frame, text="0%") + self.disk_percent.pack(side=tk.LEFT, padx=5) + + # === Configuration de l'onglet Ollama === + ollama_label = ttk.Label(self.ollama_frame, text="Serveur Ollama", font=("Arial", 14, "bold")) + ollama_label.pack(pady=10) + + # État du serveur + server_frame = ttk.LabelFrame(self.ollama_frame, text="État du serveur") + server_frame.pack(fill=tk.X, padx=10, pady=5) + + # Status + status_frame = ttk.Frame(server_frame) + status_frame.pack(fill=tk.X, padx=5, pady=2) + ttk.Label(status_frame, text="Statut:").pack(side=tk.LEFT, padx=5) + self.status_label = ttk.Label(status_frame, text="VĂ©rification...") + self.status_label.pack(side=tk.LEFT, padx=5) + + # URL + url_frame = ttk.Frame(server_frame) + url_frame.pack(fill=tk.X, padx=5, pady=2) + ttk.Label(url_frame, text="URL:").pack(side=tk.LEFT, padx=5) + self.url_label = ttk.Label(url_frame, text="http://localhost:11434") + self.url_label.pack(side=tk.LEFT, padx=5) + + # Modèles disponibles + models_frame = ttk.LabelFrame(self.ollama_frame, text="Modèles disponibles") + models_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) + + # Liste des modèles + self.tree = ttk.Treeview(models_frame, columns=("Taille", "ModifiĂ©"), show='headings') + self.tree.heading("Taille", text="Taille") + self.tree.heading("ModifiĂ©", text="ModifiĂ©") + self.tree.column("Taille", width=100) + self.tree.column("ModifiĂ©", width=150) + self.tree.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) + + # Boutons + button_frame = ttk.Frame(self.ollama_frame) + button_frame.pack(fill=tk.X, padx=10, pady=5) + + refresh_button = ttk.Button(button_frame, text="RafraĂ®chir", command=self._refresh_ollama) + refresh_button.pack(side=tk.LEFT, padx=5) + + # === Configuration de l'onglet GPU === + gpu_label = ttk.Label(self.gpu_frame, text="Ressources GPU", font=("Arial", 14, "bold")) + gpu_label.pack(pady=10) + + if self.gpu_available: + # Infos GPU + gpu_info_frame = ttk.LabelFrame(self.gpu_frame, text="Informations GPU") + gpu_info_frame.pack(fill=tk.X, padx=10, pady=5) + + # Modèle GPU + gpu_model_frame = ttk.Frame(gpu_info_frame) + gpu_model_frame.pack(fill=tk.X, padx=5, pady=2) + ttk.Label(gpu_model_frame, text="Modèle:").pack(side=tk.LEFT, padx=5) + self.gpu_model_label = ttk.Label(gpu_model_frame, text="") + self.gpu_model_label.pack(side=tk.LEFT, padx=5) + + # MĂ©moire GPU + gpu_memory_frame = ttk.Frame(gpu_info_frame) + gpu_memory_frame.pack(fill=tk.X, padx=5, pady=2) + ttk.Label(gpu_memory_frame, text="MĂ©moire:").pack(side=tk.LEFT, padx=5) + self.gpu_memory_label = ttk.Label(gpu_memory_frame, text="") + self.gpu_memory_label.pack(side=tk.LEFT, padx=5) + + # Utilisation GPU + gpu_usage_frame = ttk.LabelFrame(self.gpu_frame, text="Utilisation") + gpu_usage_frame.pack(fill=tk.X, padx=10, pady=5) + + # GPU Compute + gpu_compute_frame = ttk.Frame(gpu_usage_frame) + gpu_compute_frame.pack(fill=tk.X, padx=5, pady=5) + ttk.Label(gpu_compute_frame, text="Calcul:").pack(side=tk.LEFT, padx=5) + self.gpu_compute_progress = ttk.Progressbar(gpu_compute_frame, orient=tk.HORIZONTAL, length=300, mode='determinate') + self.gpu_compute_progress.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True) + self.gpu_compute_percent = ttk.Label(gpu_compute_frame, text="0%") + self.gpu_compute_percent.pack(side=tk.LEFT, padx=5) + + # GPU Memory + gpu_mem_usage_frame = ttk.Frame(gpu_usage_frame) + gpu_mem_usage_frame.pack(fill=tk.X, padx=5, pady=5) + ttk.Label(gpu_mem_usage_frame, text="MĂ©moire:").pack(side=tk.LEFT, padx=5) + self.gpu_mem_progress = ttk.Progressbar(gpu_mem_usage_frame, orient=tk.HORIZONTAL, length=300, mode='determinate') + self.gpu_mem_progress.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True) + self.gpu_mem_percent = ttk.Label(gpu_mem_usage_frame, text="0%") + self.gpu_mem_percent.pack(side=tk.LEFT, padx=5) + + # TempĂ©rature + gpu_temp_frame = ttk.Frame(gpu_usage_frame) + gpu_temp_frame.pack(fill=tk.X, padx=5, pady=5) + ttk.Label(gpu_temp_frame, text="TempĂ©rature:").pack(side=tk.LEFT, padx=5) + self.gpu_temp_progress = ttk.Progressbar(gpu_temp_frame, orient=tk.HORIZONTAL, length=300, mode='determinate') + self.gpu_temp_progress.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True) + self.gpu_temp_label = ttk.Label(gpu_temp_frame, text="0°C") + self.gpu_temp_label.pack(side=tk.LEFT, padx=5) + + # Graphiques processe actifs + gpu_processes_frame = ttk.LabelFrame(self.gpu_frame, text="Processus GPU") + gpu_processes_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) + + # Liste des processus + self.gpu_process_tree = ttk.Treeview(gpu_processes_frame, + columns=("PID", "Nom", "MĂ©moire"), + show='headings') + self.gpu_process_tree.heading("PID", text="PID") + self.gpu_process_tree.heading("Nom", text="Processus") + self.gpu_process_tree.heading("MĂ©moire", text="MĂ©moire") + self.gpu_process_tree.column("PID", width=50) + self.gpu_process_tree.column("Nom", width=200) + self.gpu_process_tree.column("MĂ©moire", width=100) + self.gpu_process_tree.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) + else: + no_gpu_label = ttk.Label(self.gpu_frame, + text="Aucun GPU NVIDIA dĂ©tectĂ©.", + font=("Arial", 12)) + no_gpu_label.pack(pady=50) + + install_label = ttk.Label(self.gpu_frame, + text="Pour surveiller un GPU NVIDIA, installez nvidia-smi et nvitop.", + font=("Arial", 10)) + install_label.pack(pady=10) + + # === Configuration de l'onglet Logs === + logs_label = ttk.Label(self.logs_frame, text="Journaux d'activitĂ©", font=("Arial", 14, "bold")) + logs_label.pack(pady=10) + + # Zone de logs + log_area_frame = ttk.Frame(self.logs_frame) + log_area_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) + + # Scrollbar + scrollbar = ttk.Scrollbar(log_area_frame) + scrollbar.pack(side=tk.RIGHT, fill=tk.Y) + + # Text area + self.log_text = tk.Text(log_area_frame, yscrollcommand=scrollbar.set) + self.log_text.pack(fill=tk.BOTH, expand=True) + scrollbar.config(command=self.log_text.yview) + + # Boutons + log_button_frame = ttk.Frame(self.logs_frame) + log_button_frame.pack(fill=tk.X, padx=10, pady=5) + + clear_log_button = ttk.Button(log_button_frame, text="Effacer les logs", + command=lambda: self.log_text.delete(1.0, tk.END)) + clear_log_button.pack(side=tk.LEFT, padx=5) + + # Barre d'Ă©tat en bas + self.status_bar = ttk.Label(self.root, text="Moniteur LLM Lab - Dernière mise Ă  jour: Jamais", + relief=tk.SUNKEN, anchor=tk.W) + self.status_bar.pack(side=tk.BOTTOM, fill=tk.X) + + def _update_loop(self): + """Thread principal de mise Ă  jour""" + while self.running: + try: + # Mise Ă  jour système + self._update_system_info() + + # Mise Ă  jour Ollama + if self.notebook.index(self.notebook.select()) == 1: # Onglet Ollama + self._update_ollama_info() + + # Mise Ă  jour GPU + if self.gpu_available and self.notebook.index(self.notebook.select()) == 2: # Onglet GPU + self._update_gpu_info() + + # Mise Ă  jour de la barre d'Ă©tat + now = datetime.now().strftime("%H:%M:%S") + self.status_bar.config(text=f"Moniteur LLM Lab - Dernière mise Ă  jour: {now}") + + except Exception as e: + self._log(f"Erreur de mise Ă  jour: {str(e)}") + + time.sleep(self.update_interval) + + def _update_system_info(self): + """Met Ă  jour les informations système""" + # Informations système + self.os_label.config(text=f"{platform.system()} {platform.release()}") + self.cpu_label.config(text=f"{psutil.cpu_count(logical=False)} cĹ“urs ({psutil.cpu_count()} threads)") + + # DĂ©tection avancĂ©e de la RAM + try: + ram = psutil.virtual_memory() + total_ram = ram.total / (1024 * 1024 * 1024) # GB + + # VĂ©rification supplĂ©mentaire pour Linux + if platform.system() == "Linux": + try: + # Utiliser /proc/meminfo pour une dĂ©tection plus prĂ©cise + with open('/proc/meminfo', 'r') as f: + for line in f: + if 'MemTotal' in line: + # MemTotal est en kB + mem_kb = int(line.split()[1]) + linux_ram = mem_kb / (1024 * 1024) # GB + # Utiliser la valeur la plus Ă©levĂ©e + total_ram = max(total_ram, linux_ram) + break + except Exception as e: + self._log(f"Erreur lors de la lecture de /proc/meminfo: {str(e)}") + + self.ram_label.config(text=f"{total_ram:.1f} GB") + except Exception as e: + self._log(f"Erreur lors de la dĂ©tection de la RAM: {str(e)}") + self.ram_label.config(text="DĂ©tection impossible") + + # Utilisation CPU + cpu_percent = psutil.cpu_percent() + self.cpu_progress["value"] = cpu_percent + self.cpu_percent.config(text=f"{cpu_percent:.1f}%") + + # Utilisation RAM + ram_percent = ram.percent + self.ram_progress["value"] = ram_percent + self.ram_percent.config(text=f"{ram_percent:.1f}%") + + # Utilisation disque + disk = psutil.disk_usage('/') + disk_percent = disk.percent + self.disk_progress["value"] = disk_percent + self.disk_percent.config(text=f"{disk_percent:.1f}%") + + def _update_ollama_info(self): + """Met Ă  jour les informations Ollama""" + try: + # VĂ©rifier si le serveur est en cours d'exĂ©cution + response = requests.get("http://localhost:11434/api/tags", timeout=2) + + if response.status_code == 200: + self.status_label.config(text="En ligne", foreground="green") + + # Mettre Ă  jour la liste des modèles + data = response.json() + models = data.get("models", []) + + # Effacer la liste actuelle + for item in self.tree.get_children(): + self.tree.delete(item) + + # Ajouter les modèles + for model in models: + model_name = model.get("name", "") + model_size = self._format_size(model.get("size", 0)) + modified = model.get("modified_at", "") + # Convertir le format de date + if modified: + try: + modified_dt = datetime.fromisoformat(modified.replace('Z', '+00:00')) + modified = modified_dt.strftime("%d/%m/%Y %H:%M") + except: + pass + + self.tree.insert("", tk.END, text=model_name, values=(model_size, modified), iid=model_name) + + # Mettre Ă  jour la liste globale + self.ollama_models = [model.get("name", "") for model in models] + + # VĂ©rifier s'il y a un modèle actif + if self.active_model: + self._log(f"Modèle actif: {self.active_model}") + # Mettre en surbrillance le modèle actif + if self.active_model in self.ollama_models: + self.tree.selection_set(self.active_model) + self.tree.see(self.active_model) + else: + self.status_label.config(text="Erreur", foreground="red") + self._log(f"Erreur de connexion au serveur Ollama: {response.status_code}") + + except requests.exceptions.RequestException: + self.status_label.config(text="Hors ligne", foreground="red") + self._log("Serveur Ollama non disponible") + + def _update_gpu_info(self): + """Met Ă  jour les informations GPU""" + if not self.gpu_available: + return + + try: + # ExĂ©cuter nvidia-smi pour obtenir les informations GPU + result = subprocess.run( + ['nvidia-smi', '--query-gpu=name,memory.total,memory.used,utilization.gpu,temperature.gpu', + '--format=csv,noheader,nounits'], + capture_output=True, + text=True, + check=True + ) + + if result.returncode == 0: + # Analyser les rĂ©sultats + gpu_data = result.stdout.strip().split(',') + + if len(gpu_data) >= 5: + # Nom du modèle + model_name = gpu_data[0].strip() + self.gpu_model_label.config(text=model_name) + + # MĂ©moire totale et utilisĂ©e + total_memory = float(gpu_data[1].strip()) + used_memory = float(gpu_data[2].strip()) + memory_percent = (used_memory / total_memory) * 100 if total_memory > 0 else 0 + + self.gpu_memory_label.config(text=f"{used_memory:.0f} MiB / {total_memory:.0f} MiB") + self.gpu_mem_progress["value"] = memory_percent + self.gpu_mem_percent.config(text=f"{memory_percent:.1f}%") + + # Utilisation GPU + gpu_util = float(gpu_data[3].strip()) + self.gpu_compute_progress["value"] = gpu_util + self.gpu_compute_percent.config(text=f"{gpu_util:.1f}%") + + # TempĂ©rature + temp = float(gpu_data[4].strip()) + # Échelle de tempĂ©rature: 0-100°C + self.gpu_temp_progress["value"] = temp + self.gpu_temp_label.config(text=f"{temp:.1f}°C") + + # RĂ©cupĂ©rer les processus GPU + result_processes = subprocess.run( + ['nvidia-smi', '--query-compute-apps=pid,name,used_memory', '--format=csv,noheader,nounits'], + capture_output=True, + text=True + ) + + if result_processes.returncode == 0: + # Effacer la liste actuelle + for item in self.gpu_process_tree.get_children(): + self.gpu_process_tree.delete(item) + + # Ajouter les processus + processes = result_processes.stdout.strip().split('\n') + for process in processes: + if process.strip(): + process_data = process.split(',') + if len(process_data) >= 3: + pid = process_data[0].strip() + name = process_data[1].strip() + memory = f"{process_data[2].strip()} MiB" + + # Ajouter Ă  la liste + self.gpu_process_tree.insert("", tk.END, text=pid, values=(pid, name, memory)) + + # Si c'est Ollama, marquer comme modèle actif + if "ollama" in name.lower(): + self._log(f"Ollama dĂ©tectĂ© sur GPU: PID {pid}, utilisant {memory}") + # Chercher quel modèle est actif + try: + process_info = psutil.Process(int(pid)) + cmd_line = " ".join(process_info.cmdline()) + for model in self.ollama_models: + if model in cmd_line: + self.active_model = model + self._log(f"Modèle actif dĂ©tectĂ©: {model}") + break + except: + pass + + except subprocess.SubprocessError as e: + self._log(f"Erreur lors de l'exĂ©cution de nvidia-smi: {str(e)}") + except Exception as e: + self._log(f"Erreur de mise Ă  jour GPU: {str(e)}") + + def _refresh_ollama(self): + """Force le rafraĂ®chissement des informations Ollama""" + self._update_ollama_info() + self._log("Informations Ollama rafraĂ®chies") + + def _check_gpu_available(self): + """VĂ©rifie si un GPU NVIDIA est disponible""" + try: + result = subprocess.run(['nvidia-smi'], capture_output=True, text=True) + return result.returncode == 0 + except: + return False + + def _format_size(self, size_bytes): + """Formate la taille en unitĂ©s lisibles""" + if size_bytes < 1024: + return f"{size_bytes} B" + elif size_bytes < 1024 * 1024: + return f"{size_bytes/1024:.1f} KB" + elif size_bytes < 1024 * 1024 * 1024: + return f"{size_bytes/(1024*1024):.1f} MB" + else: + return f"{size_bytes/(1024*1024*1024):.1f} GB" + + def _log(self, message): + """Ajoute un message aux logs""" + timestamp = datetime.now().strftime("%H:%M:%S") + log_message = f"[{timestamp}] {message}\n" + + # Ajouter au texte + self.log_text.insert(tk.END, log_message) + self.log_text.see(tk.END) # DĂ©filer vers le bas + + def _on_close(self): + """Gère la fermeture de l'application""" + self.running = False + time.sleep(0.5) # Attendre que le thread se termine + self.root.destroy() + +def main(): + """Point d'entrĂ©e principal""" + root = tk.Tk() + app = SystemMonitor(root) + root.mainloop() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/windows_migration_checklist.md b/windows_migration_checklist.md new file mode 100644 index 0000000..4cd6d12 --- /dev/null +++ b/windows_migration_checklist.md @@ -0,0 +1,85 @@ +# Checklist de migration vers Windows 11 Pro + +Ce document fournit une liste de vĂ©rification rapide pour migrer LLM Lab de WSL vers Windows 11 Pro. + +## Étape 1: PrĂ©requis Windows + +- [ ] Installer [Python 3.10+](https://www.python.org/downloads/) (cocher "Add Python to PATH") +- [ ] Installer [Git pour Windows](https://git-scm.com/download/win) +- [ ] Installer [Ollama pour Windows](https://ollama.com/download/windows) +- [ ] Mettre Ă  jour les [pilotes NVIDIA](https://www.nvidia.com/Download/index.aspx) (si GPU disponible) + +## Étape 2: Transfert du projet + +**Option A: Via Git (recommandĂ©e)** +- [ ] Cloner le dĂ©pĂ´t sur Windows + ```cmd + git clone llm_lab + cd llm_lab + ``` + +**Option B: Copie directe** +- [ ] AccĂ©der au dossier WSL: `\\wsl$\Ubuntu\chemin\vers\llm_lab` +- [ ] Copier tous les fichiers vers un nouveau dossier Windows + +## Étape 3: Configuration + +- [ ] ExĂ©cuter le script de configuration + ```cmd + setup_env.bat + ``` +- [ ] VĂ©rifier l'installation + ```cmd + test_installation.bat + ``` +- [ ] Optimiser Ollama (en tant qu'administrateur) + ```cmd + optimize_ollama.bat + ``` + +## Étape 4: TĂ©lĂ©chargement des modèles + +- [ ] TĂ©lĂ©charger les modèles dans Ollama + ```cmd + ollama pull mistral:latest + ollama pull codellama:13b-python + ollama pull llama2:13b + ``` + +## Étape 5: Test du système + +- [ ] Lancer l'interface graphique + ```cmd + run.bat gui + ``` +- [ ] Lancer le moniteur système + ```cmd + run.bat monitor + ``` +- [ ] Tester l'agent Mistral + ```cmd + run.bat chat test + ``` + +## Problèmes courants et solutions + +### Interface graphique ne se lance pas +- VĂ©rifier l'installation de tkinter: `python -c "import tkinter; tkinter._test()"` +- RĂ©installer Python avec l'option "tcl/tk and IDLE" cochĂ©e + +### Ollama ne trouve pas le GPU +- ExĂ©cuter `optimize_ollama.bat` en tant qu'administrateur +- VĂ©rifier que les pilotes NVIDIA sont Ă  jour: `nvidia-smi` + +### Erreurs de modules manquants +- Installer manuellement: `pip install wmi psutil requests pillow` + +## Recommandations pour de meilleures performances + +1. **Plan d'alimentation Windows**: RĂ©gler sur "Hautes performances" +2. **Panneau de configuration NVIDIA**: + - PrĂ©fĂ©rer les performances maximales + - DĂ©sactiver l'Ă©conomie d'Ă©nergie GPU +3. **Antivirus**: + - Ajouter des exceptions pour le dossier Ollama + - Exclure le processus Ollama \ No newline at end of file