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
« prev ^ index » next coverage.py v7.14.0, created at 2026-05-12 04:49 +0000
1"""
2Admin API Schemas
4Pydantic models for request/response validation.
5"""
7from __future__ import annotations
9from dataclasses import dataclass, field, asdict
10from datetime import datetime
11from typing import Optional, Dict, List, Any, Union
12from enum import Enum
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"
32class ChannelStatus(str, Enum):
33 """Channel connection status."""
34 CONNECTED = "connected"
35 DISCONNECTED = "disconnected"
36 CONNECTING = "connecting"
37 ERROR = "error"
38 PAUSED = "paused"
41class TaskStatus(str, Enum):
42 """Automation task status."""
43 PENDING = "pending"
44 RUNNING = "running"
45 COMPLETED = "completed"
46 FAILED = "failed"
47 CANCELLED = "cancelled"
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
60 def to_dict(self) -> Dict[str, Any]:
61 return asdict(self)
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)
75 def to_dict(self) -> Dict[str, Any]:
76 return asdict(self)
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 })
108 def to_dict(self) -> Dict[str, Any]:
109 return asdict(self)
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)
125 def to_dict(self) -> Dict[str, Any]:
126 return asdict(self)
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)
143 def to_dict(self) -> Dict[str, Any]:
144 return asdict(self)
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)
157 def to_dict(self) -> Dict[str, Any]:
158 return asdict(self)
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)
174 def to_dict(self) -> Dict[str, Any]:
175 return asdict(self)
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)
191 def to_dict(self) -> Dict[str, Any]:
192 return asdict(self)
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
207 def to_dict(self) -> Dict[str, Any]:
208 return asdict(self)
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
223 def to_dict(self) -> Dict[str, Any]:
224 return asdict(self)
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
239 def to_dict(self) -> Dict[str, Any]:
240 return asdict(self)
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)
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 }
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)
273 def to_dict(self) -> Dict[str, Any]:
274 return asdict(self)
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
286 def to_dict(self) -> Dict[str, Any]:
287 return asdict(self)
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)
299 def to_dict(self) -> Dict[str, Any]:
300 return asdict(self)
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)
315 def to_dict(self) -> Dict[str, Any]:
316 return asdict(self)
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)
331 def to_dict(self) -> Dict[str, Any]:
332 return asdict(self)
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
344 def to_dict(self) -> Dict[str, Any]:
345 return asdict(self)
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)
365 def to_dict(self) -> Dict[str, Any]:
366 return asdict(self)
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
378 def to_dict(self) -> Dict[str, Any]:
379 return asdict(self)
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
393 def to_dict(self) -> Dict[str, Any]:
394 return asdict(self)
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
413 def to_dict(self) -> Dict[str, Any]:
414 return asdict(self)
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
428 def to_dict(self) -> Dict[str, Any]:
429 return asdict(self)
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'
458 def to_dict(self) -> Dict[str, Any]:
459 return asdict(self)
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)
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 }
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())
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
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
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 }