Dans le développement d'interfaces pour l'Intelligence Artificielle, la gestion des réponses provenant de modèles de langage (LLM) comme GPT ou Claude nécessite une approche spécifique. Ces modèles retournent généralement du contenu structuré au format Markdown de manière progressive (streaming). Pour offrir une expérience utilisateur fluide, il est crucial de transformer ce flux textuel en HTML sécurisé et stylisé en temps réel.
Pourquoi privilégier le Markdown pour l'IA ?
Les LLM utilisent le Markdown pour structurer leurs réponses, incluant :
- Des blocs de code avec spécification du langage.
- Des tableaux comparatifs complexes.
- Des notations mathématiques (LaTeX).
- Des éléments de mise en forme standards (listes, gras, citations).
L'utilisation directe de propriétés comme dangerouslySetInnerHTML est fortement déconseillée car elle expose l'application aux failles XSS et ne permet pas nativement l'interprétation des blocs de code ou des formules mathématiques.
Sélection de la pile technologique
| Besoin | Solution recommandée |
|---|---|
| Analyse sécurisée du Markdown | react-markdown (basé sur AST) |
| Coloration syntaxique | rehype-highlight associé à highlight.js |
| Éqautions mathématiques | rehype-katex et le CSS de KaTeX |
| Tableaux et listes GFM | remark-gfm |
Implémentation technique
1. Préparation de l'environnement
Installez les dépendances nécessaires à la gestion du Markdown et des plug-ins de transformation :
npm install react-markdown remark-gfm rehype-highlight rehype-katex katex
2. Création du composant de rendu
Ce composant, baptisé MarkdownRenderer, gère la transformation du flux texte en éléments React tout en appliquant des styles personnalisés pour les tableaux et le code.
import React from 'react';
import ReactMarkdown from 'react-markdown';
import gfmPlugin from 'remark-gfm';
import highlightPlugin from 'rehype-highlight';
import katexPlugin from 'rehype-katex';
import 'katex/dist/katex.min.css';
const MarkdownRenderer = ({ markdownSource }) => {
return (
<ReactMarkdown
remarkPlugins={[gfmPlugin]}
rehypePlugins={[highlightPlugin, katexPlugin]}
components={{
// Personnalisation des blocs de code
code({ node, inline, className, children, ...props }) {
const isLanguageMatch = /language-(\w+)/.exec(className || '');
return !inline && isLanguageMatch ? (
{children}
) : (
{children}
);
},
// Conteneur responsive pour les tableaux
table({ children }) {
return (
<div style={{ overflowX: 'auto', margin: '1rem 0' }}>
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
{children}
</table>
</div>
);
}
}}
>
{markdownSource}
</ReactMarkdown>
);
};
export default MarkdownRenderer;
3. Simulation du flux de données (Streaming)
Pour reproduire l'effet "machine à écrire" typique des agents conversationnels, nous utilisons un effet React qui incrémente progressivement la chaîne de caractères affichée.
import { useState, useEffect } from 'react';
import MarkdownRenderer from './components/MarkdownRenderer';
const rawAiOutput = `
# Analyse IA en cours 🚀
Voici un exemple de rendu **dynamique**.
## Coloration de code
\`\`\`python
def salutation(nom):
return f"Bonjour, {nom} ! Bienvenue dans l'interface IA."
\`\`\`
## Données structurées
| Module | Statut | Performance |
| :--- | :--- | :--- |
| Parsing | OK | 12ms |
| Rendu | OK | 45ms |
| Sécurité | Vérifié | - |
## Mathématiques avancées
L'identité d'Euler est définie par : $e^{i\\pi} + 1 = 0$
Et la distribution normale :
$$
f(x) = \\frac{1}{\\sigma\\sqrt{2\\pi}} e^{-\\frac{1}{2}(\\frac{x-\\mu}{\\sigma})^2}
$$
`;
export default function App() {
const [visibleText, setVisibleText] = useState('');
useEffect(() => {
let charPointer = 0;
const streamTimer = setInterval(() => {
if (charPointer < rawAiOutput.length) {
setVisibleText(rawAiOutput.substring(0, charPointer + 1));
charPointer++;
} else {
clearInterval(streamTimer);
}
}, 15);
return () => clearInterval(streamTimer);
}, []);
return (
<div style={{ maxWidth: '800px', margin: '40px auto', padding: '0 20px' }}>
<div style={{
border: '1px solid #eaeaea',
borderRadius: '8px',
padding: '30px',
backgroundColor: '#ffffff',
boxShadow: '0 4px 6px rgba(0,0,0,0.1)'
}}>
<MarkdownRenderer markdownSource={visibleText} />
</div>
</div>
);
}
Optimisation et considérations avancées
Le rendu fréquent d'un arbre syntaxique (AST) à chaque nouveau caractère peut impacter les performances sur des documents très longs. Voici quelques pistes d'amélioration :
- Throttling : Au lieu de mettre à jour le state à chaque caractère, regroupez les mises à jour par petits paquets (chunks) toutes les 50ms.
- Virtualisation : Si le contenu devient massif, intégrez une bibliothèque de listes virtuelles pour ne rendre que la partie visible du Markdown.
- Gestion du scroll : Implémentez un comportement d'auto-scroll vers le bas uniquement si l'utilisateur n'a pas manuellement remonté la page pour lire une réponse précédente.
L'utilisation de react-markdown combinée à l'écosystème remark/rehype permet de transformer une simple chaîne de texte en une interface riche et sécurisée, essentielle pour toute application front-end moderne intégrant l'intelligence artificielle.