From 08b02280f8bf82eb9500d1367e3fc33217daa22f Mon Sep 17 00:00:00 2001 From: OG T Date: Fri, 3 Apr 2026 19:01:21 +0800 Subject: [PATCH] =?UTF-8?q?feat(chat):=20Gemini=20=E6=9C=88=E8=B2=BB?= =?UTF-8?q?=E7=94=A8=E4=B8=8A=E9=99=90=20$10=20USD=20+=20Redis=20=E7=B4=AF?= =?UTF-8?q?=E8=A8=88=E8=BF=BD=E8=B9=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 每次呼叫前檢查當月累計費用,超過 $10 USD 拒絕呼叫 - Redis key: gemini_cost:YYYY-MM,TTL 40 天 - 每次回覆顯示: token 數 | 本次費用 | 本月累計 - 超限時回傳警告訊息告知老闆 Co-Authored-By: Claude Sonnet 4.6 --- apps/api/src/services/chat_manager.py | 29 ++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/apps/api/src/services/chat_manager.py b/apps/api/src/services/chat_manager.py index 42240609..78d2c2b2 100644 --- a/apps/api/src/services/chat_manager.py +++ b/apps/api/src/services/chat_manager.py @@ -90,6 +90,21 @@ class ChatManager: logger.warning("openclaw_chat_failed", error="GEMINI_API_KEY not configured") return None + # 月費用上限檢查 ($10 USD) + MONTHLY_LIMIT_USD = 10.0 + from src.core.redis_client import get_redis + from src.utils.timezone import now_taipei + redis = get_redis() + month_key = f"gemini_cost:{now_taipei().strftime('%Y-%m')}" + try: + current_cost = float(await redis.get(month_key) or 0) + except Exception: + current_cost = 0.0 + + if current_cost >= MONTHLY_LIMIT_USD: + logger.warning("openclaw_gemini_monthly_limit_reached", current_usd=current_cost, limit_usd=MONTHLY_LIMIT_USD) + return f"🔴 OpenClaw 本月 Gemini 用量已達上限 ${MONTHLY_LIMIT_USD} USD(已用 ${current_cost:.4f})" + # Gemini 1.5 Flash: 快速、便宜 model = "gemini-1.5-flash" try: @@ -106,14 +121,22 @@ class ChatManager: data = resp.json() text = data["candidates"][0]["content"]["parts"][0]["text"].strip() - # Token/費用統計 + # Token/費用統計 + 累計到 Redis usage = data.get("usageMetadata", {}) in_tok = usage.get("promptTokenCount", 0) out_tok = usage.get("candidatesTokenCount", 0) cost = (in_tok * 0.000000075) + (out_tok * 0.0000003) - logger.info("openclaw_gemini_usage", in_tokens=in_tok, out_tokens=out_tok, cost_usd=round(cost, 6)) + new_total = current_cost + cost - return f"{text}\n\n📊 {in_tok+out_tok} tokens | ${cost:.4f}" + try: + await redis.set(month_key, str(round(new_total, 6)), ex=40 * 24 * 3600) # 40天 TTL + except Exception: + pass + + logger.info("openclaw_gemini_usage", in_tokens=in_tok, out_tokens=out_tok, + cost_usd=round(cost, 6), monthly_total_usd=round(new_total, 4)) + + return f"{text}\n\n📊 {in_tok+out_tok} tokens | ${cost:.4f} | 本月累計 ${new_total:.4f}" except Exception as e: logger.warning("openclaw_chat_failed", error=str(e)) return None