import logging import os import time from huggingface_hub import InferenceClient from fpdf import FPDF from .prompts import REVIEWER_DIRECTION logger = logging.getLogger(__name__) class Reviewer: def __init__(self, model_name: str, hf_key: str): self.model_name = model_name self.client = InferenceClient(token=hf_key, timeout=120) def review(self, report: str) -> str: logger.info(f"Reviewing and polishing the final report using {self.model_name}...") try: response = self.client.chat_completion( model=self.model_name, messages=[ {"role": "system", "content": REVIEWER_DIRECTION}, {"role": "user", "content": f"Please review and polish this research report:\n\n{report}"} ], max_tokens=4000, temperature=0.7 ) polished_report = response.choices[0].message.content # Clean up potential tags if "" in polished_report and "" in polished_report: polished_report = polished_report.split("")[-1].strip() elif "" in polished_report: polished_report = polished_report.split("")[-1].strip() return polished_report except Exception as e: logger.error(f"Error during report review: {e}") return report # Fallback to original report on error def generate_pdf(self, markdown_text: str, output_path: str): logger.info(f"Generating PDF at {output_path}...") try: pdf = FPDF() pdf.set_auto_page_break(auto=True, margin=15) pdf.add_page() # Basic styling setup pdf.set_font("helvetica", "B", 16) pdf.cell(0, 10, "Research Report", ln=True, align="C") pdf.ln(5) pdf.set_font("helvetica", "", 12) def safe_encode(text): # 1. Handle encoding calls for standard fonts (latin-1) text = text.encode('latin-1', 'replace').decode('latin-1') # 2. Break very long words to prevent FPDF error "Not enough horizontal space" # A4 width is ~190mm usable. Font size 12. ~90 chars is a safe limit for a single word. words = text.split(' ') processed_words = [] for word in words: if len(word) > 85: # Split string into chunks chunks = [word[i:i+85] for i in range(0, len(word), 85)] processed_words.append(" ".join(chunks)) else: processed_words.append(word) return " ".join(processed_words) # Simple Markdown cleanup clean_text = markdown_text.replace("# ", "").replace("## ", "").replace("### ", "").replace("**", "") # Split by lines and add to PDF for line in clean_text.split("\n"): safe_line = safe_encode(line) if safe_line.strip(): pdf.multi_cell(0, 6, safe_line) # Reduced line height slightly for better readability pdf.ln(2) # frequent small breaks else: pdf.ln(5) # Larger break for paragraph separation pdf.output(output_path) logger.info("PDF generated successfully.") return True except Exception as e: logger.error(f"Failed to generate PDF: {e}") return False