Files
feadulta/scripts/translate_haiku.py

88 lines
3.5 KiB
Python

#!/usr/bin/env python3
"""Traduce ES->EN con Claude Haiku 4.5 vía API directa. Prueba de coste/calidad.
Lee la ANTHROPIC_API_KEY de portfolio-tracker/.env (la misma que usa el
portfolio tracker para trade setups). Reporta tokens reales de la API.
"""
import os
import re
import sys
# Cargar API key del .env de portfolio-tracker sin pisar el entorno existente.
ENV_PATH = "/home/rafa/portfolio-tracker/.env"
if "ANTHROPIC_API_KEY" not in os.environ:
for line in open(ENV_PATH):
line = line.strip()
if line.startswith("ANTHROPIC_API_KEY="):
os.environ["ANTHROPIC_API_KEY"] = line.split("=", 1)[1].strip().strip('"').strip("'")
break
import anthropic
MODEL = "claude-haiku-4-5"
LANG_NAMES = {"en": "English", "fr": "French (français)",
"it": "Italian (italiano)", "pt": "Portuguese (português)"}
def system_prompt(lang: str) -> str:
target = LANG_NAMES[lang]
return (
f"Eres un traductor profesional de textos religiosos cristianos "
f"(espiritualidad y teología católica). Traduce del español al {target}. "
f"REGLAS ESTRICTAS:\n"
f"1. Conserva EXACTAMENTE el marcado HTML (etiquetas y atributos) y los "
f"shortcodes entre [ ] y {{ }}. No los traduzcas ni los reordenes.\n"
f"2. NO traduzcas las referencias bíblicas ni sus abreviaturas "
f"(p.ej. 'Jn 3, 16', 'Mt 5'). Déjalas idénticas.\n"
f"3. Conserva los nombres propios de persona y lugar (salvo exónimos establecidos).\n"
f"4. Términos litúrgicos correctos (p.ej. 'Cuaresma' = Lent/Carême/Quaresima/Quaresma; "
f"NO inventes palabras).\n"
f"5. Traducción FIEL: no resumas, no añadas, no comentes.\n"
f"6. Devuelve SOLO la traducción entre las marcas <<<INI>>> y <<<FIN>>>, sin nada más."
)
def extract(text: str) -> str:
# Coge el bloque <<<INI>>>...<<<FIN>>> de contenido MÁS LARGO (robusto al
# bug del runner local, donde el modelo a veces re-menciona las marcas).
blocks = re.findall(r"<<<INI>>>(.*?)<<<FIN>>>", text, re.S)
out = max(blocks, key=len).strip() if blocks else text.strip()
out = re.sub(r"^```[a-z]*\n?", "", out)
out = re.sub(r"\n?```$", "", out)
return out.strip()
def translate(text: str, lang: str, *, is_title: bool = False) -> tuple[str, object]:
client = anthropic.Anthropic()
kind = "el TÍTULO" if is_title else "el texto"
user = (
f"Traduce {kind} que va entre las marcas. "
f"Debe quedar en {LANG_NAMES[lang]} de forma natural.\n"
f"<<<INI>>>{text}<<<FIN>>>"
)
max_tokens = max(1024, int(len(text) * 0.9))
resp = client.messages.create(
model=MODEL,
max_tokens=min(max_tokens, 16000),
system=system_prompt(lang),
messages=[{"role": "user", "content": user}],
)
body = "".join(b.text for b in resp.content if b.type == "text")
return extract(body), resp.usage
if __name__ == "__main__":
path = sys.argv[1] if len(sys.argv) > 1 else "/tmp/orig_es.html"
lang = sys.argv[2] if len(sys.argv) > 2 else "en"
src = open(path).read()
out, usage = translate(src, lang)
open("/tmp/trad_haiku.html", "w").write(out)
cost = usage.input_tokens / 1e6 * 1.0 + usage.output_tokens / 1e6 * 5.0
print(f"MODEL={MODEL} lang={lang}")
print(f"input_tokens={usage.input_tokens} output_tokens={usage.output_tokens}")
print(f"coste_articulo=${cost:.5f}")
print(f"chars_in={len(src)} chars_out={len(out)}")
print("--- primeras 500 car ---")
print(out[:500])