ai-log-job: translate strategic log for active non-en languages

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Giorgio Gilestro 2026-05-27 16:57:06 +02:00
parent e190d0e35b
commit e4982cdc04
2 changed files with 181 additions and 3 deletions

View file

@ -17,8 +17,9 @@ from app.jobs._market_context import (
month_spend,
recent_headlines_by_bucket,
)
from app.models import AICall, JobRun, StrategicLog
from app.models import AICall, JobRun, StrategicLog, StrategicLogTranslation, User
from app.services.cadence import DEFAULT_POLICY
from app.services.i18n import ACTIVE_LANGUAGES
from app.services.openrouter import (
PROMPT_VERSION,
active_model,
@ -27,6 +28,58 @@ from app.services.openrouter import (
call_llm,
llm_configured,
)
from app.services.translation import translate
async def translate_log_for_active_languages(session, log_id: int) -> None:
"""Fan out per-language translations for the strategic log identified
by ``log_id``.
Reads ``users.lang`` (deduplicated, restricted to ACTIVE_LANGUAGES
minus English), one translation call per language in parallel via
``asyncio.gather``, persists each successful result as a
``StrategicLogTranslation`` row. Per-language failures are logged
but never raise the strategic log itself is already committed at
this point and translation is a best-effort enhancement.
The job orchestrator calls this AFTER the English ``StrategicLog``
row is committed; pass the row's ``id`` in.
"""
target_langs = sorted({l for l in ACTIVE_LANGUAGES if l != "en"})
if not target_langs:
return
active_langs = (await session.execute(
select(User.lang).distinct().where(User.lang.in_(target_langs))
)).scalars().all()
if not active_langs:
return
log_row = await session.get(StrategicLog, log_id)
if log_row is None:
log.warning("log.translate.missing_log", log_id=log_id)
return
async with httpx.AsyncClient(follow_redirects=True, timeout=60) as client:
results = await asyncio.gather(*[
translate(client, log_row.content, lang)
for lang in active_langs
], return_exceptions=True)
for lang, result in zip(active_langs, results):
if isinstance(result, Exception):
log.warning("log.translate.failed", lang=lang, log_id=log_id,
error=str(result)[:200])
continue
translated_md, llm_result = result
session.add(StrategicLogTranslation(
log_id=log_id, lang=lang,
content_md=translated_md,
generated_at=utcnow(),
llm_model=llm_result.model,
llm_cost_usd=llm_result.cost_usd,
))
await session.commit()
async def run() -> None:
@ -126,7 +179,7 @@ async def run() -> None:
tone=tone, analysis=analysis, error=str(e)[:200])
continue
session.add(StrategicLog(
slog = StrategicLog(
generated_at=utcnow(),
model=result.model,
anchor_date=anchor,
@ -137,7 +190,8 @@ async def run() -> None:
prompt_tokens=result.prompt_tokens,
completion_tokens=result.completion_tokens,
cost_usd=result.cost_usd,
))
)
session.add(slog)
session.add(AICall(
model=result.model,
prompt_tokens=result.prompt_tokens,
@ -146,6 +200,7 @@ async def run() -> None:
status="ok",
))
await session.commit()
await translate_log_for_active_languages(session, slog.id)
written += 1
log.info("ai_log.variant_done",
tone=tone, analysis=analysis,