aiecs 1.3.8__py3-none-any.whl → 1.4.0__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.
- aiecs/__init__.py +1 -1
- aiecs/domain/__init__.py +120 -0
- aiecs/domain/agent/__init__.py +184 -0
- aiecs/domain/agent/base_agent.py +691 -0
- aiecs/domain/agent/exceptions.py +99 -0
- aiecs/domain/agent/hybrid_agent.py +495 -0
- aiecs/domain/agent/integration/__init__.py +23 -0
- aiecs/domain/agent/integration/context_compressor.py +219 -0
- aiecs/domain/agent/integration/context_engine_adapter.py +258 -0
- aiecs/domain/agent/integration/retry_policy.py +228 -0
- aiecs/domain/agent/integration/role_config.py +217 -0
- aiecs/domain/agent/lifecycle.py +298 -0
- aiecs/domain/agent/llm_agent.py +309 -0
- aiecs/domain/agent/memory/__init__.py +13 -0
- aiecs/domain/agent/memory/conversation.py +216 -0
- aiecs/domain/agent/migration/__init__.py +15 -0
- aiecs/domain/agent/migration/conversion.py +171 -0
- aiecs/domain/agent/migration/legacy_wrapper.py +97 -0
- aiecs/domain/agent/models.py +263 -0
- aiecs/domain/agent/observability.py +443 -0
- aiecs/domain/agent/persistence.py +287 -0
- aiecs/domain/agent/prompts/__init__.py +25 -0
- aiecs/domain/agent/prompts/builder.py +164 -0
- aiecs/domain/agent/prompts/formatters.py +192 -0
- aiecs/domain/agent/prompts/template.py +264 -0
- aiecs/domain/agent/registry.py +261 -0
- aiecs/domain/agent/tool_agent.py +267 -0
- aiecs/domain/agent/tools/__init__.py +13 -0
- aiecs/domain/agent/tools/schema_generator.py +222 -0
- aiecs/main.py +2 -2
- {aiecs-1.3.8.dist-info → aiecs-1.4.0.dist-info}/METADATA +1 -1
- {aiecs-1.3.8.dist-info → aiecs-1.4.0.dist-info}/RECORD +36 -9
- {aiecs-1.3.8.dist-info → aiecs-1.4.0.dist-info}/WHEEL +0 -0
- {aiecs-1.3.8.dist-info → aiecs-1.4.0.dist-info}/entry_points.txt +0 -0
- {aiecs-1.3.8.dist-info → aiecs-1.4.0.dist-info}/licenses/LICENSE +0 -0
- {aiecs-1.3.8.dist-info → aiecs-1.4.0.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
|
+
|