401 lines
10 KiB
Python
401 lines
10 KiB
Python
"""
|
|
GumYum NPC API Shared Data Models and Exceptions
|
|
Cross-engine AI-powered NPC dialogue and quest system
|
|
|
|
This module contains shared data models and exceptions used by both sync and async clients.
|
|
"""
|
|
|
|
from enum import Enum
|
|
from typing import Dict, List, Optional, Any, Union
|
|
from datetime import datetime
|
|
|
|
try:
|
|
from pydantic import BaseModel, Field
|
|
except ImportError:
|
|
BaseModel = None
|
|
Field = None
|
|
|
|
|
|
# ============================================================================
|
|
# EXCEPTIONS
|
|
# ============================================================================
|
|
|
|
|
|
class GumYumError(Exception):
|
|
"""Base exception for all GumYum SDK errors"""
|
|
|
|
def __init__(
|
|
self,
|
|
message: str,
|
|
status_code: Optional[int] = None,
|
|
response_data: Optional[Dict[str, Any]] = None,
|
|
):
|
|
super().__init__(message)
|
|
self.message = message
|
|
self.status_code = status_code
|
|
self.response_data = response_data or {}
|
|
|
|
def __repr__(self) -> str:
|
|
return f"{self.__class__.__name__}({self.message!r}, status_code={self.status_code})"
|
|
|
|
|
|
class GumYumAPIError(GumYumError):
|
|
"""General API error from the server"""
|
|
|
|
pass
|
|
|
|
|
|
class GumYumAuthError(GumYumError):
|
|
"""Authentication or authorization error (401, 403)"""
|
|
|
|
pass
|
|
|
|
|
|
class GumYumNotFoundError(GumYumError):
|
|
"""Resource not found error (404)"""
|
|
|
|
pass
|
|
|
|
|
|
class GumYumValidationError(GumYumError):
|
|
"""Request validation error (400, 422)"""
|
|
|
|
def __init__(
|
|
self,
|
|
message: str,
|
|
status_code: Optional[int] = None,
|
|
response_data: Optional[Dict[str, Any]] = None,
|
|
validation_errors: Optional[list] = None,
|
|
):
|
|
super().__init__(message, status_code, response_data)
|
|
self.validation_errors = validation_errors or []
|
|
|
|
|
|
class GumYumServerError(GumYumError):
|
|
"""Server error (5xx)"""
|
|
|
|
pass
|
|
|
|
|
|
class GumYumNetworkError(GumYumError):
|
|
"""Network connectivity error"""
|
|
|
|
pass
|
|
|
|
|
|
class GumYumTimeoutError(GumYumNetworkError):
|
|
"""Request timeout error"""
|
|
|
|
pass
|
|
|
|
|
|
class GumYumRateLimitError(GumYumAPIError):
|
|
"""Rate limit exceeded (429)"""
|
|
|
|
def __init__(
|
|
self,
|
|
message: str,
|
|
status_code: Optional[int] = None,
|
|
response_data: Optional[Dict[str, Any]] = None,
|
|
retry_after: Optional[int] = None,
|
|
):
|
|
super().__init__(message, status_code, response_data)
|
|
self.retry_after = retry_after # Seconds to wait before retry
|
|
|
|
|
|
# ============================================================================
|
|
# ENUMS
|
|
# ============================================================================
|
|
|
|
|
|
class ChatRole(str, Enum):
|
|
"""Chat message roles"""
|
|
|
|
USER = "user"
|
|
ASSISTANT = "assistant"
|
|
SYSTEM = "system"
|
|
|
|
|
|
# ============================================================================
|
|
# CHAT MODELS
|
|
# ============================================================================
|
|
|
|
|
|
class ChatMessage(BaseModel):
|
|
"""Chat message with role and content"""
|
|
|
|
role: Union[ChatRole, str]
|
|
content: str
|
|
|
|
def to_dict(self) -> dict:
|
|
"""Convert to API-compatible dictionary"""
|
|
return {"role": self.role, "content": self.content}
|
|
|
|
|
|
class ChatChoice(BaseModel):
|
|
"""Chat completion choice"""
|
|
|
|
index: int
|
|
message: ChatMessage
|
|
finish_reason: Optional[str] = None
|
|
|
|
|
|
class Usage(BaseModel):
|
|
"""Token usage information"""
|
|
|
|
prompt_tokens: int
|
|
completion_tokens: int
|
|
total_tokens: int
|
|
# Optional fields that may be None
|
|
prompt_tokens_details: Optional[Dict[str, Any]] = None
|
|
completion_tokens_details: Optional[Dict[str, Any]] = None
|
|
|
|
|
|
class ChatCompletion(BaseModel):
|
|
"""OpenAI-compatible chat completion response"""
|
|
|
|
id: str
|
|
object: str = "chat.completion"
|
|
created: int
|
|
model: str
|
|
choices: List[ChatChoice]
|
|
usage: Optional[Usage] = None
|
|
npc_context: Optional[Dict[str, Any]] = None
|
|
mood_transition: Optional[Dict[str, Any]] = None
|
|
|
|
|
|
# ============================================================================
|
|
# NPC MODELS
|
|
# ============================================================================
|
|
|
|
|
|
class SpawnedData(BaseModel):
|
|
"""Data determined at spawn time"""
|
|
|
|
location: str
|
|
mood: str
|
|
stress_level: int # 1-9
|
|
|
|
|
|
class NPCProfile(BaseModel):
|
|
"""Full NPC profile data returned from save/update operations"""
|
|
|
|
npc_id: int # 64-bit integer NPC ID
|
|
name: str
|
|
profession: str
|
|
age: Optional[int] = None
|
|
sex: Optional[str] = None # "male", "female", "neutral"
|
|
personality_type: int # Personality type (1-9)
|
|
wing: Optional[int] = None
|
|
stress_level: Optional[int] = None
|
|
mood: Optional[str] = None
|
|
likes: List[str] = Field(default_factory=list)
|
|
dislikes: List[str] = Field(default_factory=list)
|
|
motivations: List[str] = Field(default_factory=list)
|
|
fears: List[str] = Field(default_factory=list)
|
|
relationships: List[Dict[str, Any]] = Field(default_factory=list)
|
|
background: Optional[str] = None
|
|
location: Optional[str] = None
|
|
purpose: Optional[str] = None
|
|
spawned: SpawnedData
|
|
created_at: Optional[Union[str, int]] = None
|
|
updated_at: Optional[Union[str, int]] = None
|
|
custom_name: Optional[str] = None
|
|
inventory: List[Dict[str, Any]] = Field(default_factory=list)
|
|
stats: Dict[str, Any] = Field(default_factory=dict)
|
|
skills: List[str] = Field(default_factory=list)
|
|
tags: List[str] = Field(default_factory=list)
|
|
metadata: Dict[str, Any] = Field(default_factory=dict)
|
|
universe_id: Optional[str] = None
|
|
seed: Optional[int] = None
|
|
cached: Optional[bool] = None
|
|
cache_url: Optional[str] = None
|
|
|
|
|
|
class NPC(BaseModel):
|
|
"""NPC instance with chat capabilities"""
|
|
|
|
npc_id: int
|
|
name: str
|
|
profession: str
|
|
personality_type: int # Personality type (1-9)
|
|
mood: Optional[str] = None
|
|
spawned: Optional[SpawnedData] = None
|
|
universe_id: Optional[str] = None
|
|
world_seed: Optional[int] = None
|
|
cached: bool = False
|
|
cache_url: Optional[str] = None
|
|
chat_history: List[dict] = Field(default_factory=list)
|
|
age: Optional[int] = None
|
|
sex: Optional[str] = None # "male", "female", "neutral"
|
|
wing: Optional[int] = None
|
|
stress_level: Optional[int] = None
|
|
likes: List[str] = Field(default_factory=list)
|
|
dislikes: List[str] = Field(default_factory=list)
|
|
motivations: List[str] = Field(default_factory=list)
|
|
fears: List[str] = Field(default_factory=list)
|
|
relationships: List[Dict[str, Any]] = Field(default_factory=list)
|
|
background: Optional[str] = None
|
|
location: Optional[str] = None
|
|
purpose: Optional[str] = None
|
|
|
|
|
|
# ============================================================================
|
|
# UNIVERSE MODELS
|
|
# ============================================================================
|
|
|
|
|
|
class UniverseThemeInfo(BaseModel):
|
|
"""Universe theme information"""
|
|
|
|
id: str
|
|
title: str
|
|
description: Optional[str] = None
|
|
tags: List[str] = Field(default_factory=list)
|
|
mood_id: Optional[str] = None
|
|
|
|
|
|
class UniverseListItem(BaseModel):
|
|
"""Universe list item for listing endpoints"""
|
|
|
|
universe_id: str
|
|
name: str
|
|
hash: str
|
|
custom_name: Optional[str] = None
|
|
theme_id: Optional[str] = None
|
|
mood_id: Optional[str] = None
|
|
mood_name: Optional[str] = None
|
|
|
|
|
|
class UniverseData(BaseModel):
|
|
"""Full universe data structure"""
|
|
|
|
universe_id: str
|
|
name: str
|
|
hash: str
|
|
custom_name: Optional[str] = None
|
|
theme_data: Optional[UniverseThemeInfo] = None
|
|
data: Dict[str, Any] = Field(default_factory=dict)
|
|
mood_id: Optional[str] = None
|
|
mood_name: Optional[str] = None
|
|
|
|
|
|
class PublicUniverse(BaseModel):
|
|
"""Public universe information"""
|
|
|
|
id: str
|
|
name: str
|
|
hash: Optional[str] = None
|
|
description: Optional[str] = None
|
|
tags: List[str] = Field(default_factory=list)
|
|
mood_id: Optional[str] = None
|
|
mood_name: Optional[str] = None
|
|
npc_count: int = 0
|
|
|
|
|
|
# ============================================================================
|
|
# AUTH MODELS
|
|
# ============================================================================
|
|
|
|
|
|
class AuthToken(BaseModel):
|
|
"""Authentication token response"""
|
|
|
|
access_token: str
|
|
refresh_token: str
|
|
token_type: str = "Bearer"
|
|
expires_in: int
|
|
user_id: str
|
|
username: str
|
|
|
|
|
|
class UserProfile(BaseModel):
|
|
"""User profile information"""
|
|
|
|
user_id: str
|
|
username: str
|
|
email: str
|
|
created_at: Union[str, int] # Can be timestamp or ISO string
|
|
universes_count: int = 0
|
|
npcs_count: int = 0
|
|
statistics: Optional[Dict[str, Any]] = None
|
|
|
|
|
|
class ApiKey(BaseModel):
|
|
"""API key information (without secret)"""
|
|
|
|
id: str
|
|
name: str
|
|
public_key: str
|
|
scopes: List[str]
|
|
created_at: str
|
|
last_used_at: Optional[str] = None
|
|
expires_at: Optional[str] = None
|
|
|
|
|
|
class ApiKeyPair(BaseModel):
|
|
"""API key pair with secret (only returned on creation)"""
|
|
|
|
id: str
|
|
name: str
|
|
public_key: str
|
|
secret_key: str
|
|
scopes: List[str]
|
|
created_at: str
|
|
expires_at: Optional[str] = None
|
|
|
|
|
|
# ============================================================================
|
|
# STATS MODELS
|
|
# ============================================================================
|
|
|
|
|
|
class APIUsage(BaseModel):
|
|
"""API usage statistics - flexible structure"""
|
|
|
|
model_config = {"extra": "allow"} # Allow additional fields
|
|
|
|
requests_today: Optional[int] = 0
|
|
requests_month: Optional[int] = 0
|
|
rate_limit: Optional[int] = 0
|
|
|
|
|
|
class NPCStats(BaseModel):
|
|
"""User's NPC statistics - flexible structure to match API"""
|
|
|
|
model_config = {"extra": "allow"} # Allow additional fields
|
|
|
|
total_npcs: Optional[int] = 0
|
|
total_messages: Optional[int] = 0
|
|
unique_universes: Optional[int] = 0
|
|
api_usage: Optional[Dict[str, Any]] = Field(
|
|
default_factory=dict
|
|
) # API returns dict structure
|
|
source: Optional[str] = None
|
|
|
|
|
|
# ============================================================================
|
|
# MOOD MODELS
|
|
# ============================================================================
|
|
|
|
|
|
class MoodData(BaseModel):
|
|
"""Mood data structure"""
|
|
|
|
categories: Dict[str, Dict[str, Dict[str, str]]]
|
|
|
|
|
|
class MoodItem(BaseModel):
|
|
"""Mood list item"""
|
|
|
|
mood_id: str
|
|
categories_count: int
|
|
filename: str
|
|
|
|
|
|
class MoodUploadResponse(BaseModel):
|
|
"""Mood upload response"""
|
|
|
|
success: bool
|
|
mood_id: str
|
|
categories_count: int
|