writer / compiler.py
abidlabs's picture
abidlabs HF Staff
changes
6981282
import hashlib
import subprocess
import tempfile
from dataclasses import dataclass
from functools import lru_cache
from pathlib import Path
@dataclass
class CompilationResult:
success: bool
pdf_data: bytes = b""
error_log: str = ""
def _compute_hash(content: str) -> str:
return hashlib.md5(content.encode()).hexdigest()
@lru_cache(maxsize=128)
def _cached_compile(content_hash: str, latex_content: str) -> tuple[bool, bytes, str]:
with tempfile.TemporaryDirectory() as tmpdir:
tmppath = Path(tmpdir)
tex_file = tmppath / "document.tex"
tex_file.write_text(latex_content)
try:
result = subprocess.run(
[
"pdflatex",
"-interaction=nonstopmode",
"-halt-on-error",
"-output-directory", str(tmppath),
str(tex_file)
],
capture_output=True,
text=True,
timeout=30
)
pdf_file = tmppath / "document.pdf"
if pdf_file.exists():
return (True, pdf_file.read_bytes(), "")
else:
log_file = tmppath / "document.log"
error_log = log_file.read_text() if log_file.exists() else result.stdout
return (False, b"", _extract_errors(error_log))
except subprocess.TimeoutExpired:
return (False, b"", "Compilation timed out after 30 seconds")
except FileNotFoundError:
return (False, b"", "pdflatex not found. Please install TeX Live.")
except Exception as e:
return (False, b"", f"Compilation error: {str(e)}")
def _extract_errors(log: str) -> str:
lines = log.split("\n")
error_lines = []
capture = False
for line in lines:
if line.startswith("!"):
capture = True
if capture:
error_lines.append(line)
if line.strip() == "" and error_lines:
capture = False
if len(error_lines) > 50:
break
return "\n".join(error_lines) if error_lines else log[-2000:]
def compile_latex(latex_content: str) -> CompilationResult:
content_hash = _compute_hash(latex_content)
success, pdf_data, error_log = _cached_compile(content_hash, latex_content)
return CompilationResult(success=success, pdf_data=pdf_data, error_log=error_log)