apac-ops-bot/backend/app/models/token_usage.py
SamoilenkoVadym a8151fbe66 Add comprehensive backend test suite and Phase 1 foundation
Backend Tests:
- Add pytest configuration with async support (conftest.py)
- Add model tests: User, Conversation, Message, TokenUsage, Session, UserMemory
- Add configuration tests: Settings validation and environment variables
- Add API tests: Health endpoint and future endpoint stubs
- Add database tests: Connection, transactions, query execution

Phase 1 Foundation:
- FastAPI application structure with main.py
- SQLAlchemy async models for all entities
- Alembic migrations setup
- Configuration management via Pydantic Settings
- Logging system (English only)
- Docker multi-stage builds for backend
- Docker Compose orchestration (PostgreSQL, Redis, backend)
- Frontend React + TypeScript structure
- Dark & Gold theme CSS implementation
- Environment configuration examples

All code and comments in English as per requirements.
Tests cover model relationships, cascade deletes, and constraints.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-27 14:24:59 +00:00

63 lines
2 KiB
Python

"""
TokenUsage model for tracking OpenAI API token consumption
"""
from sqlalchemy import Column, String, Integer, Numeric, DateTime, JSON, ForeignKey
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
import uuid
from app.database import Base
class TokenUsage(Base):
"""
TokenUsage model for tracking and analyzing token consumption and costs
"""
__tablename__ = "token_usage"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
user_id = Column(
UUID(as_uuid=True),
ForeignKey("users.id", ondelete="CASCADE"),
nullable=False,
index=True
)
conversation_id = Column(
UUID(as_uuid=True),
ForeignKey("conversations.id", ondelete="SET NULL"),
index=True
)
message_id = Column(
UUID(as_uuid=True),
ForeignKey("messages.id", ondelete="SET NULL"),
index=True
)
# Token counts
prompt_tokens = Column(Integer, default=0, nullable=False)
completion_tokens = Column(Integer, default=0, nullable=False)
total_tokens = Column(Integer, default=0, nullable=False)
# Model and cost information
model = Column(String(100), nullable=False) # e.g., gpt-5-nano-2025-08-07
cost_usd = Column(Numeric(10, 6), default=0.0, nullable=False) # Cost in USD
# Operation type (chat, search, etc.)
operation_type = Column(String(50))
# Timestamp
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False, index=True)
# JSON field for additional metadata
metadata = Column(JSON, default=dict, nullable=False)
# Relationships
user = relationship("User", back_populates="token_usage")
conversation = relationship("Conversation", back_populates="token_usage")
message = relationship("Message", back_populates="token_usage_records")
def __repr__(self):
return f"<TokenUsage {self.id} - {self.total_tokens} tokens>"