schemas.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. """Shared Pydantic schemas for Fabric control plane and node-agent."""
  2. from __future__ import annotations
  3. from enum import Enum
  4. from pydantic import BaseModel, Field
  5. from typing import Optional
  6. import time
  7. class PriorityClass(str, Enum):
  8. low = "low"
  9. normal = "normal"
  10. high = "high"
  11. critical = "critical"
  12. class ReclaimStrategy(str, Enum):
  13. process_kill = "process-kill"
  14. vllm_sleep = "vllm-sleep"
  15. cuda_checkpoint = "cuda-checkpoint"
  16. none = "none"
  17. class ModuleState(str, Enum):
  18. loaded = "loaded"
  19. running = "running"
  20. idle = "idle"
  21. sleeping = "sleeping"
  22. unloaded = "unloaded"
  23. error = "error"
  24. warming = "warming"
  25. class RuntimeType(str, Enum):
  26. systemd_wrap = "systemd-wrap"
  27. subprocess = "subprocess"
  28. vllm = "vllm"
  29. python_class = "python-class"
  30. comfy_graph = "comfy-graph"
  31. proc_exec = "proc-exec"
  32. class GpuResources(BaseModel):
  33. gpus: int = 1
  34. vram_mb: int | str = "auto"
  35. class LifecycleConfig(BaseModel):
  36. reclaim: ReclaimStrategy = ReclaimStrategy.process_kill
  37. idle_evict_s: int = 300
  38. warm_default: bool = False
  39. idle_evict_s: int = 300
  40. warm_default: bool = False
  41. warmup_s: int = 10
  42. class SchedulingConfig(BaseModel):
  43. priority_class: PriorityClass = PriorityClass.normal
  44. min_replicas: int = 0
  45. max_replicas: int = 8
  46. batch: bool = False
  47. class SystemdWrapConfig(BaseModel):
  48. systemd_unit: str
  49. health_url: str | None = None
  50. health_timeout_s: int = 120
  51. infer_url: str
  52. infer_method: str = "POST"
  53. class ModuleManifest(BaseModel):
  54. name: str
  55. description: str = ""
  56. version: str = "1.0"
  57. modality: str = "general"
  58. aliases: list[str] = Field(default_factory=list)
  59. runtime: RuntimeType = RuntimeType.systemd_wrap
  60. resources: GpuResources = Field(default_factory=GpuResources)
  61. lifecycle: LifecycleConfig = Field(default_factory=LifecycleConfig)
  62. scheduling: SchedulingConfig = Field(default_factory=SchedulingConfig)
  63. systemd: SystemdWrapConfig | None = None
  64. capabilities: list[str] = Field(default_factory=list)
  65. class GpuPidUsage(BaseModel):
  66. gpu_index: int
  67. pid: int
  68. vram_mb: int
  69. process_name: str = ""
  70. class GpuMemInfo(BaseModel):
  71. """Per-physical-GPU memory capacity + telemetry (additive). One entry per
  72. physical GPU (counted once, NOT per process). Absent fields degrade to
  73. 0 / None so old and new components interoperate."""
  74. gpu_index: int = 0
  75. name: str = ""
  76. mem_total_mb: int = 0
  77. mem_used_mb: int = 0
  78. mem_free_mb: int = 0
  79. util_pct: Optional[int] = None
  80. temp_c: Optional[int] = None
  81. power_w: Optional[float] = None
  82. class ModuleStatus(BaseModel):
  83. name: str
  84. state: ModuleState
  85. vram_mb: int = 0
  86. last_infer_at: float | None = None
  87. health_ok: bool = False
  88. infer_url: str = ""
  89. modality: str = "general"
  90. aliases: list[str] = Field(default_factory=list)
  91. priority_class: PriorityClass = PriorityClass.normal
  92. reclaim: ReclaimStrategy = ReclaimStrategy.process_kill
  93. idle_evict_s: int = 300
  94. warm_default: bool = False
  95. class Heartbeat(BaseModel):
  96. node_id: str
  97. node_ip: str
  98. agent_port: int = 7701
  99. hypervisor: str = "unknown"
  100. gpu_status: list[GpuPidUsage] = Field(default_factory=list)
  101. modules: list[ModuleStatus] = Field(default_factory=list)
  102. uptime_s: int = 0
  103. timestamp: float = Field(default_factory=time.time)
  104. # --- additive VRAM-capacity + telemetry (per physical GPU) ---
  105. gpus: list[GpuMemInfo] = Field(default_factory=list)
  106. vram_total_mb: int = 0
  107. vram_free_mb: int = 0
  108. driver_version: str = ""
  109. class NodeInfo(BaseModel):
  110. node_id: str
  111. node_ip: str
  112. agent_port: int = 7701
  113. hypervisor: str = "unknown"
  114. gpu_status: list[GpuPidUsage] = Field(default_factory=list)
  115. modules: list[ModuleStatus] = Field(default_factory=list)
  116. last_heartbeat: float = 0
  117. alive: bool = True
  118. # --- additive VRAM-capacity + telemetry (per physical GPU) ---
  119. gpus: list[GpuMemInfo] = Field(default_factory=list)
  120. vram_total_mb: int = 0
  121. vram_free_mb: int = 0
  122. driver_version: str = ""
  123. class PlaceRequest(BaseModel):
  124. model: str
  125. priority: PriorityClass = PriorityClass.normal
  126. class PlaceResponse(BaseModel):
  127. node_id: str
  128. node_ip: str
  129. infer_url: str
  130. module_name: str
  131. state: ModuleState
  132. class RegistryModelsResponse(BaseModel):
  133. models: list[ModuleStatus]
  134. nodes_count: int
  135. total_gpu_vram_mb: int = 0
  136. total_gpu_capacity_mb: int = 0
  137. total_gpu_free_mb: int = 0
  138. class RegistryNodesResponse(BaseModel):
  139. nodes: list[NodeInfo]