Spaces:
Running
Running
document.getElementById("analyze-button").addEventListener("click", async () => { | |
const text = document.getElementById("input-text").value; | |
const model = document.getElementById("model-select").value; | |
// Show loading state | |
const analyzeButton = document.getElementById("analyze-button"); | |
const buttonSpinner = analyzeButton.querySelector(".button-spinner"); | |
analyzeButton.classList.add("loading"); | |
buttonSpinner.classList.add("visible"); | |
analyzeButton.disabled = true; | |
try { | |
const response = await fetch("/analyze", { | |
method: "POST", | |
headers: { | |
"Content-Type": "application/json" | |
}, | |
body: JSON.stringify({ text, model }) | |
}); | |
const data = await response.json(); | |
const coloredTextDiv = document.getElementById("colored-text"); | |
coloredTextDiv.innerHTML = ""; | |
// Always add the first token | |
const firstToken = data.tokens[0]; | |
const firstTokenSpan = document.createElement("span"); | |
firstTokenSpan.classList.add("token"); | |
// Handle special tokens and regular tokens differently | |
if (firstToken === "<s>" || firstToken === "<|endoftext|>") { | |
firstTokenSpan.style.backgroundColor = "#808080"; // Gray for special tokens | |
firstTokenSpan.textContent = "■"; | |
tippy(firstTokenSpan, { | |
content: "<div><strong>Beginning of Sequence</strong></div>", | |
allowHTML: true, | |
theme: 'custom', | |
placement: 'top', | |
interactive: true | |
}); | |
} else { | |
// Handle regular first token | |
firstTokenSpan.style.backgroundColor = "#808080"; // or any other color you prefer | |
firstTokenSpan.textContent = firstToken; | |
tippy(firstTokenSpan, { | |
content: `<div><strong>First Token</strong></div>`, | |
allowHTML: true, | |
theme: 'custom', | |
placement: 'top', | |
interactive: true | |
}); | |
} | |
coloredTextDiv.appendChild(firstTokenSpan); | |
for (let index = 0; index < data.log_probs.length; index++) { | |
const token = data.tokens[index + 1]; | |
const percentile = data.percentiles[index]; | |
const logProb = data.log_probs[index]; | |
const topKPredictions = data.top_k_predictions[index]; | |
const color = getColor(data.log_probs, logProb); | |
const tokenSpan = document.createElement("span"); | |
tokenSpan.classList.add("token"); | |
tokenSpan.style.backgroundColor = color; | |
let displayToken = token; | |
let specialTokenDescription = ""; | |
// Enhanced special token handling | |
if (token === "<s>" || token === "<|endoftext|>") { | |
displayToken = "■"; | |
specialTokenDescription = "Beginning of Sequence"; | |
} else if (token === "</s>" || token === "<|endoftext|>") { | |
displayToken = "■"; | |
specialTokenDescription = "End of Sequence"; | |
} else if (token === "<0x0A>") { | |
displayToken = "■"; | |
specialTokenDescription = "Newline"; | |
} else if (token.startsWith("<") && token.endsWith(">")) { | |
displayToken = "■"; | |
specialTokenDescription = "Special Token: " + token; | |
} else { | |
// Clean up GPT-2 style tokens (Ġ and Ċ) | |
displayToken = displayToken | |
.replace(/\u2581/g, " ") // Replace underscore token | |
.replace(/Ġ/g, " ") // Replace GPT-2 space token | |
.replace(/Ċ/g, "\n"); // Replace GPT-2 newline token | |
} | |
tokenSpan.textContent = displayToken; | |
let tooltipContent = ""; | |
if (specialTokenDescription) { | |
tooltipContent += `<div style="font-weight: bold; margin-bottom: 8px;">${specialTokenDescription}</div>`; | |
} | |
tooltipContent += `<div style="font-weight: bold; margin-bottom: 4px;">Top 5 Predictions:</div>`; | |
topKPredictions.forEach(pred => { | |
let predToken = pred.token; | |
if (predToken === "<0x0A>") { | |
predToken = "\\n"; | |
} else if (predToken.startsWith("<") && predToken.endsWith(">")) { | |
predToken = "[SPECIAL]"; | |
} else { | |
predToken = predToken | |
.replace(/\u2581/g, " ") | |
.replace(/Ġ/g, " ") | |
.replace(/Ċ/g, "\n"); | |
} | |
tooltipContent += `<div style="padding-left: 8px;">${predToken}: ${pred.log_prob.toFixed(4)}</div>`; | |
}); | |
tooltipContent += `<div style="margin-top: 8px; border-top: 1px solid #555; padding-top: 8px;"> | |
<div><strong>Stats:</strong></div> | |
<div style="padding-left: 8px;">Percentile: ${percentile.toFixed(2)}</div> | |
<div style="padding-left: 8px;">Log-Likelihood: ${logProb.toFixed(4)}</div> | |
</div>`; | |
tippy(tokenSpan, { | |
content: tooltipContent, | |
allowHTML: true, | |
theme: 'custom', | |
placement: 'top', | |
interactive: true | |
}); | |
coloredTextDiv.appendChild(tokenSpan); | |
if (token === "<0x0A>") { | |
coloredTextDiv.appendChild(document.createElement("br")); | |
} | |
} | |
document.getElementById("joint-log-likelihood").textContent = data.joint_log_likelihood.toFixed(4); | |
document.getElementById("average-log-likelihood").textContent = data.average_log_likelihood.toFixed(4); | |
} catch (error) { | |
console.error("Error during analysis:", error); | |
alert("An error occurred during analysis. Please try again."); | |
} finally { | |
// Hide loading state | |
analyzeButton.classList.remove("loading"); | |
buttonSpinner.classList.remove("visible"); | |
analyzeButton.disabled = false; | |
} | |
}); | |
function getColor(allLogProbs, currentLogProb) { | |
const minLogProb = Math.min(...allLogProbs); | |
const maxLogProb = Math.max(...allLogProbs); | |
// Normalize to 0-1 range | |
let normalizedLogProb = (currentLogProb - minLogProb) / (maxLogProb - minLogProb); | |
normalizedLogProb = Math.max(0, Math.min(1, normalizedLogProb)); // Clamp | |
// Optional: Apply a power transformation (adjust the exponent as needed) | |
const power = 0.7; // Example: Less than 1 emphasizes differences at lower end | |
normalizedLogProb = Math.pow(normalizedLogProb, power); | |
const hue = normalizedLogProb * 120; // 0 (red) to 120 (green) | |
return `hsl(${hue}, 100%, 50%)`; | |
} |