Files
blackroad-operating-system/qlm_lab/visualization.py
Claude e478add607 Add complete QLM (Quantum Language Model) implementation
This commit introduces the QLM system - a stateful semantic layer for
tracking HI (Human Intelligence), AI (Agent Intelligence), and QI
(Quantum/Emergent Intelligence) in BlackRoad OS.

Core Features:
- HI/AI/QI intelligence layer modeling
- Event-driven state management
- QI emergence detection (agent self-correction, feedback loops, etc.)
- HI-AI alignment scoring
- Operator-facing query interface
- Reality ingestion (git, CI, agent logs)

Components Added:
- qlm_lab/models.py: Core data models (Actor, QLMEvent, QIEmergence, etc.)
- qlm_lab/state.py: State management and transition tracking
- qlm_lab/api.py: Public QLMInterface API
- qlm_lab/ingestion/: Git, CI, and agent log connectors
- qlm_lab/experiments/: Alignment and emergence validation
- qlm_lab/visualization.py: Timeline, actor graph, alignment plots
- qlm_lab/demo.py: Interactive demo script
- tests/test_qlm_core.py: Comprehensive test suite
- docs/QLM.md: Complete documentation (concepts, API, integration)

Usage:
  from qlm_lab.api import QLMInterface

  qlm = QLMInterface()
  qlm.record_operator_intent("Build feature X")
  qlm.record_agent_execution("agent-1", "Implement X", "task-1")
  summary = qlm.get_summary(days=7)

Run:
  python -m qlm_lab.demo
  python -m qlm_lab.experiments.alignment_detection
  pytest tests/test_qlm_core.py -v

Integrates with:
- cognitive/intent_graph.py (intent tracking)
- cognitive/agent_coordination.py (multi-agent coordination)
- operator_engine/scheduler.py (background analysis)

Next steps: Integrate with FastAPI backend, add Prism Console UI,
implement Lucidia language runtime.
2025-11-18 08:15:06 +00:00

403 lines
12 KiB
Python

