Coverage for integrations / channels / admin / schemas.py: 92.0%

349 statements  

« prev     ^ index     » next       coverage.py v7.14.0, created at 2026-05-12 04:49 +0000

1""" 

2Admin API Schemas 

3 

4Pydantic models for request/response validation. 

5""" 

6 

7from __future__ import annotations 

8 

9from dataclasses import dataclass, field, asdict 

10from datetime import datetime 

11from typing import Optional, Dict, List, Any, Union 

12from enum import Enum 

13 

14 

15class ChannelType(str, Enum): 

16 """Supported channel types.""" 

17 TELEGRAM = "telegram" 

18 DISCORD = "discord" 

19 SLACK = "slack" 

20 WHATSAPP = "whatsapp" 

21 MATRIX = "matrix" 

22 TEAMS = "teams" 

23 LINE = "line" 

24 SIGNAL = "signal" 

25 IMESSAGE = "imessage" 

26 GOOGLE_CHAT = "google_chat" 

27 WEB = "web" 

28 MATTERMOST = "mattermost" 

29 NEXTCLOUD = "nextcloud" 

30 

31 

32class ChannelStatus(str, Enum): 

33 """Channel connection status.""" 

34 CONNECTED = "connected" 

35 DISCONNECTED = "disconnected" 

36 CONNECTING = "connecting" 

37 ERROR = "error" 

38 PAUSED = "paused" 

39 

40 

41class TaskStatus(str, Enum): 

42 """Automation task status.""" 

43 PENDING = "pending" 

44 RUNNING = "running" 

45 COMPLETED = "completed" 

46 FAILED = "failed" 

47 CANCELLED = "cancelled" 

48 

49 

50@dataclass 

51class ChannelConfigSchema: 

52 """Channel configuration schema.""" 

53 channel_type: str 

54 name: str 

55 enabled: bool = True 

56 config: Dict[str, Any] = field(default_factory=dict) 

57 rate_limit: Optional[Dict[str, int]] = None 

58 security: Optional[Dict[str, Any]] = None 

59 

60 def to_dict(self) -> Dict[str, Any]: 

61 return asdict(self) 

62 

63 

64@dataclass 

65class ChannelStatusSchema: 

66 """Channel status response.""" 

67 channel_type: str 

68 name: str 

69 status: str 

70 connected_at: Optional[str] = None 

71 message_count: int = 0 

72 error: Optional[str] = None 

73 metrics: Dict[str, Any] = field(default_factory=dict) 

74 

75 def to_dict(self) -> Dict[str, Any]: 

76 return asdict(self) 

77 

78 

79@dataclass 

80class QueueConfigSchema: 

81 """Queue/pipeline configuration.""" 

82 max_size: int = 10000 

83 debounce_ms: int = 500 

84 dedupe_window_seconds: int = 60 

85 concurrency_limits: Dict[str, int] = field(default_factory=lambda: { 

86 "max_per_user": 4, 

87 "max_per_channel": 20, 

88 "max_per_chat": 2, 

89 "max_global": 100 

90 }) 

91 rate_limits: Dict[str, Any] = field(default_factory=lambda: { 

92 "requests_per_minute": 60, 

93 "requests_per_hour": 1000, 

94 "burst_limit": 10 

95 }) 

96 retry_config: Dict[str, Any] = field(default_factory=lambda: { 

97 "max_retries": 3, 

98 "base_delay_seconds": 1.0, 

99 "max_delay_seconds": 60.0, 

100 "exponential_base": 2.0 

101 }) 

102 batching: Dict[str, Any] = field(default_factory=lambda: { 

103 "enabled": True, 

104 "max_batch_size": 10, 

105 "max_wait_ms": 100 

106 }) 

107 

108 def to_dict(self) -> Dict[str, Any]: 

109 return asdict(self) 

110 

111 

112@dataclass 

113class QueueStatsSchema: 

114 """Queue statistics response.""" 

115 queue_size: int = 0 

116 pending_messages: int = 0 

117 processing_messages: int = 0 

118 completed_messages: int = 0 

119 failed_messages: int = 0 

120 avg_processing_time_ms: float = 0.0 

121 messages_per_minute: float = 0.0 

122 by_channel: Dict[str, int] = field(default_factory=dict) 

123 by_priority: Dict[str, int] = field(default_factory=dict) 

124 

125 def to_dict(self) -> Dict[str, Any]: 

126 return asdict(self) 

127 

128 

129@dataclass 

130class CommandConfigSchema: 

131 """Command configuration.""" 

132 name: str 

133 description: str 

134 pattern: str 

135 handler: str 

136 enabled: bool = True 

137 admin_only: bool = False 

138 cooldown_seconds: int = 0 

