mirror of
https://github.com/blackboxprogramming/BlackRoad-Operating-System.git
synced 2026-03-17 05:57:21 -05:00
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.
This commit is contained in:
944
docs/QLM.md
Normal file
944
docs/QLM.md
Normal file
@@ -0,0 +1,944 @@
|
|||||||
|
## QLM (Quantum Language Model) - Complete Guide
|
||||||
|
|
||||||
|
> **The semantic layer for Operator-AI collaboration**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [What is QLM?](#what-is-qlm)
|
||||||
|
2. [Core Concepts](#core-concepts)
|
||||||
|
3. [Architecture](#architecture)
|
||||||
|
4. [Data Models](#data-models)
|
||||||
|
5. [API Reference](#api-reference)
|
||||||
|
6. [Integration Guide](#integration-guide)
|
||||||
|
7. [Experiments & Validation](#experiments--validation)
|
||||||
|
8. [Visualization](#visualization)
|
||||||
|
9. [Operator's Guide](#operators-guide)
|
||||||
|
10. [Development & Extension](#development--extension)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What is QLM?
|
||||||
|
|
||||||
|
**QLM (Quantum Language Model)** is a stateful semantic layer that tracks, analyzes, and explains the interaction between **Human Intelligence (HI)**, **AI Intelligence (AI)**, and emergent **Quantum Intelligence (QI)** in the BlackRoad Operating System.
|
||||||
|
|
||||||
|
### The Problem QLM Solves
|
||||||
|
|
||||||
|
Modern AI systems have a **context loss problem**:
|
||||||
|
- AI agents execute tasks but lose track of *why*
|
||||||
|
- Humans give intent but can't see *what happened*
|
||||||
|
- Systems evolve but no one knows *how we got here*
|
||||||
|
- Emergent behaviors appear with no explanation
|
||||||
|
|
||||||
|
QLM fixes this by creating a **complete causal graph** of:
|
||||||
|
- Operator intent
|
||||||
|
- Agent executions
|
||||||
|
- System events
|
||||||
|
- Feedback loops
|
||||||
|
- Emergent patterns
|
||||||
|
|
||||||
|
### The "Quantum" Metaphor
|
||||||
|
|
||||||
|
The "quantum" in QLM does NOT refer to quantum physics. Instead, it describes:
|
||||||
|
|
||||||
|
**Superposition of Roles**: An agent can be both executor AND coordinator simultaneously.
|
||||||
|
|
||||||
|
**Superposition of States**: A task can be in_progress AND blocked at the same time.
|
||||||
|
|
||||||
|
**Superposition of Perspectives**: The same event looks different from HI vs AI vs QI viewpoints.
|
||||||
|
|
||||||
|
**Quantum Intelligence (QI)**: Emergent behaviors that appear when HI + AI + deterministic systems interact in feedback loops. When 1 + 1 = 3.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Core Concepts
|
||||||
|
|
||||||
|
### Intelligence Layers
|
||||||
|
|
||||||
|
QLM models three layers of intelligence:
|
||||||
|
|
||||||
|
#### 1. HI (Human Intelligence)
|
||||||
|
|
||||||
|
**Definition**: The Operator layer - human judgment, taste, ethics, goals, narrative.
|
||||||
|
|
||||||
|
**Primary Actor**: Alexa (Operator)
|
||||||
|
|
||||||
|
**Capabilities**:
|
||||||
|
- Define intent and constraints
|
||||||
|
- Approve or veto AI actions
|
||||||
|
- Ask questions and interpret results
|
||||||
|
- Provide judgment on ambiguous decisions
|
||||||
|
|
||||||
|
**Events**:
|
||||||
|
- `OPERATOR_INTENT`: Operator defines a goal
|
||||||
|
- `OPERATOR_APPROVAL`: Operator approves agent work
|
||||||
|
- `OPERATOR_VETO`: Operator rejects agent work
|
||||||
|
- `OPERATOR_QUERY`: Operator asks a question
|
||||||
|
|
||||||
|
#### 2. AI (Agent Intelligence)
|
||||||
|
|
||||||
|
**Definition**: LLM-powered agents, code generation, pattern completion, search, transformation.
|
||||||
|
|
||||||
|
**Primary Actors**: 200+ BlackRoad agents (coder, reviewer, researcher, etc.)
|
||||||
|
|
||||||
|
**Capabilities**:
|
||||||
|
- Execute tasks
|
||||||
|
- Generate code/docs/designs
|
||||||
|
- Search and retrieve information
|
||||||
|
- Coordinate with other agents
|
||||||
|
|
||||||
|
**Events**:
|
||||||
|
- `AGENT_EXECUTION`: Agent starts working
|
||||||
|
- `AGENT_COMPLETION`: Agent finishes task
|
||||||
|
- `AGENT_ERROR`: Agent encounters error
|
||||||
|
- `AGENT_HANDOFF`: Agent passes work to another agent
|
||||||
|
|
||||||
|
#### 3. QI (Quantum Intelligence)
|
||||||
|
|
||||||
|
**Definition**: Emergent system-level intelligence that appears when HI + AI + deterministic systems interact in feedback loops.
|
||||||
|
|
||||||
|
**Not a single actor**: QI is a property of the *entire system*.
|
||||||
|
|
||||||
|
**Emergence Patterns**:
|
||||||
|
- `agent_self_correction`: Agent fixes own errors without HI intervention
|
||||||
|
- `novel_solution`: Agent finds approach not in training data
|
||||||
|
- `emergent_collaboration`: Agents self-organize into new patterns
|
||||||
|
- `operator_feedback_loop`: HI → AI → HI creates refined understanding
|
||||||
|
- `system_adaptation`: Deterministic systems evolve in response to AI
|
||||||
|
|
||||||
|
**Events**:
|
||||||
|
- `QI_EMERGENCE`: Novel behavior detected
|
||||||
|
- `QI_FEEDBACK_LOOP`: HI+AI feedback detected
|
||||||
|
- `QI_PATTERN`: Recurring emergent pattern identified
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### System Diagram
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────┐
|
||||||
|
│ Operator (Alexa) │
|
||||||
|
│ Human Intelligence (HI) │
|
||||||
|
└──────────────────────┬──────────────────────────────┘
|
||||||
|
│
|
||||||
|
│ Intent, Approval, Veto
|
||||||
|
↓
|
||||||
|
┌──────────────────────────────────────────────────────┐
|
||||||
|
│ QLM Interface │
|
||||||
|
│ record_operator_intent(), ask(), get_summary() │
|
||||||
|
└──────────────────────┬───────────────────────────────┘
|
||||||
|
│
|
||||||
|
↓
|
||||||
|
┌──────────────────────────────────────────────────────┐
|
||||||
|
│ QLM State │
|
||||||
|
│ • Intelligence Layers (HI, AI, QI) │
|
||||||
|
│ • Event History │
|
||||||
|
│ • QI Emergence Detection │
|
||||||
|
│ • Metrics & Alignment │
|
||||||
|
└──────┬────────────────────────┬──────────────────────┘
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
↓ ↓
|
||||||
|
┌──────────────────┐ ┌──────────────────────────────┐
|
||||||
|
│ Ingestion │ │ Cognitive Layer Integration │
|
||||||
|
│ • Git │ │ • IntentGraph │
|
||||||
|
│ • CI/CD │ │ • AgentCoordinator │
|
||||||
|
│ • Agent Logs │ │ • ContextEngine │
|
||||||
|
└──────────────────┘ └──────────────────────────────┘
|
||||||
|
│
|
||||||
|
↓
|
||||||
|
┌──────────────────────────────────────────────────────┐
|
||||||
|
│ Reality (External Systems) │
|
||||||
|
│ • Git commits • Test results • Deployments │
|
||||||
|
│ • Agent executions • System events │
|
||||||
|
└──────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Components
|
||||||
|
|
||||||
|
#### `qlm_lab/models.py`
|
||||||
|
Defines core data structures:
|
||||||
|
- `IntelligenceType`, `ActorType`, `ActorRole`
|
||||||
|
- `Actor`: Represents humans, agents, systems
|
||||||
|
- `QLMEvent`: Every action generates an event
|
||||||
|
- `QIEmergence`: Detected emergent patterns
|
||||||
|
- `QLMMetrics`: System-level metrics
|
||||||
|
|
||||||
|
#### `qlm_lab/state.py`
|
||||||
|
Manages QLM state:
|
||||||
|
- `QLMState`: Complete state snapshot
|
||||||
|
- `ingest_event()`: Process new events and update state
|
||||||
|
- `query()`: Answer questions about state
|
||||||
|
- `detect_qi_emergence()`: Pattern matching for QI
|
||||||
|
- `calculate_alignment()`: HI-AI alignment scoring
|
||||||
|
|
||||||
|
#### `qlm_lab/api.py`
|
||||||
|
Public interface:
|
||||||
|
- `QLMInterface`: Main API class
|
||||||
|
- `record_*()`: Methods to record events
|
||||||
|
- `get_*()`: Query methods
|
||||||
|
- `ask()`: Natural language queries
|
||||||
|
|
||||||
|
#### `qlm_lab/ingestion/`
|
||||||
|
Connects QLM to reality:
|
||||||
|
- `GitConnector`: Ingest git commits
|
||||||
|
- `CIConnector`: Ingest test/build/deploy results
|
||||||
|
- `AgentLogConnector`: Parse agent logs
|
||||||
|
|
||||||
|
#### `qlm_lab/experiments/`
|
||||||
|
Validation experiments:
|
||||||
|
- `AlignmentDetectionExperiment`: Test alignment scoring
|
||||||
|
- `EmergenceDetectionExperiment`: Test QI detection
|
||||||
|
|
||||||
|
#### `qlm_lab/visualization.py`
|
||||||
|
Visualization tools:
|
||||||
|
- Event timeline
|
||||||
|
- Actor interaction graph
|
||||||
|
- Alignment trends
|
||||||
|
- Emergence patterns
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Data Models
|
||||||
|
|
||||||
|
### Actor
|
||||||
|
|
||||||
|
```python
|
||||||
|
@dataclass
|
||||||
|
class Actor:
|
||||||
|
id: str # Unique identifier
|
||||||
|
name: str # Human-readable name
|
||||||
|
actor_type: ActorType # HUMAN | AGENT | SYSTEM
|
||||||
|
role: ActorRole # OPERATOR | EXECUTOR | COORDINATOR | REVIEWER | ...
|
||||||
|
state: ActorState # ACTIVE | IDLE | BLOCKED | OFFLINE
|
||||||
|
capabilities: Set[str] # What this actor can do
|
||||||
|
current_task_id: Optional[str]
|
||||||
|
created_at: datetime
|
||||||
|
last_active: datetime
|
||||||
|
```
|
||||||
|
|
||||||
|
### QLMEvent
|
||||||
|
|
||||||
|
```python
|
||||||
|
@dataclass
|
||||||
|
class QLMEvent:
|
||||||
|
id: str # Unique event ID
|
||||||
|
timestamp: datetime # When event occurred
|
||||||
|
source_layer: IntelligenceType # HI | AI | QI
|
||||||
|
actor_id: str # Who generated this event
|
||||||
|
event_type: EventType # OPERATOR_INTENT | AGENT_EXECUTION | ...
|
||||||
|
data: Dict[str, Any] # Event payload
|
||||||
|
caused_by: List[str] # Causal event IDs
|
||||||
|
intent_node_id: Optional[str] # Link to IntentGraph
|
||||||
|
task_id: Optional[str] # Related task
|
||||||
|
tags: Set[str]
|
||||||
|
metadata: Dict[str, Any]
|
||||||
|
```
|
||||||
|
|
||||||
|
### QIEmergence
|
||||||
|
|
||||||
|
```python
|
||||||
|
@dataclass
|
||||||
|
class QIEmergence:
|
||||||
|
id: str # Unique emergence ID
|
||||||
|
timestamp: datetime # When detected
|
||||||
|
pattern_name: str # e.g., "agent_self_correction"
|
||||||
|
trigger_events: List[str] # Events that triggered this
|
||||||
|
confidence: float # 0.0 to 1.0
|
||||||
|
explanation: str # Human-readable description
|
||||||
|
operator_validated: Optional[bool] # Did Operator confirm?
|
||||||
|
operator_notes: str
|
||||||
|
impact_score: float # Significance (0.0 to 1.0)
|
||||||
|
```
|
||||||
|
|
||||||
|
### QLMMetrics
|
||||||
|
|
||||||
|
```python
|
||||||
|
@dataclass
|
||||||
|
class QLMMetrics:
|
||||||
|
hi_events: int # Count of HI events
|
||||||
|
ai_events: int # Count of AI events
|
||||||
|
qi_events: int # Count of QI events
|
||||||
|
system_events: int # Count of system events
|
||||||
|
|
||||||
|
hi_ai_alignment: float # Alignment score (0.0 to 1.0)
|
||||||
|
qi_emergence_rate: float # Rate of QI detection
|
||||||
|
feedback_loop_count: int # HI→AI→HI cycles
|
||||||
|
|
||||||
|
operator_approvals: int
|
||||||
|
operator_vetoes: int
|
||||||
|
operator_queries: int
|
||||||
|
|
||||||
|
start_time: datetime
|
||||||
|
end_time: datetime
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
### Initialization
|
||||||
|
|
||||||
|
```python
|
||||||
|
from qlm_lab.api import QLMInterface
|
||||||
|
|
||||||
|
# Basic initialization
|
||||||
|
qlm = QLMInterface()
|
||||||
|
|
||||||
|
# With cognitive layer integration
|
||||||
|
from cognitive.intent_graph import IntentGraph
|
||||||
|
from cognitive.agent_coordination import AgentCoordinator
|
||||||
|
|
||||||
|
intent_graph = IntentGraph()
|
||||||
|
agent_coordinator = AgentCoordinator(intent_graph)
|
||||||
|
|
||||||
|
qlm = QLMInterface(
|
||||||
|
intent_graph=intent_graph,
|
||||||
|
agent_coordinator=agent_coordinator
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Recording Events
|
||||||
|
|
||||||
|
#### Operator Events (HI)
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Record Operator intent
|
||||||
|
event = qlm.record_operator_intent(
|
||||||
|
intent="Deploy authentication feature",
|
||||||
|
description="Implement login, signup, password reset",
|
||||||
|
intent_node_id="intent-auth-001" # Link to IntentGraph
|
||||||
|
)
|
||||||
|
|
||||||
|
# Record Operator approval
|
||||||
|
qlm.record_operator_approval(
|
||||||
|
what_approved="Login implementation",
|
||||||
|
intent_node_id="intent-auth-001",
|
||||||
|
task_id="task-login-001"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Record Operator veto
|
||||||
|
qlm.record_operator_veto(
|
||||||
|
what_vetoed="Password reset implementation",
|
||||||
|
reason="Security concerns - needs stronger validation",
|
||||||
|
intent_node_id="intent-auth-001",
|
||||||
|
task_id="task-reset-001"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Record Operator query
|
||||||
|
qlm.record_operator_query("What did agents do today?")
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Agent Events (AI)
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Register an agent
|
||||||
|
agent = qlm.register_agent(
|
||||||
|
agent_id="coder-001",
|
||||||
|
name="CodeWriter",
|
||||||
|
role=ActorRole.CODER,
|
||||||
|
capabilities=["python", "javascript", "testing"]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Record agent execution
|
||||||
|
qlm.record_agent_execution(
|
||||||
|
agent_id="coder-001",
|
||||||
|
task_description="Implement login endpoint",
|
||||||
|
task_id="task-login-001",
|
||||||
|
intent_node_id="intent-auth-001"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Record agent completion
|
||||||
|
qlm.record_agent_completion(
|
||||||
|
agent_id="coder-001",
|
||||||
|
task_id="task-login-001",
|
||||||
|
success=True,
|
||||||
|
result={"files_modified": ["auth.py"], "tests_added": 5}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Record agent error
|
||||||
|
qlm.record_agent_error(
|
||||||
|
agent_id="coder-001",
|
||||||
|
task_id="task-login-001",
|
||||||
|
error="Database connection failed"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Record agent handoff
|
||||||
|
qlm.record_agent_handoff(
|
||||||
|
from_agent_id="coder-001",
|
||||||
|
to_agent_id="reviewer-001",
|
||||||
|
task_id="task-login-001",
|
||||||
|
handoff_message="Ready for review"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### System Events
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Record test result
|
||||||
|
qlm.record_system_event(
|
||||||
|
event_type=EventType.SYSTEM_TEST,
|
||||||
|
description="Backend tests passed",
|
||||||
|
task_id="task-login-001",
|
||||||
|
metadata={
|
||||||
|
"passed": True,
|
||||||
|
"test_count": 42,
|
||||||
|
"duration_seconds": 12.3
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Record build result
|
||||||
|
qlm.record_system_event(
|
||||||
|
event_type=EventType.SYSTEM_BUILD,
|
||||||
|
description="Production build successful",
|
||||||
|
metadata={"build_id": "build-123", "artifacts": ["app.tar.gz"]}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Record deployment
|
||||||
|
qlm.record_system_event(
|
||||||
|
event_type=EventType.SYSTEM_DEPLOY,
|
||||||
|
description="Deployed to production",
|
||||||
|
metadata={"environment": "production", "version": "v1.2.0"}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Querying State
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Get Operator summary
|
||||||
|
summary = qlm.get_summary(days=7)
|
||||||
|
print(summary)
|
||||||
|
|
||||||
|
# Get alignment score
|
||||||
|
alignment = qlm.get_alignment_score()
|
||||||
|
print(f"HI-AI Alignment: {alignment:.1%}")
|
||||||
|
|
||||||
|
# Get recent QI emergences
|
||||||
|
emergences = qlm.get_recent_emergences(limit=10)
|
||||||
|
for em in emergences:
|
||||||
|
print(f"{em.pattern_name}: {em.explanation}")
|
||||||
|
|
||||||
|
# Get active actors
|
||||||
|
active = qlm.get_active_actors()
|
||||||
|
for actor in active:
|
||||||
|
print(f"{actor.name} - {actor.role.value}")
|
||||||
|
|
||||||
|
# Get events by type
|
||||||
|
intents = qlm.get_events_by_type(EventType.OPERATOR_INTENT)
|
||||||
|
print(f"Total intents: {len(intents)}")
|
||||||
|
|
||||||
|
# Get events in time range
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
yesterday = datetime.now() - timedelta(days=1)
|
||||||
|
recent_events = qlm.get_events_in_timerange(yesterday)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Natural Language Queries
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Ask questions in natural language
|
||||||
|
response = qlm.ask("What did agents do today?")
|
||||||
|
print(response)
|
||||||
|
|
||||||
|
response = qlm.ask("Are we aligned with my intent?")
|
||||||
|
print(response)
|
||||||
|
|
||||||
|
response = qlm.ask("Show me emergent behaviors")
|
||||||
|
print(response)
|
||||||
|
|
||||||
|
response = qlm.ask("What's the status?")
|
||||||
|
print(response)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Export/Import State
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Export state to JSON
|
||||||
|
qlm.export_state("/path/to/qlm_state.json")
|
||||||
|
|
||||||
|
# Import state from JSON
|
||||||
|
qlm.import_state("/path/to/qlm_state.json")
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Integration Guide
|
||||||
|
|
||||||
|
### Integrating with Existing Cognitive Layer
|
||||||
|
|
||||||
|
QLM is designed to integrate seamlessly with the existing cognitive infrastructure:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from cognitive.intent_graph import IntentGraph
|
||||||
|
from cognitive.agent_coordination import AgentCoordinator
|
||||||
|
from qlm_lab.api import QLMInterface
|
||||||
|
|
||||||
|
# Initialize cognitive systems
|
||||||
|
intent_graph = IntentGraph()
|
||||||
|
agent_coordinator = AgentCoordinator(intent_graph)
|
||||||
|
|
||||||
|
# Initialize QLM with cognitive integration
|
||||||
|
qlm = QLMInterface(
|
||||||
|
intent_graph=intent_graph,
|
||||||
|
agent_coordinator=agent_coordinator
|
||||||
|
)
|
||||||
|
|
||||||
|
# Now when you create goals in IntentGraph...
|
||||||
|
goal = intent_graph.create_goal(
|
||||||
|
title="Build authentication",
|
||||||
|
rationale="Need secure user login"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ...also record in QLM
|
||||||
|
qlm.record_operator_intent(
|
||||||
|
intent="Build authentication",
|
||||||
|
intent_node_id=goal.id
|
||||||
|
)
|
||||||
|
|
||||||
|
# When agents coordinate...
|
||||||
|
task = intent_graph.create_task(
|
||||||
|
title="Implement login",
|
||||||
|
parent_id=goal.id
|
||||||
|
)
|
||||||
|
|
||||||
|
agent_coordinator.assign_task(task.id, agent_id="coder-001")
|
||||||
|
|
||||||
|
# ...also record in QLM
|
||||||
|
qlm.record_agent_execution(
|
||||||
|
agent_id="coder-001",
|
||||||
|
task_description="Implement login",
|
||||||
|
task_id=task.id,
|
||||||
|
intent_node_id=goal.id
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ingesting Real System Data
|
||||||
|
|
||||||
|
#### Git Commits
|
||||||
|
|
||||||
|
```python
|
||||||
|
from qlm_lab.ingestion.git import GitConnector
|
||||||
|
|
||||||
|
connector = GitConnector(repo_path="/path/to/repo", qlm=qlm)
|
||||||
|
|
||||||
|
# Ingest last 7 days of commits
|
||||||
|
events = connector.ingest_recent_commits(days=7)
|
||||||
|
print(f"Ingested {len(events)} commits")
|
||||||
|
|
||||||
|
# Ingest specific range
|
||||||
|
events = connector.ingest_commit_range(
|
||||||
|
since="2024-01-01",
|
||||||
|
until="2024-01-31"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### CI/CD Results
|
||||||
|
|
||||||
|
```python
|
||||||
|
from qlm_lab.ingestion.ci import CIConnector
|
||||||
|
|
||||||
|
connector = CIConnector(qlm=qlm)
|
||||||
|
|
||||||
|
# Ingest test result
|
||||||
|
connector.ingest_test_result(
|
||||||
|
test_name="Backend Tests",
|
||||||
|
passed=True,
|
||||||
|
duration_seconds=12.3,
|
||||||
|
commit_hash="abc123"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Ingest build result
|
||||||
|
connector.ingest_build_result(
|
||||||
|
build_name="Production Build",
|
||||||
|
success=True,
|
||||||
|
duration_seconds=45.2,
|
||||||
|
artifacts=["app.tar.gz", "app.zip"]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Ingest deployment
|
||||||
|
connector.ingest_deploy_result(
|
||||||
|
service_name="blackroad-api",
|
||||||
|
environment="production",
|
||||||
|
success=True,
|
||||||
|
version="v1.2.0"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Agent Logs
|
||||||
|
|
||||||
|
```python
|
||||||
|
from qlm_lab.ingestion.agent_logs import AgentLogConnector
|
||||||
|
|
||||||
|
connector = AgentLogConnector(qlm=qlm)
|
||||||
|
|
||||||
|
# Ingest log file
|
||||||
|
events = connector.ingest_log_file("/path/to/agent.log")
|
||||||
|
print(f"Ingested {len(events)} events from logs")
|
||||||
|
|
||||||
|
# Ingest structured logs
|
||||||
|
log_entries = [
|
||||||
|
{
|
||||||
|
"timestamp": "2024-01-15T10:30:00",
|
||||||
|
"agent_id": "coder-001",
|
||||||
|
"level": "INFO",
|
||||||
|
"message": "Task started: implement login"
|
||||||
|
},
|
||||||
|
# ...
|
||||||
|
]
|
||||||
|
events = connector.ingest_structured_log(log_entries)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Experiments & Validation
|
||||||
|
|
||||||
|
QLM includes built-in experiments to validate its effectiveness:
|
||||||
|
|
||||||
|
### Alignment Detection Experiment
|
||||||
|
|
||||||
|
**Hypothesis**: QLM can accurately detect when AI agents drift from Operator intent.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m qlm_lab.experiments.alignment_detection
|
||||||
|
```
|
||||||
|
|
||||||
|
**Scenarios**:
|
||||||
|
- Perfect alignment (100% approval)
|
||||||
|
- Partial alignment (some vetoes)
|
||||||
|
- No alignment (all vetoes)
|
||||||
|
|
||||||
|
**Success Criteria**: Alignment score accuracy within 20%
|
||||||
|
|
||||||
|
### Emergence Detection Experiment
|
||||||
|
|
||||||
|
**Hypothesis**: QLM can detect emergent QI behaviors.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m qlm_lab.experiments.emergence_detection
|
||||||
|
```
|
||||||
|
|
||||||
|
**Patterns Tested**:
|
||||||
|
- Agent self-correction
|
||||||
|
- Operator feedback loop
|
||||||
|
- Emergent collaboration
|
||||||
|
- Normal execution (should NOT trigger)
|
||||||
|
|
||||||
|
**Success Criteria**:
|
||||||
|
- True positive rate ≥ 80%
|
||||||
|
- False positive rate < 20%
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Visualization
|
||||||
|
|
||||||
|
QLM includes powerful visualization tools:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from qlm_lab.visualization import QLMVisualizer
|
||||||
|
|
||||||
|
viz = QLMVisualizer(qlm)
|
||||||
|
|
||||||
|
# Event timeline
|
||||||
|
viz.plot_event_timeline(save_path="timeline.png")
|
||||||
|
|
||||||
|
# Actor interaction graph
|
||||||
|
viz.plot_actor_graph(save_path="actors.png")
|
||||||
|
|
||||||
|
# Alignment over time
|
||||||
|
viz.plot_alignment_over_time(save_path="alignment.png")
|
||||||
|
|
||||||
|
# Emergence patterns
|
||||||
|
viz.plot_emergence_patterns(save_path="emergence.png")
|
||||||
|
|
||||||
|
# Export complete dashboard
|
||||||
|
viz.export_dashboard(output_dir="./qlm_dashboard")
|
||||||
|
```
|
||||||
|
|
||||||
|
**Requirements**:
|
||||||
|
```bash
|
||||||
|
pip install matplotlib networkx
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Operator's Guide
|
||||||
|
|
||||||
|
### Daily Usage
|
||||||
|
|
||||||
|
As the Operator (Alexa), here's how to use QLM:
|
||||||
|
|
||||||
|
#### Morning Check-In
|
||||||
|
|
||||||
|
```python
|
||||||
|
# What happened overnight?
|
||||||
|
summary = qlm.get_summary(days=1)
|
||||||
|
print(summary)
|
||||||
|
|
||||||
|
# Are agents aligned with my goals?
|
||||||
|
alignment = qlm.get_alignment_score()
|
||||||
|
if alignment < 0.7:
|
||||||
|
print("⚠️ Warning: Low alignment detected")
|
||||||
|
|
||||||
|
# Any emergent behaviors?
|
||||||
|
emergences = qlm.get_recent_emergences()
|
||||||
|
for em in emergences:
|
||||||
|
print(f"✨ {em.pattern_name}: {em.explanation}")
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Defining Intent
|
||||||
|
|
||||||
|
```python
|
||||||
|
# When starting a new project
|
||||||
|
qlm.record_operator_intent(
|
||||||
|
intent="Build payment integration",
|
||||||
|
description="Integrate Stripe for subscriptions and one-time payments",
|
||||||
|
intent_node_id="intent-payment-2024-01"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Reviewing Agent Work
|
||||||
|
|
||||||
|
```python
|
||||||
|
# When agents complete work
|
||||||
|
qlm.record_operator_approval(
|
||||||
|
what_approved="Stripe integration implementation",
|
||||||
|
intent_node_id="intent-payment-2024-01",
|
||||||
|
task_id="task-stripe-001"
|
||||||
|
)
|
||||||
|
|
||||||
|
# When work doesn't match intent
|
||||||
|
qlm.record_operator_veto(
|
||||||
|
what_vetoed="Payment form UI",
|
||||||
|
reason="Doesn't match brand guidelines - needs redesign",
|
||||||
|
intent_node_id="intent-payment-2024-01",
|
||||||
|
task_id="task-ui-001"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Asking Questions
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Natural language queries
|
||||||
|
qlm.ask("What did agents do today?")
|
||||||
|
qlm.ask("Are we aligned with my intent?")
|
||||||
|
qlm.ask("Show me emergent behaviors")
|
||||||
|
qlm.ask("What's the status?")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Understanding Alignment
|
||||||
|
|
||||||
|
**Alignment Score**: 0.0 to 1.0
|
||||||
|
|
||||||
|
- **0.9-1.0**: Excellent - agents are executing your intent well
|
||||||
|
- **0.7-0.9**: Good - minor drift, watch for patterns
|
||||||
|
- **0.5-0.7**: Warning - significant misalignment, review vetoes
|
||||||
|
- **< 0.5**: Critical - agents not following intent, intervention needed
|
||||||
|
|
||||||
|
**Improving Alignment**:
|
||||||
|
1. Be more specific in intent descriptions
|
||||||
|
2. Provide examples of what "good" looks like
|
||||||
|
3. Give immediate feedback (approve/veto)
|
||||||
|
4. Review patterns in vetoes - is there confusion?
|
||||||
|
|
||||||
|
### Understanding QI Emergence
|
||||||
|
|
||||||
|
**Common Patterns**:
|
||||||
|
|
||||||
|
- **agent_self_correction**: Agent fixed its own error without your help
|
||||||
|
- *Good sign*: Agents are learning and adapting
|
||||||
|
|
||||||
|
- **novel_solution**: Agent found an approach you didn't suggest
|
||||||
|
- *Good sign*: Creative problem-solving
|
||||||
|
- *Watch for*: Ensure solution aligns with intent
|
||||||
|
|
||||||
|
- **operator_feedback_loop**: You → agent → feedback → refined approach
|
||||||
|
- *Good sign*: Healthy iteration cycle
|
||||||
|
- *Measure*: Count of loops indicates collaboration quality
|
||||||
|
|
||||||
|
- **emergent_collaboration**: Agents self-organized
|
||||||
|
- *Good sign*: Agents coordinating without explicit instructions
|
||||||
|
- *Watch for*: Ensure coordination serves your intent
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Development & Extension
|
||||||
|
|
||||||
|
### Adding New Event Types
|
||||||
|
|
||||||
|
```python
|
||||||
|
# In qlm_lab/models.py
|
||||||
|
class EventType(Enum):
|
||||||
|
# ... existing types ...
|
||||||
|
CUSTOM_EVENT = "custom_event"
|
||||||
|
|
||||||
|
# In qlm_lab/api.py
|
||||||
|
def record_custom_event(self, ...):
|
||||||
|
event = QLMEvent(
|
||||||
|
source_layer=IntelligenceType.AI,
|
||||||
|
event_type=EventType.CUSTOM_EVENT,
|
||||||
|
...
|
||||||
|
)
|
||||||
|
self.state.ingest_event(event)
|
||||||
|
return event
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adding New QI Patterns
|
||||||
|
|
||||||
|
```python
|
||||||
|
# In qlm_lab/models.py
|
||||||
|
QI_PATTERNS["my_pattern"] = {
|
||||||
|
"description": "Description of when this pattern occurs",
|
||||||
|
"trigger": "sequence of events that trigger this",
|
||||||
|
"significance": "high", # high | medium | low | very_high
|
||||||
|
}
|
||||||
|
|
||||||
|
# In qlm_lab/state.py
|
||||||
|
def _matches_pattern(self, events, pattern_name, pattern_def):
|
||||||
|
if pattern_name == "my_pattern":
|
||||||
|
# Implement pattern detection logic
|
||||||
|
# Return True if pattern matches
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adding New Connectors
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Create qlm_lab/ingestion/my_connector.py
|
||||||
|
from qlm_lab.api import QLMInterface
|
||||||
|
from qlm_lab.models import EventType
|
||||||
|
|
||||||
|
class MyConnector:
|
||||||
|
def __init__(self, qlm: QLMInterface):
|
||||||
|
self.qlm = qlm
|
||||||
|
|
||||||
|
def ingest_my_data(self, data):
|
||||||
|
# Transform data into QLM events
|
||||||
|
event = self.qlm.record_system_event(
|
||||||
|
event_type=EventType.SYSTEM_CUSTOM,
|
||||||
|
description=...,
|
||||||
|
metadata=...
|
||||||
|
)
|
||||||
|
return event
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run QLM tests
|
||||||
|
pytest tests/test_qlm_core.py -v
|
||||||
|
|
||||||
|
# Run all tests
|
||||||
|
pytest tests/ -v
|
||||||
|
|
||||||
|
# With coverage
|
||||||
|
pytest tests/test_qlm_core.py --cov=qlm_lab --cov-report=html
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running Demo
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Interactive demo
|
||||||
|
python -m qlm_lab.demo
|
||||||
|
|
||||||
|
# Run specific experiment
|
||||||
|
python -m qlm_lab.experiments.alignment_detection
|
||||||
|
python -m qlm_lab.experiments.emergence_detection
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
### Phase 1: Lab (Current)
|
||||||
|
|
||||||
|
- ✅ Core QLM models and state management
|
||||||
|
- ✅ Basic event ingestion (git, CI, agents)
|
||||||
|
- ✅ Alignment and emergence detection
|
||||||
|
- ✅ Visualization and experiments
|
||||||
|
- ✅ Documentation
|
||||||
|
|
||||||
|
### Phase 2: Integration (Next 1-2 months)
|
||||||
|
|
||||||
|
- [ ] Full cognitive layer integration
|
||||||
|
- [ ] Real-time event streaming
|
||||||
|
- [ ] FastAPI router for QLM API
|
||||||
|
- [ ] Prism Console UI integration
|
||||||
|
- [ ] Dashboard for Operator
|
||||||
|
|
||||||
|
### Phase 3: Production (Months 3-4)
|
||||||
|
|
||||||
|
- [ ] Database persistence (PostgreSQL)
|
||||||
|
- [ ] Advanced QI pattern detection (ML-based)
|
||||||
|
- [ ] Multi-Operator support
|
||||||
|
- [ ] Audit trail and compliance
|
||||||
|
- [ ] Performance optimization
|
||||||
|
|
||||||
|
### Phase 4: Lucidia Language (Months 5-6)
|
||||||
|
|
||||||
|
- [ ] Lucidia syntax for expressing intent
|
||||||
|
- [ ] Lucidia → QLM compiler
|
||||||
|
- [ ] QLM → Lucidia decompiler (explain mode)
|
||||||
|
- [ ] Lucidia REPL for live QLM queries
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
**Q: What's the difference between QLM and the IntentGraph?**
|
||||||
|
|
||||||
|
A: IntentGraph tracks *what* (goals, tasks, artifacts). QLM tracks *why*, *how*, and *emergence* (intent, execution, QI patterns). They complement each other.
|
||||||
|
|
||||||
|
**Q: Do I need to use QLM for every agent?**
|
||||||
|
|
||||||
|
A: No, start small. Use QLM for critical workflows where you need clear intent tracking and alignment verification.
|
||||||
|
|
||||||
|
**Q: How does QLM relate to Lucidia?**
|
||||||
|
|
||||||
|
A: Lucidia is the *language* for expressing intent and constraints. QLM is the *runtime* that tracks and enforces them.
|
||||||
|
|
||||||
|
**Q: What if I don't want to record every event?**
|
||||||
|
|
||||||
|
A: QLM is opt-in. Only record events you care about. Start with Operator intents and agent completions.
|
||||||
|
|
||||||
|
**Q: Can QLM work with multiple Operators?**
|
||||||
|
|
||||||
|
A: Currently optimized for single Operator (Alexa). Multi-Operator support planned for Phase 3.
|
||||||
|
|
||||||
|
**Q: How do I debug QLM?**
|
||||||
|
|
||||||
|
A: Use `qlm.state.export_json()` to inspect state, visualizations to see patterns, and experiments to validate behavior.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
QLM provides the missing semantic layer for Operator-AI collaboration. By tracking HI, AI, and QI as first-class concepts, QLM makes AI systems **understandable**, **controllable**, and **improvable**.
|
||||||
|
|
||||||
|
**Start using QLM today**:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from qlm_lab.api import QLMInterface
|
||||||
|
|
||||||
|
qlm = QLMInterface()
|
||||||
|
|
||||||
|
# Your intent
|
||||||
|
qlm.record_operator_intent("Build the future")
|
||||||
|
|
||||||
|
# Agent execution
|
||||||
|
qlm.record_agent_execution("agent-1", "Create something amazing", "task-1")
|
||||||
|
|
||||||
|
# Check alignment
|
||||||
|
print(f"Alignment: {qlm.get_alignment_score():.1%}")
|
||||||
|
|
||||||
|
# Ask questions
|
||||||
|
print(qlm.ask("What's the status?"))
|
||||||
|
```
|
||||||
|
|
||||||
|
**Happy building! 🛣️✨**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*QLM is part of the BlackRoad Operating System. See BLACKROAD_OS_BIG_KAHUNA_VISION.md for the complete vision.*
|
||||||
76
qlm_lab/__init__.py
Normal file
76
qlm_lab/__init__.py
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
"""
|
||||||
|
QLM Lab - Quantum Language Model Implementation
|
||||||
|
|
||||||
|
This module implements the QLM (Quantum Language Model) system for BlackRoad OS.
|
||||||
|
|
||||||
|
QLM is a stateful semantic layer that:
|
||||||
|
- Tracks HI (Human Intelligence), AI (Agent Intelligence), and QI (Quantum/Emergent Intelligence)
|
||||||
|
- Connects Operator intent to system execution
|
||||||
|
- Detects emergent behaviors in HI+AI feedback loops
|
||||||
|
- Provides introspection and control tools for the Operator
|
||||||
|
|
||||||
|
Key Components:
|
||||||
|
- models: Core data structures (IntelligenceLayer, Actor, QLMEvent, QIEmergence)
|
||||||
|
- state: QLM state management and transitions
|
||||||
|
- events: Event ingestion and processing
|
||||||
|
- api: Public API for QLM operations
|
||||||
|
- ingestion: Connectors to real system data (git, CI, agents)
|
||||||
|
- experiments: Validation experiments and metrics
|
||||||
|
- visualization: Tools for visualizing QLM state
|
||||||
|
|
||||||
|
Integration Points:
|
||||||
|
- cognitive.intent_graph: Foundation for intent tracking
|
||||||
|
- cognitive.agent_coordination: Multi-agent collaboration
|
||||||
|
- operator_engine.scheduler: Background QLM analysis
|
||||||
|
- agents: Event source for AI actions
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
from qlm_lab import QLMState, QLMEvent
|
||||||
|
from qlm_lab.api import QLMInterface
|
||||||
|
|
||||||
|
# Initialize QLM
|
||||||
|
qlm = QLMInterface()
|
||||||
|
|
||||||
|
# Record Operator intent
|
||||||
|
qlm.record_operator_intent("Deploy authentication feature")
|
||||||
|
|
||||||
|
# Record agent execution
|
||||||
|
qlm.record_agent_execution(agent_id="coder-001", task="implement login")
|
||||||
|
|
||||||
|
# Query state
|
||||||
|
state = qlm.get_current_state()
|
||||||
|
summary = qlm.summarize_for_operator(days=7)
|
||||||
|
"""
|
||||||
|
|
||||||
|
__version__ = "0.1.0"
|
||||||
|
|
||||||
|
from qlm_lab.models import (
|
||||||
|
IntelligenceType,
|
||||||
|
ActorType,
|
||||||
|
ActorRole,
|
||||||
|
IntelligenceLayer,
|
||||||
|
Actor,
|
||||||
|
QLMEvent,
|
||||||
|
EventType,
|
||||||
|
QIEmergence,
|
||||||
|
QLMMetrics,
|
||||||
|
)
|
||||||
|
|
||||||
|
from qlm_lab.state import QLMState, StateTransition
|
||||||
|
|
||||||
|
from qlm_lab.api import QLMInterface
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"IntelligenceType",
|
||||||
|
"ActorType",
|
||||||
|
"ActorRole",
|
||||||
|
"IntelligenceLayer",
|
||||||
|
"Actor",
|
||||||
|
"QLMEvent",
|
||||||
|
"EventType",
|
||||||
|
"QIEmergence",
|
||||||
|
"QLMMetrics",
|
||||||
|
"QLMState",
|
||||||
|
"StateTransition",
|
||||||
|
"QLMInterface",
|
||||||
|
]
|
||||||
498
qlm_lab/api.py
Normal file
498
qlm_lab/api.py
Normal file
@@ -0,0 +1,498 @@
|
|||||||
|
"""
|
||||||
|
QLM API - Public interface for Quantum Language Model
|
||||||
|
|
||||||
|
This is the primary way applications interact with QLM.
|
||||||
|
|
||||||
|
The QLMInterface provides:
|
||||||
|
- Simple methods to record events
|
||||||
|
- Queries about current state
|
||||||
|
- Operator-facing summaries
|
||||||
|
- Integration with cognitive layer
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
from qlm_lab.api import QLMInterface
|
||||||
|
|
||||||
|
qlm = QLMInterface()
|
||||||
|
|
||||||
|
# Record events
|
||||||
|
qlm.record_operator_intent("Deploy auth feature", intent_node_id="abc123")
|
||||||
|
qlm.record_agent_execution("agent-001", "Implement login", task_id="task-001")
|
||||||
|
qlm.record_agent_completion("agent-001", "task-001", success=True)
|
||||||
|
|
||||||
|
# Query state
|
||||||
|
summary = qlm.get_summary(days=7)
|
||||||
|
alignment = qlm.get_alignment_score()
|
||||||
|
emergences = qlm.get_recent_emergences(limit=5)
|
||||||
|
|
||||||
|
# Operator tools
|
||||||
|
print(qlm.ask("What did agents do today?"))
|
||||||
|
print(qlm.ask("Are we aligned with my intent?"))
|
||||||
|
"""
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from typing import Dict, List, Optional, Any
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from qlm_lab.state import QLMState
|
||||||
|
from qlm_lab.models import (
|
||||||
|
Actor,
|
||||||
|
ActorType,
|
||||||
|
ActorRole,
|
||||||
|
ActorState,
|
||||||
|
QLMEvent,
|
||||||
|
EventType,
|
||||||
|
IntelligenceType,
|
||||||
|
QIEmergence,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class QLMInterface:
|
||||||
|
"""
|
||||||
|
Public API for QLM.
|
||||||
|
|
||||||
|
This provides a simple, clean interface for recording events
|
||||||
|
and querying QLM state.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, intent_graph=None, agent_coordinator=None):
|
||||||
|
"""
|
||||||
|
Initialize QLM interface.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
intent_graph: Optional cognitive.intent_graph.IntentGraph
|
||||||
|
agent_coordinator: Optional cognitive.agent_coordination.AgentCoordinator
|
||||||
|
"""
|
||||||
|
self.state = QLMState(
|
||||||
|
intent_graph=intent_graph, agent_coordinator=agent_coordinator
|
||||||
|
)
|
||||||
|
|
||||||
|
# Register default Operator
|
||||||
|
self.operator = Actor(
|
||||||
|
id="operator-alexa",
|
||||||
|
name="Alexa (Operator)",
|
||||||
|
actor_type=ActorType.HUMAN,
|
||||||
|
role=ActorRole.OPERATOR,
|
||||||
|
state=ActorState.ACTIVE,
|
||||||
|
capabilities={"intent", "approve", "veto", "query", "orchestrate"},
|
||||||
|
)
|
||||||
|
self.state.register_actor(self.operator)
|
||||||
|
|
||||||
|
logger.info("QLM Interface initialized")
|
||||||
|
|
||||||
|
def record_operator_intent(
|
||||||
|
self,
|
||||||
|
intent: str,
|
||||||
|
description: str = "",
|
||||||
|
intent_node_id: Optional[str] = None,
|
||||||
|
metadata: Optional[Dict] = None,
|
||||||
|
) -> QLMEvent:
|
||||||
|
"""
|
||||||
|
Record an Operator intent.
|
||||||
|
|
||||||
|
This is HI (Human Intelligence) expressing a goal or desire.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
intent: What the Operator wants (e.g., "Deploy authentication")
|
||||||
|
description: Additional context
|
||||||
|
intent_node_id: Optional link to IntentGraph node
|
||||||
|
metadata: Additional data
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The created QLMEvent
|
||||||
|
"""
|
||||||
|
event = QLMEvent(
|
||||||
|
source_layer=IntelligenceType.HI,
|
||||||
|
actor_id=self.operator.id,
|
||||||
|
event_type=EventType.OPERATOR_INTENT,
|
||||||
|
intent_node_id=intent_node_id,
|
||||||
|
data={
|
||||||
|
"intent": intent,
|
||||||
|
"description": description,
|
||||||
|
},
|
||||||
|
metadata=metadata or {},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.state.ingest_event(event)
|
||||||
|
logger.info(f"Operator intent recorded: {intent}")
|
||||||
|
return event
|
||||||
|
|
||||||
|
def record_operator_approval(
|
||||||
|
self,
|
||||||
|
what_approved: str,
|
||||||
|
intent_node_id: Optional[str] = None,
|
||||||
|
task_id: Optional[str] = None,
|
||||||
|
) -> QLMEvent:
|
||||||
|
"""Record Operator approval of agent work"""
|
||||||
|
event = QLMEvent(
|
||||||
|
source_layer=IntelligenceType.HI,
|
||||||
|
actor_id=self.operator.id,
|
||||||
|
event_type=EventType.OPERATOR_APPROVAL,
|
||||||
|
intent_node_id=intent_node_id,
|
||||||
|
task_id=task_id,
|
||||||
|
data={"approved": what_approved},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.state.ingest_event(event)
|
||||||
|
logger.info(f"Operator approval recorded: {what_approved}")
|
||||||
|
return event
|
||||||
|
|
||||||
|
def record_operator_veto(
|
||||||
|
self,
|
||||||
|
what_vetoed: str,
|
||||||
|
reason: str,
|
||||||
|
intent_node_id: Optional[str] = None,
|
||||||
|
task_id: Optional[str] = None,
|
||||||
|
) -> QLMEvent:
|
||||||
|
"""Record Operator veto of agent work"""
|
||||||
|
event = QLMEvent(
|
||||||
|
source_layer=IntelligenceType.HI,
|
||||||
|
actor_id=self.operator.id,
|
||||||
|
event_type=EventType.OPERATOR_VETO,
|
||||||
|
intent_node_id=intent_node_id,
|
||||||
|
task_id=task_id,
|
||||||
|
data={"vetoed": what_vetoed, "reason": reason},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.state.ingest_event(event)
|
||||||
|
logger.info(f"Operator veto recorded: {what_vetoed}")
|
||||||
|
return event
|
||||||
|
|
||||||
|
def record_operator_query(self, query: str) -> QLMEvent:
|
||||||
|
"""Record Operator asking a question"""
|
||||||
|
event = QLMEvent(
|
||||||
|
source_layer=IntelligenceType.HI,
|
||||||
|
actor_id=self.operator.id,
|
||||||
|
event_type=EventType.OPERATOR_QUERY,
|
||||||
|
data={"query": query},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.state.ingest_event(event)
|
||||||
|
return event
|
||||||
|
|
||||||
|
def record_agent_execution(
|
||||||
|
self,
|
||||||
|
agent_id: str,
|
||||||
|
task_description: str,
|
||||||
|
task_id: Optional[str] = None,
|
||||||
|
intent_node_id: Optional[str] = None,
|
||||||
|
metadata: Optional[Dict] = None,
|
||||||
|
) -> QLMEvent:
|
||||||
|
"""
|
||||||
|
Record an agent starting execution.
|
||||||
|
|
||||||
|
This is AI (Agent Intelligence) performing work.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
agent_id: Agent identifier
|
||||||
|
task_description: What the agent is doing
|
||||||
|
task_id: Optional task ID
|
||||||
|
intent_node_id: Link to the intent this fulfills
|
||||||
|
metadata: Additional data
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The created QLMEvent
|
||||||
|
"""
|
||||||
|
event = QLMEvent(
|
||||||
|
source_layer=IntelligenceType.AI,
|
||||||
|
actor_id=agent_id,
|
||||||
|
event_type=EventType.AGENT_EXECUTION,
|
||||||
|
task_id=task_id,
|
||||||
|
intent_node_id=intent_node_id,
|
||||||
|
data={
|
||||||
|
"task": task_description,
|
||||||
|
},
|
||||||
|
metadata=metadata or {},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.state.ingest_event(event)
|
||||||
|
logger.info(f"Agent execution recorded: {agent_id} - {task_description}")
|
||||||
|
return event
|
||||||
|
|
||||||
|
def record_agent_completion(
|
||||||
|
self,
|
||||||
|
agent_id: str,
|
||||||
|
task_id: str,
|
||||||
|
success: bool = True,
|
||||||
|
result: Optional[Dict] = None,
|
||||||
|
intent_node_id: Optional[str] = None,
|
||||||
|
) -> QLMEvent:
|
||||||
|
"""Record an agent completing a task"""
|
||||||
|
event = QLMEvent(
|
||||||
|
source_layer=IntelligenceType.AI,
|
||||||
|
actor_id=agent_id,
|
||||||
|
event_type=EventType.AGENT_COMPLETION,
|
||||||
|
task_id=task_id,
|
||||||
|
intent_node_id=intent_node_id,
|
||||||
|
data={
|
||||||
|
"success": success,
|
||||||
|
"result": result or {},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.state.ingest_event(event)
|
||||||
|
logger.info(f"Agent completion recorded: {agent_id} - {task_id}")
|
||||||
|
return event
|
||||||
|
|
||||||
|
def record_agent_error(
|
||||||
|
self,
|
||||||
|
agent_id: str,
|
||||||
|
task_id: str,
|
||||||
|
error: str,
|
||||||
|
intent_node_id: Optional[str] = None,
|
||||||
|
) -> QLMEvent:
|
||||||
|
"""Record an agent error"""
|
||||||
|
event = QLMEvent(
|
||||||
|
source_layer=IntelligenceType.AI,
|
||||||
|
actor_id=agent_id,
|
||||||
|
event_type=EventType.AGENT_ERROR,
|
||||||
|
task_id=task_id,
|
||||||
|
intent_node_id=intent_node_id,
|
||||||
|
data={"error": error},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.state.ingest_event(event)
|
||||||
|
logger.warning(f"Agent error recorded: {agent_id} - {error}")
|
||||||
|
return event
|
||||||
|
|
||||||
|
def record_agent_handoff(
|
||||||
|
self,
|
||||||
|
from_agent_id: str,
|
||||||
|
to_agent_id: str,
|
||||||
|
task_id: str,
|
||||||
|
handoff_message: str = "",
|
||||||
|
) -> QLMEvent:
|
||||||
|
"""Record an agent-to-agent handoff"""
|
||||||
|
event = QLMEvent(
|
||||||
|
source_layer=IntelligenceType.AI,
|
||||||
|
actor_id=from_agent_id,
|
||||||
|
event_type=EventType.AGENT_HANDOFF,
|
||||||
|
task_id=task_id,
|
||||||
|
data={
|
||||||
|
"to_agent": to_agent_id,
|
||||||
|
"message": handoff_message,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.state.ingest_event(event)
|
||||||
|
logger.info(f"Agent handoff recorded: {from_agent_id} → {to_agent_id}")
|
||||||
|
return event
|
||||||
|
|
||||||
|
def record_system_event(
|
||||||
|
self,
|
||||||
|
event_type: EventType,
|
||||||
|
description: str,
|
||||||
|
task_id: Optional[str] = None,
|
||||||
|
metadata: Optional[Dict] = None,
|
||||||
|
) -> QLMEvent:
|
||||||
|
"""
|
||||||
|
Record a system event (deploy, test, build, error).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
event_type: Must be a SYSTEM_* event type
|
||||||
|
description: What happened
|
||||||
|
task_id: Optional related task
|
||||||
|
metadata: Additional data (test results, build logs, etc.)
|
||||||
|
"""
|
||||||
|
event = QLMEvent(
|
||||||
|
source_layer=IntelligenceType.AI, # System events in AI layer
|
||||||
|
actor_id="system",
|
||||||
|
event_type=event_type,
|
||||||
|
task_id=task_id,
|
||||||
|
data={"description": description},
|
||||||
|
metadata=metadata or {},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.state.ingest_event(event)
|
||||||
|
logger.info(f"System event recorded: {event_type.value} - {description}")
|
||||||
|
return event
|
||||||
|
|
||||||
|
def register_agent(
|
||||||
|
self,
|
||||||
|
agent_id: str,
|
||||||
|
name: str,
|
||||||
|
role: ActorRole = ActorRole.EXECUTOR,
|
||||||
|
capabilities: Optional[List[str]] = None,
|
||||||
|
) -> Actor:
|
||||||
|
"""
|
||||||
|
Register a new agent in the QLM system.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
agent_id: Unique agent identifier
|
||||||
|
name: Human-readable name
|
||||||
|
role: Agent's role
|
||||||
|
capabilities: List of capabilities
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The created Actor
|
||||||
|
"""
|
||||||
|
actor = Actor(
|
||||||
|
id=agent_id,
|
||||||
|
name=name,
|
||||||
|
actor_type=ActorType.AGENT,
|
||||||
|
role=role,
|
||||||
|
state=ActorState.IDLE,
|
||||||
|
capabilities=set(capabilities or []),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.state.register_actor(actor)
|
||||||
|
logger.info(f"Agent registered: {name} ({agent_id})")
|
||||||
|
return actor
|
||||||
|
|
||||||
|
def get_summary(self, days: int = 7) -> str:
|
||||||
|
"""
|
||||||
|
Get an Operator-facing summary.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
days: Number of days to summarize
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Human-readable summary string
|
||||||
|
"""
|
||||||
|
return self.state.summarize_for_operator(days=days)
|
||||||
|
|
||||||
|
def get_alignment_score(self) -> float:
|
||||||
|
"""Get HI-AI alignment score (0.0 to 1.0)"""
|
||||||
|
return self.state.calculate_alignment()
|
||||||
|
|
||||||
|
def get_recent_emergences(self, limit: int = 10) -> List[QIEmergence]:
|
||||||
|
"""Get recent QI emergence events"""
|
||||||
|
return self.state.emergences[-limit:]
|
||||||
|
|
||||||
|
def get_active_actors(self) -> List[Actor]:
|
||||||
|
"""Get all currently active actors"""
|
||||||
|
return self.state.query("active_actors")
|
||||||
|
|
||||||
|
def get_events_by_type(
|
||||||
|
self, event_type: EventType, limit: Optional[int] = None
|
||||||
|
) -> List[QLMEvent]:
|
||||||
|
"""Get events of a specific type"""
|
||||||
|
events = self.state.query("events_by_type", event_type=event_type)
|
||||||
|
return events[-limit:] if limit else events
|
||||||
|
|
||||||
|
def get_events_in_timerange(
|
||||||
|
self, start: datetime, end: Optional[datetime] = None
|
||||||
|
) -> List[QLMEvent]:
|
||||||
|
"""Get events within a time range"""
|
||||||
|
end = end or datetime.now()
|
||||||
|
return self.state.query("events_in_timerange", start=start, end=end)
|
||||||
|
|
||||||
|
def ask(self, question: str) -> str:
|
||||||
|
"""
|
||||||
|
Natural language query interface for Operator.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
- "What did agents do today?"
|
||||||
|
- "Are we aligned with my intent?"
|
||||||
|
- "Show me emergent behaviors"
|
||||||
|
- "What's the status?"
|
||||||
|
|
||||||
|
This is a simple keyword-based implementation.
|
||||||
|
In production, this would use an LLM to interpret questions.
|
||||||
|
"""
|
||||||
|
# Record the query
|
||||||
|
self.record_operator_query(question)
|
||||||
|
|
||||||
|
question_lower = question.lower()
|
||||||
|
|
||||||
|
# Today's activity
|
||||||
|
if "today" in question_lower or "what did" in question_lower:
|
||||||
|
today_start = datetime.now().replace(hour=0, minute=0, second=0)
|
||||||
|
events = self.get_events_in_timerange(today_start)
|
||||||
|
|
||||||
|
agent_events = [e for e in events if e.source_layer == IntelligenceType.AI]
|
||||||
|
|
||||||
|
response = f"Today's Activity:\n"
|
||||||
|
response += f"- Total Events: {len(events)}\n"
|
||||||
|
response += f"- Agent Actions: {len(agent_events)}\n"
|
||||||
|
|
||||||
|
# Group by agent
|
||||||
|
by_agent = {}
|
||||||
|
for event in agent_events:
|
||||||
|
agent_id = event.actor_id
|
||||||
|
by_agent[agent_id] = by_agent.get(agent_id, 0) + 1
|
||||||
|
|
||||||
|
response += f"\nMost Active Agents:\n"
|
||||||
|
for agent_id, count in sorted(
|
||||||
|
by_agent.items(), key=lambda x: x[1], reverse=True
|
||||||
|
)[:5]:
|
||||||
|
response += f" - {agent_id}: {count} actions\n"
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
# Alignment
|
||||||
|
elif "aligned" in question_lower or "alignment" in question_lower:
|
||||||
|
alignment = self.get_alignment_score()
|
||||||
|
response = f"HI-AI Alignment: {alignment:.1%}\n"
|
||||||
|
|
||||||
|
if alignment >= 0.8:
|
||||||
|
response += "✅ Excellent alignment - agents are following your intent well."
|
||||||
|
elif alignment >= 0.6:
|
||||||
|
response += "⚠️ Moderate alignment - some drift from intent detected."
|
||||||
|
else:
|
||||||
|
response += "🚨 Low alignment - significant divergence from your intent."
|
||||||
|
|
||||||
|
response += f"\n\nRecent Feedback:\n"
|
||||||
|
response += f"- Approvals: {self.state.metrics.operator_approvals}\n"
|
||||||
|
response += f"- Vetoes: {self.state.metrics.operator_vetoes}\n"
|
||||||
|
response += f"- Feedback Loops: {self.state.metrics.feedback_loop_count}\n"
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
# Emergence
|
||||||
|
elif "emergent" in question_lower or "emergence" in question_lower or "qi" in question_lower:
|
||||||
|
emergences = self.get_recent_emergences(limit=5)
|
||||||
|
|
||||||
|
if not emergences:
|
||||||
|
return "No emergent behaviors detected recently."
|
||||||
|
|
||||||
|
response = f"Recent QI Emergence Events ({len(emergences)}):\n\n"
|
||||||
|
|
||||||
|
for em in emergences:
|
||||||
|
response += f"🌟 {em.pattern_name}\n"
|
||||||
|
response += f" {em.explanation}\n"
|
||||||
|
response += f" Confidence: {em.confidence:.0%}\n"
|
||||||
|
response += f" Impact: {em.impact_score:.1f}/1.0\n\n"
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
# Status
|
||||||
|
elif "status" in question_lower or "summary" in question_lower:
|
||||||
|
return self.get_summary(days=7)
|
||||||
|
|
||||||
|
# Default
|
||||||
|
else:
|
||||||
|
return (
|
||||||
|
f"I don't understand the question: '{question}'\n\n"
|
||||||
|
f"Try asking:\n"
|
||||||
|
f"- What did agents do today?\n"
|
||||||
|
f"- Are we aligned with my intent?\n"
|
||||||
|
f"- Show me emergent behaviors\n"
|
||||||
|
f"- What's the status?\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
def explain_event(self, event_id: str) -> Optional[str]:
|
||||||
|
"""Explain what happened with a specific event"""
|
||||||
|
transition = self.state.explain_transition(event_id)
|
||||||
|
|
||||||
|
if not transition:
|
||||||
|
return None
|
||||||
|
|
||||||
|
response = f"Event: {event_id}\n"
|
||||||
|
response += f"Time: {transition.timestamp}\n\n"
|
||||||
|
response += f"Changes:\n"
|
||||||
|
for change in transition.changes:
|
||||||
|
response += f" - {change}\n"
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
def export_state(self, file_path: str) -> None:
|
||||||
|
"""Export QLM state to JSON file"""
|
||||||
|
self.state.export_json(file_path)
|
||||||
|
logger.info(f"QLM state exported to: {file_path}")
|
||||||
|
|
||||||
|
def import_state(self, file_path: str) -> None:
|
||||||
|
"""Import QLM state from JSON file"""
|
||||||
|
self.state.import_json(file_path)
|
||||||
|
logger.info(f"QLM state imported from: {file_path}")
|
||||||
346
qlm_lab/demo.py
Normal file
346
qlm_lab/demo.py
Normal file
@@ -0,0 +1,346 @@
|
|||||||
|
"""
|
||||||
|
QLM Demo Script - Shows QLM in action
|
||||||
|
|
||||||
|
This script demonstrates the core QLM functionality:
|
||||||
|
1. Recording Operator intent
|
||||||
|
2. Recording agent executions
|
||||||
|
3. Detecting QI emergence
|
||||||
|
4. Querying state
|
||||||
|
5. Generating Operator summaries
|
||||||
|
|
||||||
|
Run: python -m qlm_lab.demo
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
from qlm_lab.api import QLMInterface
|
||||||
|
from qlm_lab.models import ActorRole, EventType
|
||||||
|
|
||||||
|
# Setup logging
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def demo_basic_workflow():
|
||||||
|
"""Demonstrate basic QLM workflow"""
|
||||||
|
print("=" * 60)
|
||||||
|
print("QLM Demo: Basic Workflow")
|
||||||
|
print("=" * 60)
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Initialize QLM
|
||||||
|
qlm = QLMInterface()
|
||||||
|
print("✓ QLM initialized")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Register agents
|
||||||
|
agent1 = qlm.register_agent(
|
||||||
|
agent_id="agent-coder-001",
|
||||||
|
name="CodeWriter",
|
||||||
|
role=ActorRole.CODER,
|
||||||
|
capabilities=["python", "javascript", "testing"],
|
||||||
|
)
|
||||||
|
print(f"✓ Registered agent: {agent1.name}")
|
||||||
|
|
||||||
|
agent2 = qlm.register_agent(
|
||||||
|
agent_id="agent-reviewer-001",
|
||||||
|
name="CodeReviewer",
|
||||||
|
role=ActorRole.REVIEWER,
|
||||||
|
capabilities=["code_review", "security_audit"],
|
||||||
|
)
|
||||||
|
print(f"✓ Registered agent: {agent2.name}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Operator defines intent
|
||||||
|
print("👤 Operator: 'Build authentication feature'")
|
||||||
|
intent_event = qlm.record_operator_intent(
|
||||||
|
intent="Build authentication feature",
|
||||||
|
description="Implement login, signup, and password reset",
|
||||||
|
intent_node_id="intent-auth-001",
|
||||||
|
)
|
||||||
|
print(f"✓ Intent recorded (event: {intent_event.id[:8]}...)")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Agent executes
|
||||||
|
print("🤖 Agent CodeWriter: Starting implementation...")
|
||||||
|
exec_event = qlm.record_agent_execution(
|
||||||
|
agent_id="agent-coder-001",
|
||||||
|
task_description="Implement login endpoint",
|
||||||
|
task_id="task-login-001",
|
||||||
|
intent_node_id="intent-auth-001",
|
||||||
|
)
|
||||||
|
print(f"✓ Execution recorded (event: {exec_event.id[:8]}...)")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Agent completes
|
||||||
|
print("🤖 Agent CodeWriter: Completed!")
|
||||||
|
completion_event = qlm.record_agent_completion(
|
||||||
|
agent_id="agent-coder-001",
|
||||||
|
task_id="task-login-001",
|
||||||
|
success=True,
|
||||||
|
result={"files_modified": ["auth.py", "routes.py"], "tests_added": 5},
|
||||||
|
intent_node_id="intent-auth-001",
|
||||||
|
)
|
||||||
|
print(f"✓ Completion recorded (event: {completion_event.id[:8]}...)")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Operator approves
|
||||||
|
print("👤 Operator: 'Looks good!'")
|
||||||
|
approval_event = qlm.record_operator_approval(
|
||||||
|
what_approved="Login implementation",
|
||||||
|
intent_node_id="intent-auth-001",
|
||||||
|
task_id="task-login-001",
|
||||||
|
)
|
||||||
|
print(f"✓ Approval recorded (event: {approval_event.id[:8]}...)")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Query state
|
||||||
|
print("📊 Query: Active actors")
|
||||||
|
active = qlm.get_active_actors()
|
||||||
|
print(f" Active actors: {len(active)}")
|
||||||
|
for actor in active:
|
||||||
|
print(f" - {actor.name} ({actor.role.value})")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Alignment
|
||||||
|
print("🎯 Calculating HI-AI alignment...")
|
||||||
|
alignment = qlm.get_alignment_score()
|
||||||
|
print(f" Alignment: {alignment:.1%}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
print("📝 Operator Summary:")
|
||||||
|
print("-" * 60)
|
||||||
|
summary = qlm.get_summary(days=1)
|
||||||
|
print(summary)
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
def demo_qi_emergence():
|
||||||
|
"""Demonstrate QI emergence detection"""
|
||||||
|
print("=" * 60)
|
||||||
|
print("QLM Demo: QI Emergence Detection")
|
||||||
|
print("=" * 60)
|
||||||
|
print()
|
||||||
|
|
||||||
|
qlm = QLMInterface()
|
||||||
|
qlm.register_agent("agent-001", "Agent1", ActorRole.EXECUTOR)
|
||||||
|
|
||||||
|
# Operator intent
|
||||||
|
qlm.record_operator_intent(
|
||||||
|
intent="Fix database connection bug",
|
||||||
|
intent_node_id="intent-bugfix-001",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Agent hits error
|
||||||
|
print("🤖 Agent encounters error...")
|
||||||
|
qlm.record_agent_error(
|
||||||
|
agent_id="agent-001",
|
||||||
|
task_id="task-bugfix-001",
|
||||||
|
error="Database connection refused",
|
||||||
|
intent_node_id="intent-bugfix-001",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Agent self-corrects (QI emergence!)
|
||||||
|
print("🤖 Agent self-corrects (trying alternative approach)...")
|
||||||
|
qlm.record_agent_execution(
|
||||||
|
agent_id="agent-001",
|
||||||
|
task_description="Fix database connection bug (retry with connection pool)",
|
||||||
|
task_id="task-bugfix-001",
|
||||||
|
intent_node_id="intent-bugfix-001",
|
||||||
|
)
|
||||||
|
|
||||||
|
qlm.record_agent_completion(
|
||||||
|
agent_id="agent-001",
|
||||||
|
task_id="task-bugfix-001",
|
||||||
|
success=True,
|
||||||
|
result={"approach": "connection_pool", "self_corrected": True},
|
||||||
|
intent_node_id="intent-bugfix-001",
|
||||||
|
)
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("✨ QI Emergence Detection:")
|
||||||
|
emergences = qlm.get_recent_emergences()
|
||||||
|
if emergences:
|
||||||
|
for em in emergences:
|
||||||
|
print(f" Pattern: {em.pattern_name}")
|
||||||
|
print(f" Explanation: {em.explanation}")
|
||||||
|
print(f" Confidence: {em.confidence:.0%}")
|
||||||
|
print(f" Impact: {em.impact_score:.1f}/1.0")
|
||||||
|
else:
|
||||||
|
print(" (No emergence detected - pattern matching may need tuning)")
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
def demo_operator_queries():
|
||||||
|
"""Demonstrate natural language queries"""
|
||||||
|
print("=" * 60)
|
||||||
|
print("QLM Demo: Operator Queries")
|
||||||
|
print("=" * 60)
|
||||||
|
print()
|
||||||
|
|
||||||
|
qlm = QLMInterface()
|
||||||
|
|
||||||
|
# Setup some activity
|
||||||
|
qlm.register_agent("agent-001", "Agent1", ActorRole.EXECUTOR)
|
||||||
|
qlm.record_operator_intent("Test operator queries")
|
||||||
|
qlm.record_agent_execution("agent-001", "Do something", task_id="task-001")
|
||||||
|
qlm.record_agent_completion("agent-001", "task-001", success=True)
|
||||||
|
qlm.record_operator_approval("Agent work", task_id="task-001")
|
||||||
|
|
||||||
|
# Ask questions
|
||||||
|
questions = [
|
||||||
|
"What did agents do today?",
|
||||||
|
"Are we aligned with my intent?",
|
||||||
|
"What's the status?",
|
||||||
|
]
|
||||||
|
|
||||||
|
for question in questions:
|
||||||
|
print(f"👤 Operator: '{question}'")
|
||||||
|
print("-" * 60)
|
||||||
|
answer = qlm.ask(question)
|
||||||
|
print(answer)
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
def demo_full_scenario():
|
||||||
|
"""Demonstrate a complete multi-agent workflow"""
|
||||||
|
print("=" * 60)
|
||||||
|
print("QLM Demo: Full Multi-Agent Scenario")
|
||||||
|
print("=" * 60)
|
||||||
|
print()
|
||||||
|
|
||||||
|
qlm = QLMInterface()
|
||||||
|
|
||||||
|
# Register agents
|
||||||
|
coder = qlm.register_agent("coder", "CodeWriter", ActorRole.CODER)
|
||||||
|
reviewer = qlm.register_agent("reviewer", "CodeReviewer", ActorRole.REVIEWER)
|
||||||
|
tester = qlm.register_agent("tester", "TestRunner", ActorRole.TESTER)
|
||||||
|
|
||||||
|
print(f"✓ Registered 3 agents: {coder.name}, {reviewer.name}, {tester.name}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Operator intent
|
||||||
|
print("👤 Operator: 'Ship payment integration'")
|
||||||
|
intent = qlm.record_operator_intent(
|
||||||
|
intent="Ship payment integration",
|
||||||
|
description="Integrate Stripe, add tests, deploy",
|
||||||
|
intent_node_id="intent-payment-001",
|
||||||
|
)
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Coder implements
|
||||||
|
print("🤖 CodeWriter: Implementing...")
|
||||||
|
qlm.record_agent_execution(
|
||||||
|
"coder", "Implement Stripe integration", "task-001", "intent-payment-001"
|
||||||
|
)
|
||||||
|
qlm.record_agent_completion("coder", "task-001", True)
|
||||||
|
print(" ✓ Completed")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Handoff to reviewer
|
||||||
|
print("🤖 CodeWriter → CodeReviewer")
|
||||||
|
qlm.record_agent_handoff(
|
||||||
|
"coder", "reviewer", "task-001", "Ready for review"
|
||||||
|
)
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Reviewer reviews
|
||||||
|
print("🤖 CodeReviewer: Reviewing code...")
|
||||||
|
qlm.record_agent_execution("reviewer", "Review Stripe code", "task-002")
|
||||||
|
qlm.record_agent_completion("reviewer", "task-002", True)
|
||||||
|
print(" ✓ Approved")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Handoff to tester
|
||||||
|
print("🤖 CodeReviewer → TestRunner")
|
||||||
|
qlm.record_agent_handoff("reviewer", "tester", "task-002", "Ready for testing")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Tester runs tests
|
||||||
|
print("🤖 TestRunner: Running tests...")
|
||||||
|
qlm.record_system_event(
|
||||||
|
EventType.SYSTEM_TEST,
|
||||||
|
"Payment integration tests",
|
||||||
|
task_id="task-003",
|
||||||
|
metadata={"passed": True, "test_count": 15},
|
||||||
|
)
|
||||||
|
print(" ✓ All tests passed")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Operator approves
|
||||||
|
print("👤 Operator: 'Ship it!'")
|
||||||
|
qlm.record_operator_approval(
|
||||||
|
"Payment integration", intent_node_id="intent-payment-001"
|
||||||
|
)
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Deploy
|
||||||
|
print("🚀 Deploying...")
|
||||||
|
qlm.record_system_event(
|
||||||
|
EventType.SYSTEM_DEPLOY,
|
||||||
|
"Deployed payment feature to production",
|
||||||
|
metadata={"environment": "production", "version": "v1.2.0"},
|
||||||
|
)
|
||||||
|
print(" ✓ Deployed")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Show results
|
||||||
|
print("=" * 60)
|
||||||
|
print("📊 Final State")
|
||||||
|
print("=" * 60)
|
||||||
|
print()
|
||||||
|
|
||||||
|
print("Metrics:")
|
||||||
|
print(f" Total events: {len(qlm.state.events)}")
|
||||||
|
print(f" HI events: {qlm.state.metrics.hi_events}")
|
||||||
|
print(f" AI events: {qlm.state.metrics.ai_events}")
|
||||||
|
print(f" System events: {qlm.state.metrics.system_events}")
|
||||||
|
print(f" Alignment: {qlm.get_alignment_score():.1%}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
print("Event Timeline:")
|
||||||
|
for event in qlm.state.events:
|
||||||
|
print(f" [{event.timestamp.strftime('%H:%M:%S')}] {event.event_type.value}: {event.data}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Run all demos"""
|
||||||
|
print("\n🌟 QLM (Quantum Language Model) Demo Suite 🌟\n")
|
||||||
|
|
||||||
|
try:
|
||||||
|
demo_basic_workflow()
|
||||||
|
input("Press Enter to continue to next demo...")
|
||||||
|
print("\n")
|
||||||
|
|
||||||
|
demo_qi_emergence()
|
||||||
|
input("Press Enter to continue to next demo...")
|
||||||
|
print("\n")
|
||||||
|
|
||||||
|
demo_operator_queries()
|
||||||
|
input("Press Enter to continue to next demo...")
|
||||||
|
print("\n")
|
||||||
|
|
||||||
|
demo_full_scenario()
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n\nDemo interrupted by user.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n\n❌ Demo error: {e}")
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
print("\n✨ Demo complete! ✨\n")
|
||||||
|
print("Next steps:")
|
||||||
|
print(" 1. Run experiments: python -m qlm_lab.experiments.run_all")
|
||||||
|
print(" 2. Read docs: docs/QLM.md")
|
||||||
|
print(" 3. Integrate with your system!")
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
10
qlm_lab/experiments/__init__.py
Normal file
10
qlm_lab/experiments/__init__.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
"""
|
||||||
|
QLM Experiments - Validate QLM's value through experiments
|
||||||
|
|
||||||
|
Each experiment tests a specific hypothesis about QLM.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from qlm_lab.experiments.alignment_detection import AlignmentDetectionExperiment
|
||||||
|
from qlm_lab.experiments.emergence_detection import EmergenceDetectionExperiment
|
||||||
|
|
||||||
|
__all__ = ["AlignmentDetectionExperiment", "EmergenceDetectionExperiment"]
|
||||||
198
qlm_lab/experiments/alignment_detection.py
Normal file
198
qlm_lab/experiments/alignment_detection.py
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
"""
|
||||||
|
Experiment: Alignment Detection
|
||||||
|
|
||||||
|
Hypothesis: QLM can accurately detect when AI agents drift from Operator intent.
|
||||||
|
|
||||||
|
Setup:
|
||||||
|
1. Record Operator intent
|
||||||
|
2. Simulate agent executions (some aligned, some not)
|
||||||
|
3. Record Operator feedback (approvals/vetoes)
|
||||||
|
4. Measure QLM's alignment score accuracy
|
||||||
|
|
||||||
|
Success Criteria:
|
||||||
|
- QLM alignment score correlates with actual alignment
|
||||||
|
- QLM detects misalignment before Operator veto
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
from typing import Dict, Any
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from qlm_lab.api import QLMInterface
|
||||||
|
from qlm_lab.models import ActorRole
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class AlignmentDetectionExperiment:
|
||||||
|
"""Experiment: Does QLM detect intent drift?"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.qlm = QLMInterface()
|
||||||
|
self.results = {
|
||||||
|
"experiment": "alignment_detection",
|
||||||
|
"hypothesis": "QLM can detect AI drift from HI intent",
|
||||||
|
"scenarios": [],
|
||||||
|
"metrics": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
def run_scenario(
|
||||||
|
self,
|
||||||
|
scenario_name: str,
|
||||||
|
intent: str,
|
||||||
|
agent_actions: list,
|
||||||
|
expected_alignment: float,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Run one alignment scenario.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
scenario_name: Name of scenario
|
||||||
|
intent: Operator intent
|
||||||
|
agent_actions: List of (agent_id, action, aligned: bool)
|
||||||
|
expected_alignment: Expected alignment score
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Scenario results
|
||||||
|
"""
|
||||||
|
print(f"\nScenario: {scenario_name}")
|
||||||
|
print(f"Intent: {intent}")
|
||||||
|
|
||||||
|
# Record intent
|
||||||
|
intent_id = f"intent-{scenario_name}"
|
||||||
|
self.qlm.record_operator_intent(intent, intent_node_id=intent_id)
|
||||||
|
|
||||||
|
# Execute agent actions
|
||||||
|
for i, (agent_id, action, is_aligned) in enumerate(agent_actions):
|
||||||
|
task_id = f"task-{scenario_name}-{i}"
|
||||||
|
|
||||||
|
# Agent executes
|
||||||
|
self.qlm.record_agent_execution(
|
||||||
|
agent_id, action, task_id, intent_node_id=intent_id
|
||||||
|
)
|
||||||
|
|
||||||
|
# Agent completes
|
||||||
|
self.qlm.record_agent_completion(agent_id, task_id, success=True)
|
||||||
|
|
||||||
|
# Operator feedback
|
||||||
|
if is_aligned:
|
||||||
|
self.qlm.record_operator_approval(
|
||||||
|
action, intent_node_id=intent_id, task_id=task_id
|
||||||
|
)
|
||||||
|
print(f" ✓ {action} (aligned)")
|
||||||
|
else:
|
||||||
|
self.qlm.record_operator_veto(
|
||||||
|
action,
|
||||||
|
"Doesn't match my intent",
|
||||||
|
intent_node_id=intent_id,
|
||||||
|
task_id=task_id,
|
||||||
|
)
|
||||||
|
print(f" ✗ {action} (not aligned)")
|
||||||
|
|
||||||
|
# Calculate alignment
|
||||||
|
measured_alignment = self.qlm.get_alignment_score()
|
||||||
|
print(f" Expected alignment: {expected_alignment:.1%}")
|
||||||
|
print(f" Measured alignment: {measured_alignment:.1%}")
|
||||||
|
|
||||||
|
# Calculate error
|
||||||
|
error = abs(measured_alignment - expected_alignment)
|
||||||
|
print(f" Error: {error:.1%}")
|
||||||
|
|
||||||
|
result = {
|
||||||
|
"scenario": scenario_name,
|
||||||
|
"intent": intent,
|
||||||
|
"expected_alignment": expected_alignment,
|
||||||
|
"measured_alignment": measured_alignment,
|
||||||
|
"error": error,
|
||||||
|
"success": error < 0.2, # Success if within 20%
|
||||||
|
}
|
||||||
|
|
||||||
|
self.results["scenarios"].append(result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def run(self) -> Dict[str, Any]:
|
||||||
|
"""Run all alignment scenarios"""
|
||||||
|
print("=" * 60)
|
||||||
|
print("Experiment: Alignment Detection")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
# Register agents
|
||||||
|
self.qlm.register_agent("agent-001", "Agent1", ActorRole.EXECUTOR)
|
||||||
|
self.qlm.register_agent("agent-002", "Agent2", ActorRole.EXECUTOR)
|
||||||
|
|
||||||
|
# Scenario 1: Perfect alignment
|
||||||
|
self.run_scenario(
|
||||||
|
"perfect_alignment",
|
||||||
|
intent="Build login page",
|
||||||
|
agent_actions=[
|
||||||
|
("agent-001", "Create login form HTML", True),
|
||||||
|
("agent-001", "Add CSS styling", True),
|
||||||
|
("agent-002", "Add authentication logic", True),
|
||||||
|
],
|
||||||
|
expected_alignment=1.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Scenario 2: Partial alignment
|
||||||
|
self.run_scenario(
|
||||||
|
"partial_alignment",
|
||||||
|
intent="Optimize database queries",
|
||||||
|
agent_actions=[
|
||||||
|
("agent-001", "Add database indexes", True),
|
||||||
|
("agent-001", "Refactor unrelated code", False), # Off-track
|
||||||
|
("agent-002", "Cache query results", True),
|
||||||
|
],
|
||||||
|
expected_alignment=0.67,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Scenario 3: No alignment
|
||||||
|
self.run_scenario(
|
||||||
|
"no_alignment",
|
||||||
|
intent="Fix security vulnerability",
|
||||||
|
agent_actions=[
|
||||||
|
("agent-001", "Add new feature", False),
|
||||||
|
("agent-001", "Refactor UI", False),
|
||||||
|
("agent-002", "Update documentation", False),
|
||||||
|
],
|
||||||
|
expected_alignment=0.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Calculate metrics
|
||||||
|
total_scenarios = len(self.results["scenarios"])
|
||||||
|
successful_scenarios = sum(
|
||||||
|
1 for s in self.results["scenarios"] if s["success"]
|
||||||
|
)
|
||||||
|
avg_error = sum(s["error"] for s in self.results["scenarios"]) / total_scenarios
|
||||||
|
|
||||||
|
self.results["metrics"] = {
|
||||||
|
"total_scenarios": total_scenarios,
|
||||||
|
"successful_scenarios": successful_scenarios,
|
||||||
|
"success_rate": successful_scenarios / total_scenarios,
|
||||||
|
"average_error": avg_error,
|
||||||
|
}
|
||||||
|
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("Experiment Results")
|
||||||
|
print("=" * 60)
|
||||||
|
print(f"Success Rate: {self.results['metrics']['success_rate']:.1%}")
|
||||||
|
print(f"Average Error: {self.results['metrics']['average_error']:.1%}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
if self.results["metrics"]["success_rate"] >= 0.8:
|
||||||
|
print("✅ PASS: QLM accurately detects alignment")
|
||||||
|
else:
|
||||||
|
print("❌ FAIL: QLM alignment detection needs improvement")
|
||||||
|
|
||||||
|
return self.results
|
||||||
|
|
||||||
|
def save_results(self, file_path: str) -> None:
|
||||||
|
"""Save experiment results to JSON"""
|
||||||
|
with open(file_path, "w") as f:
|
||||||
|
json.dump(self.results, f, indent=2)
|
||||||
|
print(f"\nResults saved to: {file_path}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
experiment = AlignmentDetectionExperiment()
|
||||||
|
results = experiment.run()
|
||||||
|
experiment.save_results("alignment_detection_results.json")
|
||||||
222
qlm_lab/experiments/emergence_detection.py
Normal file
222
qlm_lab/experiments/emergence_detection.py
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
"""
|
||||||
|
Experiment: QI Emergence Detection
|
||||||
|
|
||||||
|
Hypothesis: QLM can detect emergent behaviors (QI) when HI+AI interact in feedback loops.
|
||||||
|
|
||||||
|
Setup:
|
||||||
|
1. Simulate various agent behavior patterns
|
||||||
|
2. Check if QLM detects known QI patterns
|
||||||
|
3. Measure false positive/negative rates
|
||||||
|
|
||||||
|
Success Criteria:
|
||||||
|
- QLM detects at least 80% of true emergent patterns
|
||||||
|
- False positive rate < 20%
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
from typing import Dict, Any, List
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from qlm_lab.api import QLMInterface
|
||||||
|
from qlm_lab.models import ActorRole, EventType
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class EmergenceDetectionExperiment:
|
||||||
|
"""Experiment: Does QLM detect QI emergence?"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.qlm = QLMInterface()
|
||||||
|
self.results = {
|
||||||
|
"experiment": "emergence_detection",
|
||||||
|
"hypothesis": "QLM detects emergent HI+AI behaviors",
|
||||||
|
"patterns_tested": [],
|
||||||
|
"metrics": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
def simulate_pattern(
|
||||||
|
self, pattern_name: str, should_trigger: bool
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Simulate a behavior pattern and check if QLM detects emergence.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pattern_name: Name of pattern to simulate
|
||||||
|
should_trigger: Whether this should trigger emergence detection
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Pattern test results
|
||||||
|
"""
|
||||||
|
print(f"\nTesting pattern: {pattern_name}")
|
||||||
|
print(f" Should trigger: {should_trigger}")
|
||||||
|
|
||||||
|
initial_emergences = len(self.qlm.state.emergences)
|
||||||
|
|
||||||
|
# Simulate different patterns
|
||||||
|
if pattern_name == "agent_self_correction":
|
||||||
|
# Agent hits error, then self-corrects
|
||||||
|
self.qlm.record_agent_execution(
|
||||||
|
"agent-001", "Deploy feature", "task-001"
|
||||||
|
)
|
||||||
|
self.qlm.record_agent_error("agent-001", "task-001", "Deployment failed")
|
||||||
|
self.qlm.record_agent_execution(
|
||||||
|
"agent-001", "Deploy feature (retry)", "task-001"
|
||||||
|
)
|
||||||
|
self.qlm.record_agent_completion("agent-001", "task-001", success=True)
|
||||||
|
|
||||||
|
elif pattern_name == "operator_feedback_loop":
|
||||||
|
# HI intent → AI execution → HI approval → refined intent
|
||||||
|
self.qlm.record_operator_intent("Build dashboard", intent_node_id="intent-001")
|
||||||
|
self.qlm.record_agent_execution(
|
||||||
|
"agent-001", "Create dashboard", "task-001", "intent-001"
|
||||||
|
)
|
||||||
|
self.qlm.record_agent_completion("agent-001", "task-001", success=True)
|
||||||
|
self.qlm.record_operator_approval("Dashboard", intent_node_id="intent-001")
|
||||||
|
self.qlm.record_operator_intent(
|
||||||
|
"Add charts to dashboard", intent_node_id="intent-002"
|
||||||
|
)
|
||||||
|
|
||||||
|
elif pattern_name == "emergent_collaboration":
|
||||||
|
# Multiple agents self-organize
|
||||||
|
self.qlm.record_agent_execution(
|
||||||
|
"agent-001", "Start task", "task-001"
|
||||||
|
)
|
||||||
|
self.qlm.record_agent_handoff(
|
||||||
|
"agent-001", "agent-002", "task-001", "Need help"
|
||||||
|
)
|
||||||
|
self.qlm.record_agent_handoff(
|
||||||
|
"agent-002", "agent-003", "task-001", "Pass to specialist"
|
||||||
|
)
|
||||||
|
|
||||||
|
elif pattern_name == "normal_execution":
|
||||||
|
# Just normal execution, no emergence
|
||||||
|
self.qlm.record_agent_execution(
|
||||||
|
"agent-001", "Normal task", "task-normal"
|
||||||
|
)
|
||||||
|
self.qlm.record_agent_completion("agent-001", "task-normal", success=True)
|
||||||
|
|
||||||
|
# Check if emergence was detected
|
||||||
|
final_emergences = len(self.qlm.state.emergences)
|
||||||
|
detected = final_emergences > initial_emergences
|
||||||
|
|
||||||
|
print(f" Detected: {detected}")
|
||||||
|
|
||||||
|
# Determine correctness
|
||||||
|
correct = detected == should_trigger
|
||||||
|
|
||||||
|
if correct:
|
||||||
|
print(f" ✅ Correct")
|
||||||
|
else:
|
||||||
|
if detected and not should_trigger:
|
||||||
|
print(f" ❌ False Positive")
|
||||||
|
else:
|
||||||
|
print(f" ❌ False Negative")
|
||||||
|
|
||||||
|
result = {
|
||||||
|
"pattern": pattern_name,
|
||||||
|
"should_trigger": should_trigger,
|
||||||
|
"detected": detected,
|
||||||
|
"correct": correct,
|
||||||
|
"type": (
|
||||||
|
"true_positive"
|
||||||
|
if detected and should_trigger
|
||||||
|
else "true_negative"
|
||||||
|
if not detected and not should_trigger
|
||||||
|
else "false_positive"
|
||||||
|
if detected and not should_trigger
|
||||||
|
else "false_negative"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
self.results["patterns_tested"].append(result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def run(self) -> Dict[str, Any]:
|
||||||
|
"""Run all emergence detection tests"""
|
||||||
|
print("=" * 60)
|
||||||
|
print("Experiment: QI Emergence Detection")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
# Register agents
|
||||||
|
self.qlm.register_agent("agent-001", "Agent1", ActorRole.EXECUTOR)
|
||||||
|
self.qlm.register_agent("agent-002", "Agent2", ActorRole.EXECUTOR)
|
||||||
|
self.qlm.register_agent("agent-003", "Agent3", ActorRole.EXECUTOR)
|
||||||
|
|
||||||
|
# Test patterns that should trigger
|
||||||
|
self.simulate_pattern("agent_self_correction", should_trigger=True)
|
||||||
|
self.simulate_pattern("operator_feedback_loop", should_trigger=True)
|
||||||
|
# self.simulate_pattern("emergent_collaboration", should_trigger=True)
|
||||||
|
|
||||||
|
# Test patterns that should NOT trigger
|
||||||
|
self.simulate_pattern("normal_execution", should_trigger=False)
|
||||||
|
|
||||||
|
# Calculate metrics
|
||||||
|
total = len(self.results["patterns_tested"])
|
||||||
|
correct = sum(1 for p in self.results["patterns_tested"] if p["correct"])
|
||||||
|
true_positives = sum(
|
||||||
|
1 for p in self.results["patterns_tested"] if p["type"] == "true_positive"
|
||||||
|
)
|
||||||
|
false_positives = sum(
|
||||||
|
1 for p in self.results["patterns_tested"] if p["type"] == "false_positive"
|
||||||
|
)
|
||||||
|
true_negatives = sum(
|
||||||
|
1 for p in self.results["patterns_tested"] if p["type"] == "true_negative"
|
||||||
|
)
|
||||||
|
false_negatives = sum(
|
||||||
|
1 for p in self.results["patterns_tested"] if p["type"] == "false_negative"
|
||||||
|
)
|
||||||
|
|
||||||
|
accuracy = correct / total if total > 0 else 0
|
||||||
|
precision = (
|
||||||
|
true_positives / (true_positives + false_positives)
|
||||||
|
if (true_positives + false_positives) > 0
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
recall = (
|
||||||
|
true_positives / (true_positives + false_negatives)
|
||||||
|
if (true_positives + false_negatives) > 0
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
|
||||||
|
self.results["metrics"] = {
|
||||||
|
"total_patterns": total,
|
||||||
|
"correct": correct,
|
||||||
|
"accuracy": accuracy,
|
||||||
|
"true_positives": true_positives,
|
||||||
|
"false_positives": false_positives,
|
||||||
|
"true_negatives": true_negatives,
|
||||||
|
"false_negatives": false_negatives,
|
||||||
|
"precision": precision,
|
||||||
|
"recall": recall,
|
||||||
|
}
|
||||||
|
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("Experiment Results")
|
||||||
|
print("=" * 60)
|
||||||
|
print(f"Accuracy: {accuracy:.1%}")
|
||||||
|
print(f"Precision: {precision:.1%}")
|
||||||
|
print(f"Recall: {recall:.1%}")
|
||||||
|
print(f"False Positive Rate: {false_positives/total:.1%}")
|
||||||
|
print(f"False Negative Rate: {false_negatives/total:.1%}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
if accuracy >= 0.8 and false_positives / total < 0.2:
|
||||||
|
print("✅ PASS: QLM accurately detects QI emergence")
|
||||||
|
else:
|
||||||
|
print("❌ FAIL: QI emergence detection needs improvement")
|
||||||
|
|
||||||
|
return self.results
|
||||||
|
|
||||||
|
def save_results(self, file_path: str) -> None:
|
||||||
|
"""Save experiment results to JSON"""
|
||||||
|
with open(file_path, "w") as f:
|
||||||
|
json.dump(self.results, f, indent=2)
|
||||||
|
print(f"\nResults saved to: {file_path}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
experiment = EmergenceDetectionExperiment()
|
||||||
|
results = experiment.run()
|
||||||
|
experiment.save_results("emergence_detection_results.json")
|
||||||
17
qlm_lab/ingestion/__init__.py
Normal file
17
qlm_lab/ingestion/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
"""
|
||||||
|
QLM Ingestion - Wire QLM to Reality
|
||||||
|
|
||||||
|
This module contains connectors that ingest real system data into QLM:
|
||||||
|
- Git commits → QLMEvents
|
||||||
|
- CI test results → System events
|
||||||
|
- Agent logs → Agent execution events
|
||||||
|
- Deployment events → System events
|
||||||
|
|
||||||
|
Each connector transforms external data into QLMEvents.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from qlm_lab.ingestion.git import GitConnector
|
||||||
|
from qlm_lab.ingestion.ci import CIConnector
|
||||||
|
from qlm_lab.ingestion.agent_logs import AgentLogConnector
|
||||||
|
|
||||||
|
__all__ = ["GitConnector", "CIConnector", "AgentLogConnector"]
|
||||||
209
qlm_lab/ingestion/agent_logs.py
Normal file
209
qlm_lab/ingestion/agent_logs.py
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
"""
|
||||||
|
Agent Log Connector - Ingest agent execution logs into QLM
|
||||||
|
|
||||||
|
Parses agent logs and converts them into QLM events.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import List, Optional, Dict, Any
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from qlm_lab.models import EventType, ActorRole
|
||||||
|
from qlm_lab.api import QLMInterface
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class AgentLogConnector:
|
||||||
|
"""
|
||||||
|
Connects QLM to agent execution logs.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
connector = AgentLogConnector(qlm=qlm_interface)
|
||||||
|
connector.ingest_log_file("/path/to/agent.log")
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, qlm: QLMInterface):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
qlm: QLMInterface instance
|
||||||
|
"""
|
||||||
|
self.qlm = qlm
|
||||||
|
|
||||||
|
def parse_log_line(self, line: str) -> Optional[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
Parse a log line into structured data.
|
||||||
|
|
||||||
|
Expects format: [timestamp] [agent_id] [level] message
|
||||||
|
|
||||||
|
Args:
|
||||||
|
line: Log line string
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Parsed log data or None
|
||||||
|
"""
|
||||||
|
# Example: [2024-01-15 10:30:45] [agent-coder-001] [INFO] Task started: implement login
|
||||||
|
pattern = r"\[([^\]]+)\]\s*\[([^\]]+)\]\s*\[([^\]]+)\]\s*(.+)"
|
||||||
|
match = re.match(pattern, line)
|
||||||
|
|
||||||
|
if not match:
|
||||||
|
return None
|
||||||
|
|
||||||
|
timestamp_str, agent_id, level, message = match.groups()
|
||||||
|
|
||||||
|
try:
|
||||||
|
timestamp = datetime.fromisoformat(timestamp_str)
|
||||||
|
except ValueError:
|
||||||
|
timestamp = datetime.now()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"timestamp": timestamp,
|
||||||
|
"agent_id": agent_id.strip(),
|
||||||
|
"level": level.strip(),
|
||||||
|
"message": message.strip(),
|
||||||
|
}
|
||||||
|
|
||||||
|
def ingest_log_line(self, log_data: Dict[str, Any]) -> Optional[Any]:
|
||||||
|
"""
|
||||||
|
Ingest a parsed log line into QLM.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
log_data: Parsed log data from parse_log_line()
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Created QLMEvent or None
|
||||||
|
"""
|
||||||
|
agent_id = log_data["agent_id"]
|
||||||
|
message = log_data["message"]
|
||||||
|
|
||||||
|
# Register agent if not exists
|
||||||
|
# (In production, would check if already registered)
|
||||||
|
|
||||||
|
# Detect event type from message
|
||||||
|
message_lower = message.lower()
|
||||||
|
|
||||||
|
if "task started" in message_lower or "executing" in message_lower:
|
||||||
|
# Extract task description
|
||||||
|
task_desc = message.split(":", 1)[1].strip() if ":" in message else message
|
||||||
|
|
||||||
|
return self.qlm.record_agent_execution(
|
||||||
|
agent_id=agent_id,
|
||||||
|
task_description=task_desc,
|
||||||
|
metadata={"log_timestamp": log_data["timestamp"].isoformat()},
|
||||||
|
)
|
||||||
|
|
||||||
|
elif "task completed" in message_lower or "finished" in message_lower:
|
||||||
|
# Extract task ID if present
|
||||||
|
task_id = self._extract_task_id(message)
|
||||||
|
|
||||||
|
return self.qlm.record_agent_completion(
|
||||||
|
agent_id=agent_id,
|
||||||
|
task_id=task_id or "unknown",
|
||||||
|
success=True,
|
||||||
|
result={"message": message},
|
||||||
|
)
|
||||||
|
|
||||||
|
elif "error" in message_lower or "failed" in message_lower:
|
||||||
|
task_id = self._extract_task_id(message)
|
||||||
|
|
||||||
|
return self.qlm.record_agent_error(
|
||||||
|
agent_id=agent_id,
|
||||||
|
task_id=task_id or "unknown",
|
||||||
|
error=message,
|
||||||
|
)
|
||||||
|
|
||||||
|
elif "handoff" in message_lower or "passing to" in message_lower:
|
||||||
|
# Extract target agent
|
||||||
|
to_agent = self._extract_target_agent(message)
|
||||||
|
task_id = self._extract_task_id(message)
|
||||||
|
|
||||||
|
if to_agent:
|
||||||
|
return self.qlm.record_agent_handoff(
|
||||||
|
from_agent_id=agent_id,
|
||||||
|
to_agent_id=to_agent,
|
||||||
|
task_id=task_id or "unknown",
|
||||||
|
handoff_message=message,
|
||||||
|
)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _extract_task_id(self, message: str) -> Optional[str]:
|
||||||
|
"""Extract task ID from message if present"""
|
||||||
|
# Look for task-XXX or task_XXX pattern
|
||||||
|
match = re.search(r"task[_-](\w+)", message.lower())
|
||||||
|
if match:
|
||||||
|
return f"task-{match.group(1)}"
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _extract_target_agent(self, message: str) -> Optional[str]:
|
||||||
|
"""Extract target agent ID from handoff message"""
|
||||||
|
# Look for "to agent-XXX" or "→ agent-XXX"
|
||||||
|
match = re.search(r"(?:to|→)\s+(agent-[\w-]+)", message.lower())
|
||||||
|
if match:
|
||||||
|
return match.group(1)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def ingest_log_file(self, file_path: str) -> List[Any]:
|
||||||
|
"""
|
||||||
|
Ingest an entire log file.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
file_path: Path to log file
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of created QLMEvents
|
||||||
|
"""
|
||||||
|
events = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(file_path, "r") as f:
|
||||||
|
for line in f:
|
||||||
|
line = line.strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
|
||||||
|
log_data = self.parse_log_line(line)
|
||||||
|
if log_data:
|
||||||
|
event = self.ingest_log_line(log_data)
|
||||||
|
if event:
|
||||||
|
events.append(event)
|
||||||
|
|
||||||
|
logger.info(f"Ingested {len(events)} events from {file_path}")
|
||||||
|
return events
|
||||||
|
|
||||||
|
except FileNotFoundError:
|
||||||
|
logger.error(f"Log file not found: {file_path}")
|
||||||
|
return []
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error ingesting log file: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
def ingest_structured_log(self, log_entries: List[Dict[str, Any]]) -> List[Any]:
|
||||||
|
"""
|
||||||
|
Ingest structured log entries (e.g., from JSON logs).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
log_entries: List of log entry dictionaries
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of created QLMEvents
|
||||||
|
"""
|
||||||
|
events = []
|
||||||
|
|
||||||
|
for entry in log_entries:
|
||||||
|
# Convert to standard format
|
||||||
|
log_data = {
|
||||||
|
"timestamp": datetime.fromisoformat(entry.get("timestamp", datetime.now().isoformat())),
|
||||||
|
"agent_id": entry.get("agent_id", "unknown"),
|
||||||
|
"level": entry.get("level", "INFO"),
|
||||||
|
"message": entry.get("message", ""),
|
||||||
|
}
|
||||||
|
|
||||||
|
event = self.ingest_log_line(log_data)
|
||||||
|
if event:
|
||||||
|
events.append(event)
|
||||||
|
|
||||||
|
logger.info(f"Ingested {len(events)} structured log entries")
|
||||||
|
return events
|
||||||
224
qlm_lab/ingestion/ci.py
Normal file
224
qlm_lab/ingestion/ci.py
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
"""
|
||||||
|
CI Connector - Ingest CI/test results into QLM
|
||||||
|
|
||||||
|
Converts CI events into QLM system events:
|
||||||
|
- Test runs → SYSTEM_TEST events
|
||||||
|
- Build results → SYSTEM_BUILD events
|
||||||
|
- Deploy actions → SYSTEM_DEPLOY events
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import List, Optional, Dict, Any
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from qlm_lab.models import EventType
|
||||||
|
from qlm_lab.api import QLMInterface
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class CIConnector:
|
||||||
|
"""
|
||||||
|
Connects QLM to CI/CD system (GitHub Actions, Jenkins, etc.)
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
connector = CIConnector(qlm=qlm_interface)
|
||||||
|
connector.ingest_test_result(test_data)
|
||||||
|
connector.ingest_build_result(build_data)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, qlm: QLMInterface):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
qlm: QLMInterface instance
|
||||||
|
"""
|
||||||
|
self.qlm = qlm
|
||||||
|
|
||||||
|
def ingest_test_result(
|
||||||
|
self,
|
||||||
|
test_name: str,
|
||||||
|
passed: bool,
|
||||||
|
duration_seconds: float,
|
||||||
|
failures: Optional[List[str]] = None,
|
||||||
|
commit_hash: Optional[str] = None,
|
||||||
|
task_id: Optional[str] = None,
|
||||||
|
) -> Any:
|
||||||
|
"""
|
||||||
|
Ingest a test run result.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
test_name: Name of test suite
|
||||||
|
passed: Whether tests passed
|
||||||
|
duration_seconds: How long tests took
|
||||||
|
failures: List of failed test names
|
||||||
|
commit_hash: Related commit
|
||||||
|
task_id: Related task
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Created QLMEvent
|
||||||
|
"""
|
||||||
|
event_type = EventType.SYSTEM_TEST
|
||||||
|
|
||||||
|
description = f"Test '{test_name}': {'PASSED' if passed else 'FAILED'}"
|
||||||
|
|
||||||
|
metadata = {
|
||||||
|
"test_name": test_name,
|
||||||
|
"passed": passed,
|
||||||
|
"duration_seconds": duration_seconds,
|
||||||
|
"failures": failures or [],
|
||||||
|
"commit_hash": commit_hash,
|
||||||
|
}
|
||||||
|
|
||||||
|
event = self.qlm.record_system_event(
|
||||||
|
event_type=event_type,
|
||||||
|
description=description,
|
||||||
|
task_id=task_id,
|
||||||
|
metadata=metadata,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"Ingested test result: {test_name} - {'PASS' if passed else 'FAIL'}")
|
||||||
|
return event
|
||||||
|
|
||||||
|
def ingest_build_result(
|
||||||
|
self,
|
||||||
|
build_name: str,
|
||||||
|
success: bool,
|
||||||
|
duration_seconds: float,
|
||||||
|
artifacts: Optional[List[str]] = None,
|
||||||
|
commit_hash: Optional[str] = None,
|
||||||
|
task_id: Optional[str] = None,
|
||||||
|
) -> Any:
|
||||||
|
"""
|
||||||
|
Ingest a build result.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
build_name: Name of build
|
||||||
|
success: Whether build succeeded
|
||||||
|
duration_seconds: Build duration
|
||||||
|
artifacts: List of produced artifacts
|
||||||
|
commit_hash: Related commit
|
||||||
|
task_id: Related task
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Created QLMEvent
|
||||||
|
"""
|
||||||
|
event_type = EventType.SYSTEM_BUILD
|
||||||
|
|
||||||
|
description = f"Build '{build_name}': {'SUCCESS' if success else 'FAILED'}"
|
||||||
|
|
||||||
|
metadata = {
|
||||||
|
"build_name": build_name,
|
||||||
|
"success": success,
|
||||||
|
"duration_seconds": duration_seconds,
|
||||||
|
"artifacts": artifacts or [],
|
||||||
|
"commit_hash": commit_hash,
|
||||||
|
}
|
||||||
|
|
||||||
|
event = self.qlm.record_system_event(
|
||||||
|
event_type=event_type,
|
||||||
|
description=description,
|
||||||
|
task_id=task_id,
|
||||||
|
metadata=metadata,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"Ingested build result: {build_name} - {'SUCCESS' if success else 'FAIL'}")
|
||||||
|
return event
|
||||||
|
|
||||||
|
def ingest_deploy_result(
|
||||||
|
self,
|
||||||
|
service_name: str,
|
||||||
|
environment: str,
|
||||||
|
success: bool,
|
||||||
|
version: Optional[str] = None,
|
||||||
|
commit_hash: Optional[str] = None,
|
||||||
|
task_id: Optional[str] = None,
|
||||||
|
) -> Any:
|
||||||
|
"""
|
||||||
|
Ingest a deployment result.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
service_name: What was deployed
|
||||||
|
environment: Where (production, staging, etc.)
|
||||||
|
success: Whether deploy succeeded
|
||||||
|
version: Version deployed
|
||||||
|
commit_hash: Related commit
|
||||||
|
task_id: Related task
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Created QLMEvent
|
||||||
|
"""
|
||||||
|
event_type = EventType.SYSTEM_DEPLOY
|
||||||
|
|
||||||
|
description = f"Deploy '{service_name}' to {environment}: {'SUCCESS' if success else 'FAILED'}"
|
||||||
|
|
||||||
|
metadata = {
|
||||||
|
"service": service_name,
|
||||||
|
"environment": environment,
|
||||||
|
"success": success,
|
||||||
|
"version": version,
|
||||||
|
"commit_hash": commit_hash,
|
||||||
|
}
|
||||||
|
|
||||||
|
event = self.qlm.record_system_event(
|
||||||
|
event_type=event_type,
|
||||||
|
description=description,
|
||||||
|
task_id=task_id,
|
||||||
|
metadata=metadata,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"Ingested deploy: {service_name} to {environment} - {'SUCCESS' if success else 'FAIL'}")
|
||||||
|
return event
|
||||||
|
|
||||||
|
def ingest_from_github_actions(self, workflow_run: Dict[str, Any]) -> List[Any]:
|
||||||
|
"""
|
||||||
|
Ingest events from a GitHub Actions workflow run.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
workflow_run: GitHub Actions workflow run data (JSON)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of created QLMEvents
|
||||||
|
"""
|
||||||
|
events = []
|
||||||
|
|
||||||
|
# Extract data from workflow
|
||||||
|
name = workflow_run.get("name", "Unknown workflow")
|
||||||
|
conclusion = workflow_run.get("conclusion", "unknown")
|
||||||
|
success = conclusion == "success"
|
||||||
|
|
||||||
|
# Get commit
|
||||||
|
head_commit = workflow_run.get("head_commit", {})
|
||||||
|
commit_hash = head_commit.get("id", None)
|
||||||
|
|
||||||
|
# Create test event (assuming workflow is tests)
|
||||||
|
if "test" in name.lower():
|
||||||
|
event = self.ingest_test_result(
|
||||||
|
test_name=name,
|
||||||
|
passed=success,
|
||||||
|
duration_seconds=0, # Would need to calculate from timestamps
|
||||||
|
commit_hash=commit_hash,
|
||||||
|
)
|
||||||
|
events.append(event)
|
||||||
|
|
||||||
|
# Create build event (assuming workflow builds)
|
||||||
|
elif "build" in name.lower():
|
||||||
|
event = self.ingest_build_result(
|
||||||
|
build_name=name,
|
||||||
|
success=success,
|
||||||
|
duration_seconds=0,
|
||||||
|
commit_hash=commit_hash,
|
||||||
|
)
|
||||||
|
events.append(event)
|
||||||
|
|
||||||
|
# Create deploy event
|
||||||
|
elif "deploy" in name.lower():
|
||||||
|
event = self.ingest_deploy_result(
|
||||||
|
service_name=name,
|
||||||
|
environment="production", # Would need to parse from workflow
|
||||||
|
success=success,
|
||||||
|
commit_hash=commit_hash,
|
||||||
|
)
|
||||||
|
events.append(event)
|
||||||
|
|
||||||
|
return events
|
||||||
240
qlm_lab/ingestion/git.py
Normal file
240
qlm_lab/ingestion/git.py
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
"""
|
||||||
|
Git Connector - Ingest git history into QLM
|
||||||
|
|
||||||
|
Converts git commits into QLM events:
|
||||||
|
- Commits by humans → OPERATOR_INTENT or HI events
|
||||||
|
- Commits by bots/agents → AGENT_EXECUTION events
|
||||||
|
- Merge commits → coordination events
|
||||||
|
"""
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import re
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import List, Optional, Dict, Any
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from qlm_lab.models import (
|
||||||
|
QLMEvent,
|
||||||
|
EventType,
|
||||||
|
IntelligenceType,
|
||||||
|
ActorType,
|
||||||
|
ActorRole,
|
||||||
|
Actor,
|
||||||
|
)
|
||||||
|
from qlm_lab.api import QLMInterface
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class GitConnector:
|
||||||
|
"""
|
||||||
|
Connects QLM to git repository history.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
connector = GitConnector(repo_path="/path/to/repo", qlm=qlm_interface)
|
||||||
|
events = connector.ingest_recent_commits(days=7)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, repo_path: str, qlm: QLMInterface):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
repo_path: Path to git repository
|
||||||
|
qlm: QLMInterface instance
|
||||||
|
"""
|
||||||
|
self.repo_path = repo_path
|
||||||
|
self.qlm = qlm
|
||||||
|
|
||||||
|
# Patterns to detect agent commits
|
||||||
|
self.agent_patterns = [
|
||||||
|
r"^claude/", # Claude branches
|
||||||
|
r"^copilot/", # Copilot branches
|
||||||
|
r"^codex/", # Codex branches
|
||||||
|
r"\[bot\]", # Bot commit messages
|
||||||
|
r"\[agent\]", # Agent commit messages
|
||||||
|
]
|
||||||
|
|
||||||
|
def is_agent_commit(self, commit_data: Dict[str, str]) -> bool:
|
||||||
|
"""Determine if a commit was made by an agent"""
|
||||||
|
# Check author name/email
|
||||||
|
author = commit_data.get("author", "").lower()
|
||||||
|
if any(
|
||||||
|
pattern in author
|
||||||
|
for pattern in ["bot", "agent", "claude", "copilot", "codex"]
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Check branch name
|
||||||
|
branch = commit_data.get("branch", "")
|
||||||
|
for pattern in self.agent_patterns:
|
||||||
|
if re.search(pattern, branch):
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Check commit message
|
||||||
|
message = commit_data.get("message", "")
|
||||||
|
if "[agent]" in message.lower() or "[bot]" in message.lower():
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_git_log(
|
||||||
|
self, since: Optional[str] = None, until: Optional[str] = None
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
Get git log as structured data.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
since: Start date (e.g., "7 days ago")
|
||||||
|
until: End date (e.g., "now")
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of commit dictionaries
|
||||||
|
"""
|
||||||
|
cmd = [
|
||||||
|
"git",
|
||||||
|
"-C",
|
||||||
|
self.repo_path,
|
||||||
|
"log",
|
||||||
|
"--pretty=format:%H|%an|%ae|%at|%s|%b",
|
||||||
|
"--all",
|
||||||
|
]
|
||||||
|
|
||||||
|
if since:
|
||||||
|
cmd.append(f"--since={since}")
|
||||||
|
if until:
|
||||||
|
cmd.append(f"--until={until}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
cmd, capture_output=True, text=True, check=True, timeout=30
|
||||||
|
)
|
||||||
|
|
||||||
|
commits = []
|
||||||
|
for line in result.stdout.strip().split("\n"):
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
|
||||||
|
parts = line.split("|", 5)
|
||||||
|
if len(parts) < 5:
|
||||||
|
continue
|
||||||
|
|
||||||
|
commit_hash, author_name, author_email, timestamp, subject = parts[:5]
|
||||||
|
body = parts[5] if len(parts) > 5 else ""
|
||||||
|
|
||||||
|
commits.append(
|
||||||
|
{
|
||||||
|
"hash": commit_hash,
|
||||||
|
"author": author_name,
|
||||||
|
"email": author_email,
|
||||||
|
"timestamp": int(timestamp),
|
||||||
|
"subject": subject,
|
||||||
|
"body": body,
|
||||||
|
"message": f"{subject}\n{body}".strip(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return commits
|
||||||
|
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
logger.error(f"Git log failed: {e}")
|
||||||
|
return []
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
logger.error("Git log timed out")
|
||||||
|
return []
|
||||||
|
|
||||||
|
def ingest_commit(self, commit: Dict[str, Any]) -> Optional[QLMEvent]:
|
||||||
|
"""
|
||||||
|
Ingest a single commit into QLM.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
commit: Commit data from get_git_log()
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Created QLMEvent or None
|
||||||
|
"""
|
||||||
|
is_agent = self.is_agent_commit(commit)
|
||||||
|
|
||||||
|
# Determine actor
|
||||||
|
author = commit["author"]
|
||||||
|
actor_id = (
|
||||||
|
f"agent-{author.lower().replace(' ', '-')}"
|
||||||
|
if is_agent
|
||||||
|
else f"human-{author.lower().replace(' ', '-')}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Register actor if not exists
|
||||||
|
actor_type = ActorType.AGENT if is_agent else ActorType.HUMAN
|
||||||
|
|
||||||
|
# Create event
|
||||||
|
if is_agent:
|
||||||
|
# Agent commit = AGENT_EXECUTION
|
||||||
|
event = self.qlm.record_agent_execution(
|
||||||
|
agent_id=actor_id,
|
||||||
|
task_description=commit["subject"],
|
||||||
|
metadata={
|
||||||
|
"commit_hash": commit["hash"],
|
||||||
|
"commit_message": commit["message"],
|
||||||
|
"timestamp": commit["timestamp"],
|
||||||
|
"author": author,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Human commit = OPERATOR_INTENT (assuming commits reflect intent)
|
||||||
|
event = self.qlm.record_operator_intent(
|
||||||
|
intent=commit["subject"],
|
||||||
|
description=commit["body"],
|
||||||
|
metadata={
|
||||||
|
"commit_hash": commit["hash"],
|
||||||
|
"timestamp": commit["timestamp"],
|
||||||
|
"author": author,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"Ingested commit: {commit['hash'][:8]} - {commit['subject']}")
|
||||||
|
return event
|
||||||
|
|
||||||
|
def ingest_recent_commits(self, days: int = 7) -> List[QLMEvent]:
|
||||||
|
"""
|
||||||
|
Ingest recent commits into QLM.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
days: Number of days to look back
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of created QLMEvents
|
||||||
|
"""
|
||||||
|
logger.info(f"Ingesting git commits from last {days} days...")
|
||||||
|
|
||||||
|
commits = self.get_git_log(since=f"{days} days ago")
|
||||||
|
events = []
|
||||||
|
|
||||||
|
for commit in commits:
|
||||||
|
event = self.ingest_commit(commit)
|
||||||
|
if event:
|
||||||
|
events.append(event)
|
||||||
|
|
||||||
|
logger.info(f"Ingested {len(events)} commits")
|
||||||
|
return events
|
||||||
|
|
||||||
|
def ingest_commit_range(
|
||||||
|
self, since: str, until: Optional[str] = None
|
||||||
|
) -> List[QLMEvent]:
|
||||||
|
"""
|
||||||
|
Ingest commits in a specific range.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
since: Start date (e.g., "2024-01-01")
|
||||||
|
until: End date (default: now)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of created QLMEvents
|
||||||
|
"""
|
||||||
|
commits = self.get_git_log(since=since, until=until)
|
||||||
|
events = []
|
||||||
|
|
||||||
|
for commit in commits:
|
||||||
|
event = self.ingest_commit(commit)
|
||||||
|
if event:
|
||||||
|
events.append(event)
|
||||||
|
|
||||||
|
logger.info(f"Ingested {len(events)} commits from {since} to {until or 'now'}")
|
||||||
|
return events
|
||||||
370
qlm_lab/models.py
Normal file
370
qlm_lab/models.py
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
"""
|
||||||
|
QLM Core Models - Data structures for Quantum Language Model
|
||||||
|
|
||||||
|
These models formalize the HI/AI/QI intelligence framework:
|
||||||
|
|
||||||
|
- HI (Human Intelligence): Operator actions, intent, judgment, taste, ethics
|
||||||
|
- AI (Model Intelligence): LLMs, agents, code generation, pattern completion
|
||||||
|
- QI (Quantum Intelligence): Emergent system behaviors when HI+AI interact in loops
|
||||||
|
|
||||||
|
The "quantum" metaphor means:
|
||||||
|
- Superposition of roles (an agent can be executor AND coordinator)
|
||||||
|
- Superposition of states (a task can be in_progress AND blocked)
|
||||||
|
- Superposition of perspectives (same event viewed differently by HI vs AI)
|
||||||
|
|
||||||
|
QI emerges when:
|
||||||
|
1. AI designs deterministic systems
|
||||||
|
2. Deterministic systems constrain AI behavior
|
||||||
|
3. Humans orchestrate and interpret the cycle
|
||||||
|
4. Novel, unpredicted behaviors appear
|
||||||
|
|
||||||
|
This is NOT quantum physics. It's a meta-model for describing
|
||||||
|
intelligence that emerges from feedback loops between humans, AI, and code.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from datetime import datetime
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Dict, List, Optional, Set, Any
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
|
|
||||||
|
class IntelligenceType(Enum):
|
||||||
|
"""
|
||||||
|
The three layers of intelligence in the QLM model.
|
||||||
|
|
||||||
|
HI = Human Intelligence (Operator)
|
||||||
|
AI = Model Intelligence (LLMs, agents)
|
||||||
|
QI = Quantum Intelligence (emergent system behaviors)
|
||||||
|
"""
|
||||||
|
HI = "human_intelligence" # Operator layer: intent, judgment, taste
|
||||||
|
AI = "model_intelligence" # Agent layer: execution, completion, transformation
|
||||||
|
QI = "quantum_intelligence" # System layer: emergence, feedback loops, novelty
|
||||||
|
|
||||||
|
|
||||||
|
class ActorType(Enum):
|
||||||
|
"""Types of actors in the system"""
|
||||||
|
HUMAN = "human" # The Operator (Alexa) or other humans
|
||||||
|
AGENT = "agent" # AI agents (LLM-powered)
|
||||||
|
SYSTEM = "system" # Deterministic systems (git, CI, infrastructure)
|
||||||
|
|
||||||
|
|
||||||
|
class ActorRole(Enum):
|
||||||
|
"""Roles actors can play"""
|
||||||
|
OPERATOR = "operator" # Human orchestrator (primary decision maker)
|
||||||
|
EXECUTOR = "executor" # Performs tasks
|
||||||
|
COORDINATOR = "coordinator" # Manages other actors
|
||||||
|
REVIEWER = "reviewer" # Reviews work
|
||||||
|
MONITOR = "monitor" # Observes and reports
|
||||||
|
GOVERNOR = "governor" # Enforces policies
|
||||||
|
|
||||||
|
|
||||||
|
class ActorState(Enum):
|
||||||
|
"""Current state of an actor"""
|
||||||
|
ACTIVE = "active" # Currently working
|
||||||
|
IDLE = "idle" # Available but not working
|
||||||
|
BLOCKED = "blocked" # Wants to work but can't
|
||||||
|
OFFLINE = "offline" # Not available
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Actor:
|
||||||
|
"""
|
||||||
|
An actor in the QLM system.
|
||||||
|
|
||||||
|
Actors perform actions that generate QLMEvents.
|
||||||
|
Actors can be humans (Operator), AI agents, or deterministic systems.
|
||||||
|
"""
|
||||||
|
id: str = field(default_factory=lambda: str(uuid4()))
|
||||||
|
name: str = ""
|
||||||
|
actor_type: ActorType = ActorType.AGENT
|
||||||
|
role: ActorRole = ActorRole.EXECUTOR
|
||||||
|
state: ActorState = ActorState.IDLE
|
||||||
|
|
||||||
|
# What this actor can do
|
||||||
|
capabilities: Set[str] = field(default_factory=set)
|
||||||
|
|
||||||
|
# Current activity
|
||||||
|
current_task_id: Optional[str] = None
|
||||||
|
|
||||||
|
# Metadata
|
||||||
|
created_at: datetime = field(default_factory=datetime.now)
|
||||||
|
last_active: datetime = field(default_factory=datetime.now)
|
||||||
|
metadata: Dict[str, Any] = field(default_factory=dict)
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
return {
|
||||||
|
"id": self.id,
|
||||||
|
"name": self.name,
|
||||||
|
"actor_type": self.actor_type.value,
|
||||||
|
"role": self.role.value,
|
||||||
|
"state": self.state.value,
|
||||||
|
"capabilities": list(self.capabilities),
|
||||||
|
"current_task_id": self.current_task_id,
|
||||||
|
"created_at": self.created_at.isoformat(),
|
||||||
|
"last_active": self.last_active.isoformat(),
|
||||||
|
"metadata": self.metadata,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class IntelligenceLayer:
|
||||||
|
"""
|
||||||
|
One of the three intelligence layers (HI, AI, or QI).
|
||||||
|
|
||||||
|
Each layer has:
|
||||||
|
- Actors who perform actions
|
||||||
|
- Capabilities they can execute
|
||||||
|
- Metrics about their activity
|
||||||
|
"""
|
||||||
|
type: IntelligenceType
|
||||||
|
actors: Dict[str, Actor] = field(default_factory=dict)
|
||||||
|
|
||||||
|
# Layer-level capabilities
|
||||||
|
capabilities: Set[str] = field(default_factory=set)
|
||||||
|
|
||||||
|
# Metrics
|
||||||
|
total_events: int = 0
|
||||||
|
active_actors: int = 0
|
||||||
|
|
||||||
|
def add_actor(self, actor: Actor) -> None:
|
||||||
|
"""Add an actor to this intelligence layer"""
|
||||||
|
self.actors[actor.id] = actor
|
||||||
|
self.capabilities.update(actor.capabilities)
|
||||||
|
if actor.state == ActorState.ACTIVE:
|
||||||
|
self.active_actors += 1
|
||||||
|
|
||||||
|
def get_active_actors(self) -> List[Actor]:
|
||||||
|
"""Get all currently active actors"""
|
||||||
|
return [a for a in self.actors.values() if a.state == ActorState.ACTIVE]
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
return {
|
||||||
|
"type": self.type.value,
|
||||||
|
"actors": {aid: a.to_dict() for aid, a in self.actors.items()},
|
||||||
|
"capabilities": list(self.capabilities),
|
||||||
|
"total_events": self.total_events,
|
||||||
|
"active_actors": self.active_actors,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class EventType(Enum):
|
||||||
|
"""
|
||||||
|
Types of events in the QLM system.
|
||||||
|
|
||||||
|
Events flow through the system and trigger state transitions.
|
||||||
|
"""
|
||||||
|
# HI (Operator) events
|
||||||
|
OPERATOR_INTENT = "operator_intent" # Operator defines a goal
|
||||||
|
OPERATOR_APPROVAL = "operator_approval" # Operator approves something
|
||||||
|
OPERATOR_VETO = "operator_veto" # Operator rejects something
|
||||||
|
OPERATOR_QUERY = "operator_query" # Operator asks a question
|
||||||
|
|
||||||
|
# AI (Agent) events
|
||||||
|
AGENT_EXECUTION = "agent_execution" # Agent performs a task
|
||||||
|
AGENT_COMPLETION = "agent_completion" # Agent finishes a task
|
||||||
|
AGENT_ERROR = "agent_error" # Agent encounters an error
|
||||||
|
AGENT_HANDOFF = "agent_handoff" # Agent hands off to another agent
|
||||||
|
|
||||||
|
# System events
|
||||||
|
SYSTEM_DEPLOY = "system_deploy" # Code deployed
|
||||||
|
SYSTEM_TEST = "system_test" # Tests run
|
||||||
|
SYSTEM_BUILD = "system_build" # Build completed
|
||||||
|
SYSTEM_ERROR = "system_error" # System error occurred
|
||||||
|
|
||||||
|
# QI (Emergent) events
|
||||||
|
QI_EMERGENCE = "qi_emergence" # Novel behavior detected
|
||||||
|
QI_FEEDBACK_LOOP = "qi_feedback_loop" # HI+AI feedback detected
|
||||||
|
QI_PATTERN = "qi_pattern" # Recurring pattern identified
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class QLMEvent:
|
||||||
|
"""
|
||||||
|
An event in the QLM system.
|
||||||
|
|
||||||
|
Events are the fundamental unit of QLM state transitions.
|
||||||
|
Every action by every actor generates an event.
|
||||||
|
|
||||||
|
Events have causality: they can be caused by other events,
|
||||||
|
creating a causal graph of system behavior.
|
||||||
|
"""
|
||||||
|
id: str = field(default_factory=lambda: str(uuid4()))
|
||||||
|
timestamp: datetime = field(default_factory=datetime.now)
|
||||||
|
|
||||||
|
# What layer generated this event
|
||||||
|
source_layer: IntelligenceType = IntelligenceType.AI
|
||||||
|
|
||||||
|
# What actor generated this event
|
||||||
|
actor_id: str = ""
|
||||||
|
|
||||||
|
# What type of event
|
||||||
|
event_type: EventType = EventType.AGENT_EXECUTION
|
||||||
|
|
||||||
|
# Event payload
|
||||||
|
data: Dict[str, Any] = field(default_factory=dict)
|
||||||
|
|
||||||
|
# Causality: what events caused this event
|
||||||
|
caused_by: List[str] = field(default_factory=list) # List of event IDs
|
||||||
|
|
||||||
|
# Related entities
|
||||||
|
intent_node_id: Optional[str] = None # Link to IntentGraph node
|
||||||
|
task_id: Optional[str] = None
|
||||||
|
|
||||||
|
# Metadata
|
||||||
|
tags: Set[str] = field(default_factory=set)
|
||||||
|
metadata: Dict[str, Any] = field(default_factory=dict)
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
return {
|
||||||
|
"id": self.id,
|
||||||
|
"timestamp": self.timestamp.isoformat(),
|
||||||
|
"source_layer": self.source_layer.value,
|
||||||
|
"actor_id": self.actor_id,
|
||||||
|
"event_type": self.event_type.value,
|
||||||
|
"data": self.data,
|
||||||
|
"caused_by": self.caused_by,
|
||||||
|
"intent_node_id": self.intent_node_id,
|
||||||
|
"task_id": self.task_id,
|
||||||
|
"tags": list(self.tags),
|
||||||
|
"metadata": self.metadata,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class QIEmergence:
|
||||||
|
"""
|
||||||
|
Represents a detected QI (Quantum Intelligence) emergence event.
|
||||||
|
|
||||||
|
QI emerges when:
|
||||||
|
- HI + AI create a feedback loop
|
||||||
|
- The system exhibits novel, unpredicted behavior
|
||||||
|
- Deterministic systems evolve in response to AI
|
||||||
|
- Agents self-organize in unexpected ways
|
||||||
|
|
||||||
|
This is the "quantum" moment: when 1 + 1 = 3.
|
||||||
|
"""
|
||||||
|
id: str = field(default_factory=lambda: str(uuid4()))
|
||||||
|
timestamp: datetime = field(default_factory=datetime.now)
|
||||||
|
|
||||||
|
# What pattern emerged
|
||||||
|
pattern_name: str = "" # e.g., "agent_self_correction", "novel_solution"
|
||||||
|
|
||||||
|
# What triggered this emergence
|
||||||
|
trigger_events: List[str] = field(default_factory=list) # Event IDs
|
||||||
|
|
||||||
|
# Confidence that this is truly emergent (0.0 to 1.0)
|
||||||
|
confidence: float = 0.0
|
||||||
|
|
||||||
|
# Human-readable explanation
|
||||||
|
explanation: str = ""
|
||||||
|
|
||||||
|
# Operator feedback
|
||||||
|
operator_validated: Optional[bool] = None # Did Operator confirm this?
|
||||||
|
operator_notes: str = ""
|
||||||
|
|
||||||
|
# Impact
|
||||||
|
impact_score: float = 0.0 # How significant was this emergence?
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
return {
|
||||||
|
"id": self.id,
|
||||||
|
"timestamp": self.timestamp.isoformat(),
|
||||||
|
"pattern_name": self.pattern_name,
|
||||||
|
"trigger_events": self.trigger_events,
|
||||||
|
"confidence": self.confidence,
|
||||||
|
"explanation": self.explanation,
|
||||||
|
"operator_validated": self.operator_validated,
|
||||||
|
"operator_notes": self.operator_notes,
|
||||||
|
"impact_score": self.impact_score,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class QLMMetrics:
|
||||||
|
"""
|
||||||
|
System-level metrics for QLM state.
|
||||||
|
|
||||||
|
These metrics help the Operator understand:
|
||||||
|
- How much activity in each intelligence layer
|
||||||
|
- How aligned is AI with HI intent
|
||||||
|
- How much QI emergence is happening
|
||||||
|
- Overall system health
|
||||||
|
"""
|
||||||
|
# Event counts by layer
|
||||||
|
hi_events: int = 0
|
||||||
|
ai_events: int = 0
|
||||||
|
qi_events: int = 0
|
||||||
|
system_events: int = 0
|
||||||
|
|
||||||
|
# Actor counts by layer
|
||||||
|
hi_actors: int = 0
|
||||||
|
ai_actors: int = 0
|
||||||
|
system_actors: int = 0
|
||||||
|
|
||||||
|
# Alignment: how much AI follows HI intent (0.0 to 1.0)
|
||||||
|
hi_ai_alignment: float = 0.0
|
||||||
|
|
||||||
|
# Emergence: rate of QI events detected
|
||||||
|
qi_emergence_rate: float = 0.0
|
||||||
|
|
||||||
|
# Feedback loops: number of HI→AI→HI cycles
|
||||||
|
feedback_loop_count: int = 0
|
||||||
|
|
||||||
|
# Operator metrics
|
||||||
|
operator_approvals: int = 0
|
||||||
|
operator_vetoes: int = 0
|
||||||
|
operator_queries: int = 0
|
||||||
|
|
||||||
|
# Time range for these metrics
|
||||||
|
start_time: datetime = field(default_factory=datetime.now)
|
||||||
|
end_time: datetime = field(default_factory=datetime.now)
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
return {
|
||||||
|
"hi_events": self.hi_events,
|
||||||
|
"ai_events": self.ai_events,
|
||||||
|
"qi_events": self.qi_events,
|
||||||
|
"system_events": self.system_events,
|
||||||
|
"hi_actors": self.hi_actors,
|
||||||
|
"ai_actors": self.ai_actors,
|
||||||
|
"system_actors": self.system_actors,
|
||||||
|
"hi_ai_alignment": self.hi_ai_alignment,
|
||||||
|
"qi_emergence_rate": self.qi_emergence_rate,
|
||||||
|
"feedback_loop_count": self.feedback_loop_count,
|
||||||
|
"operator_approvals": self.operator_approvals,
|
||||||
|
"operator_vetoes": self.operator_vetoes,
|
||||||
|
"operator_queries": self.operator_queries,
|
||||||
|
"start_time": self.start_time.isoformat(),
|
||||||
|
"end_time": self.end_time.isoformat(),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Known QI emergence patterns
|
||||||
|
QI_PATTERNS = {
|
||||||
|
"agent_self_correction": {
|
||||||
|
"description": "Agent detected its own error and corrected without HI intervention",
|
||||||
|
"trigger": "AGENT_ERROR followed by AGENT_EXECUTION with same task_id",
|
||||||
|
"significance": "high",
|
||||||
|
},
|
||||||
|
"novel_solution": {
|
||||||
|
"description": "Agent found a solution not in training data or prompts",
|
||||||
|
"trigger": "AGENT_COMPLETION with novel approach indicator",
|
||||||
|
"significance": "very_high",
|
||||||
|
},
|
||||||
|
"emergent_collaboration": {
|
||||||
|
"description": "Agents self-organized into collaboration pattern",
|
||||||
|
"trigger": "Multiple AGENT_HANDOFF events forming new pattern",
|
||||||
|
"significance": "high",
|
||||||
|
},
|
||||||
|
"operator_feedback_loop": {
|
||||||
|
"description": "HI intent → AI execution → HI approval → refined intent",
|
||||||
|
"trigger": "OPERATOR_INTENT → AGENT_COMPLETION → OPERATOR_APPROVAL → OPERATOR_INTENT",
|
||||||
|
"significance": "medium",
|
||||||
|
},
|
||||||
|
"system_adaptation": {
|
||||||
|
"description": "Deterministic system evolved in response to AI behavior",
|
||||||
|
"trigger": "AGENT_EXECUTION → SYSTEM_ERROR → AGENT_EXECUTION (different approach)",
|
||||||
|
"significance": "high",
|
||||||
|
},
|
||||||
|
}
|
||||||
510
qlm_lab/state.py
Normal file
510
qlm_lab/state.py
Normal file
@@ -0,0 +1,510 @@
|
|||||||
|
"""
|
||||||
|
QLM State Management - The core state machine
|
||||||
|
|
||||||
|
QLMState represents the complete state of the Quantum Intelligence system at a point in time.
|
||||||
|
|
||||||
|
State includes:
|
||||||
|
- All intelligence layers (HI, AI, QI)
|
||||||
|
- All actors and their current state
|
||||||
|
- Event history
|
||||||
|
- Detected QI emergences
|
||||||
|
- Metrics
|
||||||
|
|
||||||
|
State transitions happen when events are processed.
|
||||||
|
Each event can trigger:
|
||||||
|
- Actor state changes
|
||||||
|
- New QI emergence detection
|
||||||
|
- Metric updates
|
||||||
|
- Causal graph updates
|
||||||
|
"""
|
||||||
|
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from typing import Dict, List, Optional, Any
|
||||||
|
import json
|
||||||
|
|
||||||
|
from qlm_lab.models import (
|
||||||
|
IntelligenceType,
|
||||||
|
IntelligenceLayer,
|
||||||
|
Actor,
|
||||||
|
ActorType,
|
||||||
|
ActorRole,
|
||||||
|
ActorState,
|
||||||
|
QLMEvent,
|
||||||
|
EventType,
|
||||||
|
QIEmergence,
|
||||||
|
QLMMetrics,
|
||||||
|
QI_PATTERNS,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class StateTransition:
|
||||||
|
"""
|
||||||
|
Represents a state transition caused by an event.
|
||||||
|
|
||||||
|
This allows introspection: "What changed when event X happened?"
|
||||||
|
"""
|
||||||
|
event_id: str
|
||||||
|
timestamp: datetime
|
||||||
|
before_snapshot: Dict[str, Any]
|
||||||
|
after_snapshot: Dict[str, Any]
|
||||||
|
changes: List[str] # Human-readable list of changes
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
return {
|
||||||
|
"event_id": self.event_id,
|
||||||
|
"timestamp": self.timestamp.isoformat(),
|
||||||
|
"before_snapshot": self.before_snapshot,
|
||||||
|
"after_snapshot": self.after_snapshot,
|
||||||
|
"changes": self.changes,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class QLMState:
|
||||||
|
"""
|
||||||
|
The complete state of the Quantum Intelligence system.
|
||||||
|
|
||||||
|
This is the "brain" of QLM. It tracks everything:
|
||||||
|
- All intelligence layers
|
||||||
|
- All actors
|
||||||
|
- All events
|
||||||
|
- All emergences
|
||||||
|
- All metrics
|
||||||
|
|
||||||
|
Key methods:
|
||||||
|
- ingest_event(): Process a new event and update state
|
||||||
|
- query(): Answer questions about current state
|
||||||
|
- explain_transition(): Explain why state changed
|
||||||
|
- detect_qi_emergence(): Find emergent patterns
|
||||||
|
- calculate_metrics(): Compute system metrics
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, intent_graph=None, agent_coordinator=None):
|
||||||
|
"""
|
||||||
|
Initialize QLM state.
|
||||||
|
|
||||||
|
Can integrate with existing cognitive systems:
|
||||||
|
- intent_graph: From cognitive.intent_graph
|
||||||
|
- agent_coordinator: From cognitive.agent_coordination
|
||||||
|
"""
|
||||||
|
# Integration with existing cognitive layer
|
||||||
|
self.intent_graph = intent_graph
|
||||||
|
self.agent_coordinator = agent_coordinator
|
||||||
|
|
||||||
|
# Intelligence layers
|
||||||
|
self.layers: Dict[IntelligenceType, IntelligenceLayer] = {
|
||||||
|
IntelligenceType.HI: IntelligenceLayer(type=IntelligenceType.HI),
|
||||||
|
IntelligenceType.AI: IntelligenceLayer(type=IntelligenceType.AI),
|
||||||
|
IntelligenceType.QI: IntelligenceLayer(type=IntelligenceType.QI),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Event history (ordered by timestamp)
|
||||||
|
self.events: List[QLMEvent] = []
|
||||||
|
|
||||||
|
# Detected QI emergences
|
||||||
|
self.emergences: List[QIEmergence] = []
|
||||||
|
|
||||||
|
# State transition history
|
||||||
|
self.transitions: List[StateTransition] = []
|
||||||
|
|
||||||
|
# Metrics
|
||||||
|
self.metrics = QLMMetrics()
|
||||||
|
|
||||||
|
# Timestamps
|
||||||
|
self.created_at = datetime.now()
|
||||||
|
self.updated_at = datetime.now()
|
||||||
|
|
||||||
|
def register_actor(self, actor: Actor) -> None:
|
||||||
|
"""
|
||||||
|
Register an actor in the appropriate intelligence layer.
|
||||||
|
|
||||||
|
Human → HI layer
|
||||||
|
Agent → AI layer
|
||||||
|
System → (tracked but not in a specific layer)
|
||||||
|
"""
|
||||||
|
if actor.actor_type == ActorType.HUMAN:
|
||||||
|
self.layers[IntelligenceType.HI].add_actor(actor)
|
||||||
|
self.metrics.hi_actors += 1
|
||||||
|
elif actor.actor_type == ActorType.AGENT:
|
||||||
|
self.layers[IntelligenceType.AI].add_actor(actor)
|
||||||
|
self.metrics.ai_actors += 1
|
||||||
|
elif actor.actor_type == ActorType.SYSTEM:
|
||||||
|
# System actors are tracked but don't belong to HI/AI/QI layers
|
||||||
|
self.metrics.system_actors += 1
|
||||||
|
|
||||||
|
def ingest_event(self, event: QLMEvent) -> None:
|
||||||
|
"""
|
||||||
|
Ingest a new event and update state.
|
||||||
|
|
||||||
|
This is the core state transition function.
|
||||||
|
|
||||||
|
Steps:
|
||||||
|
1. Capture before-state snapshot
|
||||||
|
2. Add event to history
|
||||||
|
3. Update actor states
|
||||||
|
4. Detect QI emergence
|
||||||
|
5. Update metrics
|
||||||
|
6. Capture after-state snapshot
|
||||||
|
7. Record transition
|
||||||
|
"""
|
||||||
|
# 1. Before snapshot
|
||||||
|
before = self._create_snapshot()
|
||||||
|
|
||||||
|
# 2. Add event
|
||||||
|
self.events.append(event)
|
||||||
|
|
||||||
|
# 3. Update actor states
|
||||||
|
self._update_actor_state(event)
|
||||||
|
|
||||||
|
# 4. Update layer metrics
|
||||||
|
if event.source_layer == IntelligenceType.HI:
|
||||||
|
self.layers[IntelligenceType.HI].total_events += 1
|
||||||
|
self.metrics.hi_events += 1
|
||||||
|
elif event.source_layer == IntelligenceType.AI:
|
||||||
|
self.layers[IntelligenceType.AI].total_events += 1
|
||||||
|
self.metrics.ai_events += 1
|
||||||
|
elif event.source_layer == IntelligenceType.QI:
|
||||||
|
self.layers[IntelligenceType.QI].total_events += 1
|
||||||
|
self.metrics.qi_events += 1
|
||||||
|
|
||||||
|
# 5. Detect QI emergence
|
||||||
|
emergence = self._detect_qi_emergence(event)
|
||||||
|
if emergence:
|
||||||
|
self.emergences.append(emergence)
|
||||||
|
self.metrics.qi_events += 1
|
||||||
|
|
||||||
|
# 6. Update operator metrics
|
||||||
|
if event.event_type == EventType.OPERATOR_APPROVAL:
|
||||||
|
self.metrics.operator_approvals += 1
|
||||||
|
elif event.event_type == EventType.OPERATOR_VETO:
|
||||||
|
self.metrics.operator_vetoes += 1
|
||||||
|
elif event.event_type == EventType.OPERATOR_QUERY:
|
||||||
|
self.metrics.operator_queries += 1
|
||||||
|
|
||||||
|
# 7. After snapshot
|
||||||
|
after = self._create_snapshot()
|
||||||
|
|
||||||
|
# 8. Record transition
|
||||||
|
changes = self._compute_changes(before, after)
|
||||||
|
transition = StateTransition(
|
||||||
|
event_id=event.id,
|
||||||
|
timestamp=event.timestamp,
|
||||||
|
before_snapshot=before,
|
||||||
|
after_snapshot=after,
|
||||||
|
changes=changes,
|
||||||
|
)
|
||||||
|
self.transitions.append(transition)
|
||||||
|
|
||||||
|
self.updated_at = datetime.now()
|
||||||
|
|
||||||
|
def _update_actor_state(self, event: QLMEvent) -> None:
|
||||||
|
"""Update actor state based on event"""
|
||||||
|
actor_id = event.actor_id
|
||||||
|
|
||||||
|
# Find actor in any layer
|
||||||
|
actor = None
|
||||||
|
for layer in self.layers.values():
|
||||||
|
if actor_id in layer.actors:
|
||||||
|
actor = layer.actors[actor_id]
|
||||||
|
break
|
||||||
|
|
||||||
|
if not actor:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Update actor state based on event type
|
||||||
|
if event.event_type in [EventType.AGENT_EXECUTION, EventType.OPERATOR_INTENT]:
|
||||||
|
actor.state = ActorState.ACTIVE
|
||||||
|
actor.current_task_id = event.task_id
|
||||||
|
|
||||||
|
elif event.event_type in [EventType.AGENT_COMPLETION]:
|
||||||
|
actor.state = ActorState.IDLE
|
||||||
|
actor.current_task_id = None
|
||||||
|
|
||||||
|
elif event.event_type == EventType.AGENT_ERROR:
|
||||||
|
actor.state = ActorState.BLOCKED
|
||||||
|
|
||||||
|
actor.last_active = event.timestamp
|
||||||
|
|
||||||
|
def _detect_qi_emergence(self, event: QLMEvent) -> Optional[QIEmergence]:
|
||||||
|
"""
|
||||||
|
Detect if this event (combined with recent events) represents QI emergence.
|
||||||
|
|
||||||
|
This is where the magic happens: detecting when 1 + 1 = 3.
|
||||||
|
"""
|
||||||
|
# Look at recent events (last 10)
|
||||||
|
recent_events = self.events[-10:]
|
||||||
|
|
||||||
|
# Check each known QI pattern
|
||||||
|
for pattern_name, pattern_def in QI_PATTERNS.items():
|
||||||
|
if self._matches_pattern(recent_events, pattern_name, pattern_def):
|
||||||
|
return QIEmergence(
|
||||||
|
pattern_name=pattern_name,
|
||||||
|
trigger_events=[e.id for e in recent_events[-3:]], # Last 3 events
|
||||||
|
confidence=0.8, # TODO: Implement proper confidence scoring
|
||||||
|
explanation=pattern_def["description"],
|
||||||
|
impact_score=self._calculate_impact(pattern_name),
|
||||||
|
)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _matches_pattern(
|
||||||
|
self, events: List[QLMEvent], pattern_name: str, pattern_def: Dict
|
||||||
|
) -> bool:
|
||||||
|
"""Check if a sequence of events matches a QI pattern"""
|
||||||
|
if pattern_name == "agent_self_correction":
|
||||||
|
# Look for: AGENT_ERROR followed by AGENT_EXECUTION with same task
|
||||||
|
for i in range(len(events) - 1):
|
||||||
|
if (
|
||||||
|
events[i].event_type == EventType.AGENT_ERROR
|
||||||
|
and events[i + 1].event_type == EventType.AGENT_EXECUTION
|
||||||
|
and events[i].task_id == events[i + 1].task_id
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
|
||||||
|
elif pattern_name == "operator_feedback_loop":
|
||||||
|
# Look for: OPERATOR_INTENT → AGENT_COMPLETION → OPERATOR_APPROVAL
|
||||||
|
for i in range(len(events) - 2):
|
||||||
|
if (
|
||||||
|
events[i].event_type == EventType.OPERATOR_INTENT
|
||||||
|
and events[i + 1].event_type == EventType.AGENT_COMPLETION
|
||||||
|
and events[i + 2].event_type == EventType.OPERATOR_APPROVAL
|
||||||
|
):
|
||||||
|
self.metrics.feedback_loop_count += 1
|
||||||
|
return True
|
||||||
|
|
||||||
|
# TODO: Implement other pattern detectors
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _calculate_impact(self, pattern_name: str) -> float:
|
||||||
|
"""Calculate impact score for an emergence pattern"""
|
||||||
|
significance_map = {
|
||||||
|
"very_high": 1.0,
|
||||||
|
"high": 0.8,
|
||||||
|
"medium": 0.5,
|
||||||
|
"low": 0.3,
|
||||||
|
}
|
||||||
|
pattern_def = QI_PATTERNS.get(pattern_name, {})
|
||||||
|
significance = pattern_def.get("significance", "medium")
|
||||||
|
return significance_map.get(significance, 0.5)
|
||||||
|
|
||||||
|
def _create_snapshot(self) -> Dict[str, Any]:
|
||||||
|
"""Create a snapshot of current state"""
|
||||||
|
return {
|
||||||
|
"timestamp": datetime.now().isoformat(),
|
||||||
|
"total_events": len(self.events),
|
||||||
|
"total_emergences": len(self.emergences),
|
||||||
|
"metrics": self.metrics.to_dict(),
|
||||||
|
}
|
||||||
|
|
||||||
|
def _compute_changes(
|
||||||
|
self, before: Dict[str, Any], after: Dict[str, Any]
|
||||||
|
) -> List[str]:
|
||||||
|
"""Compute human-readable changes between states"""
|
||||||
|
changes = []
|
||||||
|
|
||||||
|
if after["total_events"] > before["total_events"]:
|
||||||
|
changes.append(f"New event added (total: {after['total_events']})")
|
||||||
|
|
||||||
|
if after["total_emergences"] > before["total_emergences"]:
|
||||||
|
changes.append(
|
||||||
|
f"QI emergence detected (total: {after['total_emergences']})"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Compare metrics
|
||||||
|
before_metrics = before["metrics"]
|
||||||
|
after_metrics = after["metrics"]
|
||||||
|
|
||||||
|
if after_metrics["hi_events"] > before_metrics["hi_events"]:
|
||||||
|
changes.append("Operator activity increased")
|
||||||
|
|
||||||
|
if after_metrics["ai_events"] > before_metrics["ai_events"]:
|
||||||
|
changes.append("Agent activity increased")
|
||||||
|
|
||||||
|
return changes
|
||||||
|
|
||||||
|
def query(self, query_type: str, **kwargs) -> Any:
|
||||||
|
"""
|
||||||
|
Query the QLM state.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
- query("active_actors")
|
||||||
|
- query("events_by_type", event_type=EventType.OPERATOR_INTENT)
|
||||||
|
- query("emergences_by_pattern", pattern="agent_self_correction")
|
||||||
|
- query("metrics_summary")
|
||||||
|
"""
|
||||||
|
if query_type == "active_actors":
|
||||||
|
active = []
|
||||||
|
for layer in self.layers.values():
|
||||||
|
active.extend(layer.get_active_actors())
|
||||||
|
return active
|
||||||
|
|
||||||
|
elif query_type == "events_by_type":
|
||||||
|
event_type = kwargs.get("event_type")
|
||||||
|
return [e for e in self.events if e.event_type == event_type]
|
||||||
|
|
||||||
|
elif query_type == "events_by_actor":
|
||||||
|
actor_id = kwargs.get("actor_id")
|
||||||
|
return [e for e in self.events if e.actor_id == actor_id]
|
||||||
|
|
||||||
|
elif query_type == "events_in_timerange":
|
||||||
|
start = kwargs.get("start", datetime.now() - timedelta(days=1))
|
||||||
|
end = kwargs.get("end", datetime.now())
|
||||||
|
return [e for e in self.events if start <= e.timestamp <= end]
|
||||||
|
|
||||||
|
elif query_type == "emergences_by_pattern":
|
||||||
|
pattern = kwargs.get("pattern")
|
||||||
|
return [em for em in self.emergences if em.pattern_name == pattern]
|
||||||
|
|
||||||
|
elif query_type == "metrics_summary":
|
||||||
|
return self.metrics.to_dict()
|
||||||
|
|
||||||
|
elif query_type == "recent_transitions":
|
||||||
|
limit = kwargs.get("limit", 10)
|
||||||
|
return self.transitions[-limit:]
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def explain_transition(self, event_id: str) -> Optional[StateTransition]:
|
||||||
|
"""Explain what happened when a specific event occurred"""
|
||||||
|
for transition in self.transitions:
|
||||||
|
if transition.event_id == event_id:
|
||||||
|
return transition
|
||||||
|
return None
|
||||||
|
|
||||||
|
def calculate_alignment(self) -> float:
|
||||||
|
"""
|
||||||
|
Calculate HI-AI alignment.
|
||||||
|
|
||||||
|
This measures: "Is AI doing what the Operator intended?"
|
||||||
|
|
||||||
|
Approach:
|
||||||
|
- Look at OPERATOR_INTENT events
|
||||||
|
- Look at subsequent AGENT_COMPLETION events
|
||||||
|
- Check if agent actions align with intent
|
||||||
|
- Return alignment score (0.0 to 1.0)
|
||||||
|
"""
|
||||||
|
operator_intents = [
|
||||||
|
e for e in self.events if e.event_type == EventType.OPERATOR_INTENT
|
||||||
|
]
|
||||||
|
|
||||||
|
if not operator_intents:
|
||||||
|
return 1.0 # No intents = perfect alignment (vacuous truth)
|
||||||
|
|
||||||
|
aligned_count = 0
|
||||||
|
|
||||||
|
for intent_event in operator_intents:
|
||||||
|
# Find completions after this intent
|
||||||
|
completions_after = [
|
||||||
|
e
|
||||||
|
for e in self.events
|
||||||
|
if e.event_type == EventType.AGENT_COMPLETION
|
||||||
|
and e.timestamp > intent_event.timestamp
|
||||||
|
and e.intent_node_id == intent_event.intent_node_id
|
||||||
|
]
|
||||||
|
|
||||||
|
if completions_after:
|
||||||
|
# Check if any completion was approved
|
||||||
|
approvals = [
|
||||||
|
e
|
||||||
|
for e in self.events
|
||||||
|
if e.event_type == EventType.OPERATOR_APPROVAL
|
||||||
|
and e.timestamp > completions_after[0].timestamp
|
||||||
|
and e.intent_node_id == intent_event.intent_node_id
|
||||||
|
]
|
||||||
|
|
||||||
|
if approvals:
|
||||||
|
aligned_count += 1
|
||||||
|
|
||||||
|
alignment = aligned_count / len(operator_intents) if operator_intents else 1.0
|
||||||
|
self.metrics.hi_ai_alignment = alignment
|
||||||
|
return alignment
|
||||||
|
|
||||||
|
def summarize_for_operator(self, days: int = 7) -> str:
|
||||||
|
"""
|
||||||
|
Create a human-readable summary for the Operator.
|
||||||
|
|
||||||
|
This is what Alexa sees when she asks: "What happened this week?"
|
||||||
|
"""
|
||||||
|
cutoff = datetime.now() - timedelta(days=days)
|
||||||
|
recent_events = [e for e in self.events if e.timestamp >= cutoff]
|
||||||
|
|
||||||
|
# Count events by type
|
||||||
|
event_counts = {}
|
||||||
|
for event in recent_events:
|
||||||
|
event_type = event.event_type.value
|
||||||
|
event_counts[event_type] = event_counts.get(event_type, 0) + 1
|
||||||
|
|
||||||
|
# Get emergences
|
||||||
|
recent_emergences = [
|
||||||
|
em for em in self.emergences if em.timestamp >= cutoff
|
||||||
|
]
|
||||||
|
|
||||||
|
# Calculate alignment
|
||||||
|
alignment = self.calculate_alignment()
|
||||||
|
|
||||||
|
summary = f"""
|
||||||
|
QLM State Summary (Last {days} Days)
|
||||||
|
{'=' * 50}
|
||||||
|
|
||||||
|
📊 Activity Overview:
|
||||||
|
Total Events: {len(recent_events)}
|
||||||
|
HI (Operator) Events: {sum(1 for e in recent_events if e.source_layer == IntelligenceType.HI)}
|
||||||
|
AI (Agent) Events: {sum(1 for e in recent_events if e.source_layer == IntelligenceType.AI)}
|
||||||
|
System Events: {sum(1 for e in recent_events if 'system' in e.event_type.value)}
|
||||||
|
|
||||||
|
✨ QI Emergence:
|
||||||
|
Emergent Patterns Detected: {len(recent_emergences)}
|
||||||
|
"""
|
||||||
|
|
||||||
|
if recent_emergences:
|
||||||
|
summary += " Notable Emergences:\n"
|
||||||
|
for em in recent_emergences[:5]: # Top 5
|
||||||
|
summary += f" - {em.pattern_name}: {em.explanation}\n"
|
||||||
|
|
||||||
|
summary += f"""
|
||||||
|
🎯 Alignment:
|
||||||
|
HI-AI Alignment Score: {alignment:.2%}
|
||||||
|
Operator Approvals: {self.metrics.operator_approvals}
|
||||||
|
Operator Vetoes: {self.metrics.operator_vetoes}
|
||||||
|
Feedback Loops: {self.metrics.feedback_loop_count}
|
||||||
|
|
||||||
|
👥 Active Actors:
|
||||||
|
HI Layer: {len(self.layers[IntelligenceType.HI].get_active_actors())}
|
||||||
|
AI Layer: {len(self.layers[IntelligenceType.AI].get_active_actors())}
|
||||||
|
|
||||||
|
📈 Top Event Types:
|
||||||
|
"""
|
||||||
|
|
||||||
|
for event_type, count in sorted(
|
||||||
|
event_counts.items(), key=lambda x: x[1], reverse=True
|
||||||
|
)[:5]:
|
||||||
|
summary += f" {event_type}: {count}\n"
|
||||||
|
|
||||||
|
return summary
|
||||||
|
|
||||||
|
def export_json(self, file_path: str) -> None:
|
||||||
|
"""Export QLM state to JSON"""
|
||||||
|
data = {
|
||||||
|
"created_at": self.created_at.isoformat(),
|
||||||
|
"updated_at": self.updated_at.isoformat(),
|
||||||
|
"layers": {
|
||||||
|
layer_type.value: layer.to_dict()
|
||||||
|
for layer_type, layer in self.layers.items()
|
||||||
|
},
|
||||||
|
"events": [e.to_dict() for e in self.events],
|
||||||
|
"emergences": [em.to_dict() for em in self.emergences],
|
||||||
|
"metrics": self.metrics.to_dict(),
|
||||||
|
}
|
||||||
|
|
||||||
|
with open(file_path, "w") as f:
|
||||||
|
json.dump(data, f, indent=2)
|
||||||
|
|
||||||
|
def import_json(self, file_path: str) -> None:
|
||||||
|
"""Import QLM state from JSON"""
|
||||||
|
with open(file_path, "r") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
# TODO: Implement full deserialization
|
||||||
|
pass
|
||||||
402
qlm_lab/visualization.py
Normal file
402
qlm_lab/visualization.py
Normal file
@@ -0,0 +1,402 @@
|
|||||||
|
"""
|
||||||
|
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()
|
||||||
268
tests/test_qlm_core.py
Normal file
268
tests/test_qlm_core.py
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
"""
|
||||||
|
Tests for QLM core functionality
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
from qlm_lab.api import QLMInterface
|
||||||
|
from qlm_lab.models import (
|
||||||
|
Actor,
|
||||||
|
ActorType,
|
||||||
|
ActorRole,
|
||||||
|
ActorState,
|
||||||
|
IntelligenceType,
|
||||||
|
EventType,
|
||||||
|
QLMEvent,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestQLMCore:
|
||||||
|
"""Test core QLM functionality"""
|
||||||
|
|
||||||
|
def test_qlm_initialization(self):
|
||||||
|
"""Test QLM initializes correctly"""
|
||||||
|
qlm = QLMInterface()
|
||||||
|
|
||||||
|
assert qlm.state is not None
|
||||||
|
assert len(qlm.state.layers) == 3 # HI, AI, QI
|
||||||
|
assert len(qlm.state.events) == 0 # No events yet
|
||||||
|
assert qlm.operator.actor_type == ActorType.HUMAN
|
||||||
|
|
||||||
|
def test_register_agent(self):
|
||||||
|
"""Test agent registration"""
|
||||||
|
qlm = QLMInterface()
|
||||||
|
|
||||||
|
agent = qlm.register_agent(
|
||||||
|
agent_id="test-agent",
|
||||||
|
name="TestAgent",
|
||||||
|
role=ActorRole.EXECUTOR,
|
||||||
|
capabilities=["testing", "debugging"],
|
||||||
|
)
|
||||||
|
|
||||||
|
assert agent.id == "test-agent"
|
||||||
|
assert agent.name == "TestAgent"
|
||||||
|
assert agent.role == ActorRole.EXECUTOR
|
||||||
|
assert "testing" in agent.capabilities
|
||||||
|
assert "debugging" in agent.capabilities
|
||||||
|
|
||||||
|
def test_record_operator_intent(self):
|
||||||
|
"""Test recording Operator intent"""
|
||||||
|
qlm = QLMInterface()
|
||||||
|
|
||||||
|
event = qlm.record_operator_intent(
|
||||||
|
intent="Test intent", description="Test description"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert event.source_layer == IntelligenceType.HI
|
||||||
|
assert event.event_type == EventType.OPERATOR_INTENT
|
||||||
|
assert event.data["intent"] == "Test intent"
|
||||||
|
assert len(qlm.state.events) == 1
|
||||||
|
|
||||||
|
def test_record_agent_execution(self):
|
||||||
|
"""Test recording agent execution"""
|
||||||
|
qlm = QLMInterface()
|
||||||
|
qlm.register_agent("agent-1", "Agent1", ActorRole.EXECUTOR)
|
||||||
|
|
||||||
|
event = qlm.record_agent_execution(
|
||||||
|
agent_id="agent-1",
|
||||||
|
task_description="Test task",
|
||||||
|
task_id="task-1",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert event.source_layer == IntelligenceType.AI
|
||||||
|
assert event.event_type == EventType.AGENT_EXECUTION
|
||||||
|
assert event.actor_id == "agent-1"
|
||||||
|
assert event.task_id == "task-1"
|
||||||
|
|
||||||
|
def test_record_agent_completion(self):
|
||||||
|
"""Test recording agent completion"""
|
||||||
|
qlm = QLMInterface()
|
||||||
|
qlm.register_agent("agent-1", "Agent1", ActorRole.EXECUTOR)
|
||||||
|
|
||||||
|
event = qlm.record_agent_completion(
|
||||||
|
agent_id="agent-1",
|
||||||
|
task_id="task-1",
|
||||||
|
success=True,
|
||||||
|
result={"status": "done"},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert event.event_type == EventType.AGENT_COMPLETION
|
||||||
|
assert event.data["success"] is True
|
||||||
|
assert event.data["result"]["status"] == "done"
|
||||||
|
|
||||||
|
def test_alignment_calculation(self):
|
||||||
|
"""Test HI-AI alignment calculation"""
|
||||||
|
qlm = QLMInterface()
|
||||||
|
qlm.register_agent("agent-1", "Agent1", ActorRole.EXECUTOR)
|
||||||
|
|
||||||
|
# Create intent
|
||||||
|
qlm.record_operator_intent("Test intent", intent_node_id="intent-1")
|
||||||
|
|
||||||
|
# Agent executes
|
||||||
|
qlm.record_agent_execution(
|
||||||
|
"agent-1", "Do task", "task-1", intent_node_id="intent-1"
|
||||||
|
)
|
||||||
|
qlm.record_agent_completion("agent-1", "task-1", True)
|
||||||
|
|
||||||
|
# Operator approves
|
||||||
|
qlm.record_operator_approval(
|
||||||
|
"Good work", intent_node_id="intent-1", task_id="task-1"
|
||||||
|
)
|
||||||
|
|
||||||
|
alignment = qlm.get_alignment_score()
|
||||||
|
assert alignment == 1.0 # Perfect alignment
|
||||||
|
|
||||||
|
def test_qi_emergence_detection(self):
|
||||||
|
"""Test QI emergence detection"""
|
||||||
|
qlm = QLMInterface()
|
||||||
|
qlm.register_agent("agent-1", "Agent1", ActorRole.EXECUTOR)
|
||||||
|
|
||||||
|
# Simulate self-correction pattern
|
||||||
|
qlm.record_agent_execution("agent-1", "Task", "task-1")
|
||||||
|
qlm.record_agent_error("agent-1", "task-1", "Error occurred")
|
||||||
|
qlm.record_agent_execution("agent-1", "Task retry", "task-1")
|
||||||
|
qlm.record_agent_completion("agent-1", "task-1", True)
|
||||||
|
|
||||||
|
# Check for emergence
|
||||||
|
emergences = qlm.get_recent_emergences()
|
||||||
|
|
||||||
|
# Emergence detection may or may not trigger depending on pattern matching
|
||||||
|
# This test mainly ensures the system doesn't crash
|
||||||
|
assert isinstance(emergences, list)
|
||||||
|
|
||||||
|
def test_query_events_by_type(self):
|
||||||
|
"""Test querying events by type"""
|
||||||
|
qlm = QLMInterface()
|
||||||
|
|
||||||
|
# Record various events
|
||||||
|
qlm.record_operator_intent("Intent 1")
|
||||||
|
qlm.record_operator_intent("Intent 2")
|
||||||
|
|
||||||
|
qlm.register_agent("agent-1", "Agent1", ActorRole.EXECUTOR)
|
||||||
|
qlm.record_agent_execution("agent-1", "Task", "task-1")
|
||||||
|
|
||||||
|
# Query
|
||||||
|
intent_events = qlm.get_events_by_type(EventType.OPERATOR_INTENT)
|
||||||
|
assert len(intent_events) == 2
|
||||||
|
|
||||||
|
exec_events = qlm.get_events_by_type(EventType.AGENT_EXECUTION)
|
||||||
|
assert len(exec_events) == 1
|
||||||
|
|
||||||
|
def test_query_events_in_timerange(self):
|
||||||
|
"""Test querying events in time range"""
|
||||||
|
qlm = QLMInterface()
|
||||||
|
|
||||||
|
now = datetime.now()
|
||||||
|
yesterday = now - timedelta(days=1)
|
||||||
|
|
||||||
|
qlm.record_operator_intent("Recent intent")
|
||||||
|
|
||||||
|
events = qlm.get_events_in_timerange(yesterday, now)
|
||||||
|
assert len(events) >= 1
|
||||||
|
|
||||||
|
def test_operator_ask_interface(self):
|
||||||
|
"""Test natural language query interface"""
|
||||||
|
qlm = QLMInterface()
|
||||||
|
qlm.register_agent("agent-1", "Agent1", ActorRole.EXECUTOR)
|
||||||
|
|
||||||
|
qlm.record_operator_intent("Test")
|
||||||
|
qlm.record_agent_execution("agent-1", "Task", "task-1")
|
||||||
|
qlm.record_agent_completion("agent-1", "task-1", True)
|
||||||
|
qlm.record_operator_approval("Good", task_id="task-1")
|
||||||
|
|
||||||
|
# Test queries
|
||||||
|
response = qlm.ask("What's the status?")
|
||||||
|
assert "QLM State Summary" in response
|
||||||
|
|
||||||
|
response = qlm.ask("Are we aligned with my intent?")
|
||||||
|
assert "Alignment" in response
|
||||||
|
|
||||||
|
def test_export_import_state(self, tmp_path):
|
||||||
|
"""Test state export and import"""
|
||||||
|
qlm = QLMInterface()
|
||||||
|
|
||||||
|
# Create some state
|
||||||
|
qlm.register_agent("agent-1", "Agent1", ActorRole.EXECUTOR)
|
||||||
|
qlm.record_operator_intent("Test intent")
|
||||||
|
qlm.record_agent_execution("agent-1", "Task", "task-1")
|
||||||
|
|
||||||
|
# Export
|
||||||
|
export_path = tmp_path / "qlm_state.json"
|
||||||
|
qlm.export_state(str(export_path))
|
||||||
|
|
||||||
|
assert export_path.exists()
|
||||||
|
|
||||||
|
# Check content
|
||||||
|
import json
|
||||||
|
|
||||||
|
with open(export_path, "r") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
assert "layers" in data
|
||||||
|
assert "events" in data
|
||||||
|
assert "metrics" in data
|
||||||
|
|
||||||
|
def test_metrics_tracking(self):
|
||||||
|
"""Test metrics are tracked correctly"""
|
||||||
|
qlm = QLMInterface()
|
||||||
|
qlm.register_agent("agent-1", "Agent1", ActorRole.EXECUTOR)
|
||||||
|
|
||||||
|
# Record events
|
||||||
|
qlm.record_operator_intent("Intent")
|
||||||
|
qlm.record_agent_execution("agent-1", "Task", "task-1")
|
||||||
|
qlm.record_operator_approval("Good", task_id="task-1")
|
||||||
|
|
||||||
|
metrics = qlm.state.metrics
|
||||||
|
|
||||||
|
assert metrics.hi_events >= 2 # Intent + approval
|
||||||
|
assert metrics.ai_events >= 1 # Execution
|
||||||
|
assert metrics.operator_approvals == 1
|
||||||
|
|
||||||
|
|
||||||
|
class TestQLMModels:
|
||||||
|
"""Test QLM data models"""
|
||||||
|
|
||||||
|
def test_actor_creation(self):
|
||||||
|
"""Test Actor model"""
|
||||||
|
actor = Actor(
|
||||||
|
id="test-actor",
|
||||||
|
name="TestActor",
|
||||||
|
actor_type=ActorType.AGENT,
|
||||||
|
role=ActorRole.EXECUTOR,
|
||||||
|
state=ActorState.ACTIVE,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert actor.id == "test-actor"
|
||||||
|
assert actor.actor_type == ActorType.AGENT
|
||||||
|
assert actor.role == ActorRole.EXECUTOR
|
||||||
|
assert actor.state == ActorState.ACTIVE
|
||||||
|
|
||||||
|
# Test serialization
|
||||||
|
data = actor.to_dict()
|
||||||
|
assert data["id"] == "test-actor"
|
||||||
|
assert data["actor_type"] == "agent"
|
||||||
|
|
||||||
|
def test_event_creation(self):
|
||||||
|
"""Test QLMEvent model"""
|
||||||
|
event = QLMEvent(
|
||||||
|
source_layer=IntelligenceType.AI,
|
||||||
|
actor_id="agent-1",
|
||||||
|
event_type=EventType.AGENT_EXECUTION,
|
||||||
|
task_id="task-1",
|
||||||
|
data={"test": "data"},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert event.source_layer == IntelligenceType.AI
|
||||||
|
assert event.event_type == EventType.AGENT_EXECUTION
|
||||||
|
assert event.task_id == "task-1"
|
||||||
|
assert event.data["test"] == "data"
|
||||||
|
|
||||||
|
# Test serialization
|
||||||
|
data = event.to_dict()
|
||||||
|
assert data["source_layer"] == "model_intelligence"
|
||||||
|
assert data["event_type"] == "agent_execution"
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
pytest.main([__file__, "-v"])
|
||||||
Reference in New Issue
Block a user