feat(schemas): T30 add Pydantic statistics schemas
Add comprehensive Pydantic schemas for statistics management: - UsageStatsCreate: input validation for creating usage stats - UsageStatsResponse: orm_mode response schema - StatsSummary: aggregated statistics with totals and averages - StatsByModel: per-model breakdown with percentages - StatsByDate: daily usage aggregation - DashboardResponse: complete dashboard data structure All schemas use Decimal for cost precision and proper validation. Test: 16 unit tests, 100% coverage on stats.py
This commit is contained in:
@@ -12,6 +12,14 @@ from openrouter_monitor.schemas.auth import (
|
||||
UserRegister,
|
||||
UserResponse,
|
||||
)
|
||||
from openrouter_monitor.schemas.stats import (
|
||||
DashboardResponse,
|
||||
StatsByDate,
|
||||
StatsByModel,
|
||||
StatsSummary,
|
||||
UsageStatsCreate,
|
||||
UsageStatsResponse,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"UserRegister",
|
||||
@@ -23,4 +31,10 @@ __all__ = [
|
||||
"ApiKeyUpdate",
|
||||
"ApiKeyResponse",
|
||||
"ApiKeyListResponse",
|
||||
"UsageStatsCreate",
|
||||
"UsageStatsResponse",
|
||||
"StatsSummary",
|
||||
"StatsByModel",
|
||||
"StatsByDate",
|
||||
"DashboardResponse",
|
||||
]
|
||||
|
||||
279
src/openrouter_monitor/schemas/stats.py
Normal file
279
src/openrouter_monitor/schemas/stats.py
Normal file
@@ -0,0 +1,279 @@
|
||||
"""Statistics Pydantic schemas for OpenRouter API Key Monitor.
|
||||
|
||||
T30: Pydantic schemas for statistics management.
|
||||
"""
|
||||
import datetime
|
||||
from decimal import Decimal
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class UsageStatsCreate(BaseModel):
|
||||
"""Schema for creating usage statistics.
|
||||
|
||||
Attributes:
|
||||
api_key_id: Foreign key to api_keys table
|
||||
date: Date of the statistics
|
||||
model: AI model name
|
||||
requests_count: Number of requests (default 0)
|
||||
tokens_input: Number of input tokens (default 0)
|
||||
tokens_output: Number of output tokens (default 0)
|
||||
cost: Cost in USD (default 0)
|
||||
"""
|
||||
|
||||
api_key_id: int = Field(
|
||||
...,
|
||||
description="Foreign key to api_keys table",
|
||||
examples=[1]
|
||||
)
|
||||
date: datetime.date = Field(
|
||||
...,
|
||||
description="Date of the statistics",
|
||||
examples=["2024-01-15"]
|
||||
)
|
||||
model: str = Field(
|
||||
...,
|
||||
min_length=1,
|
||||
max_length=100,
|
||||
description="AI model name",
|
||||
examples=["gpt-4"]
|
||||
)
|
||||
requests_count: int = Field(
|
||||
default=0,
|
||||
ge=0,
|
||||
description="Number of requests",
|
||||
examples=[100]
|
||||
)
|
||||
tokens_input: int = Field(
|
||||
default=0,
|
||||
ge=0,
|
||||
description="Number of input tokens",
|
||||
examples=[5000]
|
||||
)
|
||||
tokens_output: int = Field(
|
||||
default=0,
|
||||
ge=0,
|
||||
description="Number of output tokens",
|
||||
examples=[3000]
|
||||
)
|
||||
cost: Decimal = Field(
|
||||
default=Decimal("0"),
|
||||
ge=0,
|
||||
description="Cost in USD",
|
||||
examples=["0.123456"]
|
||||
)
|
||||
|
||||
|
||||
class UsageStatsResponse(BaseModel):
|
||||
"""Schema for usage statistics response (returned to client).
|
||||
|
||||
Attributes:
|
||||
id: Primary key
|
||||
api_key_id: Foreign key to api_keys table
|
||||
date: Date of the statistics
|
||||
model: AI model name
|
||||
requests_count: Number of requests
|
||||
tokens_input: Number of input tokens
|
||||
tokens_output: Number of output tokens
|
||||
cost: Cost in USD
|
||||
created_at: Timestamp when record was created
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int = Field(
|
||||
...,
|
||||
description="Primary key",
|
||||
examples=[1]
|
||||
)
|
||||
api_key_id: int = Field(
|
||||
...,
|
||||
description="Foreign key to api_keys table",
|
||||
examples=[2]
|
||||
)
|
||||
date: datetime.date = Field(
|
||||
...,
|
||||
description="Date of the statistics",
|
||||
examples=["2024-01-15"]
|
||||
)
|
||||
model: str = Field(
|
||||
...,
|
||||
description="AI model name",
|
||||
examples=["gpt-4"]
|
||||
)
|
||||
requests_count: int = Field(
|
||||
...,
|
||||
description="Number of requests",
|
||||
examples=[100]
|
||||
)
|
||||
tokens_input: int = Field(
|
||||
...,
|
||||
description="Number of input tokens",
|
||||
examples=[5000]
|
||||
)
|
||||
tokens_output: int = Field(
|
||||
...,
|
||||
description="Number of output tokens",
|
||||
examples=[3000]
|
||||
)
|
||||
cost: Decimal = Field(
|
||||
...,
|
||||
description="Cost in USD",
|
||||
examples=["0.123456"]
|
||||
)
|
||||
created_at: datetime.datetime = Field(
|
||||
...,
|
||||
description="Timestamp when record was created",
|
||||
examples=["2024-01-15T12:00:00"]
|
||||
)
|
||||
|
||||
|
||||
class StatsSummary(BaseModel):
|
||||
"""Schema for aggregated statistics summary.
|
||||
|
||||
Attributes:
|
||||
total_requests: Total number of requests
|
||||
total_cost: Total cost in USD
|
||||
total_tokens_input: Total input tokens
|
||||
total_tokens_output: Total output tokens
|
||||
avg_cost_per_request: Average cost per request
|
||||
period_days: Number of days in the period
|
||||
"""
|
||||
|
||||
total_requests: int = Field(
|
||||
...,
|
||||
ge=0,
|
||||
description="Total number of requests",
|
||||
examples=[1000]
|
||||
)
|
||||
total_cost: Decimal = Field(
|
||||
...,
|
||||
ge=0,
|
||||
description="Total cost in USD",
|
||||
examples=["5.678901"]
|
||||
)
|
||||
total_tokens_input: int = Field(
|
||||
default=0,
|
||||
ge=0,
|
||||
description="Total input tokens",
|
||||
examples=[50000]
|
||||
)
|
||||
total_tokens_output: int = Field(
|
||||
default=0,
|
||||
ge=0,
|
||||
description="Total output tokens",
|
||||
examples=[30000]
|
||||
)
|
||||
avg_cost_per_request: Decimal = Field(
|
||||
default=Decimal("0"),
|
||||
ge=0,
|
||||
description="Average cost per request",
|
||||
examples=["0.005679"]
|
||||
)
|
||||
period_days: int = Field(
|
||||
default=0,
|
||||
ge=0,
|
||||
description="Number of days in the period",
|
||||
examples=[30]
|
||||
)
|
||||
|
||||
|
||||
class StatsByModel(BaseModel):
|
||||
"""Schema for statistics grouped by model.
|
||||
|
||||
Attributes:
|
||||
model: AI model name
|
||||
requests_count: Number of requests for this model
|
||||
cost: Total cost for this model
|
||||
percentage_requests: Percentage of total requests
|
||||
percentage_cost: Percentage of total cost
|
||||
"""
|
||||
|
||||
model: str = Field(
|
||||
...,
|
||||
description="AI model name",
|
||||
examples=["gpt-4"]
|
||||
)
|
||||
requests_count: int = Field(
|
||||
...,
|
||||
ge=0,
|
||||
description="Number of requests for this model",
|
||||
examples=[500]
|
||||
)
|
||||
cost: Decimal = Field(
|
||||
...,
|
||||
ge=0,
|
||||
description="Total cost for this model",
|
||||
examples=["3.456789"]
|
||||
)
|
||||
percentage_requests: float = Field(
|
||||
default=0.0,
|
||||
ge=0,
|
||||
le=100,
|
||||
description="Percentage of total requests",
|
||||
examples=[50.0]
|
||||
)
|
||||
percentage_cost: float = Field(
|
||||
default=0.0,
|
||||
ge=0,
|
||||
le=100,
|
||||
description="Percentage of total cost",
|
||||
examples=[60.5]
|
||||
)
|
||||
|
||||
|
||||
class StatsByDate(BaseModel):
|
||||
"""Schema for statistics grouped by date.
|
||||
|
||||
Attributes:
|
||||
date: Date of the statistics
|
||||
requests_count: Number of requests on this date
|
||||
cost: Total cost on this date
|
||||
"""
|
||||
|
||||
date: datetime.date = Field(
|
||||
...,
|
||||
description="Date of the statistics",
|
||||
examples=["2024-01-15"]
|
||||
)
|
||||
requests_count: int = Field(
|
||||
...,
|
||||
ge=0,
|
||||
description="Number of requests on this date",
|
||||
examples=[100]
|
||||
)
|
||||
cost: Decimal = Field(
|
||||
...,
|
||||
ge=0,
|
||||
description="Total cost on this date",
|
||||
examples=["0.567890"]
|
||||
)
|
||||
|
||||
|
||||
class DashboardResponse(BaseModel):
|
||||
"""Schema for complete dashboard response.
|
||||
|
||||
Attributes:
|
||||
summary: Aggregated statistics summary
|
||||
by_model: Statistics grouped by model
|
||||
by_date: Statistics grouped by date
|
||||
top_models: List of top used models
|
||||
"""
|
||||
|
||||
summary: StatsSummary = Field(
|
||||
...,
|
||||
description="Aggregated statistics summary"
|
||||
)
|
||||
by_model: List[StatsByModel] = Field(
|
||||
...,
|
||||
description="Statistics grouped by model"
|
||||
)
|
||||
by_date: List[StatsByDate] = Field(
|
||||
...,
|
||||
description="Statistics grouped by date"
|
||||
)
|
||||
top_models: List[str] = Field(
|
||||
default_factory=list,
|
||||
description="List of top used models"
|
||||
)
|
||||
Reference in New Issue
Block a user