"""Request/response schemas. NFR-MAINT-01 契约先行: schema 先定义,客户端类型由 OpenAPI 生成。 Wire responses are wrapped by the unified envelope (架构文档 §9.2); these models describe the ``data`` payload only. """ from __future__ import annotations from datetime import datetime from pydantic import BaseModel, Field class DevTokenRequest(BaseModel): device_key: str = Field( default="local-dev", min_length=1, max_length=64, description="任意稳定字符串;同一 device_key 复用同一测试用户", ) class DevTokenResponse(BaseModel): access_token: str token_type: str = "Bearer" expires_in: int user_id: str = Field(description="雪花 ID 以字符串返回,避免 JS 精度丢失") class ChatRequest(BaseModel): message: str = Field(min_length=1, max_length=2000) request_key: str | None = Field( default=None, max_length=128, description="幂等键;相同键不会重复扣配额 (架构文档 §9.1)", ) class ChatUsage(BaseModel): tokens_in: int tokens_out: int tokens_cached: int latency_ms: int cost_cents: str class ChatResponse(BaseModel): reply: str provider: str model: str prompt_id: str | None prompt_version: str | None call_id: str usage: ChatUsage quota_remaining: int class QuotaResponse(BaseModel): quota_key: str period_key: str used: int limit_value: int bonus_granted: int remaining: int reset_at: datetime | None class LedgerEntry(BaseModel): tx_id: str delta: int tx_type: str status: str reason: str | None quota_key: str | None period_key: str | None balance_after: int created_at: datetime class TaskSubmitRequest(BaseModel): echo: str = Field(default="ping", max_length=200) idempotency_key: str | None = Field(default=None, max_length=128) class TaskResponse(BaseModel): task_id: str task_type: str status: str progress: int stage_text: str | None result: dict[str, object] | None error_code: int | None error_message: str | None retry_count: int submitted_at: datetime started_at: datetime | None finished_at: datetime | None