rebase
This commit is contained in:
286
app/schemas/__init__.py
Normal file
286
app/schemas/__init__.py
Normal file
@@ -0,0 +1,286 @@
|
||||
"""Pydantic schemas (request/response) for the API layer.
|
||||
|
||||
These are NOT the same as the world's JSON-schema — see `app/core/state_validator`
|
||||
for world-schema validation. Pydantic here only handles HTTP boundary validation.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from typing import Any, Literal
|
||||
|
||||
from pydantic import BaseModel, EmailStr, Field, field_validator
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Auth
|
||||
# --------------------------------------------------------------------------- #
|
||||
class RegisterRequest(BaseModel):
|
||||
email: EmailStr
|
||||
username: str = Field(min_length=3, max_length=64, pattern=r"^[a-zA-Z0-9_]+$")
|
||||
password: str = Field(min_length=8, max_length=128)
|
||||
password_confirm: str = Field(min_length=8, max_length=128)
|
||||
|
||||
@field_validator("password_confirm")
|
||||
@classmethod
|
||||
def _match(cls, v, info):
|
||||
if "password" in info.data and v != info.data["password"]:
|
||||
raise ValueError("password and password_confirm do not match")
|
||||
return v
|
||||
|
||||
|
||||
class AdminRegisterRequest(BaseModel):
|
||||
token: str
|
||||
email: EmailStr
|
||||
username: str = Field(min_length=3, max_length=64, pattern=r"^[a-zA-Z0-9_]+$")
|
||||
password: str = Field(min_length=8, max_length=128)
|
||||
password_confirm: str = Field(min_length=8, max_length=128)
|
||||
|
||||
@field_validator("password_confirm")
|
||||
@classmethod
|
||||
def _match(cls, v, info):
|
||||
if "password" in info.data and v != info.data["password"]:
|
||||
raise ValueError("password and password_confirm do not match")
|
||||
return v
|
||||
|
||||
|
||||
class LoginRequest(BaseModel):
|
||||
login: str = Field(min_length=1, max_length=255) # email OR username
|
||||
password: str = Field(min_length=1, max_length=128)
|
||||
|
||||
|
||||
class TokenResponse(BaseModel):
|
||||
access_token: str
|
||||
refresh_token: str
|
||||
token_type: str = "bearer"
|
||||
expires_in: int = 60 * 24
|
||||
user: "UserPublic"
|
||||
|
||||
|
||||
class UserPublic(BaseModel):
|
||||
id: uuid.UUID
|
||||
email: EmailStr
|
||||
username: str
|
||||
is_admin: bool
|
||||
is_active: bool
|
||||
created_at: datetime
|
||||
last_login_at: datetime | None = None
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Worlds
|
||||
# --------------------------------------------------------------------------- #
|
||||
class WorldSummary(BaseModel):
|
||||
id: uuid.UUID
|
||||
name: str
|
||||
description: str | None
|
||||
language: str
|
||||
status: str
|
||||
last_played_at: datetime | None
|
||||
current_time: str
|
||||
created_at: datetime
|
||||
preview_player_name: str | None = None
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class WorldFull(BaseModel):
|
||||
id: uuid.UUID
|
||||
owner_id: uuid.UUID
|
||||
preset_id: uuid.UUID | None
|
||||
name: str
|
||||
description: str | None
|
||||
language: str
|
||||
rules: list
|
||||
time_schema: dict
|
||||
schemas: list
|
||||
environment_schema: list
|
||||
environment: dict
|
||||
plot_rails: dict
|
||||
current_time: str
|
||||
status: str
|
||||
intro_scene: str | None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
last_played_at: datetime | None
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class WorldCreateRequest(BaseModel):
|
||||
mode: Literal["preset", "form"]
|
||||
preset_id: uuid.UUID | None = None
|
||||
form_data: dict | None = None
|
||||
name: str = Field(min_length=1, max_length=255)
|
||||
language: str = Field(min_length=2, max_length=8, default="en")
|
||||
player_name: str = Field(min_length=1, max_length=128)
|
||||
notes: str | None = None
|
||||
|
||||
|
||||
class WorldPatchRequest(BaseModel):
|
||||
name: str | None = None
|
||||
description: str | None = None
|
||||
rules: list | None = None
|
||||
schemas: list | None = None
|
||||
environment_schema: list | None = None
|
||||
environment: dict | None = None
|
||||
plot_rails: dict | None = None
|
||||
time_schema: dict | None = None
|
||||
current_time: str | None = None
|
||||
intro_scene: str | None = None
|
||||
status: str | None = None
|
||||
updated_at: datetime | None = None # for optimistic locking
|
||||
|
||||
|
||||
class WorldEditRequest(BaseModel):
|
||||
instruction: str = Field(min_length=1, max_length=4000)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Sessions
|
||||
# --------------------------------------------------------------------------- #
|
||||
class IterateRequest(BaseModel):
|
||||
action: str = Field(min_length=1, max_length=4000)
|
||||
action_source: Literal["custom", "suggested"] = "custom"
|
||||
|
||||
|
||||
class AnswerRequest(BaseModel):
|
||||
text: str = Field(min_length=1, max_length=4000)
|
||||
|
||||
|
||||
class SessionState(BaseModel):
|
||||
world: dict
|
||||
environment: dict
|
||||
recent_steps: list[dict]
|
||||
next_actions: list[str]
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Presets
|
||||
# --------------------------------------------------------------------------- #
|
||||
class PresetSummary(BaseModel):
|
||||
id: uuid.UUID
|
||||
name: str
|
||||
description: str | None
|
||||
language: str
|
||||
is_public: bool
|
||||
status: str
|
||||
version: int
|
||||
created_at: datetime
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class PresetFull(BaseModel):
|
||||
id: uuid.UUID
|
||||
owner_id: uuid.UUID
|
||||
name: str
|
||||
description: str | None
|
||||
language: str
|
||||
rules: list
|
||||
time_schema: dict
|
||||
schemas: list
|
||||
environment_schema: list
|
||||
environment_initial: dict
|
||||
status: str
|
||||
is_public: bool
|
||||
version: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class PresetCreateRequest(BaseModel):
|
||||
name: str = Field(min_length=1, max_length=255)
|
||||
description: str | None = None
|
||||
language: str = "en"
|
||||
rules: list = Field(default_factory=list)
|
||||
time_schema: dict = Field(default_factory=lambda: {"hours_in_day": 24, "initial_date": "day_1_hour_8"})
|
||||
schemas: list = Field(default_factory=list)
|
||||
environment_schema: list = Field(default_factory=list)
|
||||
environment_initial: dict = Field(default_factory=dict)
|
||||
is_public: bool = False
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Admin
|
||||
# --------------------------------------------------------------------------- #
|
||||
class SettingsPatchRequest(BaseModel):
|
||||
"""A flat dict of {setting_key: value} to upsert."""
|
||||
|
||||
settings: dict[str, Any]
|
||||
|
||||
|
||||
class LlmLogOut(BaseModel):
|
||||
id: uuid.UUID
|
||||
stage: str
|
||||
model: str
|
||||
status: str
|
||||
latency_ms: int | None
|
||||
prompt_tokens: int | None
|
||||
completion_tokens: int | None
|
||||
error_message: str | None
|
||||
created_at: datetime
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class LlmLogDetail(BaseModel):
|
||||
id: uuid.UUID
|
||||
user_id: uuid.UUID | None
|
||||
world_id: uuid.UUID | None
|
||||
step_id: uuid.UUID | None
|
||||
stage: str
|
||||
model: str
|
||||
request_messages: list
|
||||
request_tools: list | None
|
||||
response_message: dict
|
||||
tool_calls: list | None
|
||||
prompt_tokens: int | None
|
||||
completion_tokens: int | None
|
||||
latency_ms: int | None
|
||||
temperature: float | None
|
||||
status: str
|
||||
error_message: str | None
|
||||
created_at: datetime
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class TestLlmRequest(BaseModel):
|
||||
api_url: str | None = None
|
||||
api_key: str | None = None
|
||||
model: str | None = None
|
||||
|
||||
|
||||
class TestEmbeddingsRequest(BaseModel):
|
||||
api_url: str | None = None
|
||||
api_key: str | None = None
|
||||
model: str | None = None
|
||||
provider: str | None = None
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Misc
|
||||
# --------------------------------------------------------------------------- #
|
||||
class HealthResponse(BaseModel):
|
||||
status: str
|
||||
db: bool
|
||||
qdrant: bool
|
||||
llm: bool
|
||||
embeddings: bool
|
||||
version: str
|
||||
|
||||
|
||||
class ErrorOut(BaseModel):
|
||||
error: dict[str, Any]
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Forward refs
|
||||
# --------------------------------------------------------------------------- #
|
||||
TokenResponse.model_rebuild()
|
||||
Reference in New Issue
Block a user