aiecs 1.3.8__py3-none-any.whl → 1.4.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of aiecs might be problematic. Click here for more details.

Files changed (37) hide show
  1. aiecs/__init__.py +1 -1
  2. aiecs/domain/__init__.py +120 -0
  3. aiecs/domain/agent/__init__.py +184 -0
  4. aiecs/domain/agent/base_agent.py +691 -0
  5. aiecs/domain/agent/exceptions.py +99 -0
  6. aiecs/domain/agent/hybrid_agent.py +495 -0
  7. aiecs/domain/agent/integration/__init__.py +23 -0
  8. aiecs/domain/agent/integration/context_compressor.py +219 -0
  9. aiecs/domain/agent/integration/context_engine_adapter.py +258 -0
  10. aiecs/domain/agent/integration/retry_policy.py +228 -0
  11. aiecs/domain/agent/integration/role_config.py +217 -0
  12. aiecs/domain/agent/lifecycle.py +298 -0
  13. aiecs/domain/agent/llm_agent.py +309 -0
  14. aiecs/domain/agent/memory/__init__.py +13 -0
  15. aiecs/domain/agent/memory/conversation.py +216 -0
  16. aiecs/domain/agent/migration/__init__.py +15 -0
  17. aiecs/domain/agent/migration/conversion.py +171 -0
  18. aiecs/domain/agent/migration/legacy_wrapper.py +97 -0
  19. aiecs/domain/agent/models.py +263 -0
  20. aiecs/domain/agent/observability.py +443 -0
  21. aiecs/domain/agent/persistence.py +287 -0
  22. aiecs/domain/agent/prompts/__init__.py +25 -0
  23. aiecs/domain/agent/prompts/builder.py +164 -0
  24. aiecs/domain/agent/prompts/formatters.py +192 -0
  25. aiecs/domain/agent/prompts/template.py +264 -0
  26. aiecs/domain/agent/registry.py +261 -0
  27. aiecs/domain/agent/tool_agent.py +267 -0
  28. aiecs/domain/agent/tools/__init__.py +13 -0
  29. aiecs/domain/agent/tools/schema_generator.py +222 -0
  30. aiecs/main.py +2 -2
  31. aiecs/tools/search_tool/__init__.py +1 -0
  32. {aiecs-1.3.8.dist-info → aiecs-1.4.1.dist-info}/METADATA +1 -1
  33. {aiecs-1.3.8.dist-info → aiecs-1.4.1.dist-info}/RECORD +37 -10
  34. {aiecs-1.3.8.dist-info → aiecs-1.4.1.dist-info}/WHEEL +0 -0
  35. {aiecs-1.3.8.dist-info → aiecs-1.4.1.dist-info}/entry_points.txt +0 -0
  36. {aiecs-1.3.8.dist-info → aiecs-1.4.1.dist-info}/licenses/LICENSE +0 -0
  37. {aiecs-1.3.8.dist-info → aiecs-1.4.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,691 @@
1
+ """
2
+ Base AI Agent
3
+
4
+ Abstract base class for all AI agents in the AIECS system.
5
+ """
6
+
7
+ import uuid
8
+ from abc import ABC, abstractmethod
9
+ from datetime import datetime
10
+ from typing import Dict, List, Any, Optional, Callable
11
+ import logging
12
+
13
+ from .models import (
14
+ AgentState,
15
+ AgentType,
16
+ AgentConfiguration,
17
+ AgentGoal,
18
+ AgentMetrics,
19
+ AgentCapabilityDeclaration,
20
+ AgentMemory,
21
+ GoalStatus,
22
+ GoalPriority,
23
+ MemoryType,
24
+ )
25
+ from .exceptions import (
26
+ InvalidStateTransitionError,
27
+ ConfigurationError,
28
+ AgentInitializationError,
29
+ SerializationError,
30
+ )
31
+
32
+ logger = logging.getLogger(__name__)
33
+
34
+
35
+ class BaseAIAgent(ABC):
36
+ """
37
+ Abstract base class for AI agents.
38
+
39
+ Provides common functionality for agent lifecycle management,
40
+ state management, memory, goals, and metrics tracking.
41
+ """
42
+
43
+ def __init__(
44
+ self,
45
+ agent_id: str,
46
+ name: str,
47
+ agent_type: AgentType,
48
+ config: AgentConfiguration,
49
+ description: Optional[str] = None,
50
+ version: str = "1.0.0",
51
+ ):
52
+ """
53
+ Initialize the base agent.
54
+
55
+ Args:
56
+ agent_id: Unique identifier for the agent
57
+ name: Agent name
58
+ agent_type: Type of agent
59
+ config: Agent configuration
60
+ description: Optional agent description
61
+ version: Agent version
62
+ """
63
+ # Identity
64
+ self.agent_id = agent_id
65
+ self.name = name
66
+ self.agent_type = agent_type
67
+ self.description = description or f"{agent_type.value} agent"
68
+ self.version = version
69
+
70
+ # Configuration
71
+ self._config = config
72
+
73
+ # State
74
+ self._state = AgentState.CREATED
75
+ self._previous_state: Optional[AgentState] = None
76
+
77
+ # Memory storage (in-memory dict, can be replaced with sophisticated storage)
78
+ self._memory: Dict[str, Any] = {}
79
+ self._memory_metadata: Dict[str, Dict[str, Any]] = {}
80
+
81
+ # Goals
82
+ self._goals: Dict[str, AgentGoal] = {}
83
+
84
+ # Capabilities
85
+ self._capabilities: Dict[str, AgentCapabilityDeclaration] = {}
86
+
87
+ # Metrics
88
+ self._metrics = AgentMetrics()
89
+
90
+ # Timestamps
91
+ self.created_at = datetime.utcnow()
92
+ self.updated_at = datetime.utcnow()
93
+ self.last_active_at: Optional[datetime] = None
94
+
95
+ # Current task tracking
96
+ self._current_task_id: Optional[str] = None
97
+
98
+ logger.info(f"Agent initialized: {self.agent_id} ({self.name}, {self.agent_type.value})")
99
+
100
+ # ==================== State Management ====================
101
+
102
+ @property
103
+ def state(self) -> AgentState:
104
+ """Get current agent state."""
105
+ return self._state
106
+
107
+ def get_state(self) -> AgentState:
108
+ """Get current agent state."""
109
+ return self._state
110
+
111
+ def _transition_state(self, new_state: AgentState) -> None:
112
+ """
113
+ Transition to a new state with validation.
114
+
115
+ Args:
116
+ new_state: Target state
117
+
118
+ Raises:
119
+ InvalidStateTransitionError: If transition is invalid
120
+ """
121
+ # Define valid transitions
122
+ valid_transitions = {
123
+ AgentState.CREATED: {AgentState.INITIALIZING},
124
+ AgentState.INITIALIZING: {AgentState.ACTIVE, AgentState.ERROR},
125
+ AgentState.ACTIVE: {AgentState.BUSY, AgentState.IDLE, AgentState.STOPPED, AgentState.ERROR},
126
+ AgentState.BUSY: {AgentState.ACTIVE, AgentState.ERROR},
127
+ AgentState.IDLE: {AgentState.ACTIVE, AgentState.STOPPED},
128
+ AgentState.ERROR: {AgentState.ACTIVE, AgentState.STOPPED},
129
+ AgentState.STOPPED: set(), # Terminal state
130
+ }
131
+
132
+ if new_state not in valid_transitions.get(self._state, set()):
133
+ raise InvalidStateTransitionError(
134
+ agent_id=self.agent_id,
135
+ current_state=self._state.value,
136
+ attempted_state=new_state.value,
137
+ )
138
+
139
+ self._previous_state = self._state
140
+ self._state = new_state
141
+ self.updated_at = datetime.utcnow()
142
+
143
+ logger.info(f"Agent {self.agent_id} state: {self._previous_state.value} → {new_state.value}")
144
+
145
+ # ==================== Lifecycle Methods ====================
146
+
147
+ async def initialize(self) -> None:
148
+ """
149
+ Initialize the agent.
150
+
151
+ This method should be called before the agent can be used.
152
+ Override in subclasses to add initialization logic.
153
+
154
+ Raises:
155
+ AgentInitializationError: If initialization fails
156
+ """
157
+ try:
158
+ self._transition_state(AgentState.INITIALIZING)
159
+ logger.info(f"Initializing agent {self.agent_id}...")
160
+
161
+ # Subclass initialization
162
+ await self._initialize()
163
+
164
+ self._transition_state(AgentState.ACTIVE)
165
+ self.last_active_at = datetime.utcnow()
166
+ logger.info(f"Agent {self.agent_id} initialized successfully")
167
+
168
+ except Exception as e:
169
+ self._transition_state(AgentState.ERROR)
170
+ logger.error(f"Agent {self.agent_id} initialization failed: {e}")
171
+ raise AgentInitializationError(
172
+ f"Failed to initialize agent {self.agent_id}: {str(e)}",
173
+ agent_id=self.agent_id,
174
+ )
175
+
176
+ @abstractmethod
177
+ async def _initialize(self) -> None:
178
+ """
179
+ Subclass-specific initialization logic.
180
+
181
+ Override this method in subclasses to implement
182
+ custom initialization.
183
+ """
184
+ pass
185
+
186
+ async def activate(self) -> None:
187
+ """Activate the agent."""
188
+ if self._state == AgentState.IDLE:
189
+ self._transition_state(AgentState.ACTIVE)
190
+ self.last_active_at = datetime.utcnow()
191
+ logger.info(f"Agent {self.agent_id} activated")
192
+ else:
193
+ logger.warning(
194
+ f"Agent {self.agent_id} cannot be activated from state {self._state.value}"
195
+ )
196
+
197
+ async def deactivate(self) -> None:
198
+ """Deactivate the agent (enter idle state)."""
199
+ if self._state == AgentState.ACTIVE:
200
+ self._transition_state(AgentState.IDLE)
201
+ logger.info(f"Agent {self.agent_id} deactivated")
202
+ else:
203
+ logger.warning(
204
+ f"Agent {self.agent_id} cannot be deactivated from state {self._state.value}"
205
+ )
206
+
207
+ async def shutdown(self) -> None:
208
+ """
209
+ Shutdown the agent.
210
+
211
+ Override in subclasses to add cleanup logic.
212
+ """
213
+ logger.info(f"Shutting down agent {self.agent_id}...")
214
+ await self._shutdown()
215
+ self._transition_state(AgentState.STOPPED)
216
+ logger.info(f"Agent {self.agent_id} shut down")
217
+
218
+ @abstractmethod
219
+ async def _shutdown(self) -> None:
220
+ """
221
+ Subclass-specific shutdown logic.
222
+
223
+ Override this method in subclasses to implement
224
+ custom cleanup.
225
+ """
226
+ pass
227
+
228
+ # ==================== Abstract Execution Methods ====================
229
+
230
+ @abstractmethod
231
+ async def execute_task(self, task: Dict[str, Any], context: Dict[str, Any]) -> Dict[str, Any]:
232
+ """
233
+ Execute a task.
234
+
235
+ Args:
236
+ task: Task specification
237
+ context: Execution context
238
+
239
+ Returns:
240
+ Task execution result
241
+
242
+ Raises:
243
+ TaskExecutionError: If task execution fails
244
+
245
+ Note:
246
+ Subclasses can use `_execute_with_retry()` to wrap task execution
247
+ with automatic retry logic based on agent configuration.
248
+ """
249
+ pass
250
+
251
+ @abstractmethod
252
+ async def process_message(self, message: str, sender_id: Optional[str] = None) -> Dict[str, Any]:
253
+ """
254
+ Process an incoming message.
255
+
256
+ Args:
257
+ message: Message content
258
+ sender_id: Optional sender identifier
259
+
260
+ Returns:
261
+ Response dictionary
262
+
263
+ Note:
264
+ Subclasses can use `_execute_with_retry()` to wrap message processing
265
+ with automatic retry logic based on agent configuration.
266
+ """
267
+ pass
268
+
269
+ # ==================== Retry Logic Integration ====================
270
+
271
+ async def _execute_with_retry(
272
+ self,
273
+ func: Callable,
274
+ *args,
275
+ **kwargs
276
+ ) -> Any:
277
+ """
278
+ Execute a function with retry logic using agent's retry policy.
279
+
280
+ This helper method wraps function execution with automatic retry based on
281
+ the agent's configuration. It uses EnhancedRetryPolicy for sophisticated
282
+ error handling with exponential backoff and error classification.
283
+
284
+ Args:
285
+ func: Async function to execute
286
+ *args: Function positional arguments
287
+ **kwargs: Function keyword arguments
288
+
289
+ Returns:
290
+ Function result
291
+
292
+ Raises:
293
+ Exception: If all retries are exhausted
294
+
295
+ Example:
296
+ ```python
297
+ async def _execute_task_internal(self, task, context):
298
+ # Actual task execution logic
299
+ return result
300
+
301
+ async def execute_task(self, task, context):
302
+ return await self._execute_with_retry(
303
+ self._execute_task_internal,
304
+ task,
305
+ context
306
+ )
307
+ ```
308
+ """
309
+ from .integration.retry_policy import EnhancedRetryPolicy
310
+
311
+ # Get retry policy from configuration
312
+ retry_config = self._config.retry_policy
313
+
314
+ # Create retry policy instance
315
+ retry_policy = EnhancedRetryPolicy(
316
+ max_retries=retry_config.max_retries,
317
+ base_delay=retry_config.base_delay,
318
+ max_delay=retry_config.max_delay,
319
+ exponential_base=retry_config.exponential_factor,
320
+ jitter=retry_config.jitter_factor > 0
321
+ )
322
+
323
+ # Execute with retry
324
+ return await retry_policy.execute_with_retry(func, *args, **kwargs)
325
+
326
+ # ==================== Memory Management ====================
327
+
328
+ async def add_to_memory(
329
+ self,
330
+ key: str,
331
+ value: Any,
332
+ memory_type: MemoryType = MemoryType.SHORT_TERM,
333
+ metadata: Optional[Dict[str, Any]] = None,
334
+ ) -> None:
335
+ """
336
+ Add an item to agent memory.
337
+
338
+ Args:
339
+ key: Memory key
340
+ value: Memory value
341
+ memory_type: Type of memory (short_term or long_term)
342
+ metadata: Optional metadata
343
+ """
344
+ self._memory[key] = value
345
+ self._memory_metadata[key] = {
346
+ "type": memory_type.value,
347
+ "timestamp": datetime.utcnow(),
348
+ "metadata": metadata or {},
349
+ }
350
+ logger.debug(f"Agent {self.agent_id} added memory: {key} ({memory_type.value})")
351
+
352
+ async def retrieve_memory(
353
+ self, key: str, default: Any = None
354
+ ) -> Any:
355
+ """
356
+ Retrieve an item from memory.
357
+
358
+ Args:
359
+ key: Memory key
360
+ default: Default value if key not found
361
+
362
+ Returns:
363
+ Memory value or default
364
+ """
365
+ return self._memory.get(key, default)
366
+
367
+ async def clear_memory(self, memory_type: Optional[MemoryType] = None) -> None:
368
+ """
369
+ Clear agent memory.
370
+
371
+ Args:
372
+ memory_type: If specified, clear only this type of memory
373
+ """
374
+ if memory_type is None:
375
+ self._memory.clear()
376
+ self._memory_metadata.clear()
377
+ logger.info(f"Agent {self.agent_id} cleared all memory")
378
+ else:
379
+ keys_to_remove = [
380
+ k
381
+ for k, v in self._memory_metadata.items()
382
+ if v.get("type") == memory_type.value
383
+ ]
384
+ for key in keys_to_remove:
385
+ del self._memory[key]
386
+ del self._memory_metadata[key]
387
+ logger.info(f"Agent {self.agent_id} cleared {memory_type.value} memory")
388
+
389
+ def get_memory_summary(self) -> Dict[str, Any]:
390
+ """Get a summary of agent memory."""
391
+ return {
392
+ "total_items": len(self._memory),
393
+ "short_term_count": sum(
394
+ 1 for v in self._memory_metadata.values() if v.get("type") == MemoryType.SHORT_TERM.value
395
+ ),
396
+ "long_term_count": sum(
397
+ 1 for v in self._memory_metadata.values() if v.get("type") == MemoryType.LONG_TERM.value
398
+ ),
399
+ }
400
+
401
+ # ==================== Goal Management ====================
402
+
403
+ def set_goal(
404
+ self,
405
+ description: str,
406
+ priority: GoalPriority = GoalPriority.MEDIUM,
407
+ success_criteria: Optional[str] = None,
408
+ deadline: Optional[datetime] = None,
409
+ ) -> str:
410
+ """
411
+ Set a new goal for the agent.
412
+
413
+ Args:
414
+ description: Goal description
415
+ priority: Goal priority
416
+ success_criteria: Success criteria
417
+ deadline: Goal deadline
418
+
419
+ Returns:
420
+ Goal ID
421
+ """
422
+ goal = AgentGoal(
423
+ description=description,
424
+ priority=priority,
425
+ success_criteria=success_criteria,
426
+ deadline=deadline,
427
+ )
428
+ self._goals[goal.goal_id] = goal
429
+ logger.info(f"Agent {self.agent_id} set goal: {goal.goal_id} ({priority.value})")
430
+ return goal.goal_id
431
+
432
+ def get_goals(
433
+ self, status: Optional[GoalStatus] = None
434
+ ) -> List[AgentGoal]:
435
+ """
436
+ Get agent goals.
437
+
438
+ Args:
439
+ status: Filter by status (optional)
440
+
441
+ Returns:
442
+ List of goals
443
+ """
444
+ if status is None:
445
+ return list(self._goals.values())
446
+ return [g for g in self._goals.values() if g.status == status]
447
+
448
+ def get_goal(self, goal_id: str) -> Optional[AgentGoal]:
449
+ """Get a specific goal by ID."""
450
+ return self._goals.get(goal_id)
451
+
452
+ def update_goal_status(
453
+ self,
454
+ goal_id: str,
455
+ status: GoalStatus,
456
+ progress: Optional[float] = None,
457
+ ) -> None:
458
+ """
459
+ Update goal status.
460
+
461
+ Args:
462
+ goal_id: Goal ID
463
+ status: New status
464
+ progress: Optional progress percentage
465
+ """
466
+ if goal_id not in self._goals:
467
+ logger.warning(f"Goal {goal_id} not found for agent {self.agent_id}")
468
+ return
469
+
470
+ goal = self._goals[goal_id]
471
+ goal.status = status
472
+
473
+ if progress is not None:
474
+ goal.progress = progress
475
+
476
+ if status == GoalStatus.IN_PROGRESS and goal.started_at is None:
477
+ goal.started_at = datetime.utcnow()
478
+ elif status == GoalStatus.ACHIEVED:
479
+ goal.achieved_at = datetime.utcnow()
480
+
481
+ logger.info(f"Agent {self.agent_id} updated goal {goal_id}: {status.value}")
482
+
483
+ # ==================== Configuration Management ====================
484
+
485
+ def get_config(self) -> AgentConfiguration:
486
+ """Get agent configuration."""
487
+ return self._config
488
+
489
+ def update_config(self, updates: Dict[str, Any]) -> None:
490
+ """
491
+ Update agent configuration.
492
+
493
+ Args:
494
+ updates: Configuration updates
495
+
496
+ Raises:
497
+ ConfigurationError: If configuration is invalid
498
+ """
499
+ try:
500
+ # Update configuration
501
+ for key, value in updates.items():
502
+ if hasattr(self._config, key):
503
+ setattr(self._config, key, value)
504
+ else:
505
+ logger.warning(f"Unknown config key: {key}")
506
+
507
+ self.updated_at = datetime.utcnow()
508
+ logger.info(f"Agent {self.agent_id} configuration updated")
509
+
510
+ except Exception as e:
511
+ raise ConfigurationError(
512
+ f"Failed to update configuration: {str(e)}",
513
+ agent_id=self.agent_id,
514
+ )
515
+
516
+ # ==================== Capability Management ====================
517
+
518
+ def declare_capability(
519
+ self,
520
+ capability_type: str,
521
+ level: str,
522
+ description: Optional[str] = None,
523
+ constraints: Optional[Dict[str, Any]] = None,
524
+ ) -> None:
525
+ """
526
+ Declare an agent capability.
527
+
528
+ Args:
529
+ capability_type: Type of capability
530
+ level: Proficiency level
531
+ description: Capability description
532
+ constraints: Capability constraints
533
+ """
534
+ from .models import CapabilityLevel
535
+
536
+ capability = AgentCapabilityDeclaration(
537
+ capability_type=capability_type,
538
+ level=CapabilityLevel(level),
539
+ description=description,
540
+ constraints=constraints or {},
541
+ )
542
+ self._capabilities[capability_type] = capability
543
+ logger.info(f"Agent {self.agent_id} declared capability: {capability_type} ({level})")
544
+
545
+ def has_capability(self, capability_type: str) -> bool:
546
+ """Check if agent has a capability."""
547
+ return capability_type in self._capabilities
548
+
549
+ def get_capabilities(self) -> List[AgentCapabilityDeclaration]:
550
+ """Get all agent capabilities."""
551
+ return list(self._capabilities.values())
552
+
553
+ # ==================== Metrics Tracking ====================
554
+
555
+ def get_metrics(self) -> AgentMetrics:
556
+ """Get agent metrics."""
557
+ return self._metrics
558
+
559
+ def update_metrics(
560
+ self,
561
+ execution_time: Optional[float] = None,
562
+ success: bool = True,
563
+ quality_score: Optional[float] = None,
564
+ tokens_used: Optional[int] = None,
565
+ tool_calls: Optional[int] = None,
566
+ ) -> None:
567
+ """
568
+ Update agent metrics.
569
+
570
+ Args:
571
+ execution_time: Task execution time
572
+ success: Whether task succeeded
573
+ quality_score: Quality score (0-1)
574
+ tokens_used: Tokens used
575
+ tool_calls: Number of tool calls
576
+ """
577
+ self._metrics.total_tasks_executed += 1
578
+
579
+ if success:
580
+ self._metrics.successful_tasks += 1
581
+ else:
582
+ self._metrics.failed_tasks += 1
583
+
584
+ # Update success rate
585
+ self._metrics.success_rate = (
586
+ self._metrics.successful_tasks / self._metrics.total_tasks_executed * 100
587
+ )
588
+
589
+ # Update execution time
590
+ if execution_time is not None:
591
+ self._metrics.total_execution_time += execution_time
592
+ self._metrics.average_execution_time = (
593
+ self._metrics.total_execution_time / self._metrics.total_tasks_executed
594
+ )
595
+
596
+ if self._metrics.min_execution_time is None or execution_time < self._metrics.min_execution_time:
597
+ self._metrics.min_execution_time = execution_time
598
+ if self._metrics.max_execution_time is None or execution_time > self._metrics.max_execution_time:
599
+ self._metrics.max_execution_time = execution_time
600
+
601
+ # Update quality score
602
+ if quality_score is not None:
603
+ if self._metrics.average_quality_score is None:
604
+ self._metrics.average_quality_score = quality_score
605
+ else:
606
+ # Running average
607
+ total_quality = self._metrics.average_quality_score * (
608
+ self._metrics.total_tasks_executed - 1
609
+ )
610
+ self._metrics.average_quality_score = (total_quality + quality_score) / self._metrics.total_tasks_executed
611
+
612
+ # Update resource usage
613
+ if tokens_used is not None:
614
+ self._metrics.total_tokens_used += tokens_used
615
+ if tool_calls is not None:
616
+ self._metrics.total_tool_calls += tool_calls
617
+
618
+ self._metrics.updated_at = datetime.utcnow()
619
+
620
+ # ==================== Serialization ====================
621
+
622
+ def to_dict(self) -> Dict[str, Any]:
623
+ """
624
+ Serialize agent to dictionary.
625
+
626
+ Returns:
627
+ Dictionary representation
628
+
629
+ Raises:
630
+ SerializationError: If serialization fails
631
+ """
632
+ try:
633
+ return {
634
+ "agent_id": self.agent_id,
635
+ "name": self.name,
636
+ "agent_type": self.agent_type.value,
637
+ "description": self.description,
638
+ "version": self.version,
639
+ "state": self._state.value,
640
+ "config": self._config.model_dump(),
641
+ "goals": [g.model_dump() for g in self._goals.values()],
642
+ "capabilities": [c.model_dump() for c in self._capabilities.values()],
643
+ "metrics": self._metrics.model_dump(),
644
+ "memory_summary": self.get_memory_summary(),
645
+ "created_at": self.created_at.isoformat(),
646
+ "updated_at": self.updated_at.isoformat(),
647
+ "last_active_at": self.last_active_at.isoformat() if self.last_active_at else None,
648
+ }
649
+ except Exception as e:
650
+ raise SerializationError(
651
+ f"Failed to serialize agent: {str(e)}",
652
+ agent_id=self.agent_id,
653
+ )
654
+
655
+ @classmethod
656
+ def from_dict(cls, data: Dict[str, Any]) -> "BaseAIAgent":
657
+ """
658
+ Deserialize agent from dictionary.
659
+
660
+ Args:
661
+ data: Dictionary representation
662
+
663
+ Returns:
664
+ Agent instance
665
+
666
+ Raises:
667
+ SerializationError: If deserialization fails
668
+ """
669
+ raise NotImplementedError("from_dict must be implemented by subclasses")
670
+
671
+ # ==================== Utility Methods ====================
672
+
673
+ def is_available(self) -> bool:
674
+ """Check if agent is available for tasks."""
675
+ return self._state == AgentState.ACTIVE
676
+
677
+ def is_busy(self) -> bool:
678
+ """Check if agent is currently busy."""
679
+ return self._state == AgentState.BUSY
680
+
681
+ def __str__(self) -> str:
682
+ """String representation."""
683
+ return f"Agent({self.agent_id}, {self.name}, {self.agent_type.value}, {self._state.value})"
684
+
685
+ def __repr__(self) -> str:
686
+ """Detailed representation."""
687
+ return (
688
+ f"BaseAIAgent(agent_id='{self.agent_id}', name='{self.name}', "
689
+ f"type='{self.agent_type.value}', state='{self._state.value}')"
690
+ )
691
+