feat(schemas): T35 add Pydantic public API schemas
- PublicStatsResponse: summary + period info - PublicUsageResponse: paginated usage items - PublicKeyInfo: key metadata with stats (no values!) - ApiToken schemas: create, response, create-response - 25 unit tests, 100% coverage
This commit is contained in:
@@ -20,6 +20,19 @@ from openrouter_monitor.schemas.stats import (
|
||||
UsageStatsCreate,
|
||||
UsageStatsResponse,
|
||||
)
|
||||
from openrouter_monitor.schemas.public_api import (
|
||||
ApiTokenCreate,
|
||||
ApiTokenCreateResponse,
|
||||
ApiTokenResponse,
|
||||
PaginationInfo,
|
||||
PeriodInfo,
|
||||
PublicKeyInfo,
|
||||
PublicKeyListResponse,
|
||||
PublicStatsResponse,
|
||||
PublicUsageItem,
|
||||
PublicUsageResponse,
|
||||
SummaryInfo,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"UserRegister",
|
||||
@@ -37,4 +50,16 @@ __all__ = [
|
||||
"StatsByModel",
|
||||
"StatsByDate",
|
||||
"DashboardResponse",
|
||||
# Public API schemas
|
||||
"ApiTokenCreate",
|
||||
"ApiTokenCreateResponse",
|
||||
"ApiTokenResponse",
|
||||
"PublicStatsResponse",
|
||||
"PublicUsageResponse",
|
||||
"PublicKeyInfo",
|
||||
"PublicKeyListResponse",
|
||||
"SummaryInfo",
|
||||
"PeriodInfo",
|
||||
"PublicUsageItem",
|
||||
"PaginationInfo",
|
||||
]
|
||||
|
||||
347
src/openrouter_monitor/schemas/public_api.py
Normal file
347
src/openrouter_monitor/schemas/public_api.py
Normal file
@@ -0,0 +1,347 @@
|
||||
"""Public API Pydantic schemas for OpenRouter API Key Monitor.
|
||||
|
||||
T35: Pydantic schemas for public API endpoints.
|
||||
These schemas define the data structures for the public API v1 endpoints.
|
||||
"""
|
||||
import datetime
|
||||
from decimal import Decimal
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
||||
|
||||
|
||||
class ApiTokenCreate(BaseModel):
|
||||
"""Schema for creating a new API token.
|
||||
|
||||
Attributes:
|
||||
name: Human-readable name for the token (1-100 characters)
|
||||
"""
|
||||
|
||||
name: str = Field(
|
||||
...,
|
||||
min_length=1,
|
||||
max_length=100,
|
||||
description="Human-readable name for the token",
|
||||
examples=["Production API Token", "Development Key"]
|
||||
)
|
||||
|
||||
|
||||
class ApiTokenResponse(BaseModel):
|
||||
"""Schema for API token response (returned to client).
|
||||
|
||||
IMPORTANT: This schema does NOT include the token value for security.
|
||||
The plaintext token is only shown once at creation time (ApiTokenCreateResponse).
|
||||
|
||||
Attributes:
|
||||
id: Token ID
|
||||
name: Token name
|
||||
created_at: Creation timestamp
|
||||
last_used_at: Last usage timestamp (None if never used)
|
||||
is_active: Whether the token is active
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int = Field(
|
||||
...,
|
||||
description="Token ID",
|
||||
examples=[1]
|
||||
)
|
||||
name: str = Field(
|
||||
...,
|
||||
description="Token name",
|
||||
examples=["Production Token"]
|
||||
)
|
||||
created_at: datetime.datetime = Field(
|
||||
...,
|
||||
description="Creation timestamp",
|
||||
examples=["2024-01-15T12:00:00"]
|
||||
)
|
||||
last_used_at: Optional[datetime.datetime] = Field(
|
||||
default=None,
|
||||
description="Last usage timestamp (None if never used)",
|
||||
examples=["2024-01-20T15:30:00"]
|
||||
)
|
||||
is_active: bool = Field(
|
||||
...,
|
||||
description="Whether the token is active",
|
||||
examples=[True]
|
||||
)
|
||||
|
||||
|
||||
class ApiTokenCreateResponse(BaseModel):
|
||||
"""Schema for API token creation response.
|
||||
|
||||
IMPORTANT: This is the ONLY time the plaintext token is shown.
|
||||
After creation, the token cannot be retrieved again.
|
||||
|
||||
Attributes:
|
||||
id: Token ID
|
||||
name: Token name
|
||||
token: Plaintext token (shown only once!)
|
||||
created_at: Creation timestamp
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int = Field(
|
||||
...,
|
||||
description="Token ID",
|
||||
examples=[1]
|
||||
)
|
||||
name: str = Field(
|
||||
...,
|
||||
description="Token name",
|
||||
examples=["Production Token"]
|
||||
)
|
||||
token: str = Field(
|
||||
...,
|
||||
description="Plaintext token (shown only once at creation!)",
|
||||
examples=["or_api_abc123xyz789def456"]
|
||||
)
|
||||
created_at: datetime.datetime = Field(
|
||||
...,
|
||||
description="Creation timestamp",
|
||||
examples=["2024-01-15T12:00:00"]
|
||||
)
|
||||
|
||||
|
||||
class SummaryInfo(BaseModel):
|
||||
"""Schema for statistics summary.
|
||||
|
||||
Attributes:
|
||||
total_requests: Total number of requests
|
||||
total_cost: Total cost in USD
|
||||
total_tokens: Total tokens (input + output)
|
||||
"""
|
||||
|
||||
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: int = Field(
|
||||
default=0,
|
||||
ge=0,
|
||||
description="Total tokens (input + output)",
|
||||
examples=[50000]
|
||||
)
|
||||
|
||||
|
||||
class PeriodInfo(BaseModel):
|
||||
"""Schema for statistics period information.
|
||||
|
||||
Attributes:
|
||||
start_date: Start date of the period
|
||||
end_date: End date of the period
|
||||
days: Number of days in the period
|
||||
"""
|
||||
|
||||
start_date: datetime.date = Field(
|
||||
...,
|
||||
description="Start date of the period",
|
||||
examples=["2024-01-01"]
|
||||
)
|
||||
end_date: datetime.date = Field(
|
||||
...,
|
||||
description="End date of the period",
|
||||
examples=["2024-01-31"]
|
||||
)
|
||||
days: int = Field(
|
||||
...,
|
||||
ge=0,
|
||||
description="Number of days in the period",
|
||||
examples=[30]
|
||||
)
|
||||
|
||||
|
||||
class PublicStatsResponse(BaseModel):
|
||||
"""Schema for public API stats response.
|
||||
|
||||
Attributes:
|
||||
summary: Aggregated statistics summary
|
||||
period: Period information (start_date, end_date, days)
|
||||
"""
|
||||
|
||||
summary: SummaryInfo = Field(
|
||||
...,
|
||||
description="Aggregated statistics summary"
|
||||
)
|
||||
period: PeriodInfo = Field(
|
||||
...,
|
||||
description="Period information (start_date, end_date, days)"
|
||||
)
|
||||
|
||||
|
||||
class PublicUsageItem(BaseModel):
|
||||
"""Schema for a single usage item in public API.
|
||||
|
||||
IMPORTANT: This only includes the API key NAME, not the actual key value.
|
||||
|
||||
Attributes:
|
||||
date: Date of the statistics
|
||||
api_key_name: Name of the API key (not the value!)
|
||||
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
|
||||
"""
|
||||
|
||||
date: datetime.date = Field(
|
||||
...,
|
||||
description="Date of the statistics",
|
||||
examples=["2024-01-15"]
|
||||
)
|
||||
api_key_name: str = Field(
|
||||
...,
|
||||
description="Name of the API key (not the value!)",
|
||||
examples=["Production Key"]
|
||||
)
|
||||
model: str = Field(
|
||||
...,
|
||||
description="AI model name",
|
||||
examples=["gpt-4"]
|
||||
)
|
||||
requests_count: int = Field(
|
||||
...,
|
||||
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(
|
||||
...,
|
||||
ge=0,
|
||||
description="Cost in USD",
|
||||
examples=["0.123456"]
|
||||
)
|
||||
|
||||
|
||||
class PaginationInfo(BaseModel):
|
||||
"""Schema for pagination information.
|
||||
|
||||
Attributes:
|
||||
page: Current page number (1-indexed)
|
||||
limit: Items per page
|
||||
total: Total number of items
|
||||
pages: Total number of pages
|
||||
"""
|
||||
|
||||
page: int = Field(
|
||||
...,
|
||||
ge=1,
|
||||
description="Current page number (1-indexed)",
|
||||
examples=[1]
|
||||
)
|
||||
limit: int = Field(
|
||||
...,
|
||||
ge=1,
|
||||
description="Items per page",
|
||||
examples=[100]
|
||||
)
|
||||
total: int = Field(
|
||||
...,
|
||||
ge=0,
|
||||
description="Total number of items",
|
||||
examples=[250]
|
||||
)
|
||||
pages: int = Field(
|
||||
...,
|
||||
ge=0,
|
||||
description="Total number of pages",
|
||||
examples=[3]
|
||||
)
|
||||
|
||||
|
||||
class PublicUsageResponse(BaseModel):
|
||||
"""Schema for public API usage response with pagination.
|
||||
|
||||
Attributes:
|
||||
items: List of usage items
|
||||
pagination: Pagination information
|
||||
"""
|
||||
|
||||
items: List[PublicUsageItem] = Field(
|
||||
...,
|
||||
description="List of usage items"
|
||||
)
|
||||
pagination: PaginationInfo = Field(
|
||||
...,
|
||||
description="Pagination information"
|
||||
)
|
||||
|
||||
|
||||
class PublicKeyInfo(BaseModel):
|
||||
"""Schema for public API key information.
|
||||
|
||||
IMPORTANT: This schema does NOT include the actual API key value,
|
||||
only metadata and aggregated statistics.
|
||||
|
||||
Attributes:
|
||||
id: Key ID
|
||||
name: Key name
|
||||
is_active: Whether the key is active
|
||||
stats: Aggregated statistics (total_requests, total_cost)
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int = Field(
|
||||
...,
|
||||
description="Key ID",
|
||||
examples=[1]
|
||||
)
|
||||
name: str = Field(
|
||||
...,
|
||||
description="Key name",
|
||||
examples=["Production Key"]
|
||||
)
|
||||
is_active: bool = Field(
|
||||
...,
|
||||
description="Whether the key is active",
|
||||
examples=[True]
|
||||
)
|
||||
stats: Dict = Field(
|
||||
...,
|
||||
description="Aggregated statistics (total_requests, total_cost)",
|
||||
examples=[{"total_requests": 1000, "total_cost": "5.50"}]
|
||||
)
|
||||
|
||||
|
||||
class PublicKeyListResponse(BaseModel):
|
||||
"""Schema for public API key list response.
|
||||
|
||||
Attributes:
|
||||
items: List of API keys with statistics
|
||||
total: Total number of keys
|
||||
"""
|
||||
|
||||
items: List[PublicKeyInfo] = Field(
|
||||
...,
|
||||
description="List of API keys with statistics"
|
||||
)
|
||||
total: int = Field(
|
||||
...,
|
||||
ge=0,
|
||||
description="Total number of keys",
|
||||
examples=[5]
|
||||
)
|
||||
Reference in New Issue
Block a user