#!/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 <<>> y <<>>, sin nada más." ) def extract(text: str) -> str: # Coge el bloque <<>>...<<>> de contenido MÁS LARGO (robusto al # bug del runner local, donde el modelo a veces re-menciona las marcas). blocks = re.findall(r"<<>>(.*?)<<>>", 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"<<>>{text}<<>>" ) 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])