import markdown from pygments.formatters import HtmlFormatter import re import hashlib from latex2mathml.converter import convert def format_latex(text): """ Convert a Markdown string containing LaTeX formulas to HTML with MathML. Handles: - Inline math: $...$ or \( ... \) - Display math: $$...$$ or \[ ... \] Automatically converts `aligned` to `array` and wraps display formulas in for spacing without breaking Markdown lists. Args: text (str): Markdown string with LaTeX. convert (callable): Function that converts LaTeX string to MathML. Returns: str: HTML string with MathML replacing LaTeX. """ # Pattern for LaTeX delimiters pattern = r'(\$\$.*?\$\$|\\\[.*?\\\]|\\\(.*?\\\)|\$.*?\$)' def replace_latex(match): latex_text = match.group() # Determine delimiter type is_display = False if latex_text.startswith('$$') and latex_text.endswith('$$'): latex_text_clean = latex_text[2:-2].strip() is_display = True elif latex_text.startswith(r'\[') and latex_text.endswith(r'\]'): latex_text_clean = latex_text[2:-2].strip() is_display = True elif latex_text.startswith(r'\(') and latex_text.endswith(r'\)'): latex_text_clean = latex_text[2:-2].strip() elif latex_text.startswith('$') and latex_text.endswith('$'): latex_text_clean = latex_text[1:-1].strip() else: latex_text_clean = latex_text.strip() # Replace aligned -> array latex_text_clean = re.sub( r'\\begin\{aligned\}', r'\\begin{array}{l}', latex_text_clean ) latex_text_clean = re.sub( r'\\end\{aligned\}', r'\\end{array}', latex_text_clean ) # Convert to MathML try: mathml = convert(latex_text_clean) if is_display: # Use span with display:block to preserve Markdown list numbering mathml = f'{mathml}' return mathml except Exception as e: print(f"Warning: failed to convert LaTeX: {latex_text_clean}\nError: {e}") return latex_text_clean # Replace all LaTeX with MathML html_with_mathml = re.sub(pattern, replace_latex, text, flags=re.DOTALL) return html_with_mathml def format_history(history): """Format conversation history as HTML with side-by-side responses.""" # Generate Pygments CSS for syntax highlighting pygments_css = HtmlFormatter(style='monokai').get_style_defs('.codehilite') html = f'''
''' for turn in history: # User message - escape HTML but preserve line breaks user_msg = turn["user"].replace("&", "&").replace("<", "<").replace(">", ">").replace("\n", "
") html += f'
You: {user_msg}
' # Determine opacity based on highlight opacity_a = "1.0" opacity_b = "1.0" border_a = "2px solid var(--block-border-color)" border_b = "2px solid var(--block-border-color)" if turn["highlight"] == "left": opacity_b = "0.3" border_a = "3px solid #4CAF50" elif turn["highlight"] == "right": opacity_a = "0.3" border_b = "3px solid #4CAF50" elif turn["highlight"] == "random_left": opacity_b = "0.3" border_a = "3px solid #4C50AF" elif turn["highlight"] == "random_right": # turn["highlight"] == "light_right" opacity_a = "0.3" border_b = "3px solid #4C50AF" response_a_text_with_mathml = format_latex(turn["response_a"]) response_a_html = markdown.markdown( response_a_text_with_mathml, extensions=['fenced_code', 'tables', 'codehilite'], extension_configs={'codehilite': {'guess_lang': True, 'linenums': False}} ) response_b_text_with_mathml = format_latex(turn["response_b"]) response_b_html = markdown.markdown( response_b_text_with_mathml, extensions=['fenced_code', 'tables', 'codehilite'], extension_configs={'codehilite': {'guess_lang': True, 'linenums': False}} ) # Responses side by side html += f'''
Assistant A
{response_a_html}
Assistant B
{response_b_html}
''' if turn["highlight"] == "random_left": html += '
Note: you did not specify a preference for either assistant, so in subsequent turns, Assistant A will be used as context.
' elif turn["highlight"] == "random_right": html += '
Note: you did not specify a preference for either assistant, so in subsequent turns, Assistant B will be used as context.
' html += '
' return html