Tác giả: Backend Engineer với 5 năm kinh nghiệm xây dựng hệ thống API Gateway cho SaaS đa tenant — đã triển khai thành công rate limiting cho 50+ dự án production.
Kết luận trước — Bạn có nên đọc tiếp?
Nếu bạn đang xây dựng SaaS multi-tenant và cần:
- Kiểm soát quota theo từng user/tenant riêng biệt
- Tách bạch chi phí API cho từng khách hàng
- Tránh tình trạng "noisy neighbor" khi một tenant chiếm dụng tài nguyên
- Tích hợp thanh toán tự động dựa trên usage thực tế
→ HolySheep API Gateway là giải pháp tối ưu về chi phí và độ trễ. Với mức giá từ $0.42/MTok (DeepSeek V3.2), tiết kiệm 85%+ so với API chính thức, độ trễ trung bình <50ms, và hỗ trợ thanh toán qua WeChat/Alipay — đây là lựa chọn số một cho thị trường Châu Á.
Nếu bạn chỉ cần rate limiting đơn giản hoặc budget không phải ưu tiên hàng đầu → có thể skip phần so sánh chi tiết.
Bảng so sánh: HolySheep vs API chính thức vs Đối thủ
| Tiêu chí | HolySheep AI | OpenAI API | Anthropic API | Google Gemini |
|---|---|---|---|---|
| Giá GPT-4.1 | $8/MTok | $8/MTok | - | - |
| Giá Claude Sonnet 4.5 | $15/MTok | - | $15/MTok | - |
| Giá Gemini 2.5 Flash | $2.50/MTok | - | - | $2.50/MTok |
| Giá DeepSeek V3.2 | $0.42/MTok | - | - | - |
| Độ trễ trung bình | <50ms | 100-300ms | 150-400ms | 80-200ms |
| Thanh toán | WeChat, Alipay, USD | Credit Card (quốc tế khó) | Credit Card | Credit Card |
| Tín dụng miễn phí | Có — khi đăng ký | $5 trial | Có | Hạn chế |
| User-level rate limiting | Tích hợp sẵn | Cần tự xây | Cần tự xây | Cần tự xây |
| Billing isolation | Hỗ trợ native | qua Usage-based billing | qua Projects | qua Vertex AI |
| Phù hợp | SaaS Châu Á, startup tiết kiệm | Enterprise Mỹ | Enterprise cao cấp | Google ecosystem |
Kiến trúc tổng quan: User-Level Rate Limiting + Billing Isolation
Trước khi đi vào code, mình muốn chia sẻ bài học xương máu từ dự án đầu tiên: ban đầu mình nghĩ chỉ cần rate limit theo API key là đủ. Sai lầm! Một tenant spam request khiến toàn bộ user khác bị ảnh hưởng. Giải pháp đúng là 2-tier rate limiting: layer 1 cho tenant, layer 2 cho end-user.
Sơ đồ luồng request
+-------------------+ +--------------------+ +------------------+
| Client (User) | --> | SaaS Gateway | --> | HolySheep API |
| | | | | |
| Header: | | 1. Validate tenant | | Base URL: |
| X-Tenant-ID: abc | | 2. Check user quota| | api.holysheep.ai |
| X-User-ID: 12345 | | 3. Track usage | | /v1/chat/complet-|
| Authorization: | | 4. Route request | | ions |
| Bearer <user_key>| | 5. Calculate cost | | |
+-------------------+ +--------------------+ +------------------+
|
v
+--------------------+
| Billing Database |
| - tenant_id |
| - user_id |
| - model_used |
| - tokens_consumed |
| - cost_usd |
| - rate_limit_reset|
+--------------------+
Triển khai chi tiết với Python
1. Cấu hình kết nối HolySheep API
# config.py
import os
from dataclasses import dataclass
from typing import Dict, List
@dataclass
class RateLimitConfig:
"""Cấu hình rate limit cho từng tier"""
requests_per_minute: int
tokens_per_minute: int
daily_limit_tokens: int
@dataclass
class TenantConfig:
"""Cấu hình tenant trên HolySheep"""
tenant_id: str
holysheep_api_key: str # API key riêng cho tenant
tier: str # 'free', 'pro', 'enterprise'
models: List[str]
Bảng giá HolySheep 2026 (USD/MTok)
HOLYSHEEP_PRICING = {
'gpt-4.1': 8.00,
'claude-sonnet-4.5': 15.00,
'gemini-2.5-flash': 2.50,
'deepseek-v3.2': 0.42,
}
Rate limit theo tier
RATE_LIMITS = {
'free': RateLimitConfig(
requests_per_minute=10,
tokens_per_minute=10000,
daily_limit_tokens=100000
),
'pro': RateLimitConfig(
requests_per_minute=60,
tokens_per_minute=100000,
daily_limit_tokens=1000000
),
'enterprise': RateLimitConfig(
requests_per_minute=300,
tokens_per_minute=500000,
daily_limit_tokens=10000000
),
}
HolySheep API Configuration
HOLYSHEEP_CONFIG = {
'base_url': 'https://api.holysheep.ai/v1', # LUÔN dùng endpoint này
'default_model': 'deepseek-v3.2',
'timeout': 30,
'max_retries': 3,
}
Mapping model name chuẩn hóa
MODEL_ALIASES = {
'gpt4': 'gpt-4.1',
'gpt-4': 'gpt-4.1',
'claude': 'claude-sonnet-4.5',
'claude-4': 'claude-sonnet-4.5',
'gemini': 'gemini-2.5-flash',
'deepseek': 'deepseek-v3.2',
}
2. Core Rate Limiter với Redis
# rate_limiter.py
import redis
import time
import json
from typing import Tuple, Optional
from dataclasses import dataclass
from config import RATE_LIMITS, HOLYSHEEP_PRICING
class RateLimiter:
"""
User-level rate limiter với sliding window algorithm.
Triển khai 2-tier: tenant-level + user-level
"""
def __init__(self, redis_url: str = 'redis://localhost:6379'):
self.redis = redis.from_url(redis_url, decode_responses=True)
self.window_size = 60 # 1 phút sliding window
def _get_rate_limit_key(self, tenant_id: str, user_id: str,
limit_type: str) -> str:
"""Tạo Redis key cho rate limit tracking"""
return f"ratelimit:{tenant_id}:{user_id}:{limit_type}"
def check_and_increment(
self,
tenant_id: str,
user_id: str,
tier: str,
tokens_requested: int
) -> Tuple[bool, dict]:
"""
Kiểm tra và tăng counter cho request.
Returns: (allowed, limit_info)
"""
config = RATE_LIMITS.get(tier, RATE_LIMITS['free'])
current_time = int(time.time())
# Key cho requests/giây
req_key = self._get_rate_limit_key(tenant_id, user_id, 'requests')
# Key cho tokens/phút
token_key = self._get_rate_limit_key(tenant_id, user_id, 'tokens')
# Key cho daily limit
daily_key = self._get_rate_limit_key(tenant_id, user_id, 'daily')
# Atomic transaction với Redis pipeline
pipe = self.redis.pipeline()
# 1. Clean old entries trong sliding window
window_start = current_time - self.window_size
pipe.zremrangebyscore(req_key, 0, window_start)
pipe.zremrangebyscore(token_key, 0, window_start)
# 2. Count current requests trong window
pipe.zcard(req_key)
pipe.zrangebyscore(token_key, window_start, current_time)
results = pipe.execute()
current_requests = results[2]
current_tokens_list = results[3]
current_tokens = sum(int(t) for t in current_tokens_list)
# 3. Check daily limit
daily_tokens = self._get_daily_usage(tenant_id, user_id)
# 4. Evaluate limits
allowed = True
reasons = []
if current_requests >= config.requests_per_minute:
allowed = False
reasons.append(f"RPM limit: {config.requests_per_minute}")
if current_tokens + tokens_requested > config.tokens_per_minute:
allowed = False
reasons.append(f"TPM limit: {config.tokens_per_minute}")
if daily_tokens + tokens_requested > config.daily_limit_tokens:
allowed = False
reasons.append(f"Daily limit: {config.daily_limit_tokens}")
# 5. Nếu allowed, increment counters
if allowed:
pipe = self.redis.pipeline()
pipe.zadd(req_key, {str(current_time): current_time})
pipe.zadd(token_key, {str(current_time): tokens_requested})
pipe.expire(req_key, self.window_size * 2)
pipe.expire(token_key, self.window_size * 2)
pipe.execute()
# 6. Get remaining quota
remaining_rpm = max(0, config.requests_per_minute - current_requests - 1)
remaining_tpm = max(0, config.tokens_per_minute - current_tokens - tokens_requested)
remaining_daily = max(0, config.daily_limit_tokens - daily_tokens - tokens_requested)
return allowed, {
'allowed': allowed,
'remaining_rpm': remaining_rpm,
'remaining_tpm': remaining_tpm,
'remaining_daily': remaining_daily,
'reset_in_seconds': self.window_size,
'reasons': reasons if not allowed else []
}
def _get_daily_usage(self, tenant_id: str, user_id: str) -> int:
"""Lấy tổng tokens đã dùng trong ngày"""
key = f"usage:daily:{tenant_id}:{user_id}:{time.strftime('%Y%m%d')}"
usage = self.redis.get(key)
return int(usage) if usage else 0
def record_usage(self, tenant_id: str, user_id: str,
tokens_used: int, model: str) -> float:
"""
Ghi nhận usage và tính chi phí.
Returns: cost in USD
"""
# Tính cost
price_per_mtok = HOLYSHEEP_PRICING.get(model, 0.42)
cost = (tokens_used / 1_000_000) * price_per_mtok
# Cập nhật daily counter
daily_key = f"usage:daily:{tenant_id}:{user_id}:{time.strftime('%Y%m%d')}"
pipe = self.redis.pipeline()
pipe.incrby(daily_key, tokens_used)
pipe.expire(daily_key, 86400 * 2) # 2 days TTL
pipe.execute()
# Ghi log chi tiết cho billing
billing_key = f"billing:{tenant_id}:{user_id}"
billing_entry = {
'timestamp': time.time(),
'tokens': tokens_used,
'model': model,
'cost_usd': round(cost, 6)
}
self.redis.lpush(billing_key, json.dumps(billing_entry))
self.redis.ltrim(billing_key, 0, 9999) # Keep last 10k entries
return cost
Singleton instance
rate_limiter = RateLimiter()
3. HolySheep API Client với Billing Tracking
# holysheep_client.py
import httpx
import time
from typing import Dict, List, Optional, AsyncIterator
from dataclasses import dataclass
import json
@dataclass
class UsageInfo:
"""Thông tin usage từ API response"""
prompt_tokens: int
completion_tokens: int
total_tokens: int
cost_usd: float
model: str
latency_ms: float
class HolySheepClient:
"""
Client cho HolySheep API Gateway.
Tự động track usage và billing per user/tenant.
"""
def __init__(self, base_url: str = 'https://api.holysheep.ai/v1'):
self.base_url = base_url
self.client = httpx.AsyncClient(
timeout=30.0,
limits=httpx.Limits(max_connections=100)
)
async def chat_completion(
self,
api_key: str,
model: str,
messages: List[Dict],
tenant_id: str,
user_id: str,
temperature: float = 0.7,
max_tokens: Optional[int] = None,
stream: bool = False,
) -> Dict:
"""
Gọi HolySheep chat completion API với usage tracking.
Args:
api_key: HolySheep API key của tenant
model: Model name (chuẩn hóa)
messages: Chat messages
tenant_id: Tenant identifier
user_id: User identifier
temperature, max_tokens: Generation params
stream: Enable streaming
Returns:
API response với usage metadata
"""
start_time = time.time()
headers = {
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json',
'X-Tenant-ID': tenant_id, # Custom header cho multi-tenant
'X-User-ID': user_id, # Custom header cho billing
}
payload = {
'model': model,
'messages': messages,
'temperature': temperature,
'stream': stream,
}
if max_tokens:
payload['max_tokens'] = max_tokens
# Estimate tokens cho rate limit check
estimated_tokens = self._estimate_tokens(messages)
# Gọi API
response = await self.client.post(
f'{self.base_url}/chat/completions',
headers=headers,
json=payload
)
latency_ms = (time.time() - start_time) * 1000
if response.status_code != 200:
raise HolySheepAPIError(
status_code=response.status_code,
message=response.text
)
result = response.json()
# Extract usage từ response
usage = result.get('usage', {})
usage_info = UsageInfo(
prompt_tokens=usage.get('prompt_tokens', 0),
completion_tokens=usage.get('completion_tokens', 0),
total_tokens=usage.get('total_tokens', estimated_tokens),
cost_usd=0.0, # Sẽ tính sau
model=model,
latency_ms=round(latency_ms, 2)
)
# Tính cost dựa trên pricing
from config import HOLYSHEEP_PRICING
price = HOLYSHEEP_PRICING.get(model, 0.42)
usage_info.cost_usd = round(
(usage_info.total_tokens / 1_000_000) * price,
6
)
# Attach usage info vào response
result['_usage_info'] = usage_info
return result
async def chat_completion_stream(
self,
api_key: str,
model: str,
messages: List[Dict],
tenant_id: str,
user_id: str,
temperature: float = 0.7,
max_tokens: Optional[int] = None,
) -> AsyncIterator[Dict]:
"""
Streaming version với token counting.
"""
headers = {
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json',
'X-Tenant-ID': tenant_id,
'X-User-ID': user_id,
}
payload = {
'model': model,
'messages': messages,
'temperature': temperature,
'stream': True,
}
if max_tokens:
payload['max_tokens'] = max_tokens
async with self.client.stream(
'POST',
f'{self.base_url}/chat/completions',
headers=headers,
json=payload
) as response:
if response.status_code != 200:
raise HolySheepAPIError(
status_code=response.status_code,
message=await response.text()
)
total_tokens = 0
async for line in response.aiter_lines():
if line.startswith('data: '):
data = line[6:] # Remove 'data: ' prefix
if data == '[DONE]':
break
chunk = json.loads(data)
yield chunk
# Count tokens từ chunk
if 'usage' in chunk:
total_tokens = chunk['usage'].get('total_tokens', 0)
# Yield usage info cuối cùng
yield {'_final_usage': {
'total_tokens': total_tokens,
'cost_usd': round((total_tokens / 1_000_000) *
HOLYSHEEP_PRICING.get(model, 0.42), 6)
}}
def _estimate_tokens(self, messages: List[Dict]) -> int:
"""Ước tính tokens cho rate limit check sơ bộ"""
# Rough estimate: ~4 chars = 1 token
total_chars = sum(len(str(m.get('content', ''))) for m in messages)
return total_chars // 4
async def close(self):
await self.client.aclose()
class HolySheepAPIError(Exception):
def __init__(self, status_code: int, message: str):
self.status_code = status_code
self.message = message
super().__init__(f"HolySheep API Error {status_code}: {message}")
4. FastAPI Gateway cho Multi-Tenant SaaS
# main.py
from fastapi import FastAPI, HTTPException, Header, Request
from fastapi.responses import StreamingResponse
from pydantic import BaseModel
from typing import List, Optional
import asyncio
from rate_limiter import rate_limiter
from holysheep_client import HolySheepClient, HolySheepAPIError
from config import HOLYSHEEP_CONFIG, MODEL_ALIASES, HOLYSHEEP_PRICING
app = FastAPI(title="Multi-Tenant AI Gateway")
client = HolySheepClient(HOLYSHEEP_CONFIG['base_url'])
Database giả lập - thay bằng DB thực
TENANT_DB = {
'tenant_abc': {
'api_key': 'YOUR_HOLYSHEEP_API_KEY', # API key HolySheep của tenant
'tier': 'pro',
'models': ['deepseek-v3.2', 'gpt-4.1', 'gemini-2.5-flash']
},
'tenant_xyz': {
'api_key': 'YOUR_HOLYSHEEP_API_KEY',
'tier': 'free',
'models': ['deepseek-v3.2']
}
}
class ChatRequest(BaseModel):
model: str
messages: List[dict]
temperature: float = 0.7
max_tokens: Optional[int] = None
stream: bool = False
@app.post("/v1/chat/completions")
async def chat_completions(
request: ChatRequest,
tenant_id: str = Header(..., alias='X-Tenant-ID'),
user_id: str = Header(..., alias='X-User-ID'),
):
"""Endpoint chính cho chat completions với rate limiting"""
# 1. Validate tenant
tenant = TENANT_DB.get(tenant_id)
if not tenant:
raise HTTPException(status_code=401, detail="Invalid tenant")
# 2. Normalize model name
model = MODEL_ALIASES.get(request.model, request.model)
if model not in HOLYSHEEP_PRICING:
raise HTTPException(
status_code=400,
detail=f"Model {model} not supported. Available: {list(HOLYSHEEP_PRICING.keys())}"
)
if model not in tenant['models']:
raise HTTPException(
status_code=403,
detail=f"Tenant không có quyền truy cập model {model}"
)
# 3. Estimate tokens cho rate limit check
estimated_tokens = sum(
len(str(m.get('content', ''))) // 4
for m in request.messages
)
# 4. Check rate limit
allowed, limit_info = rate_limiter.check_and_increment(
tenant_id=tenant_id,
user_id=user_id,
tier=tenant['tier'],
tokens_requested=estimated_tokens
)
if not allowed:
raise HTTPException(
status_code=429,
detail={
'error': 'Rate limit exceeded',
'retry_after_seconds': limit_info['reset_in_seconds'],
'limits': {
'rpm_remaining': limit_info['remaining_rpm'],
'tpm_remaining': limit_info['remaining_tpm'],
'daily_remaining': limit_info['remaining_daily']
},
'reasons': limit_info['reasons']
}
)
# 5. Add rate limit headers
response_headers = {
'X-RateLimit-Remaining-RPM': str(limit_info['remaining_rpm']),
'X-RateLimit-Remaining-TPM': str(limit_info['remaining_tpm']),
'X-RateLimit-Reset': str(limit_info['reset_in_seconds']),
}
try:
# 6. Gọi HolySheep API
if request.stream:
return StreamingResponse(
_stream_wrapper(
client.chat_completion_stream(
api_key=tenant['api_key'],
model=model,
messages=request.messages,
tenant_id=tenant_id,
user_id=user_id,
temperature=request.temperature,
max_tokens=request.max_tokens
),
tenant_id,
user_id,
model
),
media_type='text/event-stream',
headers=response_headers
)
else:
result = await client.chat_completion(
api_key=tenant['api_key'],
model=model,
messages=request.messages,
tenant_id=tenant_id,
user_id=user_id,
temperature=request.temperature,
max_tokens=request.max_tokens
)
# 7. Record usage cho billing
usage_info = result['_usage_info']
cost = rate_limiter.record_usage(
tenant_id=tenant_id,
user_id=user_id,
tokens_used=usage_info.total_tokens,
model=model
)
# Add billing info to response
result['usage']['cost_usd'] = cost
result['usage']['billing'] = {
'tenant_id': tenant_id,
'user_id': user_id,
'model': model,
'latency_ms': usage_info.latency_ms
}
return result
except HolySheepAPIError as e:
raise HTTPException(status_code=502, detail=str(e))
async def _stream_wrapper(stream, tenant_id: str, user_id: str, model: str):
"""Wrapper cho streaming response để track usage"""
total_tokens = 0
async for chunk in stream:
if '_final_usage' in chunk:
# Ghi nhận usage cuối cùng
usage = chunk['_final_usage']
rate_limiter.record_usage(
tenant_id, user_id, usage['total_tokens'], model
)
# Yield usage info
yield f"data: {json.dumps({'_usage': usage})}\n\n"
else:
yield f"data: {json.dumps(chunk)}\n\n"
if 'usage' in chunk:
total_tokens = chunk['usage'].get('total_tokens', 0)
=== Billing Endpoints ===
@app.get("/v1/billing/usage")
async def get_usage(
tenant_id: str = Header(..., alias='X-Tenant-ID'),
user_id: str = Header(..., alias='X-User-ID'),
period: str = 'daily' # 'daily', 'weekly', 'monthly'
):
"""Lấy usage statistics cho user"""
from billing_service import BillingService
billing = BillingService()
return await billing.get_usage_report(tenant_id, user_id, period)
@app.get("/v1/billing/invoice/{tenant_id}")
async def get_invoice(tenant_id: str):
"""Generate invoice cho tenant"""
from billing_service import BillingService
billing = BillingService()
return await billing.generate_invoice(tenant_id)
if __name__ == '__main__':
import uvicorn
uvicorn.run(app, host='0.0.0.0', port=8000)
5. Billing Service cho Usage Tracking và Invoicing
# billing_service.py
import redis
import json
import time
from typing import Dict, List, Optional
from datetime import datetime, timedelta
from dataclasses import dataclass
from config import HOLYSHEEP_PRICING
@dataclass
class UsageRecord:
timestamp: float
tokens: int
model: str
cost_usd: float
@dataclass
class BillingReport:
tenant_id: str
user_id: str
period_start: datetime
period_end: datetime
total_tokens: int
total_cost_usd: float
by_model: Dict[str, int]
by_day: Dict[str, int]
average_latency_ms: float
class BillingService:
"""
Service xử lý billing và usage reporting.
Sử dụng Redis cho real-time tracking.
"""
def __init__(self, redis_url: str = 'redis://localhost:6379'):
self.redis = redis.from_url(redis_url, decode_responses=True)
def get_usage_report(
self,
tenant_id: str,
user_id: str,
period: str = 'daily'
) -> Dict:
"""Lấy usage report cho user trong period"""
# Xác định thời gian
now = datetime.now()
if period == 'daily':
start_date = now - timedelta(days=1)
elif period == 'weekly':
start_date = now - timedelta(days=7)
else: # monthly
start_date = now - timedelta(days=30)
# Get billing entries từ Redis
billing_key = f"billing:{tenant_id}:{user_id}"
entries = self.redis.lrange(billing_key, 0, -1)
# Aggregate
total_tokens = 0
total_cost = 0.0
by_model = {}
by_day = {}
for entry in entries:
data = json.loads(entry)
timestamp = data['timestamp']
# Filter by period
if timestamp < start_date.timestamp():
continue
total_tokens += data['tokens']
total_cost += data['cost_usd']
# By model
model = data['model']
by_model[model] = by_model.get(model, 0) + data['tokens']
# By day
day = datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d')
by_day[day] = by_day.get(day, 0) + data['tokens']
# Build response
return {
'tenant_id': tenant_id,
'user_id': user_id,
'period': period,
'period_start': start_date.isoformat(),
'period_end': now.isoformat(),
'summary': {
'total_tokens': total_tokens,
'total_cost_usd': round(total_cost, 6),
'average_cost_per_mtok': round(
(total_cost / total_tokens * 1_000_000) if total_tokens > 0 else 0,
4
)
},
'by_model': by_model,
'by_day': by_day,
'pricing_reference': HOLYSHEEP_PRICING
}
def generate_invoice(self, tenant_id: str) -> Dict:
"""Generate invoice summary cho tenant (tất cả users)"""
# Lấy tất cả users của tenant
# Trong thực tế query từ database
user_ids = self._get_tenant_users(tenant_id)
invoice_items = []
grand_total_tokens = 0
grand_total_cost = 0.0
for user_id in user_ids:
report = self.get_usage_report(tenant_id, user_id, 'monthly')
if report['summary']['total_tokens'] > 0:
invoice_items.append({
'user_id': user_id,
'tokens': report['summary']['total_tokens'],
'cost_usd': report['summary']['total_cost_usd'],
'breakdown': report['by_model']
})
grand_total_tokens += report['summary']['total_tokens']
grand_total_cost += report['summary']['total_cost_usd']
return {
'invoice_id': f"INV-{tenant_id}-{int(time.time())}",
'tenant_id': tenant_id,
'billing_period': {
'start': (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d'),
'end': datetime.now().strftime('%Y-%m-%d')
},
'generated_at': datetime.now().isoformat(),
'items': invoice_items,
'subtotal_usd': round(grand_total_cost, 2),
'tax_usd': 0, # Tùy business model
'total_usd': round(grand_total_cost, 2),
'payment_methods': ['WeChat Pay', 'Alipay', 'Wire Transfer'],
'currency': 'USD',
'due_date': (datetime.now() + timedelta(days=30)).strftime('%Y-%m-%d')
}
def _get_tenant_users(self, tenant_id: str) -> List[str]:
"""Lấy danh sách user IDs của tenant"""
# Scan Redis keys
pattern = f"billing:{tenant_id}:*"
keys = list(self.redis.scan_iter(match=pattern))
users = []
for key in keys:
# Key format: billing:tenant_id:user_id
parts = key.split(':')
if len(parts) >= 3:
users.append(parts[2])
return list(set(users)) if users else []
def export_csv(self, tenant_id: str, start_date: str, end_date: str) -> str:
"""Export usage data as CSV cho accounting"""
start = datetime.fromisoformat(start_date)
end = datetime.fromisoformat(end_date)
rows = ['timestamp,user_id,model,tokens,cost_usd']
user_ids = self._get_tenant_users(tenant_id)
for user_id in user_ids:
billing_key = f"billing:{tenant_id}:{user_id}"
entries = self.redis.lrange(billing_key, 0, -1)
for entry in entries:
data = json.loads(entry