first commit
This commit is contained in:
211
api/app/models/ai_usage.py
Normal file
211
api/app/models/ai_usage.py
Normal file
@@ -0,0 +1,211 @@
|
||||
"""
|
||||
AI Usage data model for tracking AI generation costs and usage.
|
||||
|
||||
This module defines the AIUsageLog dataclass which represents a single AI usage
|
||||
event for tracking costs, tokens used, and generating usage analytics.
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime, timezone, date
|
||||
from typing import Dict, Any, Optional
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class TaskType(str, Enum):
|
||||
"""Types of AI tasks that can be tracked."""
|
||||
STORY_PROGRESSION = "story_progression"
|
||||
COMBAT_NARRATION = "combat_narration"
|
||||
QUEST_SELECTION = "quest_selection"
|
||||
NPC_DIALOGUE = "npc_dialogue"
|
||||
GENERAL = "general"
|
||||
|
||||
|
||||
@dataclass
|
||||
class AIUsageLog:
|
||||
"""
|
||||
Represents a single AI usage event for cost and usage tracking.
|
||||
|
||||
This dataclass captures all relevant information about an AI API call
|
||||
including the user, model used, tokens consumed, and estimated cost.
|
||||
Used for:
|
||||
- Cost monitoring and budgeting
|
||||
- Usage analytics per user/tier
|
||||
- Rate limiting enforcement
|
||||
- Billing and invoicing (future)
|
||||
|
||||
Attributes:
|
||||
log_id: Unique identifier for this usage log entry
|
||||
user_id: User who made the request
|
||||
timestamp: When the request was made
|
||||
model: Model identifier (e.g., "meta/meta-llama-3-8b-instruct")
|
||||
tokens_input: Number of input tokens (prompt)
|
||||
tokens_output: Number of output tokens (response)
|
||||
tokens_total: Total tokens used (input + output)
|
||||
estimated_cost: Estimated cost in USD
|
||||
task_type: Type of task (story, combat, quest, npc)
|
||||
session_id: Optional game session ID for context
|
||||
character_id: Optional character ID for context
|
||||
request_duration_ms: How long the request took in milliseconds
|
||||
success: Whether the request completed successfully
|
||||
error_message: Error message if the request failed
|
||||
"""
|
||||
|
||||
log_id: str
|
||||
user_id: str
|
||||
timestamp: datetime
|
||||
model: str
|
||||
tokens_input: int
|
||||
tokens_output: int
|
||||
tokens_total: int
|
||||
estimated_cost: float
|
||||
task_type: TaskType
|
||||
session_id: Optional[str] = None
|
||||
character_id: Optional[str] = None
|
||||
request_duration_ms: int = 0
|
||||
success: bool = True
|
||||
error_message: Optional[str] = None
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Convert usage log to dictionary for storage.
|
||||
|
||||
Returns:
|
||||
Dictionary representation suitable for Appwrite storage
|
||||
"""
|
||||
return {
|
||||
"user_id": self.user_id,
|
||||
"timestamp": self.timestamp.isoformat() if isinstance(self.timestamp, datetime) else self.timestamp,
|
||||
"model": self.model,
|
||||
"tokens_input": self.tokens_input,
|
||||
"tokens_output": self.tokens_output,
|
||||
"tokens_total": self.tokens_total,
|
||||
"estimated_cost": self.estimated_cost,
|
||||
"task_type": self.task_type.value if isinstance(self.task_type, TaskType) else self.task_type,
|
||||
"session_id": self.session_id,
|
||||
"character_id": self.character_id,
|
||||
"request_duration_ms": self.request_duration_ms,
|
||||
"success": self.success,
|
||||
"error_message": self.error_message,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: Dict[str, Any]) -> "AIUsageLog":
|
||||
"""
|
||||
Create AIUsageLog from dictionary.
|
||||
|
||||
Args:
|
||||
data: Dictionary with usage log data
|
||||
|
||||
Returns:
|
||||
AIUsageLog instance
|
||||
"""
|
||||
# Parse timestamp
|
||||
timestamp = data.get("timestamp")
|
||||
if isinstance(timestamp, str):
|
||||
timestamp = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
|
||||
elif timestamp is None:
|
||||
timestamp = datetime.now(timezone.utc)
|
||||
|
||||
# Parse task type
|
||||
task_type = data.get("task_type", "general")
|
||||
if isinstance(task_type, str):
|
||||
try:
|
||||
task_type = TaskType(task_type)
|
||||
except ValueError:
|
||||
task_type = TaskType.GENERAL
|
||||
|
||||
return cls(
|
||||
log_id=data.get("log_id", ""),
|
||||
user_id=data.get("user_id", ""),
|
||||
timestamp=timestamp,
|
||||
model=data.get("model", ""),
|
||||
tokens_input=data.get("tokens_input", 0),
|
||||
tokens_output=data.get("tokens_output", 0),
|
||||
tokens_total=data.get("tokens_total", 0),
|
||||
estimated_cost=data.get("estimated_cost", 0.0),
|
||||
task_type=task_type,
|
||||
session_id=data.get("session_id"),
|
||||
character_id=data.get("character_id"),
|
||||
request_duration_ms=data.get("request_duration_ms", 0),
|
||||
success=data.get("success", True),
|
||||
error_message=data.get("error_message"),
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class DailyUsageSummary:
|
||||
"""
|
||||
Summary of AI usage for a specific day.
|
||||
|
||||
Used for reporting and rate limiting checks.
|
||||
|
||||
Attributes:
|
||||
date: The date of this summary
|
||||
user_id: User ID
|
||||
total_requests: Number of AI requests made
|
||||
total_tokens: Total tokens consumed
|
||||
total_input_tokens: Total input tokens
|
||||
total_output_tokens: Total output tokens
|
||||
estimated_cost: Total estimated cost in USD
|
||||
requests_by_task: Breakdown of requests by task type
|
||||
"""
|
||||
|
||||
date: date
|
||||
user_id: str
|
||||
total_requests: int
|
||||
total_tokens: int
|
||||
total_input_tokens: int
|
||||
total_output_tokens: int
|
||||
estimated_cost: float
|
||||
requests_by_task: Dict[str, int] = field(default_factory=dict)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Convert summary to dictionary."""
|
||||
return {
|
||||
"date": self.date.isoformat() if isinstance(self.date, date) else self.date,
|
||||
"user_id": self.user_id,
|
||||
"total_requests": self.total_requests,
|
||||
"total_tokens": self.total_tokens,
|
||||
"total_input_tokens": self.total_input_tokens,
|
||||
"total_output_tokens": self.total_output_tokens,
|
||||
"estimated_cost": self.estimated_cost,
|
||||
"requests_by_task": self.requests_by_task,
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class MonthlyUsageSummary:
|
||||
"""
|
||||
Summary of AI usage for a specific month.
|
||||
|
||||
Used for billing and cost projections.
|
||||
|
||||
Attributes:
|
||||
year: Year
|
||||
month: Month (1-12)
|
||||
user_id: User ID
|
||||
total_requests: Number of AI requests made
|
||||
total_tokens: Total tokens consumed
|
||||
estimated_cost: Total estimated cost in USD
|
||||
daily_breakdown: List of daily summaries
|
||||
"""
|
||||
|
||||
year: int
|
||||
month: int
|
||||
user_id: str
|
||||
total_requests: int
|
||||
total_tokens: int
|
||||
estimated_cost: float
|
||||
daily_breakdown: list = field(default_factory=list)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Convert summary to dictionary."""
|
||||
return {
|
||||
"year": self.year,
|
||||
"month": self.month,
|
||||
"user_id": self.user_id,
|
||||
"total_requests": self.total_requests,
|
||||
"total_tokens": self.total_tokens,
|
||||
"estimated_cost": self.estimated_cost,
|
||||
"daily_breakdown": self.daily_breakdown,
|
||||
}
|
||||
Reference in New Issue
Block a user