Mission-Control/src/backend/app/services/monitoring/models.py

175 lines
6.5 KiB
Python

"""Pydantic schemas for OpenClaw Gateway RPC response parsing.
These schemas map the raw JSON responses from gateway RPC methods to typed
Python objects. They are used by GatewayCollectorService to parse responses
before storing data in Mission Control's monitoring models.
Note: These are NOT DB models (those are in app/models/monitoring.py).
These are purely for RPC response parsing.
"""
from __future__ import annotations
from datetime import datetime
from typing import Any
from pydantic import BaseModel, Field
class CostPeriod(BaseModel):
"""Cost period breakdown."""
start: datetime
end: datetime
total: float = Field(default=0.0)
model_costs: dict[str, float] | None = Field(default=None, alias="models")
provider_costs: dict[str, float] | None = Field(default=None)
token_counts: dict[str, int] | None = Field(default=None)
class CostResponse(BaseModel):
"""Response from usage.cost RPC method."""
period_start: datetime = Field(alias="period_start")
period_end: datetime = Field(alias="period_end")
total_cost: float = Field(alias="total", default=0.0)
model_costs: dict[str, float] | None = Field(alias="models", default=None)
provider_costs: dict[str, float] | None = Field(default=None)
token_counts: dict[str, int] | None = Field(alias="tokens", default=None)
class UsageStatusResponse(BaseModel):
"""Response from usage.status RPC method."""
tokens_used: int = Field(default=0)
tokens_limit: int | None = Field(default=None)
cost_used: float = Field(default=0.0)
cost_limit: float | None = Field(default=None)
model_usage: dict[str, dict[str, Any]] | None = Field(default=None)
class CronJobStatus(BaseModel):
"""Individual cron job status."""
job_name: str = Field(alias="name")
schedule: str
enabled: bool = Field(default=True)
last_run_at: datetime | None = Field(default=None, alias="lastRun")
next_run_at: datetime | None = Field(default=None, alias="nextRun")
status: str = Field(default="idle")
failure_count: int = Field(default=0, alias="failureCount")
last_error: str | None = Field(default=None, alias="lastError")
metadata_: dict[str, Any] | None = Field(default=None, alias="metadata")
model_config = {"populate_by_name": True}
class CronJobStatusResponse(BaseModel):
"""Response from cron.list RPC method."""
jobs: list[CronJobStatus] = Field(default_factory=list)
class SessionPreview(BaseModel):
"""Session preview data."""
session_key: str = Field(alias="key")
event_type: str = Field(default="unknown", alias="eventType")
model: str | None = Field(default=None)
agent_id: str | None = Field(default=None, alias="agentId")
channel: str | None = Field(default=None)
context_percent: float | None = Field(default=None, alias="contextPct")
token_counts: dict[str, int] | None = Field(default=None)
cost: float | None = Field(default=None)
metadata_: dict[str, Any] | None = Field(default=None, alias="metadata")
model_config = {"populate_by_name": True}
class SessionsListResponse(BaseModel):
"""Response from sessions.list RPC method."""
sessions: list[SessionPreview] = Field(default_factory=list)
class SessionPreviewResponse(BaseModel):
"""Response from sessions.preview RPC method."""
session_key: str = Field(alias="key")
event_type: str = Field(default="preview", alias="eventType")
model: str | None = Field(default=None)
agent_id: str | None = Field(default=None, alias="agentId")
channel: str | None = Field(default=None)
context_percent: float | None = Field(default=None, alias="contextPct")
token_counts: dict[str, int] | None = Field(default=None)
cost: float | None = Field(default=None)
metadata_: dict[str, Any] | None = Field(default=None, alias="metadata")
model_config = {"populate_by_name": True}
class GatewayHealthMetrics(BaseModel):
"""System health metrics from gateway health response."""
cpu_percent: float | None = Field(default=None, alias="cpu")
cpu_cores: int | None = Field(default=None, alias="cpuCores")
ram_used_bytes: int | None = Field(default=None, alias="ramUsed")
ram_total_bytes: int | None = Field(default=None, alias="ramTotal")
ram_percent: float | None = Field(default=None, alias="ramPercent")
swap_used_bytes: int | None = Field(default=None, alias="swapUsed")
swap_total_bytes: int | None = Field(default=None, alias="swapTotal")
swap_percent: float | None = Field(default=None, alias="swapPercent")
disk_path: str = Field(default="/", alias="diskPath")
disk_used_bytes: int | None = Field(default=None, alias="diskUsed")
disk_total_bytes: int | None = Field(default=None, alias="diskTotal")
disk_percent: float | None = Field(default=None, alias="diskPercent")
metadata_: dict[str, Any] | None = Field(default=None, alias="metadata")
model_config = {"populate_by_name": True}
class GatewayHealthResponse(BaseModel):
"""Response from health RPC method."""
status: str = Field(default="unknown")
pid: int | None = Field(default=None)
uptime_ms: int | None = Field(default=None, alias="uptimeMs")
memory_bytes: int | None = Field(default=None, alias="memory")
rss_bytes: int | None = Field(default=None, alias="rss")
timestamp: datetime | None = Field(default=None)
metrics: GatewayHealthMetrics | None = Field(default=None)
class GatewayStatus(BaseModel):
"""Gateway runtime status."""
gateway_live: bool = Field(default=False, alias="live")
gateway_ready: bool = Field(default=False, alias="ready")
gateway_uptime_ms: int = Field(default=0, alias="uptimeMs")
gateway_pid: int = Field(default=0, alias="pid")
gateway_version: str = Field(default="unknown", alias="version")
agents_count: int = Field(default=0, alias="agents")
class GatewayStatusResponse(BaseModel):
"""Response from status RPC method."""
status: str = Field(default="unknown")
gateway: GatewayStatus
class SubAgentRun(BaseModel):
"""Sub-agent execution record."""
parent_session_key: str = Field(alias="parentSessionKey")
session_event_id: str | None = Field(default=None, alias="sessionId")
agent: str | None = Field(default=None)
model: str | None = Field(default=None)
status: str = Field(default="pending")
duration_ms: int | None = Field(default=None, alias="durationMs")
cost: float | None = Field(default=None)
token_counts: dict[str, int] | None = Field(default=None)
metadata_: dict[str, Any] | None = Field(default=None, alias="metadata")
model_config = {"populate_by_name": True}