#!/usr/bin/env python3 import html import json import sys import textwrap from pathlib import Path try: import fire except ImportError: fire = None try: import markdown except ImportError: print( "Error: The 'markdown' library is required. Please install it:", file=sys.stderr ) print("pip install markdown", file=sys.stderr) sys.exit(1) # --- Configuration --- # Assumes template.html is in the same directory as the script SCRIPT_DIR = Path(__file__).parent.resolve() TEMPLATE_FILE = SCRIPT_DIR / "template.html" # --- Helper Functions --- def get_score_class(score): """Determines the CSS class based on the score.""" try: val = float(score) if val > 0: return "reward-positive" elif val < 0: return "reward-negative" else: return "reward-zero" except (ValueError, TypeError): return "" # No specific class if score is not numeric def create_html_for_group(group_data, index): """Generates HTML snippet for a single group.""" messages = group_data.get("messages", []) scores = group_data.get("scores", []) if len(messages) != len(scores): print( f"Warning: Group {index} has mismatched lengths for messages " f"({len(messages)}) and scores ({len(scores)}). " f"Skipping items.", file=sys.stderr, ) min_len = min(len(messages), len(scores)) messages = messages[:min_len] scores = scores[:min_len] items_html = "" for i, (msg, score) in enumerate(zip(messages, scores)): # Handle both string and nested message formats if isinstance(msg, str): # Simple string format (original behavior) content = msg elif isinstance(msg, list): # Nested conversation format: List[Message] where Message is a dict # Format each message with its role parts = [] for m in msg: if isinstance(m, dict): role = m.get("role", "unknown") content_text = m.get("content", "") parts.append(f"**{role.capitalize()}**: {content_text}") else: # Fallback for unexpected format parts.append(str(m)) content = "\n\n".join(parts) else: # Fallback for any other type content = str(msg) rendered_markdown = markdown.markdown( content, extensions=["fenced_code", "tables", "nl2br"] ) score_class = get_score_class(score) item_id = f"group-{index}-item-{i}" items_html += textwrap.dedent(f"""\
Reward: {html.escape(str(score))}
No data to display. Input file might be empty or contain invalid data.
" else: groups_content = "\n".join(all_groups_html) # Prepare title title = f"Rendered Messages - {input_filepath.name}" # Populate the main template read from the file try: final_html = html_template_content.format( title=html.escape(title), groups_html=groups_content ) except KeyError as e: print( f"Error: Template file '{TEMPLATE_FILE}' is missing a required placeholder: {{{e}}}", file=sys.stderr, ) sys.exit(1) # --- Write the output HTML file --- try: with open(output_filepath, "w", encoding="utf-8") as f: f.write(final_html) print(f"Successfully generated HTML file: {output_filepath.absolute()}") except IOError as e: print(f"Error writing output file {output_filepath}: {e}", file=sys.stderr) sys.exit(1) # --- Command Line Interface Handling --- if __name__ == "__main__": if fire is not None: fire.Fire(generate_html) else: print("`fire` is not installed.")