Newer
Older
lm-studio-web-chat / PROGRESS.md

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

  • Sélection de serveur avec presets (Ollama, LM Studio, OpenRouter, Custom)
  • Champ API key conditionnel (visible pour OpenRouter)
  • Dropdown de modèles (rempli automatiquement)
  • Chat avec streaming de réponses
  • Système de conversations (sidebar, New Chat, Delete, Switch)
  • Persistance localStorage (system prompt, sélection serveur, API key)
  • Markdown rendering (marked.js) + Highlight.js (code)
  • LaTeX support (MathJax)
  • Upload d'image locale (bouton "+" image)
  • Lightbox (click pour zoomer images)
  • Boutons "Copy" sur blocs de code
  • Spinner "Thinking..." pendant attente réponse
  • Responsive design (mobile-friendly)
  • Thème visuel purple moderne

Backend & Intégration

  • Serveur web Python (port 8084) sert fichiers statiques
  • Serveur MCP FastAPI (port 8085) → ComfyUI
  • Endpoint /generate-image (POST) reçoit prompts, envoie à ComfyUI, polling, retourne image_url
  • Randomisation des seeds dans le workflow ComfyUI (évite cache)
  • Vérification ComfyUI au démarrage (./start.sh)
  • Logs serveurs dans fichiers (server.log, mcp_server.log)
  • Scripts d'arrêt/propre (stop.sh)
  • CORS configuré (frontend:8084 → backend:8085)

Commandes

  • /genimg <description> → génère image via ComfyUI
  • Génération d'image fonctionne (prompt → ComfyUI → image dans chat)
  • 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

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 :

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) :

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()

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

{
  "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).

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

# 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