139 usage_limit: Optional[int] = None 

140 aliases: List[str] = field(default_factory=list) 

141 arguments: List[Dict[str, Any]] = field(default_factory=list) 

142 

143 def to_dict(self) -> Dict[str, Any]: 

144 return asdict(self) 

145 

146 

147@dataclass 

148class MentionGatingConfigSchema: 

149 """Mention gating configuration.""" 

150 enabled: bool = True 

151 require_mention_in_groups: bool = True 

152 allow_reply_chain: bool = True 

153 keywords: List[str] = field(default_factory=list) 

154 exempt_users: List[str] = field(default_factory=list) 

155 exempt_channels: List[str] = field(default_factory=list) 

156 

157 def to_dict(self) -> Dict[str, Any]: 

158 return asdict(self) 

159 

160 

161@dataclass 

162class WebhookConfigSchema: 

163 """Webhook configuration.""" 

164 id: Optional[str] = None 

165 name: str = "" 

166 url: str = "" 

167 secret: Optional[str] = None 

168 events: List[str] = field(default_factory=list) 

169 enabled: bool = True 

170 retry_count: int = 3 

171 timeout_seconds: int = 30 

172 headers: Dict[str, str] = field(default_factory=dict) 

173 

174 def to_dict(self) -> Dict[str, Any]: 

175 return asdict(self) 

176 

177 

178@dataclass 

179class CronJobSchema: 

180 """Cron job configuration.""" 

181 id: Optional[str] = None 

182 name: str = "" 

183 schedule: str = "" # Cron expression 

184 handler: str = "" 

185 enabled: bool = True 

186 last_run: Optional[str] = None 

187 next_run: Optional[str] = None 

188 run_count: int = 0 

189 payload: Dict[str, Any] = field(default_factory=dict) 

190 

191 def to_dict(self) -> Dict[str, Any]: 

192 return asdict(self) 

193 

194 

195@dataclass 

196class TriggerConfigSchema: 

197 """Trigger configuration.""" 

198 id: Optional[str] = None 

199 name: str = "" 

200 trigger_type: str = "" # message, reaction, join, leave, etc. 

201 pattern: Optional[str] = None 

202 conditions: Dict[str, Any] = field(default_factory=dict) 

203 actions: List[Dict[str, Any]] = field(default_factory=list) 

204 enabled: bool = True 

205 priority: int = 0 

206 

207 def to_dict(self) -> Dict[str, Any]: 

208 return asdict(self) 

209 

210 

211@dataclass 

212class WorkflowSchema: 

213 """Workflow configuration.""" 

214 id: Optional[str] = None 

215 name: str = "" 

216 description: str = "" 

217 nodes: List[Dict[str, Any]] = field(default_factory=list) 

218 edges: List[Dict[str, Any]] = field(default_factory=list) 

219 enabled: bool = True 

220 created_at: Optional[str] = None 

221 updated_at: Optional[str] = None 

222 

223 def to_dict(self) -> Dict[str, Any]: 

224 return asdict(self) 

225 

226 

227@dataclass 

228class ScheduledMessageSchema: 

229 """Scheduled message configuration.""" 

230 id: Optional[str] = None 

231 channel: str = "" 

232 chat_id: str = "" 

233 message: str = "" 

234 scheduled_time: str = "" # ISO format 

235 recurring: bool = False 

236 recurrence_pattern: Optional[str] = None 

237 enabled: bool = True 

238 

239 def to_dict(self) -> Dict[str, Any]: 

240 return asdict(self) 

241 

242 

243@dataclass 

244class AutomationConfigSchema: 

245 """Full automation configuration.""" 

246 webhooks: List[WebhookConfigSchema] = field(default_factory=list) 

247 cron_jobs: List[CronJobSchema] = field(default_factory=list) 

248 triggers: List[TriggerConfigSchema] = field(default_factory=list) 

249 workflows: List[WorkflowSchema] = field(default_factory=list) 

250 scheduled_messages: List[ScheduledMessageSchema] = field(default_factory=list) 

251 

252 def to_dict(self) -> Dict[str, Any]: 

253 return { 

254 "webhooks": [w.to_dict() for w in self.webhooks], 

255 "cron_jobs": [c.to_dict() for c in self.cron_jobs], 

256 "triggers": [t.to_dict() for t in self.triggers], 

257 "workflows": [w.to_dict() for w in self.workflows], 

258 "scheduled_messages": [s.to_dict() for s in self.scheduled_messages], 

259 } 

260 

261 

262@dataclass 

263class IdentityConfigSchema: 

264 """Agent identity configuration.""" 

265 agent_id: str = "" 

266 display_name: str = "" 

267 avatar_url: Optional[str] = None 

