Files
asif2bd_openclaw-token-opti…/scripts/token_tracker.py

157 lines
4.8 KiB
Python

#!/usr/bin/env python3
"""
Token usage tracker with budget alerts.
Monitors API usage and warns when approaching limits.
"""
import json
import os
from datetime import datetime, timedelta
from pathlib import Path
STATE_FILE = Path.home() / ".openclaw/workspace/memory/token-tracker-state.json"
def load_state():
"""Load tracking state from file."""
if STATE_FILE.exists():
with open(STATE_FILE, 'r') as f:
return json.load(f)
return {
"daily_usage": {},
"alerts_sent": [],
"last_reset": datetime.now().isoformat()
}
def save_state(state):
"""Save tracking state to file."""
STATE_FILE.parent.mkdir(parents=True, exist_ok=True)
with open(STATE_FILE, 'w') as f:
json.dump(state, f, indent=2)
def get_usage_from_session_status():
"""Parse session status to extract token usage.
Returns dict with input_tokens, output_tokens, and cost.
"""
# This would integrate with OpenClaw's session_status tool
# For now, returns placeholder structure
return {
"input_tokens": 0,
"output_tokens": 0,
"total_cost": 0.0,
"model": "anthropic/claude-sonnet-4-5"
}
def check_budget(daily_limit_usd=5.0, warn_threshold=0.8):
"""Check if usage is approaching daily budget.
Args:
daily_limit_usd: Daily spending limit in USD
warn_threshold: Fraction of limit to trigger warning (default 80%)
Returns:
dict with status, usage, limit, and alert message if applicable
"""
state = load_state()
today = datetime.now().date().isoformat()
# Reset if new day
if today not in state["daily_usage"]:
state["daily_usage"] = {today: {"cost": 0.0, "tokens": 0}}
state["alerts_sent"] = []
usage = state["daily_usage"][today]
percent_used = (usage["cost"] / daily_limit_usd) * 100
result = {
"date": today,
"cost": usage["cost"],
"tokens": usage["tokens"],
"limit": daily_limit_usd,
"percent_used": percent_used,
"status": "ok"
}
# Check thresholds
if percent_used >= 100:
result["status"] = "exceeded"
result["alert"] = f"⚠️ Daily budget exceeded! ${usage['cost']:.2f} / ${daily_limit_usd:.2f}"
elif percent_used >= (warn_threshold * 100):
result["status"] = "warning"
result["alert"] = f"⚠️ Approaching daily limit: ${usage['cost']:.2f} / ${daily_limit_usd:.2f} ({percent_used:.0f}%)"
return result
def suggest_cheaper_model(current_model, task_type="general"):
"""Suggest cheaper alternative models based on task type.
Args:
current_model: Currently configured model
task_type: Type of task (general, simple, complex)
Returns:
dict with suggestion and cost savings
"""
# Cost per 1M tokens (input/output average)
model_costs = {
"anthropic/claude-opus-4": 15.0,
"anthropic/claude-sonnet-4-5": 3.0,
"anthropic/claude-haiku-4": 0.25,
"google/gemini-2.0-flash-exp": 0.075,
"openai/gpt-4o": 2.5,
"openai/gpt-4o-mini": 0.15
}
suggestions = {
"simple": [
("anthropic/claude-haiku-4", "12x cheaper, great for file reads, routine checks"),
("google/gemini-2.0-flash-exp", "40x cheaper via OpenRouter, good for simple tasks")
],
"general": [
("anthropic/claude-sonnet-4-5", "Balanced performance and cost"),
("google/gemini-2.0-flash-exp", "Much cheaper, decent quality")
],
"complex": [
("anthropic/claude-opus-4", "Best reasoning, use sparingly"),
("anthropic/claude-sonnet-4-5", "Good balance for most complex tasks")
]
}
return {
"current": current_model,
"current_cost": model_costs.get(current_model, "unknown"),
"suggestions": suggestions.get(task_type, suggestions["general"])
}
def main():
"""CLI interface for token tracker."""
import sys
if len(sys.argv) < 2:
print("Usage: token_tracker.py [check|suggest|reset]")
sys.exit(1)
command = sys.argv[1]
if command == "check":
result = check_budget()
print(json.dumps(result, indent=2))
elif command == "suggest":
task = sys.argv[2] if len(sys.argv) > 2 else "general"
current = sys.argv[3] if len(sys.argv) > 3 else "anthropic/claude-sonnet-4-5"
result = suggest_cheaper_model(current, task)
print(json.dumps(result, indent=2))
elif command == "reset":
state = load_state()
state["daily_usage"] = {}
state["alerts_sent"] = []
save_state(state)
print("Token tracker state reset.")
else:
print(f"Unknown command: {command}")
sys.exit(1)
if __name__ == "__main__":
main()