import math LEGEND_HTML = """
{inner_html}
" def _token_span(text, color_hex, tooltip): """Inline highlight (no pill padding/margin) so tokens flow as continuous text.""" r, g, b = hex_to_rgb(color_hex) brightness = (r * 299 + g * 587 + b * 114) / 1000 text_color = "black" if brightness > 128 else "white" safe_text = text.replace("&", "&").replace("<", "<").replace(">", ">") return ( f'' f'{safe_text}' ) def _wrap_flow(inner_html): """Box matching the semantic-entropy view: words flow and wrap inside it.""" return ( "No token probability data available.
" spans = "" for item in token_logprobs: prob = math.exp(item["logprob"]) color = prob_to_color(prob) tooltip = f"p={prob:.3f} logprob={item['logprob']:.3f}" spans += _token_span(item["token"], color, tooltip) return _wrap_flow(spans) def color_code_answer_html(answer_text, sp_to_entropy, sentence_parts): """Render answer with sentence-part spans colored by semantic-entropy confidence.""" if not sentence_parts or not sp_to_entropy or not answer_text: safe = answer_text.replace("&", "&").replace("<", "<").replace(">", ">") return f"{safe}
" # Find each sentence_part as a substring and record its position colored_parts = [] for sp in sentence_parts: if not sp: continue idx = answer_text.find(sp) if idx == -1: continue entropy = sp_to_entropy.get(sp, 0.0) confidence = math.exp(-entropy) color = prob_to_color(confidence) tooltip = f"entropy={entropy:.3f} confidence={confidence:.3f}" colored_parts.append((idx, idx + len(sp), sp, color, tooltip)) colored_parts.sort(key=lambda x: x[0]) html = "" pos = 0 for start, end, text, color, tooltip in colored_parts: if start > pos: gap = answer_text[pos:start] html += gap.replace("&", "&").replace("<", "<").replace(">", ">") html += _span(text, color, tooltip) pos = end if pos < len(answer_text): tail = answer_text[pos:] html += tail.replace("&", "&").replace("<", "<").replace(">", ">") return _wrap(html)