Add TeX Live API client and deployment configs

This commit is contained in:
Solaria
2026-03-14 23:29:13 +00:00
parent a9d10583bd
commit 1a627dc099
4 changed files with 227 additions and 0 deletions
+35
View File
@@ -0,0 +1,35 @@
# TeX Live API
Lightweight LaTeX compilation API.
## Setup
```bash
# Install dependencies
pip install flask
# Install LaTeX
# macOS
brew install texlive
# Ubuntu
sudo apt install texlive-xelatex
# Run
python texlive_api.py
```
## Usage
```bash
# Compile
curl -X POST http://localhost:8080/compile \
-H "Content-Type: application/json" \
-d '{"tex": "\\documentclass{article}\\n\\begin{document}\\nHello\\n\\end{document}"}'
```
## Docker
```bash
docker-compose up -d
```
@@ -0,0 +1,18 @@
# TeX Live API Service
# Run with: docker-compose up -d
version: '3.8'
services:
texlive-api:
image: alex11br/texlive-api
ports:
- "8080:8080"
volumes:
- texlive-cache:/root/.texlive
environment:
- TEXLIVE_ENGINE=xelatex
- MAX_TIMEOUT=180
volumes:
texlive-cache:
+74
View File
@@ -0,0 +1,74 @@
# Simple TeX Live API Service
# Run: python texlive_api.py
from flask import Flask, request, jsonify, send_file
import subprocess
import tempfile
import os
import base64
from pathlib import Path
app = Flask(__name__)
ALLOWED_ENGINES = ['xelatex', 'pdflatex', 'lualatex']
MAX_TIMEOUT = 180
@app.route('/health', methods=['GET'])
def health():
return jsonify({"status": "ok"})
@app.route('/compile', methods=['POST'])
def compile():
data = request.get_json()
tex_content = data.get('tex')
engine = data.get('engine', 'xelatex')
timeout = min(data.get('timeout', MAX_TIMEOUT), MAX_TIMEOUT)
if not tex_content:
return jsonify({"error": "No tex content provided"}), 400
if engine not in ALLOWED_ENGINES:
return jsonify({"error": f"Invalid engine: {engine}"}), 400
# Create temp directory
with tempfile.TemporaryDirectory() as tmpdir:
tex_file = Path(tmpdir) / "input.tex"
tex_file.write_text(tex_content)
# Compile
try:
result = subprocess.run(
[engine, "-interaction=nonstopmode", tex_file.name],
cwd=tmpdir,
capture_output=True,
timeout=timeout,
)
pdf_file = tex_file.with_suffix('.pdf')
if pdf_file.exists():
pdf_data = base64.b64encode(pdf_file.read_bytes()).decode()
return jsonify({
"success": True,
"pdf": pdf_data,
"engine": engine,
})
else:
# Return error log
return jsonify({
"success": False,
"error": "PDF not generated",
"log": result.stderr.decode()[-2000:],
}), 500
except subprocess.TimeoutExpired:
return jsonify({"error": "Compilation timeout"}), 504
except Exception as e:
return jsonify({"error": str(e)}), 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
+100
View File
@@ -0,0 +1,100 @@
"""TeX Live API Client for Opus Orchestrator.
Compiles LaTeX via remote TeX Live API service.
"""
import json
import base64
from typing import Optional, Dict, Any
from pathlib import Path
class TeXLiveClient:
"""Client for TeX Live API service."""
def __init__(self, base_url: str = "http://localhost:8080"):
"""Initialize TeX Live client.
Args:
base_url: Base URL of TeX Live API service
"""
self.base_url = base_url.rstrip("/")
def compile(
self,
tex_content: str,
engine: str = "xelatex",
timeout: int = 120,
) -> Dict[str, Any]:
"""Compile LaTeX via API.
Args:
tex_content: LaTeX source code
engine: LaTeX engine (xelatex, pdflatex, lualatex)
timeout: Compilation timeout in seconds
Returns:
Compilation result with PDF data
"""
import requests
response = requests.post(
f"{self.base_url}/compile",
json={
"tex": tex_content,
"engine": engine,
"timeout": timeout,
},
timeout=timeout + 10,
)
if response.status_code != 200:
raise RuntimeError(f"TeX Live API error: {response.text}")
result = response.json()
if result.get("error"):
raise RuntimeError(f"LaTeX compilation failed: {result['error']}")
return result
def compile_file(
self,
tex_path: str,
engine: str = "xelatex",
) -> bytes:
"""Compile LaTeX file via API.
Args:
tex_path: Path to .tex file
engine: LaTeX engine
Returns:
Compiled PDF as bytes
"""
tex_content = Path(tex_path).read_text()
result = self.compile(tex_content, engine)
# Decode PDF from base64
pdf_data = base64.b64decode(result["pdf"])
return pdf_data
def compile_via_texlive(
tex_content: str,
base_url: str = "http://localhost:8080",
engine: str = "xelatex",
) -> bytes:
"""Convenience function to compile LaTeX via TeX Live API.
Args:
tex_content: LaTeX source
base_url: TeX Live API URL
engine: LaTeX engine
Returns:
Compiled PDF bytes
"""
client = TeXLiveClient(base_url)
result = client.compile(tex_content, engine)
return base64.b64decode(result["pdf"])