digest: record AICall rows so digest LLM spend counts toward monthly cap

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Giorgio Gilestro 2026-05-25 23:17:14 +02:00
parent 1fdf7d6a23
commit ce4b19dbb8

View file

@ -69,9 +69,15 @@ async def _already_sent_today(session, user_id: int, kind: str, today: datetime)
return (await session.execute(stmt)).first() is not None return (await session.execute(stmt)).first() is not None
async def _generate_variants(client, kind: str, ctx: dict) -> dict[str, str]: async def _generate_variants(session, client, kind: str, ctx: dict) -> dict[str, str]:
"""Returns {tone: html_content}. Missing tone means generation failed """Returns {tone: html_content}. Missing tone means generation failed
for that variant skip recipients on that tone.""" for that variant skip recipients on that tone.
Persists an AICall row per attempt so digest LLM spend counts toward
the monthly cost cap on subsequent runs."""
from app.models import AICall
from app.services.openrouter import active_model
builder = build_weekly_digest_prompt if kind == "weekly" else build_daily_digest_prompt builder = build_weekly_digest_prompt if kind == "weekly" else build_daily_digest_prompt
out: dict[str, str] = {} out: dict[str, str] = {}
for tone in ("NOVICE", "INTERMEDIATE"): for tone in ("NOVICE", "INTERMEDIATE"):
@ -83,10 +89,23 @@ async def _generate_variants(client, kind: str, ctx: dict) -> dict[str, str]:
{"role": "user", "content": usr}], {"role": "user", "content": usr}],
) )
out[tone] = result.content out[tone] = result.content
session.add(AICall(
model=result.model,
prompt_tokens=result.prompt_tokens,
completion_tokens=result.completion_tokens,
cost_usd=result.cost_usd,
status="ok",
))
await session.commit()
log.info("digest.variant_ok", kind=kind, tone=tone, log.info("digest.variant_ok", kind=kind, tone=tone,
prompt_tokens=result.prompt_tokens, prompt_tokens=result.prompt_tokens,
completion_tokens=result.completion_tokens) completion_tokens=result.completion_tokens)
except Exception as e: except Exception as e:
session.add(AICall(
model=active_model(), status="error",
error=f"{kind}/{tone}: {str(e)[:480]}",
))
await session.commit()
log.error("digest.variant_failed", kind=kind, tone=tone, log.error("digest.variant_failed", kind=kind, tone=tone,
error=str(e)[:200]) error=str(e)[:200])
return out return out
@ -173,7 +192,7 @@ async def run() -> None:
) )
async with httpx.AsyncClient(follow_redirects=True) as client: async with httpx.AsyncClient(follow_redirects=True) as client:
variants = await _generate_variants(client, kind, ctx) variants = await _generate_variants(session, client, kind, ctx)
if not variants: if not variants:
log.warning("digest.all_variants_failed", kind=kind) log.warning("digest.all_variants_failed", kind=kind)