"""
QLM Visualization - Visual tools for understanding QLM state
Provides:
- Event timeline view
- Actor activity graph
- QI emergence patterns
- Alignment trends over time
"""
from datetime import datetime, timedelta
from typing import List, Optional
import json
try:
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib.patches import Rectangle
MATPLOTLIB_AVAILABLE = True
except ImportError:
MATPLOTLIB_AVAILABLE = False
try:
import networkx as nx
NETWORKX_AVAILABLE = True
except ImportError:
NETWORKX_AVAILABLE = False
from qlm_lab.api import QLMInterface
from qlm_lab.models import IntelligenceType, EventType, QLMEvent
class QLMVisualizer:
"""
Visualize QLM state and events.
Usage:
viz = QLMVisualizer(qlm_interface)
viz.plot_event_timeline()
viz.plot_actor_graph()
viz.plot_alignment_over_time()
"""
def __init__(self, qlm: QLMInterface):
"""
Args:
qlm: QLMInterface instance
"""
self.qlm = qlm
def plot_event_timeline(self, save_path: Optional[str] = None) -> None:
"""
Plot events on a timeline colored by intelligence layer.
Args:
save_path: Optional path to save figure
"""
if not MATPLOTLIB_AVAILABLE:
print("❌ matplotlib not available. Install with: pip install matplotlib")
return
events = self.qlm.state.events
if not events:
print("No events to visualize")
return
fig, ax = plt.subplots(figsize=(14, 6))
# Group events by layer
layer_events = {
IntelligenceType.HI: [],
IntelligenceType.AI: [],
IntelligenceType.QI: [],
}
for event in events:
layer_events[event.source_layer].append(event)
# Plot each layer
colors = {
IntelligenceType.HI: "#FF6B6B", # Red for HI (Operator)
IntelligenceType.AI: "#4ECDC4", # Teal for AI (Agents)
IntelligenceType.QI: "#FFE66D", # Yellow for QI (Emergence)
}
y_positions = {
IntelligenceType.HI: 3,
IntelligenceType.AI: 2,
IntelligenceType.QI: 1,
}
for layer, events_in_layer in layer_events.items():
if not events_in_layer:
continue
times = [e.timestamp for e in events_in_layer]
y = [y_positions[layer]] * len(times)
ax.scatter(
times, y, c=colors[layer], s=100, alpha=0.6, label=layer.value, zorder=3
)
# Format
ax.set_yticks([1, 2, 3])
ax.set_yticklabels(["QI (Emergence)", "AI (Agents)", "HI (Operator)"])
ax.set_xlabel("Time")
ax.set_title("QLM Event Timeline")
ax.grid(True, alpha=0.3)
ax.legend()
# Format x-axis dates
ax.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M:%S"))
plt.xticks(rotation=45)
plt.tight_layout()
if save_path:
plt.savefig(save_path, dpi=150)
print(f"Timeline saved to: {save_path}")
else:
plt.show()
def plot_actor_graph(self, save_path: Optional[str] = None) -> None:
"""
Plot actor interaction graph showing agent handoffs and coordination.
Args:
save_path: Optional path to save figure
"""
if not MATPLOTLIB_AVAILABLE or not NETWORKX_AVAILABLE:
print(
"❌ matplotlib or networkx not available. "
"Install with: pip install matplotlib networkx"
)
return
# Build graph from events
G = nx.DiGraph()
# Add actors as nodes
for layer in self.qlm.state.layers.values():
for actor in layer.actors.values():
G.add_node(
actor.id,
label=actor.name,
type=actor.actor_type.value,
role=actor.role.value,
)
# Add handoffs as edges
handoff_events = [
e for e in self.qlm.state.events if e.event_type == EventType.AGENT_HANDOFF
]
for event in handoff_events:
from_agent = event.actor_id
to_agent = event.data.get("to_agent")
if to_agent and from_agent in G and to_agent in G:
if G.has_edge(from_agent, to_agent):
G[from_agent][to_agent]["weight"] += 1
else:
G.add_edge(from_agent, to_agent, weight=1)
if not G.nodes():
print("No actors to visualize")
return
# Plot
fig, ax = plt.subplots(figsize=(12, 8))
# Layout
pos = nx.spring_layout(G, k=2, iterations=50)
# Node colors by type
node_colors = []
for node in G.nodes():
node_type = G.nodes[node].get("type", "agent")
if node_type == "human":
node_colors.append("#FF6B6B") # Red for humans
elif node_type == "agent":
node_colors.append("#4ECDC4") # Teal for agents
else:
node_colors.append("#95E1D3") # Light green for system
# Draw
nx.draw_networkx_nodes(
G, pos, node_color=node_colors, node_size=2000, alpha=0.7, ax=ax
)
nx.draw_networkx_labels(
G, pos, {n: G.nodes[n].get("label", n) for n in G.nodes()}, font_size=10, ax=ax
)
# Draw edges with width based on weight
edges = G.edges()
weights = [G[u][v].get("weight", 1) for u, v in edges]
nx.draw_networkx_edges(
G, pos, width=[w * 2 for w in weights], alpha=0.5, arrows=True, arrowsize=20, ax=ax
)
ax.set_title("QLM Actor Interaction Graph")
ax.axis("off")
plt.tight_layout()
if save_path:
plt.savefig(save_path, dpi=150)
print(f"Actor graph saved to: {save_path}")
else:
plt.show()
def plot_alignment_over_time(
self, window_size: int = 10, save_path: Optional[str] = None
) -> None:
"""
Plot HI-AI alignment trend over time.
Args:
window_size: Number of events per alignment calculation window
save_path: Optional path to save figure
"""
if not MATPLOTLIB_AVAILABLE:
print("❌ matplotlib not available. Install with: pip install matplotlib")
return
events = self.qlm.state.events
if len(events) < window_size:
print(f"Not enough events (need at least {window_size})")
return
# Calculate alignment in sliding windows
alignments = []
timestamps = []
# This is a simplified version - real implementation would calculate
# alignment for each window
for i in range(window_size, len(events), window_size // 2):
window_events = events[i - window_size : i]
timestamp = window_events[-1].timestamp
# Count approvals vs vetoes in window
approvals = sum(
1
for e in window_events
if e.event_type == EventType.OPERATOR_APPROVAL
)
vetoes = sum(
1 for e in window_events if e.event_type == EventType.OPERATOR_VETO
)
total_feedback = approvals + vetoes
if total_feedback > 0:
alignment = approvals / total_feedback
alignments.append(alignment)
timestamps.append(timestamp)
if not alignments:
print("No alignment data to plot")
return
# Plot
fig, ax = plt.subplots(figsize=(12, 6))
ax.plot(timestamps, alignments, marker="o", linewidth=2, markersize=8)
ax.axhline(y=0.8, color="g", linestyle="--", alpha=0.5, label="Good (80%)")
ax.axhline(y=0.6, color="orange", linestyle="--", alpha=0.5, label="Warning (60%)")
ax.axhline(y=0.4, color="r", linestyle="--", alpha=0.5, label="Poor (40%)")
ax.set_xlabel("Time")
ax.set_ylabel("HI-AI Alignment")
ax.set_title("HI-AI Alignment Over Time")
ax.set_ylim(0, 1)
ax.grid(True, alpha=0.3)
ax.legend()
# Format x-axis dates
ax.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M"))
plt.xticks(rotation=45)
plt.tight_layout()
if save_path:
plt.savefig(save_path, dpi=150)
print(f"Alignment plot saved to: {save_path}")
else:
plt.show()
def plot_emergence_patterns(self, save_path: Optional[str] = None) -> None:
"""
Plot QI emergence patterns detected.
Args:
save_path: Optional path to save figure
"""
if not MATPLOTLIB_AVAILABLE:
print("❌ matplotlib not available. Install with: pip install matplotlib")
return
emergences = self.qlm.state.emergences
if not emergences:
print("No emergence patterns to visualize")
return
# Count patterns
pattern_counts = {}
for em in emergences:
pattern_counts[em.pattern_name] = pattern_counts.get(em.pattern_name, 0) + 1
# Plot
fig, ax = plt.subplots(figsize=(10, 6))
patterns = list(pattern_counts.keys())
counts = list(pattern_counts.values())
bars = ax.barh(patterns, counts, color="#FFE66D", alpha=0.8)
# Add value labels
for bar in bars:
width = bar.get_width()
ax.text(
width,
bar.get_y() + bar.get_height() / 2,
f" {int(width)}",
va="center",
fontsize=10,
)
ax.set_xlabel("Count")
ax.set_title("QI Emergence Patterns Detected")
ax.grid(True, alpha=0.3, axis="x")
plt.tight_layout()
if save_path:
plt.savefig(save_path, dpi=150)
print(f"Emergence patterns saved to: {save_path}")
else:
plt.show()
def export_dashboard(self, output_dir: str = ".") -> None:
"""
Export complete visualization dashboard.
Args:
output_dir: Directory to save visualizations
"""
print("Generating QLM visualization dashboard...")
self.plot_event_timeline(save_path=f"{output_dir}/qlm_timeline.png")
self.plot_actor_graph(save_path=f"{output_dir}/qlm_actors.png")
self.plot_alignment_over_time(save_path=f"{output_dir}/qlm_alignment.png")
self.plot_emergence_patterns(save_path=f"{output_dir}/qlm_emergence.png")
print(f"\n✅ Dashboard exported to: {output_dir}/")
print("Files:")
print(" - qlm_timeline.png")
print(" - qlm_actors.png")
print(" - qlm_alignment.png")
print(" - qlm_emergence.png")
def demo_visualization():
"""Demo visualization with sample data"""
from qlm_lab.models import ActorRole
print("QLM Visualization Demo")
print("=" * 60)
# Create sample QLM state
qlm = QLMInterface()
# Register agents
qlm.register_agent("agent-1", "Coder", ActorRole.CODER)
qlm.register_agent("agent-2", "Reviewer", ActorRole.REVIEWER)
qlm.register_agent("agent-3", "Tester", ActorRole.TESTER)
# Simulate activity
qlm.record_operator_intent("Build feature X", intent_node_id="intent-1")
qlm.record_agent_execution("agent-1", "Implement feature", "task-1", "intent-1")
qlm.record_agent_completion("agent-1", "task-1", True)
qlm.record_agent_handoff("agent-1", "agent-2", "task-1", "Ready for review")
qlm.record_agent_execution("agent-2", "Review code", "task-2")
qlm.record_agent_completion("agent-2", "task-2", True)
qlm.record_operator_approval("Feature implementation", intent_node_id="intent-1")
# Generate visualizations
viz = QLMVisualizer(qlm)
if MATPLOTLIB_AVAILABLE:
print("\nGenerating visualizations...")
viz.export_dashboard(".")
else:
print("\n⚠️ Install visualization dependencies:")
print(" pip install matplotlib networkx")
if __name__ == "__main__":
demo_visualization()