bifroest/app/usage/models.py
2026-10-27 08:34:17 +01:00

33 lines
1.6 KiB
Python

"""Verbrauch je Modellaufruf (→ Konzept 8.1, 13): Tokens und Kosten, unabhängig von Chats.
Einträge bleiben erhalten, wenn Chats oder Workspaces gelöscht werden; die Kosten
werden mit dem Preis zum Zeitpunkt des Aufrufs festgeschrieben.
"""
import uuid
from datetime import datetime
from decimal import Decimal
from sqlalchemy import BigInteger, ForeignKey, Identity, Integer, Numeric, String, Uuid
from sqlalchemy.orm import Mapped, mapped_column
from app.core.database import Base, now
class UsageRecord(Base):
__tablename__ = "usage_records"
id: Mapped[int] = mapped_column(BigInteger, Identity(), primary_key=True)
time: Mapped[datetime] = mapped_column(default=now, index=True)
# Kein Fremdschlüssel: der Run kann mit dem Chat gelöscht werden, der Verbrauch bleibt
run_id: Mapped[uuid.UUID | None] = mapped_column(Uuid, index=True)
team_id: Mapped[int | None] = mapped_column(ForeignKey("teams.id", ondelete="SET NULL"), index=True)
workspace_id: Mapped[int | None] = mapped_column(ForeignKey("workspaces.id", ondelete="SET NULL"), index=True)
user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"))
model: Mapped[str] = mapped_column(String(200))
input_tokens: Mapped[int] = mapped_column(Integer, default=0)
cached_tokens: Mapped[int] = mapped_column(Integer, default=0)
output_tokens: Mapped[int] = mapped_column(Integer, default=0)
reasoning_tokens: Mapped[int] = mapped_column(Integer, default=0)
# Leer, wenn für das Modell kein Preis hinterlegt war
cost: Mapped[Decimal | None] = mapped_column(Numeric(14, 6))