# LM Studio Web - Journal de Développement
**Date :** 2026-03-26 / 2026-03-27
**Statut :** Fonctionnel avec limitations identifiées
**Prochaine étape :** Détection intelligente des modèles vision vs texte
---
## 🎯 Objectif Principal
Interface web moderne pour chatter avec :
- LM Studio (local)
- Ollama (local)
- OpenRouter (cloud)
- + Génération d'images via ComfyUI (`/genimg`)
---
## ✅ Ce Qui Fonctionne (Acquis)
### Interface Utilisateur
- [x] Sélection de serveur avec presets (Ollama, LM Studio, OpenRouter, Custom)
- [x] Champ API key conditionnel (visible pour OpenRouter)
- [x] Dropdown de modèles (rempli automatiquement)
- [x] Chat avec streaming de réponses
- [x] Système de conversations (sidebar, New Chat, Delete, Switch)
- [x] Persistance localStorage (system prompt, sélection serveur, API key)
- [x] Markdown rendering (marked.js) + Highlight.js (code)
- [x] LaTeX support (MathJax)
- [x] Upload d'image locale (bouton "+" image)
- [x] Lightbox (click pour zoomer images)
- [x] Boutons "Copy" sur blocs de code
- [x] Spinner "Thinking..." pendant attente réponse
- [x] Responsive design (mobile-friendly)
- [x] Thème visuel purple moderne
### Backend & Intégration
- [x] Serveur web Python (port 8084) sert fichiers statiques
- [x] Serveur MCP FastAPI (port 8085) → ComfyUI
- [x] Endpoint `/generate-image` (POST) reçoit prompts, envoie à ComfyUI, polling, retourne image_url
- [x] Randomisation des seeds dans le workflow ComfyUI (évite cache)
- [x] Vérification ComfyUI au démarrage (`./start.sh`)
- [x] Logs serveurs dans fichiers (`server.log`, `mcp_server.log`)
- [x] Scripts d'arrêt/propre (`stop.sh`)
- [x] CORS configuré (frontend:8084 → backend:8085)
### Commandes
- [x] `/genimg <description>` → génère image via ComfyUI
- [x] Génération d'image fonctionne (prompt → ComfyUI → image dans chat)
- [x] Lightbox clique → zoom
---
## 🐛 Problèmes Rencontrés & Solutions
| Problème | Cause | Solution |
|----------|-------|----------|
| **CORS blocked** | Fichiers ouverts via `file://` | Serveur Python sur port 8084 + CORS middleware dans MCP |
| **`Identifier already declared`** | `main.js` chargé deux fois + code inline | Nettoyé HTML: un seul `<script src="main.js">` |
| **`Unexpected end of input`** | Corruption de `main.js` pendant édition | Rebuild complet du fichier |
| **Ollama/LM Studio 400 Bad Request** | `max_tokens: -1` (invalide) | Changé en `max_tokens: 4096` |
| **Image générée ajoutée 2×** | `addImageMessage()` + push manuel | Supprimé le push manuel dans `handleImageGeneration()` |
| **Image générée comme message `user`** | `addImageMessage(..., true)` | Changé en `false` → message `assistant` |
| **Model select vide après reconnexion** | `selectedIndex` non défini après `innerHTML = ""` | Forcé `modelSelect.selectedIndex = 0` |
| ** connexion perdue après génération image** | Doublon + format invalide → erreurs 400 → état inconsistent | Fixé doublon + format texte-only pour modèles texte |
| **Images toujours envoyées en texte-only** | `buildConversationHistory()` envoie seulement texte pour `msg.isImage` | **Non fixé** → voir section "À faire" |
---
## 🔧 Configuration Serveurs
### Presets
```javascript
SERVER_PRESETS = {
ollama: 'http://localhost:11434',
lmstudio: 'http://localhost:1234',
openrouter: 'https://openrouter.ai/api/v1'
}
```
### Ports
- **Frontend web** : `http://localhost:8084`
- **MCP backend** : `http://localhost:8085`
- **ComfyUI** : `http://127.0.0.1:8188`
- **Ollama** : `http://localhost:11434`
- **LM Studio** : `http://localhost:1234`
### Variables d'environnement
Pas besoin. Les URLs sont en dur dans le code et le selecteur.
---
## 📦 Fichiers Créés/Modifiés
### Nouveaux fichiers
- `start.sh` — Démarre ComfyUI check → web server → MCP server
- `stop.sh` — Arrête proprement les serveurs
- `styles.css` — CSS externalisé (tiré de l'ancien `index.html`)
- `PROGRESS.md` — Ce fichier
### Fichiers modifiés
- `index.html` — Ajouté `server-url-input` (hidden), removed inline script
- `main.js` — Rebuild complet + nombreuses corrections
- `backend/mcp_server.py` — Ajouté logs, seed randomization, sleep
- `README.md` — Mis à jour avec nouvelles fonctionnalités et instructions
### Fichiers temporaires (ignorer)
- `main.js.corrupted` — Sauvegarde corruption
- `index.html.bak`, `index.html.bak_jsfix` — Backups
- `server.log`, `mcp_server.log` — Logs serveurs
- `.web_server.pid`, `.mcp_server.pid` — PIDs serveurs
---
## 🔄 Workflow de Développement
1. **Modifier le code** (`main.js` ou `backend/mcp_server.py`)
2. **Arrêter serveurs** : `./stop.sh`
3. **Redémarrer** : `./start.sh`
4. **Rafraîchir navigateur** : **Ctrl+Shift+R** (hard refresh, désactive cache)
5. **Tester** dans F12 → Console + Network
**Note :** Le cache navigateur est activé par défaut. Utiliser DevTools → Network → "Disable cache" (quand DevTools ouvert) pour éviter Ctrl+Shift+R.
---
## ⚠️ Limitation Actuelle (Bloquant)
### Images toujours en texte-only
`buildConversationHistory()` dans `main.js` convertit **tous** les messages avec images (`msg.isImage`) en simple texte :
```javascript
if (msg.isImage) {
const textContent = msg.text || "Image generated.";
history.push({
role: msg.isUser ? 'user' : 'assistant',
content: textContent // ← SEULEMENT TEXTE, PAS L'IMAGE
});
}
```
**Conséquence :**
- Modèles **texte-only** (llama3.2, mistral, etc.) → OK (ne comprennent pas les images de toute façon)
- Modèles **vision** (GPT-4o, Claude 3.5 Sonnet, llava, etc.) → ❌ Ne reçoivent **pas l'image**, seulement le texte
**Résultat :** L'upload d'image et les images générées ne sont jamais "vues" par l'IA, même si le modèle le supporte.
---
## 🎯 Plan Pour Demain (Suite)
### 1. Détection automatique modèles vision
Créer une fonction `modelSupportsVision(modelName)` :
```javascript
function modelSupportsVision(modelName) {
const visionModels = [
'gpt-4o', 'gpt-4-turbo', 'gpt-4-vision',
'claude-3', 'claude-3-5', 'claude-3-opus',
'llava', 'bakllava', 'moondream'
];
return visionModels.some(v => modelName.toLowerCase().includes(v));
}
```
### 2. Adapter `buildConversationHistory()`
```javascript
currentChat.messages.forEach(msg => {
if (msg.isImage) {
const textContent = msg.text || "Image generated.";
if (modelSupportsVision(currentModel)) {
// Format multimodal (texte + image)
history.push({
role: msg.isUser ? 'user' : 'assistant',
content: [
{ type: "text", text: textContent },
{ type: "image_url", image_url: { url: msg.imageData } }
]
});
} else {
// Texte seulement (modèle texte)
history.push({
role: msg.isUser ? 'user' : 'assistant',
content: textContent
});
}
} else {
history.push({ role: msg.isUser ? 'user' : 'assistant', content: msg.content });
}
});
```
### 3. (Optionnel) Override manuel
Ajouter un checkbox "Enable vision" dans l'UI (sidebar) qui force le mode vision indépendamment du nom du modèle.
### 4. Tests à effectuer
- [ ] **Ollama + `llama3.2`** (texte-only) :
- Upload image → doit envoyer texte seulement, pas d'erreur
- Générer image → doit continuer chat sans 400
- [ ] **Ollama + `llava:7b`** (vision) :
- Upload image → l'IA doit "voir" l'image (payload contient `image_url`)
- Générer image → l'IA discute de l'image générée
- [ ] **OpenRouter + GPT-4o** (vision) :
- Mêmes tests
---
## 📝 Notes Techniques
### Format OpenAI pour messages multimodaux
```json
{
"role": "user",
"content": [
{ "type": "text", "text": "What's in this image?" },
{ "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,..." } }
]
}
```
**Important :** `image_url` peut être:
- URL externe (ComfyUI retourne `http://127.0.0.1:8188/view?...`)
- Base64 data URL (pour uploads locaux)
Dans notre code, upload → base64, généré ComfyUI → URL HTTP. Les deux fonctionnent si le modèle peut fetch l'URL externe (Ollama local peut, OpenAI/OpenRouter ne peuvent pas fetch `127.0.0.1`). Pour OpenRouter, il faudrait converter en base64.
**Non abordé ici** : conversion URL → base64 pour cloud APIs. À considérer plus tard si besoin.
### Seed randomization
Dans `mcp_server.py`, avant d'envoyer le workflow à ComfyUI, on parcourt tous les nœuds avec `inputs.seed` et on y met une valeur aléatoire. Cela évite que ComfyUI ne retourne le même résultat (cache interne).
```python
for node in workflow.values():
if isinstance(node, dict) and "inputs" in node:
if "seed" in node["inputs"]:
node["inputs"]["seed"] = random.randint(0, 2**32 - 1)
```
---
## 🧪 Commandes de Test
```bash
# Vérifier ComfyUI
curl http://127.0.0.1:8188/system_stats
# Démarrer l'application
./start.sh
# Voir logs MCP en temps réel
tail -f backend/mcp_server.log
# Arrêter
./stop.sh
```
---
## 📌 Points d'Attention
1. **Cache navigateur** : Toujours Ctrl+Shift+R après modif JS/CSS
2. **CORS** : Le serveur Python sur 8084 et MCP sur 8085 doivent être démarrés
3. **ComfyUI** : Doit être démarré à la main avant `./start.sh`
4. **Ollama models** : `llama3.2` est texte-only. Pour images, installer `llava` (`ollama pull llava`)
5. **LM Studio** : Assure-toi que le mode "Server" est activé dans l'app
6. **OpenRouter** : Besoin d'une clé API valide
---
## 🎉 What's Next?
1. ✅ Sauvegarde de l'état actuel dans `PROGRESS.md` (ce fichier)
2. [ ] Implémenter détection vision + format adaptatif
3. [ ] Tester avec modèles vision et texte
4. [ ] (Optionnel) Override manuel "Enable vision"
5. [ ] (Optionnel) Gérer data URLs pour OpenRouter (base64 conversion)
6. [ ] Nettoyer code, ajouter commentaires
---
**Signature :** Claude (Assistant IA) — Dernière mise à jour : 2026-03-26 22:00 UTC