159 lines
5.2 KiB
Python
Raw Normal View History

2025-11-04 05:17:27 +01:00
import json
import os
from datetime import datetime
from typing import Dict, Optional
2025-11-04 08:09:12 +01:00
2025-11-04 05:17:27 +01:00
from pr.core.logging import get_logger
2025-11-04 08:09:12 +01:00
logger = get_logger("usage")
2025-11-04 05:17:27 +01:00
USAGE_DB_FILE = os.path.expanduser("~/.assistant_usage.json")
EXCHANGE_RATE = 1.0 # Keep in USD
2025-11-04 05:17:27 +01:00
MODEL_COSTS = {
"x-ai/grok-code-fast-1": {"input": 0.0002, "output": 0.0015}, # per 1000 tokens in USD
2025-11-04 08:09:12 +01:00
"gpt-4": {"input": 0.03, "output": 0.06},
"gpt-4-turbo": {"input": 0.01, "output": 0.03},
"gpt-3.5-turbo": {"input": 0.0005, "output": 0.0015},
"claude-3-opus": {"input": 0.015, "output": 0.075},
"claude-3-sonnet": {"input": 0.003, "output": 0.015},
"claude-3-haiku": {"input": 0.00025, "output": 0.00125},
2025-11-04 05:17:27 +01:00
}
class UsageTracker:
def __init__(self):
self.session_usage = {
2025-11-04 08:09:12 +01:00
"requests": 0,
"total_tokens": 0,
"input_tokens": 0,
"output_tokens": 0,
"estimated_cost": 0.0,
"models_used": {},
2025-11-04 05:17:27 +01:00
}
def track_request(
self,
model: str,
input_tokens: int,
output_tokens: int,
2025-11-04 08:09:12 +01:00
total_tokens: Optional[int] = None,
2025-11-04 05:17:27 +01:00
):
if total_tokens is None:
total_tokens = input_tokens + output_tokens
2025-11-04 08:09:12 +01:00
self.session_usage["requests"] += 1
self.session_usage["total_tokens"] += total_tokens
self.session_usage["input_tokens"] += input_tokens
self.session_usage["output_tokens"] += output_tokens
2025-11-04 05:17:27 +01:00
2025-11-04 08:09:12 +01:00
if model not in self.session_usage["models_used"]:
self.session_usage["models_used"][model] = {
"requests": 0,
"tokens": 0,
"cost": 0.0,
2025-11-04 05:17:27 +01:00
}
2025-11-04 08:09:12 +01:00
model_usage = self.session_usage["models_used"][model]
model_usage["requests"] += 1
model_usage["tokens"] += total_tokens
2025-11-04 05:17:27 +01:00
cost = self._calculate_cost(model, input_tokens, output_tokens)
2025-11-04 08:09:12 +01:00
model_usage["cost"] += cost
self.session_usage["estimated_cost"] += cost
2025-11-04 05:17:27 +01:00
self._save_to_history(model, input_tokens, output_tokens, cost)
logger.debug(f"Tracked request: {model}, tokens: {total_tokens}, cost: €{cost:.4f}")
2025-11-04 05:17:27 +01:00
@staticmethod
def _calculate_cost(model: str, input_tokens: int, output_tokens: int) -> float:
2025-11-04 05:17:27 +01:00
if model not in MODEL_COSTS:
2025-11-04 08:09:12 +01:00
base_model = model.split("/")[0] if "/" in model else model
2025-11-04 05:17:27 +01:00
if base_model not in MODEL_COSTS:
logger.warning(f"Unknown model for cost calculation: {model}")
return 0.0
costs = MODEL_COSTS[base_model]
else:
costs = MODEL_COSTS[model]
input_cost = (input_tokens / 1000) * costs["input"] * EXCHANGE_RATE
output_cost = (output_tokens / 1000) * costs["output"] * EXCHANGE_RATE
2025-11-04 05:17:27 +01:00
return input_cost + output_cost
2025-11-04 08:10:37 +01:00
def _save_to_history(self, model: str, input_tokens: int, output_tokens: int, cost: float):
2025-11-04 05:17:27 +01:00
try:
history = []
if os.path.exists(USAGE_DB_FILE):
2025-11-04 08:09:12 +01:00
with open(USAGE_DB_FILE) as f:
2025-11-04 05:17:27 +01:00
history = json.load(f)
2025-11-04 08:09:12 +01:00
history.append(
{
"timestamp": datetime.now().isoformat(),
"model": model,
"input_tokens": input_tokens,
"output_tokens": output_tokens,
"total_tokens": input_tokens + output_tokens,
"cost": cost,
}
)
2025-11-04 05:17:27 +01:00
if len(history) > 10000:
history = history[-10000:]
2025-11-04 08:09:12 +01:00
with open(USAGE_DB_FILE, "w") as f:
2025-11-04 05:17:27 +01:00
json.dump(history, f, indent=2)
except Exception as e:
logger.error(f"Error saving usage history: {e}")
def get_session_summary(self) -> Dict:
return self.session_usage.copy()
def get_formatted_summary(self) -> str:
usage = self.session_usage
lines = [
"\n=== Session Usage Summary ===",
f"Total Requests: {usage['requests']}",
f"Total Tokens: {usage['total_tokens']:,}",
f" Input: {usage['input_tokens']:,}",
f" Output: {usage['output_tokens']:,}",
f"Estimated Cost: ${usage['estimated_cost']:.4f}",
]
2025-11-04 08:09:12 +01:00
if usage["models_used"]:
2025-11-04 05:17:27 +01:00
lines.append("\nModels Used:")
2025-11-04 08:09:12 +01:00
for model, stats in usage["models_used"].items():
2025-11-04 05:17:27 +01:00
lines.append(
f" {model}: {stats['requests']} requests, "
f"{stats['tokens']:,} tokens, ${stats['cost']:.4f}"
)
2025-11-04 08:09:12 +01:00
return "\n".join(lines)
2025-11-04 05:17:27 +01:00
@staticmethod
def get_total_usage() -> Dict:
if not os.path.exists(USAGE_DB_FILE):
2025-11-04 08:09:12 +01:00
return {"total_requests": 0, "total_tokens": 0, "total_cost": 0.0}
2025-11-04 05:17:27 +01:00
try:
2025-11-04 08:09:12 +01:00
with open(USAGE_DB_FILE) as f:
2025-11-04 05:17:27 +01:00
history = json.load(f)
2025-11-04 08:09:12 +01:00
total_tokens = sum(entry["total_tokens"] for entry in history)
total_cost = sum(entry["cost"] for entry in history)
2025-11-04 05:17:27 +01:00
return {
2025-11-04 08:09:12 +01:00
"total_requests": len(history),
"total_tokens": total_tokens,
"total_cost": total_cost,
2025-11-04 05:17:27 +01:00
}
except Exception as e:
logger.error(f"Error loading usage history: {e}")
2025-11-04 08:09:12 +01:00
return {"total_requests": 0, "total_tokens": 0, "total_cost": 0.0}