ai-lab-transformers-playground/ui/script.js

1058 lines
31 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Configuration globale
let apiUrl = "http://localhost:8000";
// État de l'application
const appState = {
currentTab: "sentiment",
apiConnected: false
};
// Initialisation de l'application
document.addEventListener("DOMContentLoaded", function () {
initializeApp();
});
function initializeApp() {
setupEventListeners();
checkApiStatus();
loadExamplesData();
}
function setupEventListeners() {
// Navigation entre les onglets
document.querySelectorAll(".nav-tab").forEach((tab) => {
tab.addEventListener("click", (e) => {
const tabName = e.currentTarget.dataset.tab;
switchTab(tabName);
});
});
// Changement d'URL API
document.getElementById("apiUrl").addEventListener("change", function () {
apiUrl = this.value;
checkApiStatus();
});
// Soumission des formulaires
setupFormHandlers();
}
function setupFormHandlers() {
// Empêcher la soumission par défaut de tous les formulaires
document.querySelectorAll("form").forEach((form) => {
form.addEventListener("submit", (e) => {
e.preventDefault();
});
});
}
// Navigation
function switchTab(tabName) {
// Mise à jour de l'état
appState.currentTab = tabName;
// Mise à jour des onglets
document.querySelectorAll(".nav-tab").forEach((tab) => {
tab.classList.remove("active");
});
document.querySelector(`[data-tab="${tabName}"]`).classList.add("active");
// Mise à jour du contenu
document.querySelectorAll(".tab-content").forEach((content) => {
content.classList.remove("active");
});
document.getElementById(tabName).classList.add("active");
}
// Vérification du statut de l'API
async function checkApiStatus() {
const statusElement = document.getElementById("apiStatus");
const indicator = statusElement.querySelector(".status-indicator");
const statusText = statusElement.querySelector("span");
const testButton = document.querySelector("button[onclick='checkApiStatus()']");
// Feedback visuel pendant le test
if (testButton) {
testButton.disabled = true;
testButton.innerHTML = '<span class="btn-icon">⏳</span>Test en cours...';
testButton.classList.add("loading");
}
// Indicateur de chargement
indicator.className = "status-indicator loading";
statusText.textContent = "Test de connexion...";
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
const response = await fetch(`${apiUrl}/health`, {
method: "GET",
signal: controller.signal
});
clearTimeout(timeoutId);
if (response.ok) {
const healthData = await response.json();
appState.apiConnected = true;
indicator.className = "status-indicator online";
statusText.textContent = "API Connectée";
// Notification de succès
showNotification("✅ Connexion API établie avec succès!", "success");
// Afficher les détails de l'API
showApiDetails(healthData);
} else {
throw new Error(`Erreur HTTP ${response.status}`);
}
} catch (error) {
appState.apiConnected = false;
indicator.className = "status-indicator offline";
statusText.textContent = "API Déconnectée";
let errorMessage = "❌ Impossible de se connecter à l'API";
if (error.name === "AbortError") {
errorMessage += " (Timeout)";
} else if (error.message.includes("fetch")) {
errorMessage += " (Serveur inaccessible)";
} else {
errorMessage += ` (${error.message})`;
}
showNotification(errorMessage, "error");
console.warn("Erreur de connexion API:", error.message);
} finally {
// Restaurer le bouton
if (testButton) {
testButton.disabled = false;
testButton.innerHTML = '<span class="btn-icon">🔄</span>Tester la connexion';
testButton.classList.remove("loading");
}
}
}
// Système de notifications
function showNotification(message, type = "info", duration = 5000) {
// Créer le conteneur de notifications s'il n'existe pas
let notificationContainer = document.getElementById("notification-container");
if (!notificationContainer) {
notificationContainer = document.createElement("div");
notificationContainer.id = "notification-container";
notificationContainer.className = "notification-container";
document.body.appendChild(notificationContainer);
}
// Créer la notification
const notification = document.createElement("div");
notification.className = `notification notification-${type}`;
notification.innerHTML = `
<div class="notification-content">
<span class="notification-message">${message}</span>
<button class="notification-close" onclick="this.parentElement.parentElement.remove()">×</button>
</div>
`;
// Ajouter l'animation d'entrée
notification.style.transform = "translateX(100%)";
notification.style.opacity = "0";
notificationContainer.appendChild(notification);
// Animation d'entrée
setTimeout(() => {
notification.style.transform = "translateX(0)";
notification.style.opacity = "1";
}, 10);
// Suppression automatique
if (duration > 0) {
setTimeout(() => {
if (notification.parentElement) {
notification.style.transform = "translateX(100%)";
notification.style.opacity = "0";
setTimeout(() => notification.remove(), 300);
}
}, duration);
}
}
// Affichage des détails de l'API
function showApiDetails(healthData) {
const existingDetails = document.getElementById("api-details");
if (existingDetails) {
existingDetails.remove();
}
const configSection = document.querySelector(".config-section .config-card");
const detailsDiv = document.createElement("div");
detailsDiv.id = "api-details";
detailsDiv.className = "api-details";
detailsDiv.innerHTML = `
<div class="api-details-header">
<h4>📊 Détails de l'API</h4>
<button class="btn-minimal" onclick="this.parentElement.parentElement.style.display='none'">×</button>
</div>
<div class="api-details-content">
<div class="detail-item">
<span class="detail-label">Statut:</span>
<span class="detail-value success">✅ ${healthData.status || "healthy"}</span>
</div>
<div class="detail-item">
<span class="detail-label">Pipelines chargés:</span>
<span class="detail-value">${healthData.pipelines_loaded || 0}</span>
</div>
<div class="detail-item">
<span class="detail-label">Pipelines disponibles:</span>
<span class="detail-value">${(healthData.available_pipelines || []).join(", ") || "Aucun"}</span>
</div>
</div>
`;
configSection.appendChild(detailsDiv);
}
async function makeApiRequest(endpoint, data) {
if (!appState.apiConnected) {
throw new Error("API non connectée. Vérifiez la configuration.");
}
try {
const response = await fetch(`${apiUrl}${endpoint}`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(data)
});
const result = await response.json();
if (!response.ok) {
throw new Error(result.detail || `Erreur ${response.status}`);
}
return result;
} catch (error) {
if (error.name === "TypeError" && error.message.includes("fetch")) {
throw new Error("Impossible de contacter l'API. Vérifiez que le serveur est démarré.");
}
throw error;
}
}
// Gestion de l'affichage des résultats améliorée
function showLoading(containerId) {
const container = document.getElementById(containerId);
container.innerHTML = `
<div class="loading-container">
<div class="loading-spinner">
<div class="spinner"></div>
</div>
<div class="loading-content">
<h4>🔄 Traitement en cours...</h4>
<p>Analyse de votre texte par l'IA</p>
<div class="loading-progress">
<div class="progress-bar"></div>
</div>
</div>
</div>
`;
container.classList.add("show");
}
function showResult(containerId, data, isError = false) {
const container = document.getElementById(containerId);
const headerClass = isError ? "error" : "success";
const icon = isError ? "❌" : "✅";
const title = isError ? "Erreur" : "Résultat";
let formattedContent;
if (isError) {
formattedContent = formatErrorResult(data);
} else {
formattedContent = formatResult(data, containerId);
}
// Ajouter un timestamp
const timestamp = new Date().toLocaleTimeString("fr-FR");
container.innerHTML = `
<div class="result-card">
<div class="result-header ${headerClass}">
<div class="result-header-main">
<span class="result-icon">${icon}</span>
<div class="result-title">
<span class="title">${title}</span>
<span class="timestamp">${timestamp}</span>
</div>
</div>
<div class="result-actions">
<button class="btn-minimal" onclick="copyResultToClipboard('${containerId}')" title="Copier le résultat">
📋
</button>
<button class="btn-minimal" onclick="toggleResultDetails('${containerId}')" title="Afficher/Masquer les détails JSON">
🔍
</button>
<button class="btn-minimal" onclick="document.getElementById('${containerId}').classList.remove('show')" title="Fermer">
×
</button>
</div>
</div>
<div class="result-content">
${formattedContent}
</div>
</div>
`;
container.classList.add("show");
// Animation d'entrée
const resultCard = container.querySelector(".result-card");
resultCard.style.transform = "translateY(20px)";
resultCard.style.opacity = "0";
setTimeout(() => {
resultCard.style.transform = "translateY(0)";
resultCard.style.opacity = "1";
}, 10);
}
function formatErrorResult(error) {
let errorMessage = "Une erreur inattendue s'est produite";
let suggestions = [];
if (typeof error === "object" && error.error) {
errorMessage = error.error;
// Suggestions basées sur le type d'erreur
if (errorMessage.includes("API non connectée")) {
suggestions.push("Vérifiez que le serveur API est démarré");
suggestions.push("Testez la connexion avec le bouton 'Tester la connexion'");
} else if (errorMessage.includes("requis")) {
suggestions.push("Assurez-vous que tous les champs obligatoires sont remplis");
} else if (errorMessage.includes("MASK")) {
suggestions.push("Utilisez [MASK] dans votre texte pour le fill-mask");
}
}
let suggestionsHtml = "";
if (suggestions.length > 0) {
suggestionsHtml = `
<div class="error-suggestions">
<h4>💡 Suggestions:</h4>
<ul>
${suggestions.map((s) => `<li>${s}</li>`).join("")}
</ul>
</div>
`;
}
return `
<div class="error-content">
<div class="error-message">
<strong>Message d'erreur:</strong>
<p>${errorMessage}</p>
</div>
${suggestionsHtml}
</div>
`;
}
function formatResult(data, containerId) {
const type = containerId.replace("Result", "");
switch (type) {
case "sentiment":
return formatSentimentResult(data);
case "ner":
return formatNerResult(data);
case "qa":
return formatQaResult(data);
case "fillmask":
return formatFillmaskResult(data);
case "moderation":
return formatModerationResult(data);
case "textgen":
return formatTextgenResult(data);
case "batch":
return formatBatchResult(data);
default:
return `<div class="result-json">${JSON.stringify(data, null, 2)}</div>`;
}
}
function formatSentimentResult(data) {
const sentiment = data.sentiment || data.label;
const confidence = data.confidence || data.score;
const badgeClass = sentiment?.toLowerCase() === "positive" ? "positive" : sentiment?.toLowerCase() === "negative" ? "negative" : "neutral";
// Calcul de la barre de progression pour la confiance
const confidencePercent = confidence ? (confidence * 100).toFixed(1) : 0;
const progressColor = confidencePercent > 80 ? "var(--success-500)" : confidencePercent > 60 ? "var(--warning-500)" : "var(--error-500)";
return `
<div class="sentiment-result">
<div class="sentiment-main">
<div class="sentiment-label">
<span class="label-title">Sentiment détecté:</span>
<span class="badge ${badgeClass}">${sentiment || "Non déterminé"}</span>
</div>
<div class="confidence-section">
<div class="confidence-header">
<span class="confidence-title">Niveau de confiance:</span>
<span class="confidence-value">${confidencePercent}%</span>
</div>
<div class="confidence-bar">
<div class="confidence-progress" style="width: ${confidencePercent}%; background-color: ${progressColor}"></div>
</div>
</div>
</div>
<div class="sentiment-interpretation">
${getSentimentInterpretation(sentiment, confidence)}
</div>
<details class="result-details">
<summary>🔍 Détails techniques</summary>
<div class="result-json">${JSON.stringify(data, null, 2)}</div>
</details>
</div>
`;
}
function formatNerResult(data) {
let entitiesHtml = "";
let entitiesStats = {};
if (data.entities && data.entities.length > 0) {
// Compter les entités par type
data.entities.forEach((entity) => {
const label = entity.label;
entitiesStats[label] = (entitiesStats[label] || 0) + 1;
});
entitiesHtml = data.entities
.map((entity, index) => {
const label = entity.label?.toLowerCase() || "misc";
const confidence = entity.confidence ? (entity.confidence * 100).toFixed(1) : null;
return `
<div class="entity-item">
<span class="entity ${label}" title="Confiance: ${confidence || "N/A"}%">
${entity.text}
</span>
<span class="entity-label">${entity.label}</span>
${confidence ? `<span class="entity-confidence">${confidence}%</span>` : ""}
</div>
`;
})
.join("");
} else {
entitiesHtml = `<div class="no-entities">🔍 Aucune entité nommée détectée dans ce texte</div>`;
}
// Statistiques des entités
const statsHtml =
Object.keys(entitiesStats).length > 0
? `
<div class="entities-stats">
<h4>📊 Statistiques des entités:</h4>
<div class="stats-grid">
${Object.entries(entitiesStats)
.map(
([type, count]) => `
<div class="stat-item">
<span class="stat-type">${type}</span>
<span class="stat-count">${count}</span>
</div>
`
)
.join("")}
</div>
</div>
`
: "";
return `
<div class="ner-result">
<div class="entities-section">
<h4>🏷️ Entités détectées:</h4>
<div class="entities-container">
${entitiesHtml}
</div>
</div>
${statsHtml}
<details class="result-details">
<summary>🔍 Détails techniques</summary>
<div class="result-json">${JSON.stringify(data, null, 2)}</div>
</details>
</div>
`;
}
function formatQaResult(data) {
const confidence = data.confidence ? (data.confidence * 100).toFixed(1) : null;
const progressColor = confidence > 80 ? "var(--success-500)" : confidence > 60 ? "var(--warning-500)" : "var(--error-500)";
return `
<div class="qa-result">
<div class="qa-main">
<div class="qa-question">
<h4>❓ Question:</h4>
<p class="question-text">${data.question}</p>
</div>
<div class="qa-answer">
<h4>💡 Réponse:</h4>
<div class="answer-text">${data.answer || "Aucune réponse trouvée dans le contexte fourni"}</div>
</div>
${
confidence
? `
<div class="qa-confidence">
<div class="confidence-header">
<span>Fiabilité de la réponse:</span>
<span class="confidence-value">${confidence}%</span>
</div>
<div class="confidence-bar">
<div class="confidence-progress" style="width: ${confidence}%; background-color: ${progressColor}"></div>
</div>
</div>
`
: ""
}
</div>
<details class="result-details">
<summary>🔍 Détails techniques</summary>
<div class="result-json">${JSON.stringify(data, null, 2)}</div>
</details>
</div>
`;
}
function formatFillmaskResult(data) {
let predictionsHtml = "";
if (data.predictions && data.predictions.length > 0) {
predictionsHtml = data.predictions
.map((pred, index) => {
const score = pred.score ? (pred.score * 100).toFixed(1) : null;
const rankClass = index === 0 ? "rank-first" : index === 1 ? "rank-second" : "rank-other";
return `
<div class="prediction-item ${rankClass}">
<div class="prediction-rank">#${index + 1}</div>
<div class="prediction-content">
<span class="prediction-token">${pred.token || pred.token_str}</span>
${score ? `<span class="prediction-score">${score}%</span>` : ""}
</div>
${
score
? `
<div class="prediction-bar">
<div class="prediction-progress" style="width: ${score}%"></div>
</div>
`
: ""
}
</div>
`;
})
.join("");
} else {
predictionsHtml = `<div class="no-predictions">🔍 Aucune prédiction disponible</div>`;
}
return `
<div class="fillmask-result">
<div class="predictions-section">
<h4>🎭 Mots prédits pour remplacer [MASK]:</h4>
<div class="predictions-container">
${predictionsHtml}
</div>
</div>
<details class="result-details">
<summary>🔍 Détails techniques</summary>
<div class="result-json">${JSON.stringify(data, null, 2)}</div>
</details>
</div>
`;
}
function formatModerationResult(data) {
const flagged = data.flagged;
const badgeClass = flagged ? "negative" : "positive";
const status = flagged ? "CONTENU SIGNALÉ" : "CONTENU APPROPRIÉ";
const icon = flagged ? "⚠️" : "✅";
let categoriesHtml = "";
if (data.categories) {
const categories = Object.entries(data.categories);
if (categories.length > 0) {
categoriesHtml = `
<div class="moderation-details">
<h4>📋 Détails de l'analyse:</h4>
<div class="moderation-categories">
${categories
.map(([key, value]) => {
let displayKey = key;
let displayValue = value;
if (key === "toxic_score") {
displayKey = "Score de toxicité";
displayValue = typeof value === "number" ? `${(value * 100).toFixed(1)}%` : value;
} else if (key === "is_modified") {
displayKey = "Contenu modifié";
displayValue = value ? "Oui" : "Non";
} else if (key === "words_replaced") {
displayKey = "Mots remplacés";
} else if (key === "restored_text") {
displayKey = "Texte restauré";
}
return `
<div class="category-item">
<span class="category-label">${displayKey}:</span>
<span class="category-value">${displayValue}</span>
</div>
`;
})
.join("")}
</div>
</div>
`;
}
}
return `
<div class="moderation-result">
<div class="moderation-status ${badgeClass}">
<span class="status-icon">${icon}</span>
<span class="status-text">${status}</span>
</div>
${categoriesHtml}
<details class="result-details">
<summary>🔍 Détails techniques</summary>
<div class="result-json">${JSON.stringify(data, null, 2)}</div>
</details>
</div>
`;
}
function formatTextgenResult(data) {
const generatedText = data.generated_text || "Aucun texte généré";
const prompt = data.prompt || "";
return `
<div class="textgen-result">
<div class="textgen-prompt">
<h4>📝 Prompt initial:</h4>
<div class="prompt-text">${prompt}</div>
</div>
<div class="textgen-output">
<h4>✨ Texte généré:</h4>
<div class="generated-text">
<div class="text-content">${generatedText}</div>
<div class="text-actions">
<button class="btn-minimal" onclick="copyToClipboard('${generatedText.replace(/'/g, "\\'")}')">
📋 Copier le texte
</button>
<button class="btn-minimal" onclick="regenerateText()">
🔄 Régénérer
</button>
</div>
</div>
</div>
<details class="result-details">
<summary>🔍 Détails techniques</summary>
<div class="result-json">${JSON.stringify(data, null, 2)}</div>
</details>
</div>
`;
}
function formatBatchResult(data) {
if (!data.results || data.results.length === 0) {
return "<em>Aucun résultat</em>";
}
const resultsHtml = data.results
.map((result, index) => {
return `
<div class="batch-item">
<div class="batch-item-header">Résultat ${index + 1}</div>
<div class="result-json">${JSON.stringify(result, null, 2)}</div>
</div>
`;
})
.join("");
return `
<div style="margin-bottom: 1rem;">
<strong>Résumé:</strong> ${data.processed_count} traités, ${data.failed_count} échecs
</div>
<div class="batch-results">
${resultsHtml}
</div>
`;
}
// Fonctions de traitement pour chaque endpoint
async function analyzeSentiment(event) {
event.preventDefault();
showLoading("sentimentResult");
const text = document.getElementById("sentimentText").value.trim();
const model = document.getElementById("sentimentModel").value;
if (!text) {
showResult("sentimentResult", { error: "Le texte est requis" }, true);
return;
}
try {
const data = { text };
if (model) data.model_name = model;
const result = await makeApiRequest("/sentiment", data);
showResult("sentimentResult", result);
} catch (error) {
showResult("sentimentResult", { error: error.message }, true);
}
}
async function analyzeNER(event) {
event.preventDefault();
showLoading("nerResult");
const text = document.getElementById("nerText").value.trim();
const model = document.getElementById("nerModel").value;
if (!text) {
showResult("nerResult", { error: "Le texte est requis" }, true);
return;
}
try {
const data = { text };
if (model) data.model_name = model;
const result = await makeApiRequest("/ner", data);
showResult("nerResult", result);
} catch (error) {
showResult("nerResult", { error: error.message }, true);
}
}
async function answerQuestion(event) {
event.preventDefault();
showLoading("qaResult");
const question = document.getElementById("qaQuestion").value.trim();
const context = document.getElementById("qaContext").value.trim();
const model = document.getElementById("qaModel").value;
if (!question || !context) {
showResult("qaResult", { error: "La question et le contexte sont requis" }, true);
return;
}
try {
const data = { question, context };
if (model) data.model_name = model;
const result = await makeApiRequest("/qa", data);
showResult("qaResult", result);
} catch (error) {
showResult("qaResult", { error: error.message }, true);
}
}
async function fillMask(event) {
event.preventDefault();
showLoading("fillmaskResult");
const text = document.getElementById("fillmaskText").value.trim();
const model = document.getElementById("fillmaskModel").value;
if (!text) {
showResult("fillmaskResult", { error: "Le texte est requis" }, true);
return;
}
if (!text.includes("[MASK]")) {
showResult("fillmaskResult", { error: "Le texte doit contenir [MASK]" }, true);
return;
}
try {
const data = { text };
if (model) data.model_name = model;
const result = await makeApiRequest("/fillmask", data);
showResult("fillmaskResult", result);
} catch (error) {
showResult("fillmaskResult", { error: error.message }, true);
}
}
async function moderateContent(event) {
event.preventDefault();
showLoading("moderationResult");
const text = document.getElementById("moderationText").value.trim();
const model = document.getElementById("moderationModel").value;
if (!text) {
showResult("moderationResult", { error: "Le texte est requis" }, true);
return;
}
try {
const data = { text };
if (model) data.model_name = model;
const result = await makeApiRequest("/moderation", data);
showResult("moderationResult", result);
} catch (error) {
showResult("moderationResult", { error: error.message }, true);
}
}
async function generateText(event) {
event.preventDefault();
showLoading("textgenResult");
const text = document.getElementById("textgenPrompt").value.trim();
const model = document.getElementById("textgenModel").value;
if (!text) {
showResult("textgenResult", { error: "Le prompt est requis" }, true);
return;
}
try {
const data = { text };
if (model) data.model_name = model;
const result = await makeApiRequest("/textgen", data);
showResult("textgenResult", result);
} catch (error) {
showResult("textgenResult", { error: error.message }, true);
}
}
async function processBatch() {
showLoading("batchResult");
const type = document.getElementById("batchType").value;
const textsInput = document.getElementById("batchTexts").value.trim();
if (!textsInput) {
showResult("batchResult", { error: "Les textes sont requis" }, true);
return;
}
const texts = textsInput
.split("\n")
.filter((line) => line.trim())
.map((line) => line.trim());
if (texts.length === 0) {
showResult("batchResult", { error: "Aucun texte valide fourni" }, true);
return;
}
try {
const data = { texts };
const result = await makeApiRequest(`/${type}/batch`, data);
showResult("batchResult", result);
} catch (error) {
showResult("batchResult", { error: error.message }, true);
}
}
// Exemples prédéfinis (en anglais pour optimiser la compatibilité avec les modèles)
const examples = {
sentiment: "I love this project! It's really well designed and useful for testing NLP APIs.",
ner: "Apple Inc. is an American multinational technology company headquartered in Cupertino, California. Tim Cook is the current CEO of Apple.",
qa: {
question: "What is the capital of France?",
context:
"France is a country located in Western Europe. Paris is the capital and largest city of France. The city is famous for the Eiffel Tower and the Louvre Museum."
},
fillmask: "The capital of France is [MASK].",
moderation: "This project is fantastic! Thank you for this excellent work.",
textgen: "Once upon a time, in a distant kingdom,",
batch: `I love this product!
This is really terrible.
Not bad at all.
Excellent work!
I hate all of this.`
};
function loadExample(type) {
const example = examples[type];
if (!example) return;
switch (type) {
case "sentiment":
document.getElementById("sentimentText").value = example;
break;
case "ner":
document.getElementById("nerText").value = example;
break;
case "qa":
document.getElementById("qaQuestion").value = example.question;
document.getElementById("qaContext").value = example.context;
break;
case "fillmask":
document.getElementById("fillmaskText").value = example;
break;
case "moderation":
document.getElementById("moderationText").value = example;
break;
case "textgen":
document.getElementById("textgenPrompt").value = example;
break;
case "batch":
document.getElementById("batchTexts").value = example;
break;
}
}
function loadExamplesData() {
// Cette fonction peut être étendue pour charger des exemples depuis une source externe
console.log("Exemples chargés");
}
// Fonctions utilitaires pour l'UX
function getSentimentInterpretation(sentiment, confidence) {
const confidenceLevel = confidence > 0.8 ? "très élevée" : confidence > 0.6 ? "élevée" : confidence > 0.4 ? "modérée" : "faible";
let interpretation = "";
if (sentiment?.toLowerCase() === "positive") {
interpretation = `😊 Le texte exprime un sentiment positif avec une confiance ${confidenceLevel}.`;
} else if (sentiment?.toLowerCase() === "negative") {
interpretation = `😔 Le texte exprime un sentiment négatif avec une confiance ${confidenceLevel}.`;
} else {
interpretation = `😐 Le sentiment du texte est neutre avec une confiance ${confidenceLevel}.`;
}
return `<div class="interpretation">${interpretation}</div>`;
}
function copyToClipboard(text) {
navigator.clipboard
.writeText(text)
.then(() => {
showNotification("📋 Texte copié dans le presse-papiers!", "success", 2000);
})
.catch(() => {
showNotification("❌ Impossible de copier le texte", "error", 2000);
});
}
function copyResultToClipboard(containerId) {
const container = document.getElementById(containerId);
const resultData = container.querySelector(".result-json");
if (resultData) {
copyToClipboard(resultData.textContent);
}
}
function toggleResultDetails(containerId) {
const container = document.getElementById(containerId);
const details = container.querySelector(".result-details");
if (details) {
details.open = !details.open;
}
}
function regenerateText() {
const currentPrompt = document.getElementById("textgenPrompt").value;
if (currentPrompt) {
const form = document.querySelector("#textgen form");
const event = new Event("submit");
generateText(event);
}
}
// Amélioration des fonctions de traitement avec feedback
async function analyzeSentiment(event) {
event.preventDefault();
showLoading("sentimentResult");
const text = document.getElementById("sentimentText").value.trim();
const model = document.getElementById("sentimentModel").value;
if (!text) {
showResult("sentimentResult", { error: "Le texte est requis pour l'analyse de sentiment" }, true);
return;
}
try {
showNotification("🔄 Analyse de sentiment en cours...", "info", 2000);
const data = { text };
if (model) data.model_name = model;
const result = await makeApiRequest("/sentiment", data);
showResult("sentimentResult", result);
showNotification("✅ Analyse de sentiment terminée!", "success", 3000);
} catch (error) {
showResult("sentimentResult", { error: error.message }, true);
showNotification("❌ Erreur lors de l'analyse de sentiment", "error", 4000);
}
}
async function analyzeNER(event) {
event.preventDefault();
showLoading("nerResult");
const text = document.getElementById("nerText").value.trim();
const model = document.getElementById("nerModel").value;
if (!text) {
showResult("nerResult", { error: "Le texte est requis pour la reconnaissance d'entités" }, true);
return;
}
try {
showNotification("🔄 Reconnaissance d'entités en cours...", "info", 2000);
const data = { text };
if (model) data.model_name = model;
const result = await makeApiRequest("/ner", data);
showResult("nerResult", result);
const entityCount = result.entities ? result.entities.length : 0;
showNotification(`${entityCount} entité(s) détectée(s)!`, "success", 3000);
} catch (error) {
showResult("nerResult", { error: error.message }, true);
showNotification("❌ Erreur lors de la reconnaissance d'entités", "error", 4000);
}
}
async function answerQuestion(event) {
event.preventDefault();
showLoading("qaResult");
const question = document.getElementById("qaQuestion").value.trim();
const context = document.getElementById("qaContext").value.trim();
const model = document.getElementById("qaModel").value;
if (!question || !context) {
showResult("qaResult", { error: "La question et le contexte sont requis" }, true);
return;
}
try {
showNotification("🔄 Recherche de réponse en cours...", "info", 2000);
const data = { question, context };
if (model) data.model_name = model;
const result = await makeApiRequest("/qa", data);
showResult("qaResult", result);
showNotification("✅ Réponse trouvée!", "success", 3000);
} catch (error) {
showResult("qaResult", { error: error.message }, true);
showNotification("❌ Erreur lors de la recherche de réponse", "error", 4000);
}
}