268 bio: str = "" 

269 personality: Dict[str, Any] = field(default_factory=dict) 

270 response_style: Dict[str, Any] = field(default_factory=dict) 

271 per_channel_identity: Dict[str, Dict[str, Any]] = field(default_factory=dict) 

272 

273 def to_dict(self) -> Dict[str, Any]: 

274 return asdict(self) 

275 

276 

277@dataclass 

278class AvatarSchema: 

279 """Avatar configuration.""" 

280 id: Optional[str] = None 

281 name: str = "" 

282 url: str = "" 

283 channel: Optional[str] = None 

284 is_default: bool = False 

285 

286 def to_dict(self) -> Dict[str, Any]: 

287 return asdict(self) 

288 

289 

290@dataclass 

291class SenderMappingSchema: 

292 """Sender mapping configuration.""" 

293 platform_user_id: str = "" 

294 internal_user_id: str = "" 

295 channel: str = "" 

296 display_name: Optional[str] = None 

297 metadata: Dict[str, Any] = field(default_factory=dict) 

298 

299 def to_dict(self) -> Dict[str, Any]: 

300 return asdict(self) 

301 

302 

303@dataclass 

304class PluginConfigSchema: 

305 """Plugin configuration.""" 

306 id: str = "" 

307 name: str = "" 

308 version: str = "" 

309 description: str = "" 

310 enabled: bool = True 

311 config: Dict[str, Any] = field(default_factory=dict) 

312 hooks: List[str] = field(default_factory=list) 

313 dependencies: List[str] = field(default_factory=list) 

314 

315 def to_dict(self) -> Dict[str, Any]: 

316 return asdict(self) 

317 

318 

319@dataclass 

320class SessionConfigSchema: 

321 """Session configuration.""" 

322 session_id: str = "" 

323 user_id: str = "" 

324 channel: str = "" 

325 chat_id: str = "" 

326 created_at: str = "" 

327 last_activity: str = "" 

328 context: Dict[str, Any] = field(default_factory=dict) 

329 metadata: Dict[str, Any] = field(default_factory=dict) 

330 

331 def to_dict(self) -> Dict[str, Any]: 

332 return asdict(self) 

333 

334 

335@dataclass 

336class PairingRequestSchema: 

337 """Session pairing request.""" 

338 source_channel: str = "" 

339 source_chat_id: str = "" 

340 target_channel: str = "" 

341 target_chat_id: str = "" 

342 bidirectional: bool = True 

343 

344 def to_dict(self) -> Dict[str, Any]: 

345 return asdict(self) 

346 

347 

348@dataclass 

349class MetricsSchema: 

350 """System metrics response.""" 

351 timestamp: str = "" 

352 uptime_seconds: float = 0.0 

353 total_messages_processed: int = 0 

354 messages_per_minute: float = 0.0 

355 active_sessions: int = 0 

356 active_channels: int = 0 

357 queue_depth: int = 0 

358 memory_usage_mb: float = 0.0 

359 cpu_usage_percent: float = 0.0 

360 error_rate: float = 0.0 

361 latency_p50_ms: float = 0.0 

362 latency_p99_ms: float = 0.0 

363 by_channel: Dict[str, Dict[str, Any]] = field(default_factory=dict) 

364 

365 def to_dict(self) -> Dict[str, Any]: 

366 return asdict(self) 

367 

368 

369@dataclass 

370class MemoryStoreConfigSchema: 

371 """Memory store configuration.""" 

372 backend: str = "file" # file, redis, sqlite 

373 max_entries: int = 10000 

374 ttl_seconds: Optional[int] = None 

375 path: Optional[str] = None 

376 connection_string: Optional[str] = None 

377 

378 def to_dict(self) -> Dict[str, Any]: 

379 return asdict(self) 

380 

381 

382@dataclass 

383class SecurityConfigSchema: 

384 """Security configuration.""" 

385 allowed_users: List[str] = field(default_factory=list) 

386 blocked_users: List[str] = field(default_factory=list) 

387 allowed_channels: List[str] = field(default_factory=list) 

388 blocked_channels: List[str] = field(default_factory=list) 

389 rate_limit_per_user: int = 60 

390 require_authentication: bool = False 

391 encryption_enabled: bool = False 

392 

393 def to_dict(self) -> Dict[str, Any]: 

394 return asdict(self) 

395 

396 

397@dataclass 

398class MediaConfigSchema: 

399 """Media handling configuration.""" 

400 max_file_size_mb: int = 25 

401 allowed_types: List[str] = field(default_factory=lambda: [ 

402 "image/jpeg", "image/png", "image/gif", "image/webp", 

403 "audio/mpeg", "audio/ogg", "audio/wav", 

404 "video/mp4", "video/webm", 

405 "application/pdf", "text/plain" 

406 ]) 

