ChiragPatankar commited on
Commit
5557ce0
·
verified ·
1 Parent(s): 712e441

Upload app/billing/usage_tracker.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app/billing/usage_tracker.py +173 -0
app/billing/usage_tracker.py ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Usage tracking service.
3
+ Tracks token usage and costs for each LLM request.
4
+ """
5
+ from sqlalchemy.orm import Session
6
+ from sqlalchemy import func, and_
7
+ from datetime import datetime, timedelta
8
+ from typing import Optional
9
+ import uuid
10
+ import logging
11
+
12
+ from app.db.models import UsageEvent, UsageDaily, UsageMonthly, Tenant
13
+ from app.billing.pricing import calculate_cost
14
+ from app.billing.quota import ensure_tenant_exists
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ def track_usage(
20
+ db: Session,
21
+ tenant_id: str,
22
+ user_id: str,
23
+ kb_id: str,
24
+ provider: str,
25
+ model: str,
26
+ prompt_tokens: int,
27
+ completion_tokens: int,
28
+ request_timestamp: Optional[datetime] = None
29
+ ) -> UsageEvent:
30
+ """
31
+ Track a single usage event.
32
+
33
+ Args:
34
+ db: Database session
35
+ tenant_id: Tenant ID
36
+ user_id: User ID
37
+ kb_id: Knowledge base ID
38
+ provider: "gemini" or "openai"
39
+ model: Model name
40
+ prompt_tokens: Input tokens
41
+ completion_tokens: Output tokens
42
+ request_timestamp: Request timestamp (defaults to now)
43
+
44
+ Returns:
45
+ Created UsageEvent
46
+ """
47
+ # Ensure tenant exists
48
+ ensure_tenant_exists(db, tenant_id)
49
+
50
+ # Calculate cost
51
+ total_tokens = prompt_tokens + completion_tokens
52
+ estimated_cost = calculate_cost(provider, model, prompt_tokens, completion_tokens)
53
+
54
+ # Create usage event
55
+ request_id = f"req_{uuid.uuid4().hex[:16]}"
56
+ timestamp = request_timestamp or datetime.utcnow()
57
+
58
+ usage_event = UsageEvent(
59
+ request_id=request_id,
60
+ tenant_id=tenant_id,
61
+ user_id=user_id,
62
+ kb_id=kb_id,
63
+ provider=provider,
64
+ model=model,
65
+ prompt_tokens=prompt_tokens,
66
+ completion_tokens=completion_tokens,
67
+ total_tokens=total_tokens,
68
+ estimated_cost_usd=estimated_cost,
69
+ request_timestamp=timestamp
70
+ )
71
+
72
+ db.add(usage_event)
73
+
74
+ # Update daily aggregation
75
+ _update_daily_usage(db, tenant_id, timestamp, provider, total_tokens, estimated_cost)
76
+
77
+ # Update monthly aggregation
78
+ _update_monthly_usage(db, tenant_id, timestamp, provider, total_tokens, estimated_cost)
79
+
80
+ db.commit()
81
+ db.refresh(usage_event)
82
+
83
+ logger.info(
84
+ f"Tracked usage: tenant={tenant_id}, provider={provider}, "
85
+ f"tokens={total_tokens}, cost=${estimated_cost:.6f}"
86
+ )
87
+
88
+ return usage_event
89
+
90
+
91
+ def _update_daily_usage(
92
+ db: Session,
93
+ tenant_id: str,
94
+ timestamp: datetime,
95
+ provider: str,
96
+ tokens: int,
97
+ cost: float
98
+ ):
99
+ """Update daily usage aggregation."""
100
+ date = timestamp.date()
101
+ date_start = datetime.combine(date, datetime.min.time())
102
+
103
+ daily = db.query(UsageDaily).filter(
104
+ and_(
105
+ UsageDaily.tenant_id == tenant_id,
106
+ UsageDaily.date == date_start
107
+ )
108
+ ).first()
109
+
110
+ if daily:
111
+ daily.total_requests += 1
112
+ daily.total_tokens += tokens
113
+ daily.total_cost_usd += cost
114
+ if provider == "gemini":
115
+ daily.gemini_requests += 1
116
+ elif provider == "openai":
117
+ daily.openai_requests += 1
118
+ daily.updated_at = datetime.utcnow()
119
+ else:
120
+ daily = UsageDaily(
121
+ tenant_id=tenant_id,
122
+ date=date_start,
123
+ total_requests=1,
124
+ total_tokens=tokens,
125
+ total_cost_usd=cost,
126
+ gemini_requests=1 if provider == "gemini" else 0,
127
+ openai_requests=1 if provider == "openai" else 0
128
+ )
129
+ db.add(daily)
130
+
131
+
132
+ def _update_monthly_usage(
133
+ db: Session,
134
+ tenant_id: str,
135
+ timestamp: datetime,
136
+ provider: str,
137
+ tokens: int,
138
+ cost: float
139
+ ):
140
+ """Update monthly usage aggregation."""
141
+ year = timestamp.year
142
+ month = timestamp.month
143
+
144
+ monthly = db.query(UsageMonthly).filter(
145
+ and_(
146
+ UsageMonthly.tenant_id == tenant_id,
147
+ UsageMonthly.year == year,
148
+ UsageMonthly.month == month
149
+ )
150
+ ).first()
151
+
152
+ if monthly:
153
+ monthly.total_requests += 1
154
+ monthly.total_tokens += tokens
155
+ monthly.total_cost_usd += cost
156
+ if provider == "gemini":
157
+ monthly.gemini_requests += 1
158
+ elif provider == "openai":
159
+ monthly.openai_requests += 1
160
+ monthly.updated_at = datetime.utcnow()
161
+ else:
162
+ monthly = UsageMonthly(
163
+ tenant_id=tenant_id,
164
+ year=year,
165
+ month=month,
166
+ total_requests=1,
167
+ total_tokens=tokens,
168
+ total_cost_usd=cost,
169
+ gemini_requests=1 if provider == "gemini" else 0,
170
+ openai_requests=1 if provider == "openai" else 0
171
+ )
172
+ db.add(monthly)
173
+