devloop 0.2.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.
- devloop/__init__.py +3 -0
- devloop/agents/__init__.py +33 -0
- devloop/agents/agent_health_monitor.py +105 -0
- devloop/agents/ci_monitor.py +237 -0
- devloop/agents/code_rabbit.py +248 -0
- devloop/agents/doc_lifecycle.py +374 -0
- devloop/agents/echo.py +24 -0
- devloop/agents/file_logger.py +46 -0
- devloop/agents/formatter.py +511 -0
- devloop/agents/git_commit_assistant.py +421 -0
- devloop/agents/linter.py +399 -0
- devloop/agents/performance_profiler.py +284 -0
- devloop/agents/security_scanner.py +322 -0
- devloop/agents/snyk.py +292 -0
- devloop/agents/test_runner.py +484 -0
- devloop/agents/type_checker.py +242 -0
- devloop/cli/__init__.py +1 -0
- devloop/cli/commands/__init__.py +1 -0
- devloop/cli/commands/custom_agents.py +144 -0
- devloop/cli/commands/feedback.py +161 -0
- devloop/cli/commands/summary.py +50 -0
- devloop/cli/main.py +430 -0
- devloop/cli/main_v1.py +144 -0
- devloop/collectors/__init__.py +17 -0
- devloop/collectors/base.py +55 -0
- devloop/collectors/filesystem.py +126 -0
- devloop/collectors/git.py +171 -0
- devloop/collectors/manager.py +159 -0
- devloop/collectors/process.py +221 -0
- devloop/collectors/system.py +195 -0
- devloop/core/__init__.py +21 -0
- devloop/core/agent.py +206 -0
- devloop/core/agent_template.py +498 -0
- devloop/core/amp_integration.py +166 -0
- devloop/core/auto_fix.py +224 -0
- devloop/core/config.py +272 -0
- devloop/core/context.py +0 -0
- devloop/core/context_store.py +530 -0
- devloop/core/contextual_feedback.py +311 -0
- devloop/core/custom_agent.py +439 -0
- devloop/core/debug_trace.py +289 -0
- devloop/core/event.py +105 -0
- devloop/core/event_store.py +316 -0
- devloop/core/feedback.py +311 -0
- devloop/core/learning.py +351 -0
- devloop/core/manager.py +219 -0
- devloop/core/performance.py +433 -0
- devloop/core/proactive_feedback.py +302 -0
- devloop/core/summary_formatter.py +159 -0
- devloop/core/summary_generator.py +275 -0
- devloop-0.2.0.dist-info/METADATA +705 -0
- devloop-0.2.0.dist-info/RECORD +55 -0
- devloop-0.2.0.dist-info/WHEEL +4 -0
- devloop-0.2.0.dist-info/entry_points.txt +3 -0
- devloop-0.2.0.dist-info/licenses/LICENSE +21 -0
devloop/core/feedback.py
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
"""Feedback system for agent behavior learning."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import time
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from enum import Enum
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Dict, List, Optional
|
|
11
|
+
from uuid import uuid4
|
|
12
|
+
|
|
13
|
+
import aiofiles
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class FeedbackType(Enum):
|
|
17
|
+
"""Types of feedback that can be given to agents."""
|
|
18
|
+
|
|
19
|
+
THUMBS_UP = "thumbs_up"
|
|
20
|
+
THUMBS_DOWN = "thumbs_down"
|
|
21
|
+
RATING = "rating" # 1-5 stars
|
|
22
|
+
COMMENT = "comment"
|
|
23
|
+
DISMISS = "dismiss" # User dismissed/ignored the agent's action
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class Feedback:
|
|
28
|
+
"""Individual feedback item."""
|
|
29
|
+
|
|
30
|
+
id: str
|
|
31
|
+
agent_name: str
|
|
32
|
+
event_type: str
|
|
33
|
+
feedback_type: FeedbackType
|
|
34
|
+
value: Any # thumbs_up/down: bool, rating: int 1-5, comment: str, dismiss: None
|
|
35
|
+
comment: Optional[str] = None
|
|
36
|
+
context: Optional[Dict[str, Any]] = None # Agent result data, file info, etc.
|
|
37
|
+
timestamp: Optional[float] = None
|
|
38
|
+
|
|
39
|
+
def __post_init__(self):
|
|
40
|
+
if self.timestamp is None:
|
|
41
|
+
self.timestamp = time.time()
|
|
42
|
+
|
|
43
|
+
# Validate feedback values
|
|
44
|
+
if self.feedback_type == FeedbackType.RATING:
|
|
45
|
+
if not isinstance(self.value, int) or not (1 <= self.value <= 5):
|
|
46
|
+
raise ValueError("Rating must be an integer between 1 and 5")
|
|
47
|
+
elif self.feedback_type in (FeedbackType.THUMBS_UP, FeedbackType.THUMBS_DOWN):
|
|
48
|
+
if not isinstance(self.value, bool):
|
|
49
|
+
raise ValueError("Thumbs feedback must be a boolean")
|
|
50
|
+
elif self.feedback_type == FeedbackType.COMMENT:
|
|
51
|
+
if not isinstance(self.value, str):
|
|
52
|
+
raise ValueError("Comment feedback must be a string")
|
|
53
|
+
elif self.feedback_type == FeedbackType.DISMISS:
|
|
54
|
+
if self.value is not None:
|
|
55
|
+
raise ValueError("Dismiss feedback value must be None")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass
|
|
59
|
+
class AgentPerformance:
|
|
60
|
+
"""Aggregated performance metrics for an agent."""
|
|
61
|
+
|
|
62
|
+
agent_name: str
|
|
63
|
+
total_executions: int = 0
|
|
64
|
+
successful_executions: int = 0
|
|
65
|
+
average_duration: float = 0.0
|
|
66
|
+
feedback_count: int = 0
|
|
67
|
+
thumbs_up_count: int = 0
|
|
68
|
+
thumbs_down_count: int = 0
|
|
69
|
+
average_rating: float = 0.0
|
|
70
|
+
last_updated: Optional[float] = None
|
|
71
|
+
|
|
72
|
+
def __post_init__(self):
|
|
73
|
+
if self.last_updated is None:
|
|
74
|
+
self.last_updated = time.time()
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class FeedbackStore:
|
|
78
|
+
"""Persistent storage for agent feedback and performance data."""
|
|
79
|
+
|
|
80
|
+
def __init__(self, storage_path: Path):
|
|
81
|
+
self.storage_path = storage_path
|
|
82
|
+
self.storage_path.mkdir(parents=True, exist_ok=True)
|
|
83
|
+
self.feedback_file = storage_path / "feedback.jsonl"
|
|
84
|
+
self.performance_file = storage_path / "performance.json"
|
|
85
|
+
|
|
86
|
+
async def store_feedback(self, feedback: Feedback) -> None:
|
|
87
|
+
"""Store a feedback item."""
|
|
88
|
+
feedback_dict = {
|
|
89
|
+
"id": feedback.id,
|
|
90
|
+
"agent_name": feedback.agent_name,
|
|
91
|
+
"event_type": feedback.event_type,
|
|
92
|
+
"feedback_type": feedback.feedback_type.value,
|
|
93
|
+
"value": feedback.value,
|
|
94
|
+
"comment": feedback.comment,
|
|
95
|
+
"context": feedback.context,
|
|
96
|
+
"timestamp": feedback.timestamp,
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async with aiofiles.open(self.feedback_file, "a") as f:
|
|
100
|
+
await f.write(json.dumps(feedback_dict) + "\n")
|
|
101
|
+
|
|
102
|
+
async def get_feedback_for_agent(
|
|
103
|
+
self, agent_name: str, limit: int = 100
|
|
104
|
+
) -> List[Feedback]:
|
|
105
|
+
"""Get recent feedback for a specific agent."""
|
|
106
|
+
feedback_items: List[Feedback] = []
|
|
107
|
+
|
|
108
|
+
if not self.feedback_file.exists():
|
|
109
|
+
return feedback_items
|
|
110
|
+
|
|
111
|
+
async with aiofiles.open(self.feedback_file, "r") as f:
|
|
112
|
+
lines = await f.readlines()
|
|
113
|
+
|
|
114
|
+
# Parse feedback items for this agent
|
|
115
|
+
for line in reversed(lines): # Most recent first
|
|
116
|
+
if len(feedback_items) >= limit:
|
|
117
|
+
break
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
data = json.loads(line.strip())
|
|
121
|
+
if data["agent_name"] == agent_name:
|
|
122
|
+
feedback_items.append(
|
|
123
|
+
Feedback(
|
|
124
|
+
id=data["id"],
|
|
125
|
+
agent_name=data["agent_name"],
|
|
126
|
+
event_type=data["event_type"],
|
|
127
|
+
feedback_type=FeedbackType(data["feedback_type"]),
|
|
128
|
+
value=data["value"],
|
|
129
|
+
comment=data["comment"],
|
|
130
|
+
context=data["context"],
|
|
131
|
+
timestamp=data["timestamp"],
|
|
132
|
+
)
|
|
133
|
+
)
|
|
134
|
+
except (json.JSONDecodeError, KeyError):
|
|
135
|
+
continue
|
|
136
|
+
|
|
137
|
+
return feedback_items
|
|
138
|
+
|
|
139
|
+
async def update_performance(
|
|
140
|
+
self, agent_name: str, success: bool, duration: float
|
|
141
|
+
) -> None:
|
|
142
|
+
"""Update performance metrics for an agent."""
|
|
143
|
+
performance = await self.get_performance(agent_name)
|
|
144
|
+
|
|
145
|
+
performance.total_executions += 1
|
|
146
|
+
if success:
|
|
147
|
+
performance.successful_executions += 1
|
|
148
|
+
|
|
149
|
+
# Update rolling average duration
|
|
150
|
+
if performance.total_executions == 1:
|
|
151
|
+
performance.average_duration = duration
|
|
152
|
+
else:
|
|
153
|
+
performance.average_duration = (
|
|
154
|
+
(performance.average_duration * (performance.total_executions - 1))
|
|
155
|
+
+ duration
|
|
156
|
+
) / performance.total_executions
|
|
157
|
+
|
|
158
|
+
performance.last_updated = time.time()
|
|
159
|
+
|
|
160
|
+
await self._save_performance(performance)
|
|
161
|
+
|
|
162
|
+
async def update_performance_with_feedback(
|
|
163
|
+
self, agent_name: str, feedback: Feedback
|
|
164
|
+
) -> None:
|
|
165
|
+
"""Update performance metrics with feedback data."""
|
|
166
|
+
performance = await self.get_performance(agent_name)
|
|
167
|
+
|
|
168
|
+
performance.feedback_count += 1
|
|
169
|
+
|
|
170
|
+
if feedback.feedback_type == FeedbackType.THUMBS_UP and feedback.value:
|
|
171
|
+
performance.thumbs_up_count += 1
|
|
172
|
+
elif feedback.feedback_type == FeedbackType.THUMBS_DOWN and feedback.value:
|
|
173
|
+
performance.thumbs_down_count += 1
|
|
174
|
+
elif feedback.feedback_type == FeedbackType.RATING:
|
|
175
|
+
# Update rolling average rating
|
|
176
|
+
if performance.feedback_count == 1:
|
|
177
|
+
performance.average_rating = feedback.value
|
|
178
|
+
else:
|
|
179
|
+
performance.average_rating = (
|
|
180
|
+
(performance.average_rating * (performance.feedback_count - 1))
|
|
181
|
+
+ feedback.value
|
|
182
|
+
) / performance.feedback_count
|
|
183
|
+
|
|
184
|
+
performance.last_updated = time.time()
|
|
185
|
+
|
|
186
|
+
await self._save_performance(performance)
|
|
187
|
+
|
|
188
|
+
async def get_performance(self, agent_name: str) -> AgentPerformance:
|
|
189
|
+
"""Get performance metrics for an agent."""
|
|
190
|
+
if not self.performance_file.exists():
|
|
191
|
+
return AgentPerformance(agent_name=agent_name)
|
|
192
|
+
|
|
193
|
+
async with aiofiles.open(self.performance_file, "r") as f:
|
|
194
|
+
content = await f.read()
|
|
195
|
+
|
|
196
|
+
try:
|
|
197
|
+
data = json.loads(content)
|
|
198
|
+
agent_data = data.get(agent_name, {})
|
|
199
|
+
return AgentPerformance(
|
|
200
|
+
agent_name=agent_name,
|
|
201
|
+
total_executions=agent_data.get("total_executions", 0),
|
|
202
|
+
successful_executions=agent_data.get("successful_executions", 0),
|
|
203
|
+
average_duration=agent_data.get("average_duration", 0.0),
|
|
204
|
+
feedback_count=agent_data.get("feedback_count", 0),
|
|
205
|
+
thumbs_up_count=agent_data.get("thumbs_up_count", 0),
|
|
206
|
+
thumbs_down_count=agent_data.get("thumbs_down_count", 0),
|
|
207
|
+
average_rating=agent_data.get("average_rating", 0.0),
|
|
208
|
+
last_updated=agent_data.get("last_updated", time.time()),
|
|
209
|
+
)
|
|
210
|
+
except (json.JSONDecodeError, KeyError):
|
|
211
|
+
return AgentPerformance(agent_name=agent_name)
|
|
212
|
+
|
|
213
|
+
async def _save_performance(self, performance: AgentPerformance) -> None:
|
|
214
|
+
"""Save performance data to disk."""
|
|
215
|
+
# Load existing data
|
|
216
|
+
data = {}
|
|
217
|
+
if self.performance_file.exists():
|
|
218
|
+
async with aiofiles.open(self.performance_file, "r") as f:
|
|
219
|
+
content = await f.read()
|
|
220
|
+
try:
|
|
221
|
+
data = json.loads(content)
|
|
222
|
+
except json.JSONDecodeError:
|
|
223
|
+
data = {}
|
|
224
|
+
|
|
225
|
+
# Update with new performance data
|
|
226
|
+
data[performance.agent_name] = {
|
|
227
|
+
"total_executions": performance.total_executions,
|
|
228
|
+
"successful_executions": performance.successful_executions,
|
|
229
|
+
"average_duration": performance.average_duration,
|
|
230
|
+
"feedback_count": performance.feedback_count,
|
|
231
|
+
"thumbs_up_count": performance.thumbs_up_count,
|
|
232
|
+
"thumbs_down_count": performance.thumbs_down_count,
|
|
233
|
+
"average_rating": performance.average_rating,
|
|
234
|
+
"last_updated": performance.last_updated,
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
# Save back to file
|
|
238
|
+
async with aiofiles.open(self.performance_file, "w") as f:
|
|
239
|
+
await f.write(json.dumps(data, indent=2))
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class FeedbackAPI:
|
|
243
|
+
"""API for collecting and managing feedback."""
|
|
244
|
+
|
|
245
|
+
def __init__(self, feedback_store: FeedbackStore):
|
|
246
|
+
self.feedback_store = feedback_store
|
|
247
|
+
|
|
248
|
+
async def submit_feedback(
|
|
249
|
+
self,
|
|
250
|
+
agent_name: str,
|
|
251
|
+
event_type: str,
|
|
252
|
+
feedback_type: FeedbackType,
|
|
253
|
+
value: Any,
|
|
254
|
+
comment: Optional[str] = None,
|
|
255
|
+
context: Optional[Dict[str, Any]] = None,
|
|
256
|
+
) -> str:
|
|
257
|
+
"""Submit feedback for an agent."""
|
|
258
|
+
feedback = Feedback(
|
|
259
|
+
id=str(uuid4()),
|
|
260
|
+
agent_name=agent_name,
|
|
261
|
+
event_type=event_type,
|
|
262
|
+
feedback_type=feedback_type,
|
|
263
|
+
value=value,
|
|
264
|
+
comment=comment,
|
|
265
|
+
context=context,
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
await self.feedback_store.store_feedback(feedback)
|
|
269
|
+
await self.feedback_store.update_performance_with_feedback(agent_name, feedback)
|
|
270
|
+
|
|
271
|
+
return feedback.id
|
|
272
|
+
|
|
273
|
+
async def get_agent_insights(self, agent_name: str) -> Dict[str, Any]:
|
|
274
|
+
"""Get insights about an agent's performance and feedback."""
|
|
275
|
+
performance = await self.feedback_store.get_performance(agent_name)
|
|
276
|
+
recent_feedback = await self.feedback_store.get_feedback_for_agent(
|
|
277
|
+
agent_name, limit=20
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
success_rate = (
|
|
281
|
+
(performance.successful_executions / performance.total_executions * 100)
|
|
282
|
+
if performance.total_executions > 0
|
|
283
|
+
else 0
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
thumbs_up_rate = (
|
|
287
|
+
(performance.thumbs_up_count / performance.feedback_count * 100)
|
|
288
|
+
if performance.feedback_count > 0
|
|
289
|
+
else 0
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
return {
|
|
293
|
+
"agent_name": agent_name,
|
|
294
|
+
"performance": {
|
|
295
|
+
"total_executions": performance.total_executions,
|
|
296
|
+
"success_rate": round(success_rate, 1),
|
|
297
|
+
"average_duration": round(performance.average_duration, 2),
|
|
298
|
+
"feedback_count": performance.feedback_count,
|
|
299
|
+
"thumbs_up_rate": round(thumbs_up_rate, 1),
|
|
300
|
+
"average_rating": round(performance.average_rating, 1),
|
|
301
|
+
},
|
|
302
|
+
"recent_feedback": [
|
|
303
|
+
{
|
|
304
|
+
"type": f.feedback_type.value,
|
|
305
|
+
"value": f.value,
|
|
306
|
+
"comment": f.comment,
|
|
307
|
+
"timestamp": f.timestamp,
|
|
308
|
+
}
|
|
309
|
+
for f in recent_feedback[:5] # Last 5 feedback items
|
|
310
|
+
],
|
|
311
|
+
}
|
devloop/core/learning.py
ADDED
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
"""Agent behavior learning system for Phase 3."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, Dict, List, Optional
|
|
9
|
+
|
|
10
|
+
import aiofiles
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class BehaviorPattern:
|
|
15
|
+
"""A learned behavior pattern for an agent."""
|
|
16
|
+
|
|
17
|
+
agent_name: str
|
|
18
|
+
pattern_name: str
|
|
19
|
+
description: str
|
|
20
|
+
conditions: Dict[str, Any]
|
|
21
|
+
recommended_action: str
|
|
22
|
+
confidence: float # 0.0 to 1.0
|
|
23
|
+
frequency: int = 1
|
|
24
|
+
last_observed: Optional[float] = None
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class LearningSystem:
|
|
28
|
+
"""System for learning from agent behavior and feedback."""
|
|
29
|
+
|
|
30
|
+
def __init__(self, storage_path: Path):
|
|
31
|
+
"""Initialize learning system.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
storage_path: Path to store learning data
|
|
35
|
+
"""
|
|
36
|
+
self.storage_path = storage_path
|
|
37
|
+
self.storage_path.mkdir(parents=True, exist_ok=True)
|
|
38
|
+
self.patterns_file = storage_path / "patterns.json"
|
|
39
|
+
self.insights_file = storage_path / "insights.json"
|
|
40
|
+
|
|
41
|
+
async def learn_pattern(
|
|
42
|
+
self,
|
|
43
|
+
agent_name: str,
|
|
44
|
+
pattern_name: str,
|
|
45
|
+
description: str,
|
|
46
|
+
conditions: Dict[str, Any],
|
|
47
|
+
recommended_action: str,
|
|
48
|
+
confidence: float = 0.8,
|
|
49
|
+
) -> None:
|
|
50
|
+
"""Learn a behavior pattern from agent execution.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
agent_name: Name of the agent
|
|
54
|
+
pattern_name: Name of the pattern
|
|
55
|
+
description: Description of the pattern
|
|
56
|
+
conditions: Conditions that trigger the pattern
|
|
57
|
+
recommended_action: Recommended action for this pattern
|
|
58
|
+
confidence: Confidence level (0.0-1.0)
|
|
59
|
+
"""
|
|
60
|
+
patterns = await self._load_patterns()
|
|
61
|
+
|
|
62
|
+
key = f"{agent_name}:{pattern_name}"
|
|
63
|
+
if key in patterns:
|
|
64
|
+
# Update existing pattern
|
|
65
|
+
pattern = patterns[key]
|
|
66
|
+
pattern["frequency"] += 1
|
|
67
|
+
pattern["confidence"] = min(1.0, pattern["confidence"] + 0.05)
|
|
68
|
+
else:
|
|
69
|
+
# Create new pattern
|
|
70
|
+
import time
|
|
71
|
+
|
|
72
|
+
pattern = {
|
|
73
|
+
"agent_name": agent_name,
|
|
74
|
+
"pattern_name": pattern_name,
|
|
75
|
+
"description": description,
|
|
76
|
+
"conditions": conditions,
|
|
77
|
+
"recommended_action": recommended_action,
|
|
78
|
+
"confidence": confidence,
|
|
79
|
+
"frequency": 1,
|
|
80
|
+
"last_observed": time.time(),
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
patterns[key] = pattern
|
|
84
|
+
await self._save_patterns(patterns)
|
|
85
|
+
|
|
86
|
+
async def get_patterns_for_agent(self, agent_name: str) -> List[BehaviorPattern]:
|
|
87
|
+
"""Get learned patterns for an agent.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
agent_name: Name of the agent
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
List of learned behavior patterns
|
|
94
|
+
"""
|
|
95
|
+
patterns_data = await self._load_patterns()
|
|
96
|
+
patterns = []
|
|
97
|
+
|
|
98
|
+
for key, data in patterns_data.items():
|
|
99
|
+
if data.get("agent_name") == agent_name:
|
|
100
|
+
patterns.append(
|
|
101
|
+
BehaviorPattern(
|
|
102
|
+
agent_name=data["agent_name"],
|
|
103
|
+
pattern_name=data["pattern_name"],
|
|
104
|
+
description=data["description"],
|
|
105
|
+
conditions=data["conditions"],
|
|
106
|
+
recommended_action=data["recommended_action"],
|
|
107
|
+
confidence=data["confidence"],
|
|
108
|
+
frequency=data.get("frequency", 1),
|
|
109
|
+
last_observed=data.get("last_observed"),
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
return sorted(patterns, key=lambda p: p.confidence, reverse=True)
|
|
114
|
+
|
|
115
|
+
async def get_recommendations(self, agent_name: str) -> List[Dict[str, Any]]:
|
|
116
|
+
"""Get recommendations based on learned patterns.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
agent_name: Name of the agent
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
List of recommendations
|
|
123
|
+
"""
|
|
124
|
+
patterns = await self.get_patterns_for_agent(agent_name)
|
|
125
|
+
recommendations = []
|
|
126
|
+
|
|
127
|
+
for pattern in patterns:
|
|
128
|
+
if pattern.confidence >= 0.7: # Only high-confidence patterns
|
|
129
|
+
recommendations.append(
|
|
130
|
+
{
|
|
131
|
+
"pattern": pattern.pattern_name,
|
|
132
|
+
"description": pattern.description,
|
|
133
|
+
"action": pattern.recommended_action,
|
|
134
|
+
"confidence": pattern.confidence,
|
|
135
|
+
"frequency": pattern.frequency,
|
|
136
|
+
}
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
return recommendations
|
|
140
|
+
|
|
141
|
+
async def store_insight(
|
|
142
|
+
self,
|
|
143
|
+
agent_name: str,
|
|
144
|
+
insight_type: str,
|
|
145
|
+
data: Dict[str, Any],
|
|
146
|
+
) -> None:
|
|
147
|
+
"""Store an insight about agent behavior.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
agent_name: Name of the agent
|
|
151
|
+
insight_type: Type of insight
|
|
152
|
+
data: Insight data
|
|
153
|
+
"""
|
|
154
|
+
insights = await self._load_insights()
|
|
155
|
+
|
|
156
|
+
key = f"{agent_name}:{insight_type}"
|
|
157
|
+
if key in insights:
|
|
158
|
+
insights[key]["count"] += 1
|
|
159
|
+
insights[key]["latest_data"] = data
|
|
160
|
+
else:
|
|
161
|
+
import time
|
|
162
|
+
|
|
163
|
+
insights[key] = {
|
|
164
|
+
"agent_name": agent_name,
|
|
165
|
+
"insight_type": insight_type,
|
|
166
|
+
"data": data,
|
|
167
|
+
"latest_data": data,
|
|
168
|
+
"count": 1,
|
|
169
|
+
"first_observed": time.time(),
|
|
170
|
+
"last_observed": time.time(),
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
insights[key]["last_observed"] = __import__("time").time()
|
|
174
|
+
await self._save_insights(insights)
|
|
175
|
+
|
|
176
|
+
async def get_insights_for_agent(self, agent_name: str) -> Dict[str, Any]:
|
|
177
|
+
"""Get all insights for an agent.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
agent_name: Name of the agent
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
Dictionary of insights
|
|
184
|
+
"""
|
|
185
|
+
insights_data = await self._load_insights()
|
|
186
|
+
agent_insights = {}
|
|
187
|
+
|
|
188
|
+
for key, data in insights_data.items():
|
|
189
|
+
if data.get("agent_name") == agent_name:
|
|
190
|
+
insight_type = data["insight_type"]
|
|
191
|
+
agent_insights[insight_type] = {
|
|
192
|
+
"count": data["count"],
|
|
193
|
+
"data": data.get("latest_data", data.get("data", {})),
|
|
194
|
+
"first_observed": data.get("first_observed"),
|
|
195
|
+
"last_observed": data.get("last_observed"),
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return agent_insights
|
|
199
|
+
|
|
200
|
+
async def suggest_optimization(self, agent_name: str) -> Optional[Dict[str, Any]]:
|
|
201
|
+
"""Suggest optimizations for an agent based on learning.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
agent_name: Name of the agent
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
Optimization suggestion or None
|
|
208
|
+
"""
|
|
209
|
+
insights = await self.get_insights_for_agent(agent_name)
|
|
210
|
+
|
|
211
|
+
# Analyze patterns
|
|
212
|
+
suggestions = []
|
|
213
|
+
|
|
214
|
+
if "slow_execution" in insights:
|
|
215
|
+
suggestions.append(
|
|
216
|
+
{
|
|
217
|
+
"issue": "Slow execution detected",
|
|
218
|
+
"suggestion": "Consider increasing debounce time or reducing scope",
|
|
219
|
+
"priority": "medium",
|
|
220
|
+
}
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
if "high_memory_usage" in insights:
|
|
224
|
+
suggestions.append(
|
|
225
|
+
{
|
|
226
|
+
"issue": "High memory usage",
|
|
227
|
+
"suggestion": "Consider processing files in batches",
|
|
228
|
+
"priority": "high",
|
|
229
|
+
}
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
if "frequent_errors" in insights:
|
|
233
|
+
suggestions.append(
|
|
234
|
+
{
|
|
235
|
+
"issue": "Frequent errors",
|
|
236
|
+
"suggestion": "Review error handling and edge cases",
|
|
237
|
+
"priority": "high",
|
|
238
|
+
}
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
if suggestions:
|
|
242
|
+
return suggestions[0]
|
|
243
|
+
|
|
244
|
+
return None
|
|
245
|
+
|
|
246
|
+
async def _load_patterns(self) -> Dict[str, Dict[str, Any]]:
|
|
247
|
+
"""Load all patterns from file.
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
Dictionary of patterns
|
|
251
|
+
"""
|
|
252
|
+
if not self.patterns_file.exists():
|
|
253
|
+
return {}
|
|
254
|
+
|
|
255
|
+
async with aiofiles.open(self.patterns_file, "r") as f:
|
|
256
|
+
content = await f.read()
|
|
257
|
+
|
|
258
|
+
try:
|
|
259
|
+
return json.loads(content)
|
|
260
|
+
except json.JSONDecodeError:
|
|
261
|
+
return {}
|
|
262
|
+
|
|
263
|
+
async def _save_patterns(self, patterns: Dict[str, Dict[str, Any]]) -> None:
|
|
264
|
+
"""Save patterns to file.
|
|
265
|
+
|
|
266
|
+
Args:
|
|
267
|
+
patterns: Dictionary of patterns to save
|
|
268
|
+
"""
|
|
269
|
+
async with aiofiles.open(self.patterns_file, "w") as f:
|
|
270
|
+
await f.write(json.dumps(patterns, indent=2))
|
|
271
|
+
|
|
272
|
+
async def _load_insights(self) -> Dict[str, Dict[str, Any]]:
|
|
273
|
+
"""Load all insights from file.
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
Dictionary of insights
|
|
277
|
+
"""
|
|
278
|
+
if not self.insights_file.exists():
|
|
279
|
+
return {}
|
|
280
|
+
|
|
281
|
+
async with aiofiles.open(self.insights_file, "r") as f:
|
|
282
|
+
content = await f.read()
|
|
283
|
+
|
|
284
|
+
try:
|
|
285
|
+
return json.loads(content)
|
|
286
|
+
except json.JSONDecodeError:
|
|
287
|
+
return {}
|
|
288
|
+
|
|
289
|
+
async def _save_insights(self, insights: Dict[str, Dict[str, Any]]) -> None:
|
|
290
|
+
"""Save insights to file.
|
|
291
|
+
|
|
292
|
+
Args:
|
|
293
|
+
insights: Dictionary of insights to save
|
|
294
|
+
"""
|
|
295
|
+
async with aiofiles.open(self.insights_file, "w") as f:
|
|
296
|
+
await f.write(json.dumps(insights, indent=2))
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
class AdaptiveAgentConfig:
|
|
300
|
+
"""Adaptive configuration that learns from behavior."""
|
|
301
|
+
|
|
302
|
+
def __init__(self, learning_system: LearningSystem, agent_name: str):
|
|
303
|
+
"""Initialize adaptive config.
|
|
304
|
+
|
|
305
|
+
Args:
|
|
306
|
+
learning_system: Learning system instance
|
|
307
|
+
agent_name: Name of the agent
|
|
308
|
+
"""
|
|
309
|
+
self.learning_system = learning_system
|
|
310
|
+
self.agent_name = agent_name
|
|
311
|
+
self._config_cache: Dict[str, Any] = {}
|
|
312
|
+
|
|
313
|
+
async def get_optimal_parameters(self) -> Dict[str, Any]:
|
|
314
|
+
"""Get parameters optimized based on learning.
|
|
315
|
+
|
|
316
|
+
Returns:
|
|
317
|
+
Optimized configuration parameters
|
|
318
|
+
"""
|
|
319
|
+
insights = await self.learning_system.get_insights_for_agent(self.agent_name)
|
|
320
|
+
parameters = {
|
|
321
|
+
"debounce_seconds": 1.0,
|
|
322
|
+
"timeout_seconds": 30,
|
|
323
|
+
"retry_count": 3,
|
|
324
|
+
"batch_size": 10,
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
# Adapt based on insights
|
|
328
|
+
if "slow_execution" in insights:
|
|
329
|
+
parameters["debounce_seconds"] = 2.0
|
|
330
|
+
parameters["batch_size"] = 5
|
|
331
|
+
|
|
332
|
+
if "frequent_errors" in insights:
|
|
333
|
+
parameters["retry_count"] = 5
|
|
334
|
+
|
|
335
|
+
return parameters
|
|
336
|
+
|
|
337
|
+
async def should_execute(self) -> bool:
|
|
338
|
+
"""Determine if agent should execute based on learning.
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
True if agent should execute
|
|
342
|
+
"""
|
|
343
|
+
insights = await self.learning_system.get_insights_for_agent(self.agent_name)
|
|
344
|
+
|
|
345
|
+
# Skip if too many recent errors
|
|
346
|
+
if "frequent_errors" in insights:
|
|
347
|
+
error_count = insights["frequent_errors"]["count"]
|
|
348
|
+
if error_count > 10:
|
|
349
|
+
return False
|
|
350
|
+
|
|
351
|
+
return True
|