Files
microdao-daarion/services/living-map-service/models.py
Apple 3de3c8cb36 feat: Add presence heartbeat for Matrix online status
- matrix-gateway: POST /internal/matrix/presence/online endpoint
- usePresenceHeartbeat hook with activity tracking
- Auto away after 5 min inactivity
- Offline on page close/visibility change
- Integrated in MatrixChatRoom component
2025-11-27 00:19:40 -08:00

248 lines
7.1 KiB
Python

"""
Living Map Service Models
Phase 9: Full Stack Living Map
"""
from pydantic import BaseModel, Field
from typing import Optional, List, Dict, Any, Literal
from datetime import datetime
from decimal import Decimal
# ============================================================================
# Layer Types
# ============================================================================
LayerType = Literal["city", "space", "nodes", "agents"]
EntityType = Literal["city", "space", "node", "agent", "dao", "microdao", "channel"]
EntityStatus = Literal["active", "inactive", "online", "offline", "warning", "error", "idle"]
# ============================================================================
# City Layer Models
# ============================================================================
class CityItem(BaseModel):
"""MicroDAO in City layer"""
id: str
slug: str
name: str
status: EntityStatus
agents: int = 0
nodes: int = 0
members: int = 0
description: Optional[str] = None
class CityLayer(BaseModel):
"""City layer data"""
microdaos_total: int = 0
active_users: int = 0
active_agents: int = 0
health: str = "green"
items: List[CityItem] = []
# ============================================================================
# Space Layer Models
# ============================================================================
class PlanetItem(BaseModel):
"""DAO planet in Space layer"""
id: str
name: str
type: str = "dao"
status: EntityStatus
orbits: List[str] = [] # node IDs
treasury_value: Optional[Decimal] = None
active_proposals: int = 0
class NodeInSpace(BaseModel):
"""Node in Space layer (orbital view)"""
id: str
name: str
status: EntityStatus
cpu: float = 0.0
gpu: float = 0.0
memory: float = 0.0
alerts: List[str] = []
class SpaceLayer(BaseModel):
"""Space layer data"""
planets: List[PlanetItem] = []
nodes: List[NodeInSpace] = []
# ============================================================================
# Nodes Layer Models
# ============================================================================
class NodeMetrics(BaseModel):
"""Node hardware metrics"""
cpu: float = 0.0
gpu: float = 0.0
ram: float = 0.0
disk: float = 0.0
net_in: int = 0
net_out: int = 0
temperature: Optional[float] = None
class NodeItem(BaseModel):
"""Node in Nodes layer"""
id: str
name: str
microdao_id: Optional[str] = None
status: EntityStatus
metrics: NodeMetrics
alerts: List[str] = []
uptime_seconds: Optional[int] = None
last_seen: Optional[datetime] = None
class NodesLayer(BaseModel):
"""Nodes layer data"""
items: List[NodeItem] = []
total_cpu: float = 0.0
total_gpu: float = 0.0
total_ram: float = 0.0
# ============================================================================
# Agents Layer Models
# ============================================================================
class AgentUsage(BaseModel):
"""Agent usage statistics"""
llm_calls_24h: int = 0
tokens_24h: int = 0
messages_24h: int = 0
avg_response_time_ms: Optional[float] = None
class AgentItem(BaseModel):
"""Agent in Agents layer"""
id: str
name: str
kind: str
microdao_id: Optional[str] = None
status: EntityStatus
usage: AgentUsage
model: Optional[str] = None
last_active: Optional[datetime] = None
class AgentsLayer(BaseModel):
"""Agents layer data"""
items: List[AgentItem] = []
total_agents: int = 0
online_agents: int = 0
total_llm_calls_24h: int = 0
# ============================================================================
# Snapshot Models
# ============================================================================
class SnapshotMeta(BaseModel):
"""Metadata for snapshot"""
source_services: List[str] = []
generated_at: datetime
version: str = "1.0"
request_id: Optional[str] = None
class LivingMapSnapshot(BaseModel):
"""Complete Living Map state"""
generated_at: datetime
layers: Dict[str, Any] # Flexible for different layer structures
meta: SnapshotMeta
class LivingMapSnapshotTyped(BaseModel):
"""Typed version of Living Map snapshot"""
generated_at: datetime
layers: Dict[str, Any] = Field(default_factory=dict)
meta: SnapshotMeta
class Config:
arbitrary_types_allowed = True
# ============================================================================
# Entity Models
# ============================================================================
class EntitySummary(BaseModel):
"""Minimal entity info for lists"""
id: str
type: EntityType
label: str
status: EntityStatus
layer: LayerType
class EntityDetail(BaseModel):
"""Detailed entity information"""
id: str
type: EntityType
layer: LayerType
data: Dict[str, Any]
# ============================================================================
# History / Event Models
# ============================================================================
class HistoryItem(BaseModel):
"""Single history event"""
id: str
timestamp: datetime
event_type: str
payload: Dict[str, Any]
source_service: Optional[str] = None
entity_id: Optional[str] = None
entity_type: Optional[str] = None
class HistoryResponse(BaseModel):
"""History query response"""
items: List[HistoryItem]
total: int
has_more: bool = False
# ============================================================================
# WebSocket Models
# ============================================================================
class WSMessageType(BaseModel):
"""WebSocket message type"""
kind: Literal["snapshot", "event", "ping", "error"]
class WSSnapshotMessage(BaseModel):
"""WebSocket snapshot message"""
kind: Literal["snapshot"] = "snapshot"
data: LivingMapSnapshot
class WSEventMessage(BaseModel):
"""WebSocket event message"""
kind: Literal["event"] = "event"
event_type: str
timestamp: datetime
payload: Dict[str, Any]
class WSPingMessage(BaseModel):
"""WebSocket ping message"""
kind: Literal["ping"] = "ping"
timestamp: datetime
class WSErrorMessage(BaseModel):
"""WebSocket error message"""
kind: Literal["error"] = "error"
error: str
timestamp: datetime
# ============================================================================
# Query Parameters
# ============================================================================
class HistoryQueryParams(BaseModel):
"""Query parameters for history endpoint"""
since: Optional[datetime] = None
until: Optional[datetime] = None
event_type: Optional[str] = None
entity_id: Optional[str] = None
limit: int = Field(default=200, ge=1, le=1000)
offset: int = Field(default=0, ge=0)
class EntitiesQueryParams(BaseModel):
"""Query parameters for entities endpoint"""
type: Optional[EntityType] = None
layer: Optional[LayerType] = None
status: Optional[EntityStatus] = None
limit: int = Field(default=100, ge=1, le=500)
offset: int = Field(default=0, ge=0)