240 lines
5.7 KiB
Python
240 lines
5.7 KiB
Python
"""Pydantic schemas for API request/response."""
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
from typing import Any, Dict, List, Optional
|
|
from uuid import UUID
|
|
|
|
from pydantic import BaseModel, ConfigDict, EmailStr, Field
|
|
|
|
|
|
# === Auth ===
|
|
class UserRegister(BaseModel):
|
|
email: EmailStr
|
|
username: str = Field(min_length=3, max_length=64)
|
|
password: str = Field(min_length=6, max_length=128)
|
|
|
|
|
|
class UserLogin(BaseModel):
|
|
# Accepts either email or username — the backend resolves it.
|
|
login: str
|
|
password: str
|
|
|
|
|
|
class UserOut(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: UUID
|
|
email: EmailStr
|
|
username: str
|
|
is_admin: bool
|
|
is_active: bool
|
|
preferred_language: str
|
|
created_at: datetime
|
|
|
|
|
|
class TokenOut(BaseModel):
|
|
access_token: str
|
|
token_type: str = "bearer"
|
|
user: UserOut
|
|
|
|
|
|
class AdminSetupRequest(BaseModel):
|
|
token: str
|
|
email: EmailStr
|
|
username: str = Field(min_length=3, max_length=64)
|
|
password: str = Field(min_length=6, max_length=128)
|
|
|
|
|
|
# === Settings ===
|
|
class SettingsUpdate(BaseModel):
|
|
values: Dict[str, Any]
|
|
|
|
|
|
class SettingsOut(BaseModel):
|
|
values: Dict[str, Any]
|
|
editable_keys: List[str]
|
|
|
|
|
|
# === Presets ===
|
|
class PresetOut(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: UUID
|
|
slug: str
|
|
title: str
|
|
description: Optional[str] = None
|
|
language: str
|
|
is_public: bool
|
|
is_builtin: bool
|
|
payload: Dict[str, Any]
|
|
created_at: datetime
|
|
|
|
|
|
class PresetCreate(BaseModel):
|
|
slug: str
|
|
title: str
|
|
description: Optional[str] = None
|
|
language: str = "en"
|
|
is_public: bool = True
|
|
payload: Dict[str, Any]
|
|
|
|
|
|
# === Worlds ===
|
|
class WorldCreate(BaseModel):
|
|
name: str = Field(min_length=1, max_length=255)
|
|
language: str = "en"
|
|
preset_id: Optional[UUID] = None
|
|
|
|
|
|
class WorldDefinition(BaseModel):
|
|
setting_description: str = ""
|
|
rules: Dict[str, Any] = Field(default_factory=dict)
|
|
world_schema: Dict[str, Any] = Field(default_factory=dict)
|
|
plot_rails: Dict[str, Any] = Field(default_factory=dict)
|
|
initial_state: Dict[str, Any] = Field(default_factory=dict)
|
|
initial_time: Optional[str] = None
|
|
|
|
|
|
class WorldOut(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: UUID
|
|
owner_id: UUID
|
|
name: str
|
|
language: str
|
|
definition: Dict[str, Any]
|
|
state: Dict[str, Any]
|
|
current_time: Optional[str]
|
|
status: str
|
|
preset_id: Optional[UUID] = None
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class WorldUpdate(BaseModel):
|
|
name: Optional[str] = None
|
|
definition: Optional[Dict[str, Any]] = None
|
|
state: Optional[Dict[str, Any]] = None
|
|
current_time: Optional[str] = None
|
|
status: Optional[str] = None
|
|
|
|
|
|
# === World Builder ===
|
|
class WorldBuilderStart(BaseModel):
|
|
"""Kick off a new world-building conversation."""
|
|
world_name: str = Field(min_length=1, max_length=255)
|
|
language: str = "en"
|
|
# Either pick a preset to start from, or fill the freeform brief.
|
|
preset_id: Optional[UUID] = None
|
|
setting_brief: str = ""
|
|
character_brief: str = ""
|
|
rules_brief: str = ""
|
|
notes: str = ""
|
|
|
|
|
|
class WorldBuilderMessage(BaseModel):
|
|
"""User reply in the world-builder dialogue."""
|
|
session_id: UUID
|
|
message: str
|
|
|
|
|
|
class WorldBuilderReply(BaseModel):
|
|
"""AI reply in the world-builder dialogue."""
|
|
session_id: UUID
|
|
turn: int
|
|
ai_message: str
|
|
proposed_definition: Optional[WorldDefinition] = None
|
|
is_final: bool = False # True when AI thinks world is ready to commit
|
|
followup_questions: List[str] = Field(default_factory=list)
|
|
|
|
|
|
class WorldBuilderCommit(BaseModel):
|
|
"""User accepts the proposed world definition and creates the world."""
|
|
session_id: UUID
|
|
name: Optional[str] = None
|
|
|
|
|
|
# === Sessions ===
|
|
class SessionOut(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: UUID
|
|
world_id: UUID
|
|
title: str
|
|
is_active: bool
|
|
created_at: datetime
|
|
last_played_at: Optional[datetime] = None
|
|
|
|
|
|
class SessionCreate(BaseModel):
|
|
world_id: UUID
|
|
title: Optional[str] = None
|
|
|
|
|
|
# === Messages ===
|
|
class MessageOut(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: UUID
|
|
seq: int
|
|
role: str
|
|
kind: str
|
|
content: str
|
|
payload: Dict[str, Any]
|
|
is_pinned: bool
|
|
hidden: bool
|
|
created_at: datetime
|
|
|
|
|
|
# === Iteration ===
|
|
class IterationRequest(BaseModel):
|
|
"""Player submits an action/choice for the next iteration."""
|
|
session_id: UUID
|
|
action_text: str = Field(min_length=1, max_length=4000)
|
|
|
|
|
|
class IterationEvent(BaseModel):
|
|
"""SSE event sent to the frontend during an iteration."""
|
|
type: str # status / plan / tool_call / tool_result / narrative_chunk / step_complete / error / done
|
|
data: Dict[str, Any] = Field(default_factory=dict)
|
|
|
|
|
|
# === Glossary ===
|
|
class GlossaryEntryOut(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: UUID
|
|
kind: str
|
|
name: str
|
|
description: str
|
|
payload: Dict[str, Any]
|
|
|
|
|
|
# === LLM Logs ===
|
|
class LlmLogOut(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: UUID
|
|
purpose: str
|
|
model: str
|
|
base_url: str
|
|
prompt_tokens: Optional[int]
|
|
completion_tokens: Optional[int]
|
|
total_tokens: Optional[int]
|
|
latency_ms: Optional[int]
|
|
error: Optional[str]
|
|
created_at: datetime
|
|
|
|
|
|
# === Triggers ===
|
|
class TriggerOut(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: UUID
|
|
session_id: UUID
|
|
fire_at: str
|
|
description: str
|
|
payload: Dict[str, Any]
|
|
fired: bool
|
|
created_at: datetime
|
|
|
|
|
|
class TriggerCreate(BaseModel):
|
|
session_id: UUID
|
|
fire_at: str
|
|
description: str
|
|
payload: Dict[str, Any] = Field(default_factory=dict)
|