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,217 @@
1
+ """
2
+ Role-Based Configuration
3
+
4
+ Load agent configuration from role templates.
5
+ """
6
+
7
+ import logging
8
+ import yaml
9
+ from typing import Dict, Any, Optional
10
+ from pathlib import Path
11
+
12
+ from ..models import AgentConfiguration
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class RoleConfiguration:
18
+ """
19
+ Manages role-based agent configurations.
20
+
21
+ Example:
22
+ role_config = RoleConfiguration.load_from_file("roles/developer.yaml")
23
+ agent_config = role_config.to_agent_config()
24
+ """
25
+
26
+ def __init__(
27
+ self,
28
+ role_name: str,
29
+ goal: Optional[str] = None,
30
+ backstory: Optional[str] = None,
31
+ domain_knowledge: Optional[str] = None,
32
+ llm_model: Optional[str] = None,
33
+ temperature: float = 0.7,
34
+ max_tokens: Optional[int] = None,
35
+ tools: Optional[list] = None,
36
+ **kwargs
37
+ ):
38
+ """
39
+ Initialize role configuration.
40
+
41
+ Args:
42
+ role_name: Role name
43
+ goal: Agent goal
44
+ backstory: Agent backstory
45
+ domain_knowledge: Domain knowledge
46
+ llm_model: LLM model to use
47
+ temperature: LLM temperature
48
+ max_tokens: Max tokens
49
+ tools: List of tool names
50
+ **kwargs: Additional configuration
51
+ """
52
+ self.role_name = role_name
53
+ self.goal = goal
54
+ self.backstory = backstory
55
+ self.domain_knowledge = domain_knowledge
56
+ self.llm_model = llm_model
57
+ self.temperature = temperature
58
+ self.max_tokens = max_tokens
59
+ self.tools = tools or []
60
+ self.additional_config = kwargs
61
+
62
+ def to_agent_config(self) -> AgentConfiguration:
63
+ """
64
+ Convert to AgentConfiguration.
65
+
66
+ Returns:
67
+ AgentConfiguration instance
68
+ """
69
+ return AgentConfiguration(
70
+ goal=self.goal,
71
+ backstory=self.backstory,
72
+ domain_knowledge=self.domain_knowledge,
73
+ llm_model=self.llm_model,
74
+ temperature=self.temperature,
75
+ max_tokens=self.max_tokens,
76
+ **self.additional_config
77
+ )
78
+
79
+ @classmethod
80
+ def load_from_file(cls, file_path: str) -> "RoleConfiguration":
81
+ """
82
+ Load role configuration from YAML file.
83
+
84
+ Args:
85
+ file_path: Path to YAML file
86
+
87
+ Returns:
88
+ RoleConfiguration instance
89
+ """
90
+ path = Path(file_path)
91
+
92
+ if not path.exists():
93
+ raise FileNotFoundError(f"Role configuration file not found: {file_path}")
94
+
95
+ with open(path, 'r') as f:
96
+ data = yaml.safe_load(f)
97
+
98
+ logger.info(f"Loaded role configuration from {file_path}")
99
+ return cls(**data)
100
+
101
+ @classmethod
102
+ def load_from_dict(cls, data: Dict[str, Any]) -> "RoleConfiguration":
103
+ """
104
+ Load role configuration from dictionary.
105
+
106
+ Args:
107
+ data: Configuration dictionary
108
+
109
+ Returns:
110
+ RoleConfiguration instance
111
+ """
112
+ return cls(**data)
113
+
114
+ def merge_with(self, other: "RoleConfiguration") -> "RoleConfiguration":
115
+ """
116
+ Merge with another configuration (other takes precedence).
117
+
118
+ Args:
119
+ other: Other configuration
120
+
121
+ Returns:
122
+ New merged RoleConfiguration
123
+ """
124
+ merged_data = {
125
+ "role_name": other.role_name or self.role_name,
126
+ "goal": other.goal or self.goal,
127
+ "backstory": other.backstory or self.backstory,
128
+ "domain_knowledge": other.domain_knowledge or self.domain_knowledge,
129
+ "llm_model": other.llm_model or self.llm_model,
130
+ "temperature": other.temperature if other.temperature != 0.7 else self.temperature,
131
+ "max_tokens": other.max_tokens or self.max_tokens,
132
+ "tools": other.tools if other.tools else self.tools,
133
+ **{**self.additional_config, **other.additional_config},
134
+ }
135
+ return RoleConfiguration(**merged_data)
136
+
137
+ def to_dict(self) -> Dict[str, Any]:
138
+ """Convert to dictionary."""
139
+ return {
140
+ "role_name": self.role_name,
141
+ "goal": self.goal,
142
+ "backstory": self.backstory,
143
+ "domain_knowledge": self.domain_knowledge,
144
+ "llm_model": self.llm_model,
145
+ "temperature": self.temperature,
146
+ "max_tokens": self.max_tokens,
147
+ "tools": self.tools,
148
+ **self.additional_config,
149
+ }
150
+
151
+
152
+ def load_role_config(
153
+ role_name: str,
154
+ base_path: str = "./agent_roles"
155
+ ) -> RoleConfiguration:
156
+ """
157
+ Load role configuration by name.
158
+
159
+ Args:
160
+ role_name: Role name
161
+ base_path: Base directory for role configurations
162
+
163
+ Returns:
164
+ RoleConfiguration instance
165
+ """
166
+ file_path = Path(base_path) / f"{role_name}.yaml"
167
+ return RoleConfiguration.load_from_file(str(file_path))
168
+
169
+
170
+ # Predefined role templates
171
+ ROLE_TEMPLATES = {
172
+ "developer": {
173
+ "role_name": "developer",
174
+ "goal": "Write clean, efficient, and maintainable code",
175
+ "backstory": "You are an experienced software developer with expertise in multiple programming languages and best practices",
176
+ "domain_knowledge": "Software development, design patterns, testing, debugging",
177
+ "temperature": 0.3,
178
+ },
179
+ "researcher": {
180
+ "role_name": "researcher",
181
+ "goal": "Gather, analyze, and synthesize information from various sources",
182
+ "backstory": "You are a meticulous researcher skilled at finding and evaluating information",
183
+ "domain_knowledge": "Research methodologies, critical analysis, information synthesis",
184
+ "temperature": 0.5,
185
+ },
186
+ "analyst": {
187
+ "role_name": "analyst",
188
+ "goal": "Analyze data and provide actionable insights",
189
+ "backstory": "You are a data analyst with strong analytical and problem-solving skills",
190
+ "domain_knowledge": "Data analysis, statistics, visualization, interpretation",
191
+ "temperature": 0.4,
192
+ },
193
+ "creative": {
194
+ "role_name": "creative",
195
+ "goal": "Generate creative and innovative ideas",
196
+ "backstory": "You are a creative thinker with a knack for innovative solutions",
197
+ "domain_knowledge": "Creative thinking, brainstorming, innovation",
198
+ "temperature": 0.9,
199
+ },
200
+ }
201
+
202
+
203
+ def get_role_template(role_name: str) -> RoleConfiguration:
204
+ """
205
+ Get predefined role template.
206
+
207
+ Args:
208
+ role_name: Role name
209
+
210
+ Returns:
211
+ RoleConfiguration instance
212
+ """
213
+ if role_name not in ROLE_TEMPLATES:
214
+ raise ValueError(f"Unknown role template: {role_name}")
215
+
216
+ return RoleConfiguration.load_from_dict(ROLE_TEMPLATES[role_name])
217
+
@@ -0,0 +1,298 @@
1
+ """
2
+ Agent Lifecycle Management
3
+
4
+ Manages agent lifecycle transitions and state management.
5
+ """
6
+
7
+ import logging
8
+ from typing import Dict, Any, Optional, List
9
+ from datetime import datetime
10
+
11
+ from .base_agent import BaseAIAgent
12
+ from .models import AgentState
13
+ from .registry import AgentRegistry, get_global_registry
14
+ from .exceptions import (
15
+ InvalidStateTransitionError,
16
+ AgentInitializationError,
17
+ AgentNotFoundError,
18
+ )
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ class AgentLifecycleManager:
24
+ """
25
+ Manages agent lifecycle: creation, initialization, activation, deactivation, shutdown.
26
+
27
+ Ensures proper state transitions and coordinates with registry.
28
+ """
29
+
30
+ def __init__(self, registry: Optional[AgentRegistry] = None):
31
+ """
32
+ Initialize lifecycle manager.
33
+
34
+ Args:
35
+ registry: Optional custom registry (uses global if not provided)
36
+ """
37
+ self.registry = registry or get_global_registry()
38
+ logger.info("AgentLifecycleManager initialized")
39
+
40
+ async def create_and_initialize(self, agent: BaseAIAgent) -> BaseAIAgent:
41
+ """
42
+ Register and initialize an agent.
43
+
44
+ Args:
45
+ agent: Agent to initialize
46
+
47
+ Returns:
48
+ Initialized agent
49
+
50
+ Raises:
51
+ AgentInitializationError: If initialization fails
52
+ """
53
+ try:
54
+ # Register agent
55
+ self.registry.register(agent)
56
+ logger.info(f"Agent {agent.agent_id} registered")
57
+
58
+ # Initialize agent
59
+ await agent.initialize()
60
+ logger.info(f"Agent {agent.agent_id} initialized")
61
+
62
+ return agent
63
+
64
+ except Exception as e:
65
+ logger.error(f"Failed to create and initialize agent {agent.agent_id}: {e}")
66
+
67
+ # Cleanup on failure
68
+ try:
69
+ if self.registry.exists(agent.agent_id):
70
+ self.registry.unregister(agent.agent_id)
71
+ except Exception as cleanup_error:
72
+ logger.error(f"Cleanup failed: {cleanup_error}")
73
+
74
+ raise AgentInitializationError(
75
+ f"Agent initialization failed: {str(e)}",
76
+ agent_id=agent.agent_id
77
+ )
78
+
79
+ async def activate(self, agent_id: str) -> None:
80
+ """
81
+ Activate an agent.
82
+
83
+ Args:
84
+ agent_id: Agent identifier
85
+
86
+ Raises:
87
+ AgentNotFoundError: If agent not found
88
+ InvalidStateTransitionError: If activation not allowed
89
+ """
90
+ agent = self.registry.get(agent_id)
91
+
92
+ try:
93
+ await agent.activate()
94
+ logger.info(f"Agent {agent_id} activated")
95
+ except Exception as e:
96
+ logger.error(f"Failed to activate agent {agent_id}: {e}")
97
+ raise
98
+
99
+ async def deactivate(self, agent_id: str) -> None:
100
+ """
101
+ Deactivate an agent.
102
+
103
+ Args:
104
+ agent_id: Agent identifier
105
+
106
+ Raises:
107
+ AgentNotFoundError: If agent not found
108
+ InvalidStateTransitionError: If deactivation not allowed
109
+ """
110
+ agent = self.registry.get(agent_id)
111
+
112
+ try:
113
+ await agent.deactivate()
114
+ logger.info(f"Agent {agent_id} deactivated")
115
+ except Exception as e:
116
+ logger.error(f"Failed to deactivate agent {agent_id}: {e}")
117
+ raise
118
+
119
+ async def shutdown(self, agent_id: str, unregister: bool = True) -> None:
120
+ """
121
+ Shutdown an agent.
122
+
123
+ Args:
124
+ agent_id: Agent identifier
125
+ unregister: Whether to unregister after shutdown
126
+
127
+ Raises:
128
+ AgentNotFoundError: If agent not found
129
+ """
130
+ agent = self.registry.get(agent_id)
131
+
132
+ try:
133
+ await agent.shutdown()
134
+ logger.info(f"Agent {agent_id} shut down")
135
+
136
+ if unregister:
137
+ self.registry.unregister(agent_id)
138
+ logger.info(f"Agent {agent_id} unregistered")
139
+
140
+ except Exception as e:
141
+ logger.error(f"Failed to shutdown agent {agent_id}: {e}")
142
+ raise
143
+
144
+ async def restart(self, agent_id: str) -> None:
145
+ """
146
+ Restart an agent (deactivate → initialize → activate).
147
+
148
+ Args:
149
+ agent_id: Agent identifier
150
+
151
+ Raises:
152
+ AgentNotFoundError: If agent not found
153
+ """
154
+ agent = self.registry.get(agent_id)
155
+
156
+ try:
157
+ # Deactivate
158
+ if agent.state in [AgentState.ACTIVE, AgentState.BUSY]:
159
+ await agent.deactivate()
160
+
161
+ # Re-initialize
162
+ await agent.initialize()
163
+
164
+ # Re-activate
165
+ await agent.activate()
166
+
167
+ logger.info(f"Agent {agent_id} restarted")
168
+
169
+ except Exception as e:
170
+ logger.error(f"Failed to restart agent {agent_id}: {e}")
171
+ raise
172
+
173
+ async def shutdown_all(self) -> Dict[str, Any]:
174
+ """
175
+ Shutdown all registered agents.
176
+
177
+ Returns:
178
+ Dictionary with shutdown results
179
+ """
180
+ results = {
181
+ "success": [],
182
+ "failed": [],
183
+ "total": self.registry.count(),
184
+ }
185
+
186
+ agents = self.registry.list_all()
187
+
188
+ for agent in agents:
189
+ try:
190
+ await self.shutdown(agent.agent_id, unregister=True)
191
+ results["success"].append(agent.agent_id)
192
+ except Exception as e:
193
+ logger.error(f"Failed to shutdown agent {agent.agent_id}: {e}")
194
+ results["failed"].append({
195
+ "agent_id": agent.agent_id,
196
+ "error": str(e),
197
+ })
198
+
199
+ logger.info(
200
+ f"Shutdown all agents: {len(results['success'])} succeeded, "
201
+ f"{len(results['failed'])} failed"
202
+ )
203
+
204
+ return results
205
+
206
+ def get_agent_status(self, agent_id: str) -> Dict[str, Any]:
207
+ """
208
+ Get agent status information.
209
+
210
+ Args:
211
+ agent_id: Agent identifier
212
+
213
+ Returns:
214
+ Status dictionary
215
+
216
+ Raises:
217
+ AgentNotFoundError: If agent not found
218
+ """
219
+ agent = self.registry.get(agent_id)
220
+
221
+ return {
222
+ "agent_id": agent.agent_id,
223
+ "name": agent.name,
224
+ "type": agent.agent_type.value,
225
+ "state": agent.state.value,
226
+ "version": agent.version,
227
+ "created_at": agent.created_at.isoformat() if agent.created_at else None,
228
+ "last_active_at": agent.last_active_at.isoformat() if agent.last_active_at else None,
229
+ "current_task_id": agent._current_task_id,
230
+ "metrics": {
231
+ "total_tasks_executed": agent.get_metrics().total_tasks_executed,
232
+ "successful_tasks": agent.get_metrics().successful_tasks,
233
+ "failed_tasks": agent.get_metrics().failed_tasks,
234
+ "average_execution_time": agent.get_metrics().average_execution_time,
235
+ "total_tool_calls": agent.get_metrics().total_tool_calls,
236
+ },
237
+ }
238
+
239
+ def list_agent_statuses(
240
+ self,
241
+ agent_type: Optional[str] = None,
242
+ state: Optional[str] = None
243
+ ) -> "List[Dict[str, Any]]":
244
+ """
245
+ List agent statuses with optional filtering.
246
+
247
+ Args:
248
+ agent_type: Optional filter by agent type
249
+ state: Optional filter by state
250
+
251
+ Returns:
252
+ List of status dictionaries
253
+ """
254
+ agents = self.registry.list_all()
255
+
256
+ # Filter by type
257
+ if agent_type:
258
+ from .models import AgentType
259
+ try:
260
+ type_enum = AgentType(agent_type)
261
+ agents = [a for a in agents if a.agent_type == type_enum]
262
+ except ValueError:
263
+ logger.warning(f"Invalid agent type: {agent_type}")
264
+
265
+ # Filter by state
266
+ if state:
267
+ from .models import AgentState
268
+ try:
269
+ state_enum = AgentState(state)
270
+ agents = [a for a in agents if a.state == state_enum]
271
+ except ValueError:
272
+ logger.warning(f"Invalid state: {state}")
273
+
274
+ return [self.get_agent_status(a.agent_id) for a in agents]
275
+
276
+
277
+ # Global lifecycle manager
278
+ _global_lifecycle_manager: Optional[AgentLifecycleManager] = None
279
+
280
+
281
+ def get_global_lifecycle_manager() -> AgentLifecycleManager:
282
+ """
283
+ Get or create global lifecycle manager.
284
+
285
+ Returns:
286
+ Global AgentLifecycleManager instance
287
+ """
288
+ global _global_lifecycle_manager
289
+ if _global_lifecycle_manager is None:
290
+ _global_lifecycle_manager = AgentLifecycleManager()
291
+ return _global_lifecycle_manager
292
+
293
+
294
+ def reset_global_lifecycle_manager() -> None:
295
+ """Reset global lifecycle manager (primarily for testing)."""
296
+ global _global_lifecycle_manager
297
+ _global_lifecycle_manager = None
298
+