cc-codeconductor 0.4.3 → 0.5.0
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.
- package/README.md +1 -1
- package/dist/index.js +2642 -226
- package/package.json +1 -1
- package/presets/agy/skills/evaluation/SKILL.md +6 -0
- package/presets/agy/skills/openspec/SKILL.md +32 -0
- package/presets/agy/workflows/cc-openspec.md +62 -0
- package/presets/agy/workflows/cc-scorecard.md +17 -0
- package/presets/claude/commands/cc/openspec.md +144 -0
- package/presets/claude/commands/cc/review.md +13 -2
- package/presets/claude/commands/cc/scorecard.md +65 -0
- package/presets/claude/skills/evaluation/SKILL.md +42 -0
- package/presets/claude/skills/openspec/SKILL.md +54 -0
- package/presets/codex/AGENTS.md +57 -0
- package/presets/cursor/.cursorignore +15 -0
- package/presets/cursor/AGENTS.md +504 -0
- package/presets/cursor/agents/architect.md +211 -0
- package/presets/cursor/agents/complexity-auditor.md +76 -0
- package/presets/cursor/agents/contract-builder.md +75 -0
- package/presets/cursor/agents/docs.md +180 -0
- package/presets/cursor/agents/goal-planner.md +71 -0
- package/presets/cursor/agents/implementer.md +161 -0
- package/presets/cursor/agents/orchestrator.md +377 -0
- package/presets/cursor/agents/repo-explorer.md +100 -0
- package/presets/cursor/agents/reviewer.md +237 -0
- package/presets/cursor/agents/security-reviewer.md +113 -0
- package/presets/cursor/agents/task-coach.md +145 -0
- package/presets/cursor/agents/tester.md +241 -0
- package/presets/cursor/commands/cc/api-contract.md +58 -0
- package/presets/cursor/commands/cc/db-migration.md +58 -0
- package/presets/cursor/commands/cc/feature.md +115 -0
- package/presets/cursor/commands/cc/fix.md +121 -0
- package/presets/cursor/commands/cc/openspec.md +144 -0
- package/presets/cursor/commands/cc/pagespeed.md +103 -0
- package/presets/cursor/commands/cc/refactor.md +148 -0
- package/presets/cursor/commands/cc/review.md +137 -0
- package/presets/cursor/commands/cc/scorecard.md +65 -0
- package/presets/cursor/commands/cc/tdd-cycle.md +226 -0
- package/presets/cursor/commands/cc/test-plan.md +138 -0
- package/presets/cursor/rules/behavioral-discipline.mdc +14 -0
- package/presets/cursor/rules/context-budget.mdc +12 -0
- package/presets/cursor/rules/orchestration.mdc +12 -0
- package/presets/cursor/rules/yagni-stdlib.mdc +11 -0
- package/presets/cursor/skills/android/SKILL.md +122 -0
- package/presets/cursor/skills/api-versioning/SKILL.md +394 -0
- package/presets/cursor/skills/astro/SKILL.md +322 -0
- package/presets/cursor/skills/auth-token-inspector/SKILL.md +33 -0
- package/presets/cursor/skills/code-review/SKILL.md +208 -0
- package/presets/cursor/skills/conductor-setup/SKILL.md +127 -0
- package/presets/cursor/skills/django-orm/SKILL.md +463 -0
- package/presets/cursor/skills/django-testing/SKILL.md +417 -0
- package/presets/cursor/skills/django-uv/SKILL.md +409 -0
- package/presets/cursor/skills/drizzle-schema-architect/SKILL.md +54 -0
- package/presets/cursor/skills/evaluation/SKILL.md +8 -0
- package/presets/cursor/skills/fastapi-pydantic-strict/SKILL.md +46 -0
- package/presets/cursor/skills/find-skills/SKILL.md +144 -0
- package/presets/cursor/skills/jpa-nplusone-detector/SKILL.md +49 -0
- package/presets/cursor/skills/jpa-postgres/SKILL.md +626 -0
- package/presets/cursor/skills/laravel-specialist/SKILL.md +267 -0
- package/presets/cursor/skills/laravel-specialist/references/eloquent.md +351 -0
- package/presets/cursor/skills/laravel-specialist/references/livewire.md +512 -0
- package/presets/cursor/skills/laravel-specialist/references/queues.md +423 -0
- package/presets/cursor/skills/laravel-specialist/references/routing.md +362 -0
- package/presets/cursor/skills/laravel-specialist/references/testing.md +522 -0
- package/presets/cursor/skills/livewire-alpine-bridge/SKILL.md +39 -0
- package/presets/cursor/skills/multi-agent-orchestration/README.md +144 -0
- package/presets/cursor/skills/multi-agent-orchestration/SKILL.md +579 -0
- package/presets/cursor/skills/multi-agent-orchestration/examples/framework_implementations.py +362 -0
- package/presets/cursor/skills/multi-agent-orchestration/examples/orchestration_patterns.py +411 -0
- package/presets/cursor/skills/multi-agent-orchestration/scripts/agent_communication.py +334 -0
- package/presets/cursor/skills/multi-agent-orchestration/scripts/benchmarking.py +341 -0
- package/presets/cursor/skills/multi-agent-orchestration/scripts/workflow_management.py +334 -0
- package/presets/cursor/skills/nextjs-typescript/SKILL.md +394 -0
- package/presets/cursor/skills/openspec/SKILL.md +52 -0
- package/presets/cursor/skills/pagespeed-insights/SKILL.md +445 -0
- package/presets/cursor/skills/pagespeed-insights/reference.md +50 -0
- package/presets/cursor/skills/pagespeed-perf/SKILL.md +281 -0
- package/presets/cursor/skills/php-pro/SKILL.md +210 -0
- package/presets/cursor/skills/php-pro/references/async-patterns.md +412 -0
- package/presets/cursor/skills/php-pro/references/laravel-patterns.md +377 -0
- package/presets/cursor/skills/php-pro/references/modern-php-features.md +323 -0
- package/presets/cursor/skills/php-pro/references/symfony-patterns.md +466 -0
- package/presets/cursor/skills/php-pro/references/testing-quality.md +466 -0
- package/presets/cursor/skills/python/SKILL.md +613 -0
- package/presets/cursor/skills/python-django-stack/SKILL.md +500 -0
- package/presets/cursor/skills/python-fastapi-stack/SKILL.md +464 -0
- package/presets/cursor/skills/security/SKILL.md +384 -0
- package/presets/cursor/skills/seo-analytics-injector/SKILL.md +44 -0
- package/presets/cursor/skills/spring-auth-auditor/SKILL.md +33 -0
- package/presets/cursor/skills/spring-boot-feature/SKILL.md +566 -0
- package/presets/cursor/skills/spring-boot-kotlin/SKILL.md +408 -0
- package/presets/cursor/skills/spring-boot-testing-strategy/SKILL.md +479 -0
- package/presets/cursor/skills/sqlalchemy/SKILL.md +473 -0
- package/presets/cursor/skills/tailwind-responsive-auditor/SKILL.md +32 -0
- package/presets/cursor/skills/tdd-mutation-tester/SKILL.md +28 -0
- package/presets/cursor/skills/testing-tdd/SKILL.md +592 -0
- package/presets/cursor/skills/workflow-orchestration-patterns/SKILL.md +98 -0
- package/presets/cursor/skills/workflow-orchestration-patterns/references/details.md +223 -0
- package/presets/opencode/agents/architect.md +1 -2
- package/presets/opencode/agents/complexity-auditor.md +1 -0
- package/presets/opencode/agents/contract-builder.md +93 -0
- package/presets/opencode/agents/docs.md +1 -2
- package/presets/opencode/agents/goal-planner.md +82 -0
- package/presets/opencode/agents/implementer.md +9 -2
- package/presets/opencode/agents/orchestrator.md +50 -12
- package/presets/opencode/agents/repo-explorer.md +0 -1
- package/presets/opencode/agents/reviewer.md +23 -2
- package/presets/opencode/agents/security-reviewer.md +129 -0
- package/presets/opencode/agents/task-coach.md +1 -2
- package/presets/opencode/agents/tester.md +1 -2
- package/presets/opencode/commands/cc-openspec.md +61 -0
- package/presets/opencode/commands/cc-scorecard.md +16 -0
- package/presets/opencode/prompts/v0.5.0/architect.md +222 -0
- package/presets/opencode/prompts/v0.5.0/complexity-auditor.md +91 -0
- package/presets/opencode/prompts/v0.5.0/contract-builder.md +84 -0
- package/presets/opencode/prompts/v0.5.0/docs.md +190 -0
- package/presets/opencode/prompts/v0.5.0/goal-planner.md +80 -0
- package/presets/opencode/prompts/v0.5.0/implementer.md +171 -0
- package/presets/opencode/prompts/v0.5.0/orchestrator.md +388 -0
- package/presets/opencode/prompts/v0.5.0/repo-explorer.md +111 -0
- package/presets/opencode/prompts/v0.5.0/reviewer.md +248 -0
- package/presets/opencode/prompts/v0.5.0/security-reviewer.md +123 -0
- package/presets/opencode/prompts/v0.5.0/task-coach.md +156 -0
- package/presets/opencode/prompts/v0.5.0/tester.md +252 -0
- package/presets/opencode/skills/evaluation/SKILL.md +6 -0
- package/presets/opencode/skills/openspec/SKILL.md +50 -0
- package/presets/templates/BACKLOG.md +33 -0
- package/presets/templates/execution-profile.yml +6 -0
- package/presets/templates/model-comparison.md +11 -0
- package/presets/templates/regression-checklist.yml +10 -0
- package/src/presets/manifests/agy.yml +2 -2
- package/src/presets/manifests/claude.yml +2 -2
- package/src/presets/manifests/codex.yml +2 -2
- package/src/presets/manifests/cursor.yml +19 -3
- package/src/presets/manifests/gemini.yml +2 -2
- package/src/presets/manifests/opencode.yml +2 -2
- package/src/presets/models/agy.yml +21 -0
- package/src/presets/models/claude.yml +18 -0
- package/src/presets/models/codex.yml +18 -0
- package/src/presets/models/cursor.yml +39 -9
- package/src/presets/models/gemini.yml +18 -0
- package/src/presets/models/opencode.yml +18 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agent Communication Management
|
|
3
|
+
|
|
4
|
+
Handle agent-to-agent communication, message passing, and shared state.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Dict, List, Optional, Any, Callable
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from enum import Enum
|
|
10
|
+
import json
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class MessageType(Enum):
|
|
14
|
+
"""Types of messages between agents."""
|
|
15
|
+
|
|
16
|
+
DIRECT = "direct"
|
|
17
|
+
BROADCAST = "broadcast"
|
|
18
|
+
REQUEST = "request"
|
|
19
|
+
RESPONSE = "response"
|
|
20
|
+
FEEDBACK = "feedback"
|
|
21
|
+
ERROR = "error"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class Message:
|
|
26
|
+
"""Message between agents."""
|
|
27
|
+
|
|
28
|
+
sender: str
|
|
29
|
+
recipient: str
|
|
30
|
+
content: str
|
|
31
|
+
message_type: MessageType
|
|
32
|
+
timestamp: float = field(default_factory=lambda: 0)
|
|
33
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
34
|
+
|
|
35
|
+
def to_dict(self) -> Dict:
|
|
36
|
+
"""Convert to dictionary."""
|
|
37
|
+
return {
|
|
38
|
+
"sender": self.sender,
|
|
39
|
+
"recipient": self.recipient,
|
|
40
|
+
"content": self.content,
|
|
41
|
+
"type": self.message_type.value,
|
|
42
|
+
"timestamp": self.timestamp,
|
|
43
|
+
"metadata": self.metadata,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class MessageBroker:
|
|
48
|
+
"""Central message broker for agent communication."""
|
|
49
|
+
|
|
50
|
+
def __init__(self):
|
|
51
|
+
"""Initialize message broker."""
|
|
52
|
+
self.message_queue: List[Message] = []
|
|
53
|
+
self.agent_inboxes: Dict[str, List[Message]] = {}
|
|
54
|
+
self.message_handlers: Dict[MessageType, List[Callable]] = {}
|
|
55
|
+
|
|
56
|
+
def send_message(self, message: Message) -> bool:
|
|
57
|
+
"""
|
|
58
|
+
Send message from one agent to another.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
message: Message object
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
Whether message was delivered
|
|
65
|
+
"""
|
|
66
|
+
self.message_queue.append(message)
|
|
67
|
+
|
|
68
|
+
# Add to recipient's inbox
|
|
69
|
+
if message.recipient not in self.agent_inboxes:
|
|
70
|
+
self.agent_inboxes[message.recipient] = []
|
|
71
|
+
self.agent_inboxes[message.recipient].append(message)
|
|
72
|
+
|
|
73
|
+
# Trigger handlers
|
|
74
|
+
self._trigger_handlers(message)
|
|
75
|
+
|
|
76
|
+
return True
|
|
77
|
+
|
|
78
|
+
def broadcast_message(self, sender: str, content: str, recipients: List[str]):
|
|
79
|
+
"""
|
|
80
|
+
Broadcast message to multiple agents.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
sender: Sending agent
|
|
84
|
+
content: Message content
|
|
85
|
+
recipients: List of recipient agents
|
|
86
|
+
"""
|
|
87
|
+
for recipient in recipients:
|
|
88
|
+
message = Message(
|
|
89
|
+
sender=sender,
|
|
90
|
+
recipient=recipient,
|
|
91
|
+
content=content,
|
|
92
|
+
message_type=MessageType.BROADCAST,
|
|
93
|
+
)
|
|
94
|
+
self.send_message(message)
|
|
95
|
+
|
|
96
|
+
def request_response(
|
|
97
|
+
self, sender: str, recipient: str, content: str, timeout: float = 5.0
|
|
98
|
+
) -> Optional[Message]:
|
|
99
|
+
"""
|
|
100
|
+
Send request and wait for response.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
sender: Requesting agent
|
|
104
|
+
recipient: Agent to respond
|
|
105
|
+
content: Request content
|
|
106
|
+
timeout: Response timeout in seconds
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
Response message or None
|
|
110
|
+
"""
|
|
111
|
+
message = Message(
|
|
112
|
+
sender=sender,
|
|
113
|
+
recipient=recipient,
|
|
114
|
+
content=content,
|
|
115
|
+
message_type=MessageType.REQUEST,
|
|
116
|
+
metadata={"timeout": timeout},
|
|
117
|
+
)
|
|
118
|
+
self.send_message(message)
|
|
119
|
+
|
|
120
|
+
# Placeholder - wait for response
|
|
121
|
+
# In production, implement actual timeout/wait mechanism
|
|
122
|
+
return None
|
|
123
|
+
|
|
124
|
+
def get_inbox(self, agent: str) -> List[Message]:
|
|
125
|
+
"""Get messages for specific agent."""
|
|
126
|
+
return self.agent_inboxes.get(agent, [])
|
|
127
|
+
|
|
128
|
+
def clear_inbox(self, agent: str) -> None:
|
|
129
|
+
"""Clear agent's inbox."""
|
|
130
|
+
self.agent_inboxes[agent] = []
|
|
131
|
+
|
|
132
|
+
def register_handler(
|
|
133
|
+
self, message_type: MessageType, handler: Callable
|
|
134
|
+
) -> None:
|
|
135
|
+
"""Register handler for message type."""
|
|
136
|
+
if message_type not in self.message_handlers:
|
|
137
|
+
self.message_handlers[message_type] = []
|
|
138
|
+
self.message_handlers[message_type].append(handler)
|
|
139
|
+
|
|
140
|
+
def _trigger_handlers(self, message: Message) -> None:
|
|
141
|
+
"""Trigger registered handlers."""
|
|
142
|
+
handlers = self.message_handlers.get(message.message_type, [])
|
|
143
|
+
for handler in handlers:
|
|
144
|
+
try:
|
|
145
|
+
handler(message)
|
|
146
|
+
except Exception:
|
|
147
|
+
pass
|
|
148
|
+
|
|
149
|
+
def get_statistics(self) -> Dict[str, Any]:
|
|
150
|
+
"""Get communication statistics."""
|
|
151
|
+
type_counts = {}
|
|
152
|
+
for message in self.message_queue:
|
|
153
|
+
msg_type = message.message_type.value
|
|
154
|
+
type_counts[msg_type] = type_counts.get(msg_type, 0) + 1
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
"total_messages": len(self.message_queue),
|
|
158
|
+
"by_type": type_counts,
|
|
159
|
+
"total_agents": len(self.agent_inboxes),
|
|
160
|
+
"agents": list(self.agent_inboxes.keys()),
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class SharedMemory:
|
|
165
|
+
"""Shared memory for tool-mediated agent communication."""
|
|
166
|
+
|
|
167
|
+
def __init__(self):
|
|
168
|
+
"""Initialize shared memory."""
|
|
169
|
+
self.memory: Dict[str, Any] = {}
|
|
170
|
+
self.access_log: List[Dict] = []
|
|
171
|
+
|
|
172
|
+
def write(self, key: str, value: Any, agent: str = "system") -> None:
|
|
173
|
+
"""
|
|
174
|
+
Write to shared memory.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
key: Memory key
|
|
178
|
+
value: Value to store
|
|
179
|
+
agent: Agent writing
|
|
180
|
+
"""
|
|
181
|
+
self.memory[key] = {
|
|
182
|
+
"value": value,
|
|
183
|
+
"writer": agent,
|
|
184
|
+
"access_count": 0,
|
|
185
|
+
}
|
|
186
|
+
self.access_log.append({
|
|
187
|
+
"action": "write",
|
|
188
|
+
"key": key,
|
|
189
|
+
"agent": agent,
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
def read(self, key: str, agent: str = "system") -> Optional[Any]:
|
|
193
|
+
"""
|
|
194
|
+
Read from shared memory.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
key: Memory key
|
|
198
|
+
agent: Agent reading
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
Stored value or None
|
|
202
|
+
"""
|
|
203
|
+
if key in self.memory:
|
|
204
|
+
entry = self.memory[key]
|
|
205
|
+
entry["access_count"] += 1
|
|
206
|
+
self.access_log.append({
|
|
207
|
+
"action": "read",
|
|
208
|
+
"key": key,
|
|
209
|
+
"agent": agent,
|
|
210
|
+
})
|
|
211
|
+
return entry["value"]
|
|
212
|
+
return None
|
|
213
|
+
|
|
214
|
+
def append(self, key: str, value: Any, agent: str = "system") -> None:
|
|
215
|
+
"""Append to list in shared memory."""
|
|
216
|
+
if key not in self.memory:
|
|
217
|
+
self.memory[key] = {
|
|
218
|
+
"value": [],
|
|
219
|
+
"writer": agent,
|
|
220
|
+
"access_count": 0,
|
|
221
|
+
}
|
|
222
|
+
if isinstance(self.memory[key]["value"], list):
|
|
223
|
+
self.memory[key]["value"].append(value)
|
|
224
|
+
|
|
225
|
+
def get_all(self) -> Dict[str, Any]:
|
|
226
|
+
"""Get all shared memory contents."""
|
|
227
|
+
return {
|
|
228
|
+
key: entry["value"] for key, entry in self.memory.items()
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
def get_statistics(self) -> Dict[str, Any]:
|
|
232
|
+
"""Get memory access statistics."""
|
|
233
|
+
total_accesses = sum(
|
|
234
|
+
entry["access_count"] for entry in self.memory.values()
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
"total_keys": len(self.memory),
|
|
239
|
+
"total_accesses": total_accesses,
|
|
240
|
+
"access_log_size": len(self.access_log),
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
class ContextManager:
|
|
245
|
+
"""Manage context sharing between agents."""
|
|
246
|
+
|
|
247
|
+
def __init__(self):
|
|
248
|
+
"""Initialize context manager."""
|
|
249
|
+
self.contexts: Dict[str, Dict[str, Any]] = {}
|
|
250
|
+
self.global_context: Dict[str, Any] = {}
|
|
251
|
+
|
|
252
|
+
def create_context(self, context_id: str, initial_data: Optional[Dict] = None) -> None:
|
|
253
|
+
"""Create new context."""
|
|
254
|
+
self.contexts[context_id] = initial_data or {}
|
|
255
|
+
|
|
256
|
+
def update_context(self, context_id: str, data: Dict) -> None:
|
|
257
|
+
"""Update context data."""
|
|
258
|
+
if context_id in self.contexts:
|
|
259
|
+
self.contexts[context_id].update(data)
|
|
260
|
+
|
|
261
|
+
def get_context(self, context_id: str) -> Dict[str, Any]:
|
|
262
|
+
"""Get context data."""
|
|
263
|
+
return self.contexts.get(context_id, {})
|
|
264
|
+
|
|
265
|
+
def set_global_context(self, key: str, value: Any) -> None:
|
|
266
|
+
"""Set global context variable."""
|
|
267
|
+
self.global_context[key] = value
|
|
268
|
+
|
|
269
|
+
def get_global_context(self, key: str) -> Optional[Any]:
|
|
270
|
+
"""Get global context variable."""
|
|
271
|
+
return self.global_context.get(key)
|
|
272
|
+
|
|
273
|
+
def context_to_string(self, context_id: str) -> str:
|
|
274
|
+
"""Convert context to formatted string."""
|
|
275
|
+
context = self.get_context(context_id)
|
|
276
|
+
return json.dumps(context, indent=2)
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
class CommunicationProtocol:
|
|
280
|
+
"""Define communication protocol between agents."""
|
|
281
|
+
|
|
282
|
+
def __init__(self, broker: MessageBroker, shared_memory: SharedMemory):
|
|
283
|
+
"""Initialize protocol."""
|
|
284
|
+
self.broker = broker
|
|
285
|
+
self.shared_memory = shared_memory
|
|
286
|
+
|
|
287
|
+
def request_analysis(
|
|
288
|
+
self, requester: str, analyzer: str, subject: str
|
|
289
|
+
) -> None:
|
|
290
|
+
"""Request analysis from another agent."""
|
|
291
|
+
message = Message(
|
|
292
|
+
sender=requester,
|
|
293
|
+
recipient=analyzer,
|
|
294
|
+
content=f"Analyze: {subject}",
|
|
295
|
+
message_type=MessageType.REQUEST,
|
|
296
|
+
metadata={"action": "analyze"},
|
|
297
|
+
)
|
|
298
|
+
self.broker.send_message(message)
|
|
299
|
+
|
|
300
|
+
def share_findings(self, source_agent: str, key: str, findings: Dict) -> None:
|
|
301
|
+
"""Share findings through shared memory."""
|
|
302
|
+
self.shared_memory.write(key, findings, source_agent)
|
|
303
|
+
|
|
304
|
+
def aggregate_results(
|
|
305
|
+
self, agents: List[str], result_key: str
|
|
306
|
+
) -> Dict[str, Any]:
|
|
307
|
+
"""Aggregate results from multiple agents."""
|
|
308
|
+
results = {}
|
|
309
|
+
for agent in agents:
|
|
310
|
+
agent_results = self.shared_memory.read(f"{agent}_{result_key}")
|
|
311
|
+
if agent_results:
|
|
312
|
+
results[agent] = agent_results
|
|
313
|
+
|
|
314
|
+
return results
|
|
315
|
+
|
|
316
|
+
def notify_status(self, agent: str, status: str) -> None:
|
|
317
|
+
"""Notify status update."""
|
|
318
|
+
message = Message(
|
|
319
|
+
sender=agent,
|
|
320
|
+
recipient="orchestrator",
|
|
321
|
+
content=f"Status: {status}",
|
|
322
|
+
message_type=MessageType.FEEDBACK,
|
|
323
|
+
)
|
|
324
|
+
self.broker.send_message(message)
|
|
325
|
+
|
|
326
|
+
def error_propagation(self, source_agent: str, error: str) -> None:
|
|
327
|
+
"""Propagate error to relevant agents."""
|
|
328
|
+
message = Message(
|
|
329
|
+
sender=source_agent,
|
|
330
|
+
recipient="orchestrator",
|
|
331
|
+
content=f"Error: {error}",
|
|
332
|
+
message_type=MessageType.ERROR,
|
|
333
|
+
)
|
|
334
|
+
self.broker.send_message(message)
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Multi-Agent System Benchmarking
|
|
3
|
+
|
|
4
|
+
Evaluate team performance and agent effectiveness.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Dict, List, Any, Callable, Optional
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
import time
|
|
10
|
+
import statistics
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class BenchmarkResult:
|
|
15
|
+
"""Result from benchmark run."""
|
|
16
|
+
|
|
17
|
+
test_name: str
|
|
18
|
+
total_time: float
|
|
19
|
+
task_count: int
|
|
20
|
+
success_count: int
|
|
21
|
+
failure_count: int
|
|
22
|
+
quality_score: float
|
|
23
|
+
cost_tokens: int
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class TeamBenchmark:
|
|
27
|
+
"""Benchmark multi-agent team performance."""
|
|
28
|
+
|
|
29
|
+
def __init__(self):
|
|
30
|
+
"""Initialize benchmarking suite."""
|
|
31
|
+
self.results: List[BenchmarkResult] = []
|
|
32
|
+
self.agent_metrics: Dict[str, Dict] = {}
|
|
33
|
+
|
|
34
|
+
def run_benchmark(
|
|
35
|
+
self,
|
|
36
|
+
test_name: str,
|
|
37
|
+
orchestrator,
|
|
38
|
+
test_data: List[Dict],
|
|
39
|
+
quality_metric: Optional[Callable] = None,
|
|
40
|
+
) -> BenchmarkResult:
|
|
41
|
+
"""
|
|
42
|
+
Run benchmark test.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
test_name: Name of benchmark
|
|
46
|
+
orchestrator: Orchestrator instance
|
|
47
|
+
test_data: Test cases
|
|
48
|
+
quality_metric: Function to evaluate quality
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Benchmark result
|
|
52
|
+
"""
|
|
53
|
+
start_time = time.time()
|
|
54
|
+
results = []
|
|
55
|
+
costs = []
|
|
56
|
+
|
|
57
|
+
for test_case in test_data:
|
|
58
|
+
try:
|
|
59
|
+
result = orchestrator.execute(test_case)
|
|
60
|
+
results.append(result)
|
|
61
|
+
# Assume cost in test_case
|
|
62
|
+
costs.append(test_case.get("cost", 0))
|
|
63
|
+
except Exception:
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
end_time = time.time()
|
|
67
|
+
|
|
68
|
+
success_count = len(results)
|
|
69
|
+
failure_count = len(test_data) - success_count
|
|
70
|
+
total_time = end_time - start_time
|
|
71
|
+
|
|
72
|
+
# Calculate quality score
|
|
73
|
+
quality_score = (
|
|
74
|
+
quality_metric(results) if quality_metric else success_count / len(test_data)
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
benchmark_result = BenchmarkResult(
|
|
78
|
+
test_name=test_name,
|
|
79
|
+
total_time=total_time,
|
|
80
|
+
task_count=len(test_data),
|
|
81
|
+
success_count=success_count,
|
|
82
|
+
failure_count=failure_count,
|
|
83
|
+
quality_score=quality_score,
|
|
84
|
+
cost_tokens=sum(costs),
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
self.results.append(benchmark_result)
|
|
88
|
+
return benchmark_result
|
|
89
|
+
|
|
90
|
+
def compare_orchestration_methods(
|
|
91
|
+
self,
|
|
92
|
+
methods: Dict[str, Callable],
|
|
93
|
+
test_data: List[Dict],
|
|
94
|
+
) -> Dict[str, Any]:
|
|
95
|
+
"""
|
|
96
|
+
Compare different orchestration methods.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
methods: Dict of method name to orchestrator
|
|
100
|
+
test_data: Test cases
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Comparison results
|
|
104
|
+
"""
|
|
105
|
+
comparison = {}
|
|
106
|
+
|
|
107
|
+
for method_name, orchestrator in methods.items():
|
|
108
|
+
result = self.run_benchmark(method_name, orchestrator, test_data)
|
|
109
|
+
comparison[method_name] = {
|
|
110
|
+
"total_time": result.total_time,
|
|
111
|
+
"success_rate": result.success_count / result.task_count,
|
|
112
|
+
"quality_score": result.quality_score,
|
|
113
|
+
"efficiency": result.success_count / (result.total_time + 0.001),
|
|
114
|
+
"cost_per_success": (
|
|
115
|
+
result.cost_tokens / result.success_count
|
|
116
|
+
if result.success_count > 0
|
|
117
|
+
else float("inf")
|
|
118
|
+
),
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return comparison
|
|
122
|
+
|
|
123
|
+
def get_summary(self) -> Dict[str, Any]:
|
|
124
|
+
"""Get benchmark summary."""
|
|
125
|
+
if not self.results:
|
|
126
|
+
return {"status": "no_results"}
|
|
127
|
+
|
|
128
|
+
times = [r.total_time for r in self.results]
|
|
129
|
+
success_rates = [
|
|
130
|
+
r.success_count / r.task_count for r in self.results
|
|
131
|
+
]
|
|
132
|
+
quality_scores = [r.quality_score for r in self.results]
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
"total_benchmarks": len(self.results),
|
|
136
|
+
"avg_time": statistics.mean(times),
|
|
137
|
+
"median_time": statistics.median(times),
|
|
138
|
+
"avg_success_rate": statistics.mean(success_rates),
|
|
139
|
+
"avg_quality": statistics.mean(quality_scores),
|
|
140
|
+
"benchmarks": [r.__dict__ for r in self.results],
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class AgentEffectiveness:
|
|
145
|
+
"""Measure individual agent effectiveness."""
|
|
146
|
+
|
|
147
|
+
def __init__(self):
|
|
148
|
+
"""Initialize effectiveness tracker."""
|
|
149
|
+
self.agent_stats: Dict[str, Dict] = {}
|
|
150
|
+
|
|
151
|
+
def record_agent_task(
|
|
152
|
+
self,
|
|
153
|
+
agent: str,
|
|
154
|
+
task_id: str,
|
|
155
|
+
success: bool,
|
|
156
|
+
quality_score: float,
|
|
157
|
+
duration: float,
|
|
158
|
+
) -> None:
|
|
159
|
+
"""Record agent task execution."""
|
|
160
|
+
if agent not in self.agent_stats:
|
|
161
|
+
self.agent_stats[agent] = {
|
|
162
|
+
"tasks": [],
|
|
163
|
+
"successes": 0,
|
|
164
|
+
"failures": 0,
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
stats = self.agent_stats[agent]
|
|
168
|
+
stats["tasks"].append({
|
|
169
|
+
"task_id": task_id,
|
|
170
|
+
"success": success,
|
|
171
|
+
"quality": quality_score,
|
|
172
|
+
"duration": duration,
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
if success:
|
|
176
|
+
stats["successes"] += 1
|
|
177
|
+
else:
|
|
178
|
+
stats["failures"] += 1
|
|
179
|
+
|
|
180
|
+
def get_agent_metrics(self, agent: str) -> Dict[str, Any]:
|
|
181
|
+
"""Get metrics for specific agent."""
|
|
182
|
+
if agent not in self.agent_stats:
|
|
183
|
+
return {"status": "no_data"}
|
|
184
|
+
|
|
185
|
+
stats = self.agent_stats[agent]
|
|
186
|
+
tasks = stats["tasks"]
|
|
187
|
+
|
|
188
|
+
if not tasks:
|
|
189
|
+
return {"status": "no_tasks"}
|
|
190
|
+
|
|
191
|
+
success_rate = stats["successes"] / (stats["successes"] + stats["failures"])
|
|
192
|
+
quality_scores = [t["quality"] for t in tasks]
|
|
193
|
+
durations = [t["duration"] for t in tasks]
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
"agent": agent,
|
|
197
|
+
"total_tasks": len(tasks),
|
|
198
|
+
"success_rate": success_rate,
|
|
199
|
+
"avg_quality": statistics.mean(quality_scores),
|
|
200
|
+
"avg_duration": statistics.mean(durations),
|
|
201
|
+
"reliability": success_rate, # Alias for clarity
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
def rank_agents(self) -> List[tuple]:
|
|
205
|
+
"""Rank agents by effectiveness."""
|
|
206
|
+
rankings = []
|
|
207
|
+
|
|
208
|
+
for agent in self.agent_stats.keys():
|
|
209
|
+
metrics = self.get_agent_metrics(agent)
|
|
210
|
+
if "success_rate" in metrics:
|
|
211
|
+
score = (
|
|
212
|
+
metrics["success_rate"] * 0.4 +
|
|
213
|
+
metrics["avg_quality"] * 0.4 +
|
|
214
|
+
(1 - min(metrics["avg_duration"] / 10.0, 1.0)) * 0.2
|
|
215
|
+
)
|
|
216
|
+
rankings.append((agent, score, metrics))
|
|
217
|
+
|
|
218
|
+
rankings.sort(key=lambda x: x[1], reverse=True)
|
|
219
|
+
return rankings
|
|
220
|
+
|
|
221
|
+
def get_team_report(self) -> Dict[str, Any]:
|
|
222
|
+
"""Get team effectiveness report."""
|
|
223
|
+
rankings = self.rank_agents()
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
"total_agents": len(self.agent_stats),
|
|
227
|
+
"rankings": [
|
|
228
|
+
{
|
|
229
|
+
"rank": i + 1,
|
|
230
|
+
"agent": agent,
|
|
231
|
+
"score": score,
|
|
232
|
+
"metrics": metrics,
|
|
233
|
+
}
|
|
234
|
+
for i, (agent, score, metrics) in enumerate(rankings)
|
|
235
|
+
],
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
class CollaborationMetrics:
|
|
240
|
+
"""Measure collaboration effectiveness between agents."""
|
|
241
|
+
|
|
242
|
+
def __init__(self):
|
|
243
|
+
"""Initialize collaboration metrics."""
|
|
244
|
+
self.interactions: List[Dict] = []
|
|
245
|
+
|
|
246
|
+
def record_interaction(
|
|
247
|
+
self,
|
|
248
|
+
agent_a: str,
|
|
249
|
+
agent_b: str,
|
|
250
|
+
message: str,
|
|
251
|
+
response_time: float,
|
|
252
|
+
successful: bool,
|
|
253
|
+
) -> None:
|
|
254
|
+
"""Record agent-to-agent interaction."""
|
|
255
|
+
self.interactions.append({
|
|
256
|
+
"agent_a": agent_a,
|
|
257
|
+
"agent_b": agent_b,
|
|
258
|
+
"message": message,
|
|
259
|
+
"response_time": response_time,
|
|
260
|
+
"successful": successful,
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
def get_collaboration_graph(self) -> Dict[str, List[str]]:
|
|
264
|
+
"""Get collaboration graph between agents."""
|
|
265
|
+
graph = {}
|
|
266
|
+
|
|
267
|
+
for interaction in self.interactions:
|
|
268
|
+
agent_a = interaction["agent_a"]
|
|
269
|
+
agent_b = interaction["agent_b"]
|
|
270
|
+
|
|
271
|
+
if agent_a not in graph:
|
|
272
|
+
graph[agent_a] = []
|
|
273
|
+
if agent_b not in graph[agent_a]:
|
|
274
|
+
graph[agent_a].append(agent_b)
|
|
275
|
+
|
|
276
|
+
return graph
|
|
277
|
+
|
|
278
|
+
def get_interaction_metrics(self) -> Dict[str, Any]:
|
|
279
|
+
"""Get interaction metrics."""
|
|
280
|
+
if not self.interactions:
|
|
281
|
+
return {"total_interactions": 0}
|
|
282
|
+
|
|
283
|
+
response_times = [i["response_time"] for i in self.interactions]
|
|
284
|
+
success_rate = sum(1 for i in self.interactions if i["successful"]) / len(
|
|
285
|
+
self.interactions
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
"total_interactions": len(self.interactions),
|
|
290
|
+
"success_rate": success_rate,
|
|
291
|
+
"avg_response_time": statistics.mean(response_times),
|
|
292
|
+
"median_response_time": statistics.median(response_times),
|
|
293
|
+
"max_response_time": max(response_times),
|
|
294
|
+
"collaboration_score": success_rate * (1 - min(statistics.mean(response_times) / 10.0, 1.0)),
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
def get_agent_collaboration_analysis(self, agent: str) -> Dict[str, Any]:
|
|
298
|
+
"""Get collaboration analysis for specific agent."""
|
|
299
|
+
agent_interactions = [
|
|
300
|
+
i for i in self.interactions
|
|
301
|
+
if i["agent_a"] == agent or i["agent_b"] == agent
|
|
302
|
+
]
|
|
303
|
+
|
|
304
|
+
if not agent_interactions:
|
|
305
|
+
return {"agent": agent, "status": "no_interactions"}
|
|
306
|
+
|
|
307
|
+
partners = set()
|
|
308
|
+
for interaction in agent_interactions:
|
|
309
|
+
if interaction["agent_a"] == agent:
|
|
310
|
+
partners.add(interaction["agent_b"])
|
|
311
|
+
else:
|
|
312
|
+
partners.add(interaction["agent_a"])
|
|
313
|
+
|
|
314
|
+
success_rate = sum(
|
|
315
|
+
1 for i in agent_interactions if i["successful"]
|
|
316
|
+
) / len(agent_interactions)
|
|
317
|
+
|
|
318
|
+
return {
|
|
319
|
+
"agent": agent,
|
|
320
|
+
"collaboration_partners": list(partners),
|
|
321
|
+
"total_interactions": len(agent_interactions),
|
|
322
|
+
"collaboration_success_rate": success_rate,
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def create_benchmark_suite() -> Dict[str, Callable]:
|
|
327
|
+
"""Create standard benchmark suite."""
|
|
328
|
+
return {
|
|
329
|
+
"sequential_tasks": lambda orchestrator: orchestrator.execute(
|
|
330
|
+
{"type": "sequential", "task_count": 5}
|
|
331
|
+
),
|
|
332
|
+
"parallel_tasks": lambda orchestrator: orchestrator.execute(
|
|
333
|
+
{"type": "parallel", "task_count": 10}
|
|
334
|
+
),
|
|
335
|
+
"complex_workflow": lambda orchestrator: orchestrator.execute(
|
|
336
|
+
{"type": "complex", "task_count": 20}
|
|
337
|
+
),
|
|
338
|
+
"error_handling": lambda orchestrator: orchestrator.execute(
|
|
339
|
+
{"type": "with_errors", "task_count": 10}
|
|
340
|
+
),
|
|
341
|
+
}
|