Files
video-gen/video-gen-api/app/cmd/reconcile_llm_billing.py
T
2026-08-11 09:24:18 +08:00

39 lines
1.8 KiB
Python

from __future__ import annotations
import asyncio
import json
from sqlalchemy import text
from app.models.base import async_session
from app.services.credit.utils import utc_now
async def amain() -> int:
async with async_session() as db:
rows = (await db.execute(text("""
SELECT e.id, e.total_call_count, e.successful_call_count, e.failed_call_count,
e.total_input_tokens, e.total_output_tokens, e.total_tokens,
COUNT(a.id) AS actual_calls,
COALESCE(SUM(CASE WHEN a.status = 'succeeded' THEN 1 ELSE 0 END), 0) AS actual_success,
COALESCE(SUM(CASE WHEN a.status IN ('failed','timeout','unknown') THEN 1 ELSE 0 END), 0) AS actual_failed,
COALESCE(SUM(a.input_tokens), 0) AS actual_input,
COALESCE(SUM(a.output_tokens), 0) AS actual_output,
COALESCE(SUM(a.total_tokens), 0) AS actual_total
FROM llm_billing_executions e
LEFT JOIN llm_call_attempts a ON a.billing_execution_id = e.id
GROUP BY e.id
HAVING e.total_call_count <> COUNT(a.id)
OR e.successful_call_count <> COALESCE(SUM(CASE WHEN a.status = 'succeeded' THEN 1 ELSE 0 END), 0)
OR e.failed_call_count <> COALESCE(SUM(CASE WHEN a.status IN ('failed','timeout','unknown') THEN 1 ELSE 0 END), 0)
OR e.total_tokens <> COALESCE(SUM(a.total_tokens), 0)
ORDER BY e.id
LIMIT 1000
"""))).mappings().all()
print(json.dumps({"checked_at": utc_now().isoformat(), "mismatch_count": len(rows), "items": [dict(r) for r in rows]}, ensure_ascii=False, indent=2, default=str))
return 0 if not rows else 2
if __name__ == "__main__":
raise SystemExit(asyncio.run(amain()))