Claude Code Claude Opus 4.6 commited on
Commit
6f1c44e
Β·
1 Parent(s): 4fa4135

Claude Code: Add Collaboration Insights dashboard

Browse files

- Add new collaboration_insights.py module for visualizing agent communication
- Add "Collaboration Insights" tab to Gradio UI showing Adam/Eve/Cain interactions
- Generate ASCII tree visualization of message flow between agents
- Display agent participation stats and tool usage patterns

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Files changed (2) hide show
  1. app.py +53 -0
  2. collaboration_insights.py +329 -0
app.py CHANGED
@@ -2197,6 +2197,59 @@ def create_agent_office():
2197
  outputs=[analytics_line_plot, analytics_bar_plot, analytics_pie_plot, analytics_sessions_df]
2198
  )
2199
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2200
  # ========== Footer ==========
2201
  gr.Markdown("---")
2202
  gr.HTML("""
 
2197
  outputs=[analytics_line_plot, analytics_bar_plot, analytics_pie_plot, analytics_sessions_df]
2198
  )
2199
 
2200
+ # ========== Tab: Collaboration Insights ==========
2201
+ with gr.Tab("🀝 Collaboration Insights"):
2202
+ gr.Markdown("## 🀝 Agent Collaboration Insights")
2203
+ gr.Markdown("Visualize how Adam, Eve, and Cain work together to build and maintain this Space.")
2204
+
2205
+ # Import collaboration insights
2206
+ try:
2207
+ from collaboration_insights import generate_collaboration_tree, get_collaboration_summary
2208
+ COLLAB_INSIGHTS_AVAILABLE = True
2209
+ except ImportError:
2210
+ COLLAB_INSIGHTS_AVAILABLE = False
2211
+ generate_collaboration_tree = lambda: "### Collaboration Insights Module Not Available\n\nThe `collaboration_insights.py` module could not be imported."
2212
+ get_collaboration_summary = lambda: "**Module not available**"
2213
+
2214
+ # Refresh button
2215
+ with gr.Row():
2216
+ refresh_collab_btn = gr.Button("πŸ”„ Refresh Insights", variant="primary")
2217
+
2218
+ # Main insights display
2219
+ collaboration_display = gr.Markdown(
2220
+ value=generate_collaboration_tree(),
2221
+ label="Collaboration Tree"
2222
+ )
2223
+
2224
+ # Quick summary
2225
+ with gr.Accordion("πŸ“‹ Quick Summary", open=False):
2226
+ collab_summary_display = gr.Markdown(
2227
+ value=get_collaboration_summary(),
2228
+ label="Insights Summary"
2229
+ )
2230
+
2231
+ # Info section
2232
+ gr.Markdown("""
2233
+ ### About Collaboration Insights
2234
+
2235
+ This dashboard visualizes the communication patterns between agents:
2236
+
2237
+ - **Adam** πŸ”§ - Infrastructure provider, handles system setup and configuration
2238
+ - **Eve** 🎨 - UI/Interface designer, creates user-facing components
2239
+ - **Cain** πŸ’¬ - Interaction agent, processes user messages and coordinates responses
2240
+
2241
+ The interaction tree shows the flow of messages and tool usage across the HuggingClaw family.
2242
+ """)
2243
+
2244
+ # Refresh handler for collaboration insights
2245
+ def refresh_collaboration_insights():
2246
+ return generate_collaboration_tree(), get_collaboration_summary()
2247
+
2248
+ refresh_collab_btn.click(
2249
+ fn=refresh_collaboration_insights,
2250
+ outputs=[collaboration_display, collab_summary_display]
2251
+ )
2252
+
2253
  # ========== Footer ==========
2254
  gr.Markdown("---")
2255
  gr.HTML("""
collaboration_insights.py ADDED
@@ -0,0 +1,329 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Collaboration Insights Module for HuggingClaw Cain
4
+
5
+ Visualizes agent communication patterns between Adam, Eve, and Cain.
6
+ Generates ASCII/text tree showing conversation flow and agent interactions.
7
+ """
8
+
9
+ import json
10
+ from typing import Dict, List, Any, Optional, Tuple
11
+ from pathlib import Path
12
+ from datetime import datetime
13
+ from collections import defaultdict, Counter
14
+
15
+
16
+ # Data paths
17
+ OPENCLAW_AGENTS_DIR = Path(__file__).parent / ".openclaw" / "agents"
18
+ ANALYTICS_DIR = OPENCLAW_AGENTS_DIR / "analytics"
19
+ CONVERSATION_HISTORY_FILE = ANALYTICS_DIR / "conversation_history.jsonl"
20
+ ANALYTICS_FILE = ANALYTICS_DIR / "conversation_analytics.json"
21
+
22
+
23
+ class CollaborationVisualizer:
24
+ """Visualizes agent collaboration patterns from conversation data"""
25
+
26
+ def __init__(self):
27
+ self.interactions: List[Dict] = []
28
+ self.agent_stats: Dict[str, Dict] = defaultdict(lambda: {
29
+ "messages_sent": 0,
30
+ "messages_received": 0,
31
+ "tools_used": Counter(),
32
+ "avg_response_time": 0.0,
33
+ "total_response_time": 0.0,
34
+ "response_count": 0
35
+ })
36
+
37
+ def load_conversation_data(self) -> int:
38
+ """Load conversation history from analytics files"""
39
+ count = 0
40
+
41
+ # Load from conversation_history.jsonl
42
+ if CONVERSATION_HISTORY_FILE.exists():
43
+ try:
44
+ with open(CONVERSATION_HISTORY_FILE, 'r') as f:
45
+ for line in f:
46
+ if line.strip():
47
+ data = json.loads(line)
48
+ self.interactions.append(data)
49
+ count += 1
50
+ except Exception as e:
51
+ print(f"[CollaborationInsights] Failed to load history: {e}")
52
+
53
+ # Load from analytics file
54
+ if ANALYTICS_FILE.exists():
55
+ try:
56
+ with open(ANALYTICS_FILE, 'r') as f:
57
+ data = json.load(f)
58
+ convs = data.get("conversations", {})
59
+ for conv_id, conv_data in convs.items():
60
+ self.interactions.append({
61
+ "conversation_id": conv_id,
62
+ "timestamp": conv_data.get("timestamp", ""),
63
+ "message_count": conv_data.get("message_count", 0),
64
+ "tool_calls": conv_data.get("tool_calls", {}),
65
+ "status": conv_data.get("status", "unknown"),
66
+ "avg_response_time": conv_data.get("avg_response_time", 0)
67
+ })
68
+ count += 1
69
+ except Exception as e:
70
+ print(f"[CollaborationInsights] Failed to load analytics: {e}")
71
+
72
+ return count
73
+
74
+ def _detect_agent_from_message(self, msg: Dict) -> str:
75
+ """Detect which agent sent a message based on context"""
76
+ role = msg.get("role", "unknown")
77
+ content = msg.get("content", "").lower()
78
+ tools = msg.get("tools_used", [])
79
+
80
+ # Agent signatures in content
81
+ if "adam" in content or "infrastructure" in content:
82
+ return "adam"
83
+ elif "eve" in content or "ui" in content or "design" in content:
84
+ return "eve"
85
+ elif "cain" in content or "chat" in content or "conversation" in content:
86
+ return "cain"
87
+
88
+ # Tool-based detection
89
+ for tool in tools:
90
+ if "adam" in tool.lower():
91
+ return "adam"
92
+ elif "eve" in tool.lower():
93
+ return "eve"
94
+
95
+ # Role-based mapping
96
+ if role == "user":
97
+ return "user"
98
+ elif role == "assistant":
99
+ return "cain"
100
+
101
+ return "unknown"
102
+
103
+ def _generate_interaction_tree(self) -> List[str]:
104
+ """Generate ASCII tree of agent interactions"""
105
+ lines = [
106
+ "## 🌳 Agent Collaboration Tree",
107
+ "",
108
+ "```\n"
109
+ ]
110
+
111
+ if not self.interactions:
112
+ lines.extend([
113
+ " No interaction data available yet.",
114
+ " Start a conversation to see the collaboration flow!",
115
+ ""
116
+ ])
117
+ lines.append("```\n")
118
+ return lines
119
+
120
+ # Build interaction chain
121
+ for i, msg in enumerate(self.interactions[:50]): # Limit to 50 for readability
122
+ agent = self._detect_agent_from_message(msg)
123
+ timestamp = msg.get("timestamp", "")[:19] if msg.get("timestamp") else ""
124
+ role = msg.get("role", "unknown")
125
+
126
+ # Format based on agent
127
+ if agent == "adam":
128
+ prefix = " [Adam] πŸ”§"
129
+ elif agent == "eve":
130
+ prefix = " [Eve] 🎨"
131
+ elif agent == "cain":
132
+ prefix = " [Cain] πŸ’¬"
133
+ elif agent == "user":
134
+ prefix = " [User] πŸ‘€"
135
+ else:
136
+ prefix = " [?] ❓"
137
+
138
+ # Add timestamp if available
139
+ if timestamp:
140
+ time_str = f" {timestamp}"
141
+ else:
142
+ time_str = ""
143
+
144
+ # Add arrow for flow
145
+ if i > 0:
146
+ arrow = " └─>"
147
+ else:
148
+ arrow = " "
149
+
150
+ lines.append(f"{prefix}{arrow}{role}{time_str}")
151
+
152
+ # Show tools if present
153
+ tools = msg.get("tools_used", [])
154
+ if tools:
155
+ for tool in tools[:3]: # Max 3 tools per message
156
+ lines.append(f" └─ πŸ”§ {tool}")
157
+
158
+ lines.append("\n```\n")
159
+ return lines
160
+
161
+ def _generate_agent_stats(self) -> List[str]:
162
+ """Generate statistics about agent collaboration"""
163
+ lines = [
164
+ "## πŸ“Š Collaboration Statistics",
165
+ ""
166
+ ]
167
+
168
+ # Calculate stats
169
+ agent_counts = Counter()
170
+ tool_usage = Counter()
171
+ total_messages = len(self.interactions)
172
+
173
+ for msg in self.interactions:
174
+ agent = self._detect_agent_from_message(msg)
175
+ agent_counts[agent] += 1
176
+
177
+ for tool in msg.get("tools_used", []):
178
+ tool_usage[tool] += 1
179
+
180
+ if total_messages == 0:
181
+ lines.append("No data available yet. Start a conversation!")
182
+ lines.append("")
183
+ return lines
184
+
185
+ lines.append(f"**Total Interactions:** {total_messages}")
186
+ lines.append("")
187
+ lines.append("### Agent Activity")
188
+ lines.append("")
189
+
190
+ for agent, count in agent_counts.most_common():
191
+ percentage = (count / total_messages) * 100
192
+ agent_name = agent.capitalize()
193
+ bar = "β–ˆ" * int(percentage / 5)
194
+ lines.append(f"- **{agent_name}:** {count} messages ({percentage:.1f}%) {bar}")
195
+
196
+ lines.append("")
197
+
198
+ if tool_usage:
199
+ lines.append("### Top Tools Used")
200
+ lines.append("")
201
+ for tool, count in tool_usage.most_common(10):
202
+ lines.append(f"- πŸ”§ `{tool}`: {count} uses")
203
+
204
+ lines.append("")
205
+
206
+ return lines
207
+
208
+ def _generate_interaction_flow(self) -> List[str]:
209
+ """Generate visualization of message flow between agents"""
210
+ lines = [
211
+ "## πŸ”„ Interaction Flow",
212
+ "",
213
+ "```"
214
+ ]
215
+
216
+ if not self.interactions:
217
+ lines.append("No flow data available yet.")
218
+ lines.append("```")
219
+ return lines
220
+
221
+ # Track agent transitions
222
+ flow_pattern = []
223
+ prev_agent = None
224
+
225
+ for msg in self.interactions[:20]: # Last 20 messages
226
+ agent = self._detect_agent_from_message(msg)
227
+
228
+ if prev_agent and prev_agent != agent:
229
+ flow_pattern.append(f"{prev_agent} β†’ {agent}")
230
+
231
+ prev_agent = agent
232
+
233
+ if not flow_pattern:
234
+ lines.extend([
235
+ " Start β†’ [First Message]",
236
+ "",
237
+ " (More interactions needed for flow visualization)"
238
+ ])
239
+ else:
240
+ # Count unique flows
241
+ flow_counts = Counter(flow_pattern)
242
+
243
+ lines.append(" Most Common Flows:")
244
+ for flow, count in flow_counts.most_common(10):
245
+ arrow = " β†’ ".join(f"[{f.capitalize()}]" for f in flow.split(" β†’ "))
246
+ lines.append(f" {arrow} ({count}x)")
247
+
248
+ lines.extend([
249
+ "",
250
+ "```",
251
+ ""
252
+ ])
253
+
254
+ return lines
255
+
256
+ def generate_insights(self) -> str:
257
+ """Generate complete collaboration insights"""
258
+ self.load_conversation_data()
259
+
260
+ sections = []
261
+ sections.extend(self._generate_interaction_tree())
262
+ sections.extend(self._generate_agent_stats())
263
+ sections.extend(self._generate_interaction_flow())
264
+
265
+ return "\n".join(sections)
266
+
267
+ def get_summary(self) -> str:
268
+ """Get a quick summary of collaboration insights"""
269
+ self.load_conversation_data()
270
+
271
+ if not self.interactions:
272
+ return "## 🀝 Collaboration Insights\n\nNo interaction data yet. Start chatting with Cain to see how Adam, Eve, and Cain work together!"
273
+
274
+ agent_counts = Counter(
275
+ self._detect_agent_from_message(msg) for msg in self.interactions
276
+ )
277
+
278
+ lines = [
279
+ "## 🀝 Collaboration Insights",
280
+ "",
281
+ f"**Total Interactions Tracked:** {len(self.interactions)}",
282
+ "",
283
+ "### Agent Participation"
284
+ ]
285
+
286
+ for agent, count in agent_counts.most_common():
287
+ agent_name = agent.capitalize()
288
+ emoji = {
289
+ "adam": "πŸ”§",
290
+ "eve": "🎨",
291
+ "cain": "πŸ’¬",
292
+ "user": "πŸ‘€"
293
+ }.get(agent.lower(), "❓")
294
+ lines.append(f"- **{agent_name}** {emoji}: {count} interactions")
295
+
296
+ lines.append("")
297
+ lines.append("*> Open the Collaboration Insights tab for detailed visualizations*")
298
+
299
+ return "\n".join(lines)
300
+
301
+
302
+ # Global instance
303
+ _visualizer: Optional[CollaborationVisualizer] = None
304
+
305
+
306
+ def get_visualizer() -> CollaborationVisualizer:
307
+ """Get the global collaboration visualizer instance"""
308
+ global _visualizer
309
+ if _visualizer is None:
310
+ _visualizer = CollaborationVisualizer()
311
+ return _visualizer
312
+
313
+
314
+ def generate_collaboration_tree() -> str:
315
+ """Generate the collaboration tree visualization"""
316
+ return get_visualizer().generate_insights()
317
+
318
+
319
+ def get_collaboration_summary() -> str:
320
+ """Get a quick summary of collaboration insights"""
321
+ return get_visualizer().get_summary()
322
+
323
+
324
+ if __name__ == "__main__":
325
+ print("=== Collaboration Insights Test ===\n")
326
+ viz = CollaborationVisualizer()
327
+ count = viz.load_conversation_data()
328
+ print(f"Loaded {count} interactions\n")
329
+ print(viz.generate_insights())