feat: trackeo de coste por llamada Claude — tabla api_usage + /costs
Build & Deploy ResearchOwl / build-and-push (push) Successful in 6s

This commit is contained in:
ChemaVX
2026-05-03 20:06:06 +00:00
parent 65917518ce
commit b33ae202b8
4 changed files with 129 additions and 8 deletions
+53
View File
@@ -78,6 +78,7 @@ async def cmd_start(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
"`/generate <type>` — Generate output (podcast|blog|report|thread)\n"
"`/sources` — List all sources found\n"
"`/outputs` — List generated outputs\n"
"`/costs` — Show API usage costs\n"
"`/cancel` — Cancel current research\n"
"`/help` — Show this message",
parse_mode=ParseMode.MARKDOWN
@@ -419,6 +420,57 @@ async def cmd_outputs(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
await db_conn.close()
async def cmd_costs(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
if not is_authorized(update.effective_user.id):
return
chat_id = update.effective_chat.id
db_conn = await get_db()
db = ResearchDB(db_conn)
try:
cursor = await db_conn.execute(
"SELECT * FROM research_sessions WHERE telegram_chat_id = ? ORDER BY created_at DESC LIMIT 1",
(chat_id,)
)
row = await cursor.fetchone()
if not row:
await update.message.reply_text("No sessions found.")
return
session_id = row["id"]
topic = row["topic"]
by_type = {r["call_type"]: r for r in await db.get_usage_stats(session_id)}
totals = await db.get_total_usage_stats()
lines = [f"📊 *Costes ResearchOwl*\n"]
lines.append(f"Última sesión (`{topic}`):")
session_total = 0.0
for call_type, label in [("scoring", "Scoring"), ("generation", "Generación")]:
row_data = by_type.get(call_type)
if row_data:
calls = row_data["calls"]
tokens = row_data["total_tokens"]
cost = row_data["total_cost"]
session_total += cost
lines.append(f" {label}: {calls} llamadas · {tokens:,} tokens · ${cost:.4f}")
else:
lines.append(f" {label}: —")
lines.append(f" Total: ${session_total:.4f}")
lines.append("")
lines.append("Acumulado total:")
acc_cost = totals.get("total_cost") or 0.0
acc_sessions = totals.get("sessions") or 0
lines.append(f" ${acc_cost:.4f} ({acc_sessions} sesiones)")
await update.message.reply_text("\n".join(lines), parse_mode=ParseMode.MARKDOWN)
finally:
await db_conn.close()
async def cmd_process(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
if not is_authorized(update.effective_user.id):
return
@@ -576,6 +628,7 @@ def create_bot() -> Application:
app.add_handler(CommandHandler("generate", cmd_generate))
app.add_handler(CommandHandler("sources", cmd_sources))
app.add_handler(CommandHandler("outputs", cmd_outputs))
app.add_handler(CommandHandler("costs", cmd_costs))
app.add_handler(CommandHandler("process", cmd_process))
app.add_handler(CommandHandler("cancel", cmd_cancel))
app.add_handler(CommandHandler("purge", cmd_purge))