Une bibliothèque JavaScript révolutionnaire permet d'exécuter des modèles d'IA directement dans votre navigateur. Trois exemples concrets à tester dès aujourd'hui.
TRANSFORMERS.JS : L'IA QUI TRAVAILLE SANS SERVEUR
Imaginez un modèle de langage qui tourne sur votre ordinateur, votre téléphone ou même votre tablette, sans avoir besoin de connexion internet ni de serveur distant. C'est exactement ce que propose Transformers.js, une bibliothèque JavaScript qui exécute des modèles d'intelligence artificielle directement dans le navigateur. Plus besoin d'attendre des secondes pour une réponse : tout se passe en local, en quelques millisecondes.
Cette technologie s'appuie sur des réseaux de neurones profonds (des algorithmes inspirés du cerveau humain) optimisés pour fonctionner sur du Matériel grand public. Le résultat ? Des outils d'IA accessibles à tous, sans frais, sans inscription, et sans dépendre d'une entreprise extérieure.
COMMENT ÇA MARCHE : 3 LIGNES DE CODE POUR DÉBUTER
Le principe est simple : on importe la fonction pipeline depuis la bibliothèque Transformers.js, puis on charge un modèle adapté à la tâche souhaitée. Par exemple, pour analyser le sentiment d'un texte, il suffit de trois lignes :
import { pipeline } from '@huggingface/transformers';
const classifier = await pipeline('sentiment-analysis');
const result = await classifier('J’adore les transformers .');
Dans cet exemple, le modèle va attribuer une étiquette (« POSITIF » ou « NÉGATIF ») et un score de confiance à la phrase. Le tout s'exécute en arrière-plan, sans que vous ayez besoin de comprendre comment fonctionne le modèle.
Les modèles utilisés sont des versions allégées de grands modèles de langage, comme DistilBERT (un cousin simplifié de BERT), qui consomment moins de mémoire et de puissance de calcul. Ils sont téléchargés une seule fois puis stockés dans le cache du navigateur, ce qui accélère les futures exécutions.
PERSONNALISER SON MODÈLE : VITESSE, TAILLE ET PRÉCISION
Transformers.js offre plusieurs options pour adapter le modèle à vos besoins. Par exemple, vous pouvez choisir entre trois modes d'exécution :
- WASM (WebAssembly) : fonctionne sur tous les navigateurs, même les plus anciens.
- WebGPU : utilise la carte graphique pour des calculs plus rapides, mais uniquement sur les appareils compatibles.
- Quantification : réduit la taille du modèle pour un téléchargement plus rapide. Par exemple, la quantification sur 4 bits (
dtype: 'q4') divise par deux la taille du modèle, au prix d'une légère baisse de précision.
Voici comment spécifier ces options lors du chargement du modèle :
// Mode par défaut (WASM)
const pipe = await pipeline('sentiment-analysis');
// Mode WebGPU pour une exécution plus rapide
const pipe = await pipeline('sentiment-analysis', null, { device: 'webgpu' });
// Modèle quantifié sur 4 bits pour un téléchargement plus rapide
const pipe = await pipeline('sentiment-analysis',
'Xenova/distilbert-base-uncased-finetuned-sst-2-english',
{ dtype: 'q4' }
);
Le choix dépend de votre matériel et de l'usage que vous souhaitez en faire. Pour un site web grand public, le mode WASM est le plus fiable. Pour une application interne nécessitant des réponses rapides, WebGPU peut faire la différence.
SUIVRE LA PROGRESSION : UNE EXPÉRIENCE UTILISATEUR OPTIMALE
Rien de plus frustrant pour un utilisateur que de ne pas savoir si quelque chose se charge. Transformers.js permet d'afficher une barre de progression pendant le téléchargement du modèle. Voici comment l'implémenter :
const pipe = await pipeline(
'sentiment-analysis',
'Xenova/distilbert-base-uncased-finetuned-sst-2-english',
{
dtype: 'q8',
progress_callback: (progress) => {
if (progress.status === 'progress') {
const pct = Math.round(progress.progress);
document.getElementById('progress').textContent =
Chargement du modèle : ${pct}%;
}
if (progress.status === 'ready') {
document.getElementById('progress').textContent = 'Modèle prêt .';
}
}
}
);
Ce code affiche un message comme « Chargement du modèle : 67% » pendant le téléchargement, puis « Modèle prêt . » une fois terminé. C'est une fonctionnalité essentielle pour éviter que les utilisateurs ne pensent que l'application est bloquée.
EXEMPLE 1 : CLASSER DES TEXTES PAR SENTIMENT
Première application concrète : un classifieur de sentiment. Cet outil analyse un texte pour déterminer s'il est positif, négatif ou neutre. Parfait pour les avis clients, les commentaires sur les réseaux sociaux, ou les retours d'expérience.
Voici comment utiliser le modèle pour analyser une seule phrase :
const classifier = await pipeline('sentiment-analysis');
const result = await classifier('Ce produit a complètement dépassé mes attentes.');
// Résultat : [{ label: 'POSITIF', score: 0.9997 }]
Et pour analyser plusieurs phrases en une seule fois :
const results = await classifier([
'C’est génial .',
'Totalement cassé, une perte d’argent.'
]);
// Résultat : [
// { label: 'POSITIF', score: 0.9998 },
// { label: 'NÉGATIF', score: 0.9991 }
// ]
Le score représente la confiance du modèle dans son analyse. Plus il est proche de 1, plus la prédiction est fiable. Dans cet exemple, le modèle est sûr à 99,97% que la première phrase est positive.
CRÉER UNE INTERFACE WEB POUR LE CLASSIFIEUR
Voici un exemple complet d'interface web pour analyser le sentiment d'un texte. Cette page HTML permet à l'utilisateur de coller un texte, puis de cliquer sur un bouton pour obtenir une analyse instantanée.
<.DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Classifieur de sentiment avec Transformers.js</title>
<style>
body { font-family: system-ui, sans-serif; max-width: 680px;
margin: 2rem auto; padding: 0 1rem; }
textarea { width: 100%; height: 100px; padding: 0.5rem;
font-size: 1rem; margin-bottom: 0.5rem; }
button { padding: 0.5rem 1.5rem; font-size: 1rem; cursor: pointer; }
button:disabled { opacity: 0.5; cursor: not-allowed; }
#status { color: #666; font-size: 0.9rem; margin: 0.5rem 0; }
#result { margin-top: 1rem; font-size: 1.1rem; font-weight: bold; }
.positive { color: #16a34a; }
.negative { color: #dc2626; }
</style>
</head>
<body>
<h1>Analyseur de sentiment</h1>
<p>Exécute entièrement dans votre navigateur -- aucun serveur, aucun appel API.</p>
<textarea id="input" placeholder="Entrez un texte à analyser.">
J’ai vraiment adoré utiliser ce produit. La mise en place était simple et tout fonctionne parfaitement.
</textarea>
<button id="classify-btn" disabled>Chargement du modèle.</button>
<div id="status">Téléchargement du modèle en cours (cela peut prendre quelques instants).</div>
<div id="result"></div>
<script type="module">
import { pipeline } from
'https://cdn.jsdelivr.net/npm/@huggingface/[email protected]';
const statusEl = document.getElementById('status');
const resultEl = document.getElementById('result');
const btn = document.getElementById('classify-btn');
const inputEl = document.getElementById('input');
let classifier;
async function loadModel() {
classifier = await pipeline(
'text-classification',
'Xenova/distilbert-base-uncased-finetuned-sst-2-english',
{
dtype: 'q8',
progress_callback: (p) => {
if (p.status === 'progress') {
const pct = Math.round(p.progress ?? 0);
statusEl.textContent = Téléchargement du modèle : ${pct}%;
}
}
}
);
btn.textContent = 'Analyser';
btn.disabled = false;
statusEl.textContent = 'Modèle chargé et mis en cache. Les prochaines analyses seront instantanées.';
}
async function classify() {
const text = inputEl.value.trim();
if (.text) return;
btn.disabled = true;
btn.textContent = 'Analyse en cours.';
resultEl.textContent = '';
const results = await classifier(text);
const { label, score } = results[0];
const pct = (score * 100).toFixed(1);
const cssClass = label === 'POSITIVE' ? 'positive' : 'negative';
resultEl.innerHTML =
`${label} -- ${pct}% de confiance;
btn.disabled = false;
btn.textContent = 'Analyser';
}
btn.addEventListener('click', classify);
loadModel().catch(err => {
statusEl.textContent = Erreur lors du chargement du modèle : ${err.message}`;
});
</script>
</body>
</html>
Cette page affiche un champ de texte où l'utilisateur peut coller un avis ou un commentaire. Un bouton « Analyser » déclenche le traitement, et le résultat s'affiche avec une couleur verte pour positif ou rouge pour négatif. Le modèle est téléchargé une seule fois, puis réutilisé pour les analyses suivantes.
EXEMPLE 2 : CLASSER DES MESSAGES SANS ENTRAÎNEMENT
Deuxième application : un classifieur zéro-exemple. Contrairement à un classifieur classique qui nécessite des données d'entraînement, ce modèle peut attribuer une catégorie à un texte sans avoir jamais vu d'exemple auparavant. Parfait pour router des tickets de support ou trier des messages automatiquement.
Voici comment l'utiliser pour attribuer un ticket à un service :
const classifier = await pipeline('zero-shot-classification',
'Xenova/bart-large-mnli');
const result = await classifier(
'Ma facture est erronée et j’ai été débité deux fois.',
['facturation', 'support technique', 'livraison', 'retours', 'accès compte']
);
// Résultat : {
// sequence: 'Ma facture est erronée et j’ai été débité deux fois.',
// labels: ['facturation', 'retours', 'accès compte', 'support technique', 'livraison'],
// scores: [0.871, 0.063, 0.031, 0.022, 0.013]
// }
Le modèle attribue un score à chaque catégorie, indiquant sa confiance dans l'affectation. Dans cet exemple, il est sûr à 87,1% que le ticket concerne la facturation. Cette technologie s'appuie sur un modèle nommé BART (un autre type de réseau de neurones), spécialisé dans la compréhension du langage naturel.
CRÉER UN ROUTEUR DE TICKETS DE SUPPORT
Voici une interface web qui permet de router automatiquement un ticket de support vers le bon service. L'utilisateur colle un message, et le modèle détermine si c'est un problème de facturation, de livraison, de support technique, etc.
<.DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Routeur de tickets de support</title>
<style>
body { font-family: system-ui, sans-serif; max-width: 720px;
margin: 2rem auto; padding: 0 1rem; }
textarea { width: 100%; height: 120px; padding: 0.5rem; font-size: 1rem; }
button { margin-top: 0.5rem; padding: 0.5rem 1.5rem;
font-size: 1rem; cursor: pointer; }
button:disabled { opacity: 0.5; cursor: not-allowed; }
#status { color: #666; font-size: 0.9rem; margin: 0.5rem 0; }
.result-row { display: flex; justify-content: space-between;
padding: 0.4rem 0; border-bottom: 1px solid #eee; }
.bar-container { width: 60%; background: #f0f0f0;
border-radius: 4px; height: 18px; }
.bar { background: #2563eb; height: 100%;
border-radius: 4px; transition: width 0.3s; }
.label-name { min-width: 160px; font-weight: 500; }
.score-text { min-width: 50px; text-align: right; color: #555; }
</style>
</head>
<body>
<h1>Routeur de tickets</h1>
<p>Collez un ticket de support. Le modèle l’achemine vers le bon service sans besoin de données d’entraînement.</p>
<textarea id="ticket">
J’ai passé une commande il y a trois jours mais elle n’a toujours pas été expédiée. J’ai un événement ce week-end et j’ai vraiment besoin que ça arrive à temps. Mon numéro de commande est #48821.
</textarea>
<button id="route-btn" disabled>Chargement du modèle.</button>
<div id="status">Téléchargement du modèle en cours.</div>
<div id="results"></div>
<script type="module">
import { pipeline } from
'https://cdn.jsdelivr.net/npm/@huggingface/[email protected]';
const statusEl = document.getElementById('status');
const resultsEl = document.getElementById('results');
const btn = document.getElementById('route-btn');
const ticketEl = document.getElementById('ticket');
const DEPARTEMENTS = [
'livraison et expédition',
'facturation et paiement',
'support technique',
'retours et remboursements',
'compte et connexion'
];
let classifier;
async function loadModel() {
classifier = await pipeline(
'zero-shot-classification',
'Xenova/bart-large-mnli',
{
dtype: 'q8',
progress_callback: (p) => {
if (p.status === 'progress') {
statusEl.textContent =
Téléchargement du modèle : ${Math.round(p.progress ?? 0)}%;
}
}
}
);
btn.disabled = false;
btn.textContent = 'Router le ticket';
statusEl.textContent = 'Modèle prêt.';
}
async function routeTicket() {
const text = ticketEl.value.trim();
if (.text) return;
btn.disabled = true;
btn.textContent = 'Aiguillage en cours.';
resultsEl.innerHTML = '';
const output = await classifier(text, DEPARTEMENTS, {
multi_label: false
});
const winner = output.labels[0];
const confidence = (output.scores[0] * 100).toFixed(1);
let html = ` {
const pct = (output.scores[i] * 100).toFixed(1);
const barWidth = (output.scores[i] * 100).toFixed(0);
html +=
<div class="result-row">
<span class="label-name">${label}</span>
<div class="bar-container">
<div class="bar"></div>
</div>
<span class="score-text">${pct}%</span>
</div>;
});
resultsEl.innerHTML = html;
btn.disabled = false;
btn.textContent = 'Router le ticket';
}
btn.addEventListener('click', routeTicket);
loadModel().catch(err => {
statusEl.textContent = Erreur : ${err.message};
});
</script>
</body>
</html>
Cette interface affiche un champ de texte où l'utilisateur peut coller un ticket de support. Après avoir cliqué sur le bouton, le modèle détermine le service le plus adapté et affiche un graphique en barres montrant la confiance pour chaque catégorie. Par exemple, un ticket concernant une commande non expédiée sera automatiquement aiguillé vers le service « livraison et expédition » avec un score élevé.
EXEMPLE 3 : RÉPONDRE À DES QUESTIONS SUR UN DOCUMENT
Troisième application : un système de réponse aux questions. Cet outil permet de poser une question sur un texte long (comme une politique de retour ou un contrat) et d'obtenir une réponse précise extraite directement du document. Idéal pour les FAQ, les guides utilisateurs ou les documents internes.
Voici comment l'utiliser pour trouver une information dans un texte :
const qa = await pipeline('question-answering',
'Xenova/distilbert-base-uncased-distilled-squad');
const result = await qa({
question: 'Quel est le délai de retour pour les produits électroniques ?',
context: Notre politique de retour permet aux clients de retourner la plupart des articles dans les 30 jours suivant l'achat. Les produits électroniques doivent être retournés dans les 15 jours et doivent être dans leur emballage d'origine. Les logiciels et téléchargements numériques ne sont pas remboursables.
});
// Résultat : {
// answer: '15 jours',
// score: 0.9823,
// start: 97, // indice de caractère du début de la réponse
// end: 104 // indice de caractère de la fin de la réponse
// }
Le modèle extrait la réponse « 15 jours » directement du texte, avec un score de confiance de 98,23%. Les indices start et end permettent de localiser la réponse dans le document original, ce qui est utile pour afficher un extrait mis en évidence.
CRÉER UNE INTERFACE DE QUESTION-RÉPONSE SUR UN DOCUMENT
Voici une interface web qui permet de poser des questions sur un document. L'utilisateur colle un texte (par exemple, une politique de retour), puis pose une question pour en extraire l'information pertinente.
<.DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Réponses aux questions sur un document</title>
<style>
body { font-family: system-ui, sans-serif; max-width: 720px;
margin: 2rem auto; padding: 0 1rem; }
label { font-weight: 600; display: block; margin-top: 1rem; }
textarea { width: 100%; padding: 0.5rem; font-size: 0.95rem; }
input[type="text"] { width: 100%; padding: 0.5rem;
font-size: 0.95rem; box-sizing: border-box; }
button { margin-top: 0.75rem; padding: 0.5rem 1.5rem;
font-size: 1rem; cursor: pointer; }
button:disabled { opacity: 0.5; cursor: not-allowed; }
#status { color: #666; font-size: 0.9rem; margin: 0.5rem 0; }
#answer-box { margin-top: 1rem; padding: 1rem;
background: #f8fafc; border-left: 3px solid #2563eb; }
.highlight { background: #fef08a; border-radius: 2px; }
.confidence { color: #666; font-size: 0.85rem; margin-top: 0.5rem; }
</style>
</head>
<body>
<h1>Réponses aux questions sur un document</h1>
<p>Collez n’importe quel document, puis posez des questions à son sujet. Les réponses sont extraites directement du texte.</p>
<label for="context">Document / Contexte</label>
<textarea id="context" rows="8">
Politique de retour de Acme Corp (mise à jour mars 2025)
Les clients peuvent retourner la plupart des articles standard dans les 30 jours suivant la date d’achat pour un remboursement intégral. Les produits électroniques et périphériques ont une fenêtre de retour plus courte de 15 jours et doivent être retournés dans leur emballage d’origine, non ouvert, pour être éligibles.
Les remboursements sont traités dans les 3-5 jours ouvrés après réception de l’article retourné. Les frais de port initiaux ne sont pas remboursables. Pour les articles d’une valeur supérieure à 200 €, les clients doivent contacter le support à l’adresse [email protected] avant d’initier un retour.
Les licences logicielles et les téléchargements numériques ne sont pas remboursables dans aucune circonstance. Les cartes cadeaux ne peuvent pas être retournées ni échangées contre de l’argent.
</textarea>
<label for="question">Votre question</label>
<input type="text" id="question"
value="Combien de temps faut-il pour traiter un remboursement ?" />
<button id="ask-btn" disabled>Chargement du modèle.</button>
<div id="status">Téléchargement du modèle en cours.</div>
<div id="answer-box"></div>
<script type="module">
import { pipeline } from
'https://cdn.jsdelivr.net/npm/@huggingface/[email protected]';
const contextEl = document.getElementById('context');
const questionEl = document.getElementById('question');
const statusEl = document.getElementById('status');
const answerBox = document.getElementById('answer-box');
const btn = document.getElementById('ask-btn');
const SEUIL_CONFIANCE = 0.1;
let qaModel;
async function loadModel() {
qaModel = await pipeline(
'question-answering',
'Xenova/distilbert-base-uncased-distilled-squad',
{
dtype: 'q8',
progress_callback: (p) => {
if (p.status === 'progress') {
statusEl.textContent =
Téléchargement du modèle : ${Math.round(p.progress ?? 0)}%;
}
}
}
);
btn.disabled = false;
btn.textContent = 'Poser la question';
statusEl.textContent = 'Modèle prêt.';
}
async function askQuestion() {
const context = contextEl.value.trim();
const question = questionEl.value.trim();
if (.context || .question) return;
btn.disabled = true;
btn.textContent = 'Réflexion en cours.';
answerBox.style.display = 'none';
const result = await qaModel({ question, context });
answerBox.style.display = 'block';
if (result.score < SEUIL_CONFIANCE) {
answerBox.innerHTML =
<strong>Réponse non trouvée</strong>
<p class="confidence">Le modèle n’a pas pu trouver de réponse claire à cette question dans le texte fourni.</p>;
} else {
const before = context.slice(0, result.start);
const answer = context.slice(result.start, result.end);
const after = context.slice(result.end);
const highlight = ${before}<mark class="highlight">${answer}</mark>${after};
const confiance = (result.score * 100).toFixed(1);
answerBox.innerHTML =
<strong>Réponse :</strong> ${result.answer}
<p class="confidence">Confiance : ${confiance}%</p>
<details>
<summary style="color: #2563eb">
Afficher la réponse mise en évidence dans le document
</summary>
<pre style="white-space:pre-wrap; font-size:0.85rem;
margin-top:0.5rem">${highlight}</pre>
</details>;
}
btn.disabled = false;
btn.textContent = 'Poser la question';
}
btn.addEventListener('click', askQuestion);
questionEl.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && .btn.disabled) askQuestion();
});
loadModel().catch(err => {
statusEl.textContent = Erreur : ${err.message};
});
</script>
</body>
</html>
Cette interface permet à l'utilisateur de coller un document (comme une politique de retour) puis de poser une question. Le modèle extrait la réponse et l'affiche avec un niveau de confiance. Si la confiance est trop faible, il indique que la réponse n'a pas été trouvée. Un bouton « Afficher la réponse mise en évidence » permet de visualiser l'extrait du document où se trouve la réponse.
ANALYSER UN TICKET DE SUPPORT EN UN CLIN D'ŒIL
Pour aller plus loin, voici un exemple d'interface qui combine les trois outils précédents : un analyseur de tickets de support complet. Cette page analyse un ticket en temps réel et affiche trois informations clés :
- Le sentiment (positif, négatif ou neutre).
- Le service concerné (livraison, facturation, etc.).
- Les informations clés extraites du texte (numéro de commande, problème, etc.).
Voici le code complet de cette interface :
<.DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Analyseur de tickets de support</title>
<style>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; max-width: 800px;
margin: 2rem auto; padding: 0 1rem; background: #f9fafb; }
h1 { margin-bottom: 0.25rem; }
.subtitle { color: #666; margin-bottom: 1.5rem; }
textarea { width: 100%; height: 130px; padding: 0.75rem;
font-size: 0.95rem; border: 1px solid #d1d5db;
border-radius: 6px; resize: vertical; }
button { padding: 0.6rem 1.8rem; font-size: 1rem;
background: #2563eb; color: white; border: none;
border-radius: 6px; cursor: pointer; margin-top: 0.5rem; }
button:disabled { background: #93c5fd; cursor: not-allowed; }
#status { font-size: 0.85rem; color: #666; margin: 0.5rem 0; }
.cards { display: grid; grid-template-columns: repeat(3, 1fr);
gap: 1rem; margin-top: 1.5rem; }
.card { background: white; border-radius: 8px; padding: 1rem;
border: 1px solid #e5e7eb; }
.card h3 { margin: 0 0 0.75rem; font-size: 0.9rem;
text-transform: uppercase; letter-spacing: 0.05em;
color: #6b7280; }
.card .value { font-size: 1.15rem; font-weight: 600; }
.card .sub { font-size: 0.85rem; color: #666; margin-top: 0.25rem; }
.positive { color: #16a34a; }
.negative { color: #dc2626; }
.neutral { color: #d97706; }
.dept-bar { display: flex; align-items: center; gap: 0.5rem;
margin-top: 0.4rem; font-size: 0.85rem; }
.bar-bg { flex: 1; background: #f0f0f0; border-radius: 3px; height: 8px; }
.bar-fill { background: #2563eb; height: 100%;
border-radius: 3px; transition: width 0.4s; }
.qa-item { margin-top: 0.6rem; font-size: 0.9rem; }
.qa-label { font-weight: 600; color: #374151; }
.qa-ans { color: #111; }
.qa-low { color: #9ca3af; font-style: italic; }
@media (max-width: 600px) {
.cards { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<h1>Analyseur de tickets de support</h1>
<p class="subtitle">Powered by Transformers.js -- exécute entièrement dans votre navigateur</p>
<textarea id="ticket">
Bonjour, j’ai commandé un support pour ordinateur portable mardi dernier (commande n°73021) mais il est arrivé complètement cassé -- un des bras s’est brisé dès l’ouverture de la boîte. Je suis client depuis trois ans et c’est vraiment très décevant. J’ai besoin qu’un remplacement soit envoyé le plus rapidement possible ou que je sois remboursé intégralement. Merci de me faire savoir ce que vous proposez.
</textarea>
<button id="analyze-btn" disabled>Chargement des modèles.</button>
<div id="status">Initialisation -- téléchargement des modèles en cours.</div>
<div class="cards" id="cards">
<div class="card" id="card-sentiment">
<h3>Sentiment</h3>
<div class="value" id="sent-label">--</div>
<div class="sub" id="sent-score">--</div>
</div>
<div class="card" id="card-route">
<h3>Service concerné</h3>
<div id="dept-results"></div>
</div>
<div class="card" id="card-qa">
<h3>Informations clés</h3>
<div id="qa-results"></div>
</div>
</div>
<script type="module">
import { pipeline } from
'https://cdn.jsdelivr.net/npm/@huggingface/[email protected]';
const ticketEl = document.getElementById('ticket');
const btn = document.getElementById('analyze-btn');
const statusEl = document.getElementById('status');
const cardsEl = document.getElementById('cards');
const DEPARTEMENTS = [
'retours et remboursements',
'livraison et expédition',
'facturation et paiement',
'support technique',
'gestion de compte'
];
const QUESTIONS_QA = [
{ label: 'Numéro de commande', question: 'Quel est le numéro de commande ?' },
{ label: 'Problème', question: 'Quel est le problème rencontré ?' }
];
let sentimentModel;
let routeurModel;
let qaModel;
async function loadModels() {
// Chargement du modèle de sentiment
sentimentModel = await pipeline(
'text-classification',
'Xenova/distilbert-base-uncased-finetuned-sst-2-english',
{ dtype: 'q8', progress_callback: updateStatus }
);
// Chargement du modèle de routage
routeurModel = await pipeline(
'zero-shot-classification',
'Xenova/bart-large-mnli',
{ dtype: 'q8', progress_callback: updateStatus }
);
// Chargement du modèle de réponse aux questions
qaModel = await pipeline(
'question-answering',
'Xenova/distilbert-base-uncased-distilled-squad',
{ dtype: 'q8', progress_callback: updateStatus }
);
btn.textContent = 'Analyser le ticket';
btn.disabled = false;
statusEl.textContent = 'Tous les modèles sont prêts.';
}
function updateStatus(progress) {
if (progress.status === 'progress') {
statusEl.textContent = Téléchargement des modèles : ${Math.round(progress.progress ?? 0)}%;
}
}
async function analyzeTicket() {
const text = ticketEl.value.trim();
if (.text) return;
btn.disabled = true;
btn.textContent = 'Analyse en cours.';
cardsEl.style.display = 'none';
// Analyse du sentiment
const sentimentResult = await sentimentModel(text);
const sentimentLabel = sentimentResult[0].label;
const sentimentScore = (sentimentResult[0].score * 100).toFixed(1);
const sentimentClass = sentimentLabel === 'POSITIVE' ? 'positive' :
sentimentLabel === 'NEGATIVE' ? 'negative' : 'neutral';
document.getElementById('sent-label').innerHTML =
`${sentimentLabel};
document.getElementById('sent-score').textContent = ${sentimentScore}% de confiance;
// Routage du ticket
const routeResult = await routeurModel(text, DEPARTEMENTS, { multi_label: false });
const deptWinner = routeResult.labels[0];
const deptScore = (routeResult.scores[0] * 100).toFixed(1);
let deptHtml = 