407 vision_enabled: bool = True 

408 audio_transcription_enabled: bool = True 

409 tts_enabled: bool = False 

410 image_generation_enabled: bool = False 

411 link_preview_enabled: bool = True 

412 

413 def to_dict(self) -> Dict[str, Any]: 

414 return asdict(self) 

415 

416 

417@dataclass 

418class ResponseConfigSchema: 

419 """Response handling configuration.""" 

420 typing_indicator_enabled: bool = True 

421 typing_delay_ms: int = 50 

422 reactions_enabled: bool = True 

423 templates_enabled: bool = True 

424 streaming_enabled: bool = False 

425 max_response_length: int = 4096 

426 split_long_messages: bool = True 

427 

428 def to_dict(self) -> Dict[str, Any]: 

429 return asdict(self) 

430 

431 

432@dataclass 

433class EmbodiedAIConfigSchema: 

434 """Embodied AI / HevolveAI feed configuration.""" 

435 enabled: bool = True 

436 # Visual feeds 

437 screen_capture_enabled: bool = True 

438 screen_capture_fps: float = 0.5 

439 camera_enabled: bool = True 

440 camera_fps: float = 2.0 

441 camera_id: int = 0 

442 visual_source_mode: str = 'auto' # auto|screen|camera 

443 # Audio feed 

444 audio_enabled: bool = True 

445 audio_sample_rate: int = 16000 

446 audio_chunk_duration: float = 1.0 

447 # Learning 

448 learning_mode: str = 'hybrid' # passive|active|hybrid 

449 observation_interval: float = 10.0 

450 visual_min_change: float = 0.05 

451 # Active exploration 

452 exploration_safe_mode: bool = True 

453 exploration_max_actions_per_min: int = 10 

454 exploration_rate: float = 0.3 

455 # HevolveAI connection 

456 hevolveai_url: str = 'http://localhost:8000' 

457 

458 def to_dict(self) -> Dict[str, Any]: 

459 return asdict(self) 

460 

461 

462@dataclass 

463class GlobalConfigSchema: 

464 """Global system configuration.""" 

465 queue: QueueConfigSchema = field(default_factory=QueueConfigSchema) 

466 security: SecurityConfigSchema = field(default_factory=SecurityConfigSchema) 

467 media: MediaConfigSchema = field(default_factory=MediaConfigSchema) 

468 response: ResponseConfigSchema = field(default_factory=ResponseConfigSchema) 

469 memory: MemoryStoreConfigSchema = field(default_factory=MemoryStoreConfigSchema) 

470 embodied_ai: EmbodiedAIConfigSchema = field(default_factory=EmbodiedAIConfigSchema) 

471 

472 def to_dict(self) -> Dict[str, Any]: 

473 return { 

474 "queue": self.queue.to_dict(), 

475 "security": self.security.to_dict(), 

476 "media": self.media.to_dict(), 

477 "response": self.response.to_dict(), 

478 "memory": self.memory.to_dict(), 

479 "embodied_ai": self.embodied_ai.to_dict(), 

480 } 

481 

482 

483# Response wrappers 

484@dataclass 

485class APIResponse: 

486 """Standard API response wrapper.""" 

487 success: bool = True 

488 data: Any = None 

489 error: Optional[str] = None 

490 timestamp: str = field(default_factory=lambda: datetime.now().isoformat()) 

491 

492 def to_dict(self) -> Dict[str, Any]: 

493 result = { 

494 "success": self.success, 

495 "timestamp": self.timestamp, 

496 } 

497 if self.data is not None: 

498 if hasattr(self.data, "to_dict"): 

499 result["data"] = self.data.to_dict() 

500 elif isinstance(self.data, list): 

501 result["data"] = [ 

502 d.to_dict() if hasattr(d, "to_dict") else d for d in self.data 

503 ] 

504 else: 

505 result["data"] = self.data 

506 if self.error: 

507 result["error"] = self.error 

508 return result 

509 

510 

511@dataclass 

512class PaginatedResponse: 

513 """Paginated API response.""" 

514 items: List[Any] = field(default_factory=list) 

515 total: int = 0 

516 page: int = 1 

517 page_size: int = 20 

518 has_next: bool = False 

519 has_prev: bool = False 

520 

521 def to_dict(self) -> Dict[str, Any]: 

522 return { 

523 "items": [ 

524 i.to_dict() if hasattr(i, "to_dict") else i for i in self.items 

525 ], 

526 "total": self.total, 

527 "page": self.page, 

528 "page_size": self.page_size, 

529 "has_next": self.has_next, 

530 "has_prev": self.has_prev, 

531 }