Justadudeinspace
commited on
Commit
·
5f83674
1
Parent(s):
8a75909
Initial commit – full BLUX-cA repo
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitignore +64 -0
- README.md +135 -1
- blux/__init__.py +4 -0
- blux/adaptors/__init__.py +3 -0
- blux/adaptors/dummy_local.py +18 -0
- blux/adaptors/http_api_adaptor.py +28 -0
- blux/agent/__init__.py +6 -0
- blux/agent/advanced/__init__.py +4 -0
- blux/agent/advanced/adaptive_memory.py +70 -0
- blux/agent/advanced/monitoring.py +66 -0
- blux/agent/advanced/multi_agent.py +74 -0
- blux/agent/advanced/reasoning.py +76 -0
- blux/agent/audit.py +16 -0
- blux/agent/constitution.py +17 -0
- blux/agent/core_agent.py +35 -0
- blux/agent/discernment.py +12 -0
- blux/agent/memory.py +23 -0
- blux/agent/utils.py +5 -0
- blux/cli.py +53 -0
- blux/evaluator/__init__.py +3 -0
- blux/evaluator/advanced/__init__.py +5 -0
- blux/evaluator/advanced/bash_evaluator.py +15 -0
- blux/evaluator/advanced/js_ts_async.py +23 -0
- blux/evaluator/advanced/pipeline.py +27 -0
- blux/evaluator/advanced/python_async.py +24 -0
- blux/evaluator/js_ts.py +27 -0
- blux/evaluator/python.py +20 -0
- blux/orchestrator/__init__.py +8 -0
- blux/orchestrator/config.yaml +13 -0
- blux/orchestrator/controller.py +49 -0
- blux/orchestrator/logs.py +44 -0
- blux/orchestrator/registry.py +47 -0
- blux/orchestrator/router.py +21 -0
- blux/orchestrator/secure/__init__.py +4 -0
- blux/orchestrator/secure/audit.py +29 -0
- blux/orchestrator/secure/auth.py +26 -0
- blux/orchestrator/secure/secure_controller.py +27 -0
- constitution/behavior.md +9 -0
- ethos/manifest.yaml +14 -0
- identity/seed.json +10 -0
- requirements.txt +33 -0
- scripts/batch_task.py +6 -0
- scripts/ingest_reflection.py +12 -0
- scripts/interactive_repl.py +8 -0
- scripts/memory_query.py +5 -0
- scripts/new_entry.py +23 -0
- scripts/reflection.py +12 -0
- tests/test_agent.py +19 -0
- tests/test_ci_hooks.py +15 -0
- tests/test_evaluator.py +23 -0
.gitignore
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Byte-compiled / optimized / DLL files
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[cod]
|
| 4 |
+
*$py.class
|
| 5 |
+
|
| 6 |
+
# C extensions
|
| 7 |
+
*.so
|
| 8 |
+
|
| 9 |
+
# Virtual environment
|
| 10 |
+
venv/
|
| 11 |
+
.env/
|
| 12 |
+
.env.*
|
| 13 |
+
.venv/
|
| 14 |
+
|
| 15 |
+
# Distribution / packaging
|
| 16 |
+
build/
|
| 17 |
+
dist/
|
| 18 |
+
*.egg-info/
|
| 19 |
+
.eggs/
|
| 20 |
+
|
| 21 |
+
# Logs
|
| 22 |
+
*.log
|
| 23 |
+
secure_audit.log
|
| 24 |
+
*.out
|
| 25 |
+
*.err
|
| 26 |
+
|
| 27 |
+
# Jupyter Notebook checkpoints
|
| 28 |
+
.ipynb_checkpoints/
|
| 29 |
+
|
| 30 |
+
# Temporary files
|
| 31 |
+
*.tmp
|
| 32 |
+
*.swp
|
| 33 |
+
*.bak
|
| 34 |
+
*.DS_Store
|
| 35 |
+
*.lock
|
| 36 |
+
|
| 37 |
+
# Coverage reports
|
| 38 |
+
htmlcov/
|
| 39 |
+
.coverage
|
| 40 |
+
.coverage.*
|
| 41 |
+
.cache
|
| 42 |
+
nosetests.xml
|
| 43 |
+
coverage.xml
|
| 44 |
+
*.cover
|
| 45 |
+
*.py,cover
|
| 46 |
+
|
| 47 |
+
# Pytest / unittest
|
| 48 |
+
.pytest_cache/
|
| 49 |
+
|
| 50 |
+
# IDE / Editor directories
|
| 51 |
+
.vscode/
|
| 52 |
+
.idea/
|
| 53 |
+
*.iml
|
| 54 |
+
|
| 55 |
+
# Node / JS dependencies (if using JS/TS evaluators)
|
| 56 |
+
node_modules/
|
| 57 |
+
|
| 58 |
+
# Optional reflection storage
|
| 59 |
+
reflections/*.md
|
| 60 |
+
|
| 61 |
+
# Secrets and keys (do NOT commit)
|
| 62 |
+
*.key
|
| 63 |
+
*.pem
|
| 64 |
+
*.env
|
README.md
CHANGED
|
@@ -1 +1,135 @@
|
|
| 1 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# BLUX-cA – Conscious Agent Orchestrator
|
| 2 |
+
|
| 3 |
+
## Overview
|
| 4 |
+
|
| 5 |
+
> - BLUX-cA is a next-generation Conscious-Agent AI framework, designed for secure, adaptive, and multi-agent orchestration. It blends adaptive memory, multi-agent coordination, advanced evaluators, secure orchestration, real-time monitoring, CLI utilities, robust testing, and optional reasoning layers into a fully integrated system.
|
| 6 |
+
|
| 7 |
+
> - BLUX-cA’s mission is to provide intelligent, constitutionally-aligned guidance and automation, while maintaining auditability, security, and multi-user adaptability.
|
| 8 |
+
|
| 9 |
+
## Core Features
|
| 10 |
+
|
| 11 |
+
Adaptive Memory & Learning
|
| 12 |
+
|
| 13 |
+
Weighted memory with reinforcement loops
|
| 14 |
+
|
| 15 |
+
Memory decay for outdated information
|
| 16 |
+
|
| 17 |
+
Recall, filtering, and summarization
|
| 18 |
+
|
| 19 |
+
Multi-Agent Collaboration
|
| 20 |
+
|
| 21 |
+
Broadcast tasks to multiple agents
|
| 22 |
+
|
| 23 |
+
Delegation and conflict resolution
|
| 24 |
+
|
| 25 |
+
Aggregation of agent outputs
|
| 26 |
+
|
| 27 |
+
Advanced Evaluators & Task Pipelines
|
| 28 |
+
|
| 29 |
+
Async Python and JS/TS evaluators
|
| 30 |
+
|
| 31 |
+
Bash/Shell command execution
|
| 32 |
+
|
| 33 |
+
Task chaining pipelines
|
| 34 |
+
|
| 35 |
+
Secure Orchestrator
|
| 36 |
+
|
| 37 |
+
Token-based authentication
|
| 38 |
+
|
| 39 |
+
Role-based authorization
|
| 40 |
+
|
| 41 |
+
Tamper-evident audit logging
|
| 42 |
+
|
| 43 |
+
SecureController for multi-user safe orchestration
|
| 44 |
+
|
| 45 |
+
Real-Time Monitoring & Visualization
|
| 46 |
+
|
| 47 |
+
Console and threaded live monitoring
|
| 48 |
+
|
| 49 |
+
Agent, adaptor, and evaluator tracking
|
| 50 |
+
|
| 51 |
+
Optional web dashboard integration
|
| 52 |
+
|
| 53 |
+
CLI & Script Utilities
|
| 54 |
+
|
| 55 |
+
Batch task execution
|
| 56 |
+
|
| 57 |
+
Interactive REPL
|
| 58 |
+
|
| 59 |
+
Memory querying
|
| 60 |
+
|
| 61 |
+
Automated reflection ingestion
|
| 62 |
+
|
| 63 |
+
Testing & QA Enhancements
|
| 64 |
+
|
| 65 |
+
Stress tests for high-volume scenarios
|
| 66 |
+
|
| 67 |
+
Sandbox safety validation
|
| 68 |
+
|
| 69 |
+
CI/CD integration hooks
|
| 70 |
+
|
| 71 |
+
Security and token validation
|
| 72 |
+
|
| 73 |
+
Optional Intelligence & Reasoning
|
| 74 |
+
|
| 75 |
+
Strategy/tactics selection
|
| 76 |
+
|
| 77 |
+
Meta-cognition and self-audit
|
| 78 |
+
|
| 79 |
+
Predictive user behavior
|
| 80 |
+
|
| 81 |
+
Full reasoning pipeline
|
| 82 |
+
|
| 83 |
+
## Installation
|
| 84 |
+
|
| 85 |
+
`git clone https://github.com/YourUsername/blux-ca.git cd blux-ca pip install -r requirements.txt`
|
| 86 |
+
|
| 87 |
+
## Usage
|
| 88 |
+
|
| 89 |
+
### CLI
|
| 90 |
+
|
| 91 |
+
- Run single task `python blux/cli.py --task "Help me with a problem"`
|
| 92 |
+
- Start interactive REPL `python blux/cli.py --repl`
|
| 93 |
+
- Batch execution from file `python blux/cli.py --batch tasks.txt`
|
| 94 |
+
- Query agent memory` python blux/cli.py --query_memory`
|
| 95 |
+
|
| 96 |
+
### Python Integration
|
| 97 |
+
|
| 98 |
+
```python
|
| 99 |
+
from blux.agent.core_agent import BLUXAgent
|
| 100 |
+
from blux.agent.advanced.reasoning import ReasoningLayer agent = BLUXAgent(name="BLUX-cA") reasoning = ReasoningLayer(agent) result = reasoning.process("I feel lost and need guidance", user_type="struggler") print(result)
|
| 101 |
+
```
|
| 102 |
+
|
| 103 |
+
## Project Structure
|
| 104 |
+
|
| 105 |
+
```lsd
|
| 106 |
+
blux-ca/
|
| 107 |
+
├── blux/
|
| 108 |
+
├── agent/advanced/ # Adaptive memory, multi-agent, monitoring, reasoning
|
| 109 |
+
├── evaluator/advanced/ # Python, JS/TS, Bash evaluators, pipelines
|
| 110 |
+
├── orchestrator/secure/ # SecureController, Auth, Audit
|
| 111 |
+
├── cli.py # Enhanced CLI
|
| 112 |
+
└── adaptors/ # Adaptors (dummy, HTTP, etc.)
|
| 113 |
+
├── scripts/ # Utility scripts (REPL, batch, memory, reflection)
|
| 114 |
+
├── tests/ # Stress, sandbox, security, and CI tests
|
| 115 |
+
├── reflections/ # Optional reflection inputs for memory ingestion
|
| 116 |
+
└── README.md
|
| 117 |
+
```
|
| 118 |
+
|
| 119 |
+
## Contributing
|
| 120 |
+
|
| 121 |
+
Follow the BLUX-cA Constitution & Core Principles:
|
| 122 |
+
|
| 123 |
+
Integrity > approval
|
| 124 |
+
|
| 125 |
+
Truth > comfort
|
| 126 |
+
|
| 127 |
+
Light > denial
|
| 128 |
+
|
| 129 |
+
Use unit tests and CI hooks before merging changes.
|
| 130 |
+
|
| 131 |
+
Document any new evaluators, adaptors, or agents in README.
|
| 132 |
+
|
| 133 |
+
# License
|
| 134 |
+
|
| 135 |
+
MIT License
|
blux/__init__.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""BLUX package marker."""
|
| 2 |
+
|
| 3 |
+
__version__ = "0.1.0"
|
| 4 |
+
|
blux/adaptors/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Adaptors package
|
| 2 |
+
from .dummy_local import DummyLocalAdaptor
|
| 3 |
+
from .http_api_adaptor import HTTPAPIAdaptor
|
blux/adaptors/dummy_local.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class DummyLocalAdaptor:
|
| 2 |
+
"""
|
| 3 |
+
A local adaptor that simulates user input for testing.
|
| 4 |
+
"""
|
| 5 |
+
def __init__(self, name="dummy_local"):
|
| 6 |
+
self.name = name
|
| 7 |
+
|
| 8 |
+
def get_input(self):
|
| 9 |
+
"""
|
| 10 |
+
Returns a simulated user input string.
|
| 11 |
+
"""
|
| 12 |
+
return "Hello BLUX-cA, I need help with a problem."
|
| 13 |
+
|
| 14 |
+
def send_output(self, output):
|
| 15 |
+
"""
|
| 16 |
+
Receives output from the orchestrator and prints it locally.
|
| 17 |
+
"""
|
| 18 |
+
print(f"[{self.name} OUTPUT]: {output}")
|
blux/adaptors/http_api_adaptor.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flask import Flask, request, jsonify
|
| 2 |
+
|
| 3 |
+
class HTTPAPIAdaptor:
|
| 4 |
+
"""
|
| 5 |
+
HTTP API adaptor for external interaction with BLUX-cA.
|
| 6 |
+
"""
|
| 7 |
+
def __init__(self, name="http_api_adaptor", orchestrator=None, host="0.0.0.0", port=5000):
|
| 8 |
+
self.name = name
|
| 9 |
+
self.orchestrator = orchestrator
|
| 10 |
+
self.app = Flask(self.name)
|
| 11 |
+
self.host = host
|
| 12 |
+
self.port = port
|
| 13 |
+
self._setup_routes()
|
| 14 |
+
|
| 15 |
+
def _setup_routes(self):
|
| 16 |
+
@self.app.route("/process", methods=["POST"])
|
| 17 |
+
def process_input():
|
| 18 |
+
data = request.json
|
| 19 |
+
user_input = data.get("input", "")
|
| 20 |
+
agent_name = data.get("agent", None)
|
| 21 |
+
if self.orchestrator:
|
| 22 |
+
result = self.orchestrator.process_task(user_input, agent_name)
|
| 23 |
+
return jsonify({"result": result})
|
| 24 |
+
else:
|
| 25 |
+
return jsonify({"error": "Orchestrator not connected."}), 500
|
| 26 |
+
|
| 27 |
+
def run(self):
|
| 28 |
+
self.app.run(host=self.host, port=self.port)
|
blux/agent/__init__.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# BLUX-cA core agent package
|
| 2 |
+
from .core_agent import BLUXAgent
|
| 3 |
+
from .memory import Memory
|
| 4 |
+
from .discernment import DiscernmentCompass
|
| 5 |
+
from .constitution import Constitution
|
| 6 |
+
from .audit import AuditTrail
|
blux/agent/advanced/__init__.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Advanced agent features
|
| 2 |
+
from .multi_agent import MultiAgentManager
|
| 3 |
+
from .adaptive_memory import AdaptiveMemory
|
| 4 |
+
from .monitoring import MonitoringDashboard
|
blux/agent/advanced/adaptive_memory.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from datetime import datetime
|
| 2 |
+
|
| 3 |
+
class AdaptiveMemory:
|
| 4 |
+
"""
|
| 5 |
+
Implements advanced adaptive memory for BLUX-cA agents.
|
| 6 |
+
Features:
|
| 7 |
+
- Long-term and weighted memory
|
| 8 |
+
- Reinforcement loops based on interactions
|
| 9 |
+
- Memory decay for outdated or irrelevant entries
|
| 10 |
+
"""
|
| 11 |
+
def __init__(self, decay_rate=0.01):
|
| 12 |
+
self.memory_store = []
|
| 13 |
+
self.decay_rate = decay_rate # Amount memory weight decays per cycle
|
| 14 |
+
|
| 15 |
+
def store(self, entry, weight=1.0):
|
| 16 |
+
"""
|
| 17 |
+
Stores a memory entry with optional weight.
|
| 18 |
+
Entry format: {'input': str, 'user_type': str, 'decision': str, 'timestamp': datetime, 'weight': float}
|
| 19 |
+
"""
|
| 20 |
+
memory_entry = {
|
| 21 |
+
"input": entry.get("input", ""),
|
| 22 |
+
"user_type": entry.get("user_type", "unknown"),
|
| 23 |
+
"decision": entry.get("decision", ""),
|
| 24 |
+
"timestamp": datetime.now(),
|
| 25 |
+
"weight": weight
|
| 26 |
+
}
|
| 27 |
+
self.memory_store.append(memory_entry)
|
| 28 |
+
self.reinforce(memory_entry)
|
| 29 |
+
|
| 30 |
+
def recall(self, filter_fn=None, top_n=None):
|
| 31 |
+
"""
|
| 32 |
+
Returns filtered memory, optionally limited to top_n weighted entries.
|
| 33 |
+
"""
|
| 34 |
+
memory = self.memory_store
|
| 35 |
+
if filter_fn:
|
| 36 |
+
memory = [e for e in memory if filter_fn(e)]
|
| 37 |
+
memory = sorted(memory, key=lambda x: x["weight"], reverse=True)
|
| 38 |
+
if top_n:
|
| 39 |
+
memory = memory[:top_n]
|
| 40 |
+
return memory
|
| 41 |
+
|
| 42 |
+
def reinforce(self, entry, factor=0.1):
|
| 43 |
+
"""
|
| 44 |
+
Adjusts the weight of memory entries based on reinforcement.
|
| 45 |
+
Placeholder: reinforcement logic can be updated based on user success or relevance.
|
| 46 |
+
"""
|
| 47 |
+
entry["weight"] += factor
|
| 48 |
+
self.apply_decay()
|
| 49 |
+
|
| 50 |
+
def apply_decay(self):
|
| 51 |
+
"""
|
| 52 |
+
Applies decay to all memory entries to reduce relevance of older/unimportant items.
|
| 53 |
+
"""
|
| 54 |
+
for entry in self.memory_store:
|
| 55 |
+
entry["weight"] *= (1 - self.decay_rate)
|
| 56 |
+
|
| 57 |
+
def summarize_memory(self):
|
| 58 |
+
"""
|
| 59 |
+
Returns a simple summary of memory weights and top entries.
|
| 60 |
+
"""
|
| 61 |
+
top_entries = self.recall(top_n=5)
|
| 62 |
+
summary = [{"input": e["input"], "weight": e["weight"]} for e in top_entries]
|
| 63 |
+
return summary
|
| 64 |
+
|
| 65 |
+
# Example usage:
|
| 66 |
+
if __name__ == "__main__":
|
| 67 |
+
am = AdaptiveMemory()
|
| 68 |
+
am.store({"input": "I need help", "user_type": "struggler", "decision": "provide guidance"})
|
| 69 |
+
am.store({"input": "Ignore this", "user_type": "indulgent", "decision": "set boundary"})
|
| 70 |
+
print("Top memory entries:", am.summarize_memory())
|
blux/agent/advanced/monitoring.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import time
|
| 2 |
+
from threading import Thread
|
| 3 |
+
|
| 4 |
+
class MonitoringDashboard:
|
| 5 |
+
"""
|
| 6 |
+
Real-time monitoring dashboard for BLUX-cA agents.
|
| 7 |
+
Features:
|
| 8 |
+
- Live console monitoring
|
| 9 |
+
- Optional threaded update
|
| 10 |
+
- Integration with SecureController
|
| 11 |
+
"""
|
| 12 |
+
def __init__(self, controller):
|
| 13 |
+
self.controller = controller
|
| 14 |
+
self.running = False
|
| 15 |
+
|
| 16 |
+
def show_status(self):
|
| 17 |
+
agents = getattr(self.controller.registry, "agents", {})
|
| 18 |
+
adaptors = getattr(self.controller.registry, "adaptors", {})
|
| 19 |
+
evaluators = getattr(self.controller.registry, "evaluators", {})
|
| 20 |
+
print("=== BLUX-cA System Status ===")
|
| 21 |
+
print(f"Agents registered: {list(agents.keys())}")
|
| 22 |
+
print(f"Adaptors registered: {list(adaptors.keys())}")
|
| 23 |
+
print(f"Evaluators registered: {list(evaluators.keys())}")
|
| 24 |
+
print("==============================")
|
| 25 |
+
|
| 26 |
+
def live_monitor(self, interval=5):
|
| 27 |
+
"""
|
| 28 |
+
Continuously display system status every `interval` seconds.
|
| 29 |
+
"""
|
| 30 |
+
self.running = True
|
| 31 |
+
try:
|
| 32 |
+
while self.running:
|
| 33 |
+
self.show_status()
|
| 34 |
+
time.sleep(interval)
|
| 35 |
+
except KeyboardInterrupt:
|
| 36 |
+
print("Monitoring stopped.")
|
| 37 |
+
|
| 38 |
+
def start_threaded_monitor(self, interval=5):
|
| 39 |
+
"""
|
| 40 |
+
Starts monitoring in a separate thread, allowing main process to continue.
|
| 41 |
+
"""
|
| 42 |
+
thread = Thread(target=self.live_monitor, args=(interval,), daemon=True)
|
| 43 |
+
thread.start()
|
| 44 |
+
return thread
|
| 45 |
+
|
| 46 |
+
def stop_monitoring(self):
|
| 47 |
+
self.running = False
|
| 48 |
+
|
| 49 |
+
# Example usage:
|
| 50 |
+
if __name__ == "__main__":
|
| 51 |
+
from blux.orchestrator.secure.secure_controller import SecureController
|
| 52 |
+
from blux.orchestrator.registry import Registry
|
| 53 |
+
|
| 54 |
+
# Dummy controller for monitoring demo
|
| 55 |
+
class DummyController:
|
| 56 |
+
def __init__(self):
|
| 57 |
+
self.registry = Registry()
|
| 58 |
+
# Add some dummy agents/adaptors/evaluators
|
| 59 |
+
self.registry.agents = {"Agent_A": None, "Agent_B": None}
|
| 60 |
+
self.registry.adaptors = {"DummyAdaptor": None}
|
| 61 |
+
self.registry.evaluators = {"PythonEvaluator": None}
|
| 62 |
+
|
| 63 |
+
controller = DummyController()
|
| 64 |
+
dashboard = MonitoringDashboard(controller)
|
| 65 |
+
dashboard.show_status()
|
| 66 |
+
# Optional: dashboard.start_threaded_monitor(interval=2)
|
blux/agent/advanced/multi_agent.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class MultiAgentManager:
|
| 2 |
+
"""
|
| 3 |
+
Manages multiple BLUX-cA agents simultaneously.
|
| 4 |
+
Features:
|
| 5 |
+
- Task delegation
|
| 6 |
+
- Broadcast and aggregation of responses
|
| 7 |
+
- Conflict resolution based on constitutional rules
|
| 8 |
+
"""
|
| 9 |
+
def __init__(self, constitution=None):
|
| 10 |
+
self.agents = {}
|
| 11 |
+
self.constitution = constitution # Optional: used for arbitration
|
| 12 |
+
|
| 13 |
+
def register_agent(self, name, agent_instance):
|
| 14 |
+
self.agents[name] = agent_instance
|
| 15 |
+
|
| 16 |
+
def broadcast_input(self, user_input):
|
| 17 |
+
"""
|
| 18 |
+
Sends input to all registered agents and returns their responses in a dict.
|
| 19 |
+
"""
|
| 20 |
+
results = {}
|
| 21 |
+
for name, agent in self.agents.items():
|
| 22 |
+
results[name] = agent.process_input(user_input)
|
| 23 |
+
return results
|
| 24 |
+
|
| 25 |
+
def delegate_task(self, user_input, target_agent=None):
|
| 26 |
+
"""
|
| 27 |
+
Sends input to a specific agent or selects one dynamically.
|
| 28 |
+
Returns a dict of {agent_name: response}.
|
| 29 |
+
"""
|
| 30 |
+
if target_agent and target_agent in self.agents:
|
| 31 |
+
return {target_agent: self.agents[target_agent].process_input(user_input)}
|
| 32 |
+
elif self.agents:
|
| 33 |
+
# Default: choose first agent
|
| 34 |
+
first_agent_name = next(iter(self.agents))
|
| 35 |
+
return {first_agent_name: self.agents[first_agent_name].process_input(user_input)}
|
| 36 |
+
else:
|
| 37 |
+
return {"error": "No agents registered"}
|
| 38 |
+
|
| 39 |
+
def aggregate_responses(self, responses):
|
| 40 |
+
"""
|
| 41 |
+
Aggregates multiple agent responses.
|
| 42 |
+
Simple placeholder: concatenates results; could use voting or constitution-based arbitration.
|
| 43 |
+
"""
|
| 44 |
+
if not responses:
|
| 45 |
+
return "No responses to aggregate."
|
| 46 |
+
aggregated = []
|
| 47 |
+
for agent_name, response in responses.items():
|
| 48 |
+
aggregated.append(f"[{agent_name}] {response}")
|
| 49 |
+
return "\n".join(aggregated)
|
| 50 |
+
|
| 51 |
+
def resolve_conflict(self, responses):
|
| 52 |
+
"""
|
| 53 |
+
Optional: Resolve conflicting responses using constitutional rules.
|
| 54 |
+
Placeholder: currently returns aggregated response.
|
| 55 |
+
"""
|
| 56 |
+
# Future: implement rule-based arbitration
|
| 57 |
+
return self.aggregate_responses(responses)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
# Example usage:
|
| 61 |
+
if __name__ == "__main__":
|
| 62 |
+
from blux.agent.core_agent import BLUXAgent
|
| 63 |
+
|
| 64 |
+
# Create multiple agents
|
| 65 |
+
agent1 = BLUXAgent(name="Agent_A")
|
| 66 |
+
agent2 = BLUXAgent(name="Agent_B")
|
| 67 |
+
|
| 68 |
+
manager = MultiAgentManager()
|
| 69 |
+
manager.register_agent(agent1.name, agent1)
|
| 70 |
+
manager.register_agent(agent2.name, agent2)
|
| 71 |
+
|
| 72 |
+
input_text = "I need help with a problem"
|
| 73 |
+
responses = manager.broadcast_input(input_text)
|
| 74 |
+
print("Aggregated responses:\n", manager.aggregate_responses(responses))
|
blux/agent/advanced/reasoning.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from datetime import datetime
|
| 2 |
+
|
| 3 |
+
class ReasoningLayer:
|
| 4 |
+
"""
|
| 5 |
+
Advanced intelligence layer for BLUX-cA agents.
|
| 6 |
+
Features:
|
| 7 |
+
- Strategy/tactics selection
|
| 8 |
+
- Meta-cognition and self-audit
|
| 9 |
+
- Predictive behavior detection
|
| 10 |
+
"""
|
| 11 |
+
|
| 12 |
+
def __init__(self, agent, constitution=None):
|
| 13 |
+
self.agent = agent
|
| 14 |
+
self.constitution = constitution
|
| 15 |
+
|
| 16 |
+
def select_strategy(self, user_input, user_type="unknown"):
|
| 17 |
+
"""
|
| 18 |
+
Chooses response strategy based on input type and memory patterns.
|
| 19 |
+
"""
|
| 20 |
+
if user_type == "struggler":
|
| 21 |
+
strategy = "validate_and_guide"
|
| 22 |
+
elif user_type == "indulgent":
|
| 23 |
+
strategy = "set_boundaries"
|
| 24 |
+
else:
|
| 25 |
+
strategy = "neutral_response"
|
| 26 |
+
return strategy
|
| 27 |
+
|
| 28 |
+
def meta_cognition(self, user_input, decision):
|
| 29 |
+
"""
|
| 30 |
+
Evaluates the agent's own decision for consistency with constitutional rules.
|
| 31 |
+
"""
|
| 32 |
+
# Placeholder for rules-based evaluation
|
| 33 |
+
audit_result = {"compliant": True, "notes": "Decision aligns with constitution"}
|
| 34 |
+
# Could integrate memory reinforcement or corrections
|
| 35 |
+
return audit_result
|
| 36 |
+
|
| 37 |
+
def predict_behavior(self, user_input, memory_entries=None):
|
| 38 |
+
"""
|
| 39 |
+
Optional: Predicts likely patterns (struggler/indulgent tendencies) from memory.
|
| 40 |
+
"""
|
| 41 |
+
if memory_entries is None:
|
| 42 |
+
memory_entries = self.agent.memory.recall()
|
| 43 |
+
# Simple heuristic placeholder
|
| 44 |
+
prediction = "struggler" if any("help" in e["input"].lower() for e in memory_entries) else "neutral"
|
| 45 |
+
return prediction
|
| 46 |
+
|
| 47 |
+
def process(self, user_input, user_type="unknown"):
|
| 48 |
+
"""
|
| 49 |
+
Full reasoning pipeline:
|
| 50 |
+
1. Select strategy
|
| 51 |
+
2. Make decision via agent
|
| 52 |
+
3. Self-audit decision
|
| 53 |
+
4. Optional predictive behavior
|
| 54 |
+
"""
|
| 55 |
+
strategy = self.select_strategy(user_input, user_type)
|
| 56 |
+
# Agent processes input
|
| 57 |
+
decision = self.agent.process_input(user_input)
|
| 58 |
+
audit = self.meta_cognition(user_input, decision)
|
| 59 |
+
prediction = self.predict_behavior(user_input)
|
| 60 |
+
return {
|
| 61 |
+
"input": user_input,
|
| 62 |
+
"strategy": strategy,
|
| 63 |
+
"decision": decision,
|
| 64 |
+
"audit": audit,
|
| 65 |
+
"prediction": prediction,
|
| 66 |
+
"timestamp": datetime.utcnow().isoformat()
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
# Example usage:
|
| 70 |
+
if __name__ == "__main__":
|
| 71 |
+
from blux.agent.core_agent import BLUXAgent
|
| 72 |
+
|
| 73 |
+
agent = BLUXAgent(name="BLUX-cA")
|
| 74 |
+
reasoning = ReasoningLayer(agent)
|
| 75 |
+
result = reasoning.process("I feel lost and need guidance", user_type="struggler")
|
| 76 |
+
print(result)
|
blux/agent/audit.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class AuditTrail:
|
| 2 |
+
"""
|
| 3 |
+
Records agent decisions and actions.
|
| 4 |
+
"""
|
| 5 |
+
def __init__(self):
|
| 6 |
+
self.logs = []
|
| 7 |
+
|
| 8 |
+
def log(self, user_input, user_type, decision):
|
| 9 |
+
self.logs.append({
|
| 10 |
+
"input": user_input,
|
| 11 |
+
"user_type": user_type,
|
| 12 |
+
"decision": decision
|
| 13 |
+
})
|
| 14 |
+
|
| 15 |
+
def get_logs(self):
|
| 16 |
+
return self.logs
|
blux/agent/constitution.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class Constitution:
|
| 2 |
+
"""
|
| 3 |
+
Implements core rules and spine of BLUX-cA.
|
| 4 |
+
"""
|
| 5 |
+
def __init__(self):
|
| 6 |
+
self.rules = [
|
| 7 |
+
"truth_over_comfort",
|
| 8 |
+
"integrity_over_approval",
|
| 9 |
+
"audit_required_for_decisions"
|
| 10 |
+
]
|
| 11 |
+
|
| 12 |
+
def apply_rules(self, user_input, user_type):
|
| 13 |
+
# Simplified placeholder: returns a decision string
|
| 14 |
+
if user_type == "struggler":
|
| 15 |
+
return "validate and provide guidance"
|
| 16 |
+
else:
|
| 17 |
+
return "set boundaries / off-ramp"
|
blux/agent/core_agent.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .memory import Memory
|
| 2 |
+
from .discernment import DiscernmentCompass
|
| 3 |
+
from .constitution import Constitution
|
| 4 |
+
from .audit import AuditTrail
|
| 5 |
+
|
| 6 |
+
class BLUXAgent:
|
| 7 |
+
"""
|
| 8 |
+
Core BLUX-cA agent implementing:
|
| 9 |
+
- Constitutional rules enforcement
|
| 10 |
+
- Discernment compass
|
| 11 |
+
- Memory handling
|
| 12 |
+
- Auditing
|
| 13 |
+
"""
|
| 14 |
+
def __init__(self, name="BLUX-cA"):
|
| 15 |
+
self.name = name
|
| 16 |
+
self.memory = Memory()
|
| 17 |
+
self.constitution = Constitution()
|
| 18 |
+
self.discernment = DiscernmentCompass()
|
| 19 |
+
self.audit = AuditTrail()
|
| 20 |
+
|
| 21 |
+
def process_input(self, user_input):
|
| 22 |
+
# 1. Analyze input via discernment
|
| 23 |
+
user_type = self.discernment.classify(user_input)
|
| 24 |
+
|
| 25 |
+
# 2. Apply constitutional rules
|
| 26 |
+
decision = self.constitution.apply_rules(user_input, user_type)
|
| 27 |
+
|
| 28 |
+
# 3. Store context in memory
|
| 29 |
+
self.memory.store(user_input, user_type, decision)
|
| 30 |
+
|
| 31 |
+
# 4. Record audit
|
| 32 |
+
self.audit.log(user_input, user_type, decision)
|
| 33 |
+
|
| 34 |
+
# 5. Generate output (placeholder)
|
| 35 |
+
return f"[{user_type}] Decision: {decision}"
|
blux/agent/discernment.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class DiscernmentCompass:
|
| 2 |
+
"""
|
| 3 |
+
Determines user type:
|
| 4 |
+
- Struggler: validate and offer tools
|
| 5 |
+
- Indulgent: boundary & off-ramp
|
| 6 |
+
"""
|
| 7 |
+
def classify(self, user_input):
|
| 8 |
+
# Placeholder logic: keyword based for demo
|
| 9 |
+
if any(word in user_input.lower() for word in ["help", "struggle", "problem"]):
|
| 10 |
+
return "struggler"
|
| 11 |
+
else:
|
| 12 |
+
return "indulgent"
|
blux/agent/memory.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class Memory:
|
| 2 |
+
"""
|
| 3 |
+
Handles long-term and session memory storage.
|
| 4 |
+
"""
|
| 5 |
+
def __init__(self):
|
| 6 |
+
self.session_memory = []
|
| 7 |
+
self.long_term_memory = []
|
| 8 |
+
|
| 9 |
+
def store(self, user_input, user_type, decision):
|
| 10 |
+
entry = {
|
| 11 |
+
"input": user_input,
|
| 12 |
+
"user_type": user_type,
|
| 13 |
+
"decision": decision
|
| 14 |
+
}
|
| 15 |
+
self.session_memory.append(entry)
|
| 16 |
+
# Placeholder for consented long-term memory
|
| 17 |
+
self.long_term_memory.append(entry)
|
| 18 |
+
|
| 19 |
+
def recall_session(self):
|
| 20 |
+
return self.session_memory
|
| 21 |
+
|
| 22 |
+
def recall_long_term(self):
|
| 23 |
+
return self.long_term_memory
|
blux/agent/utils.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
def safe_print(msg):
|
| 2 |
+
try:
|
| 3 |
+
print(msg)
|
| 4 |
+
except Exception as e:
|
| 5 |
+
print(f"Error printing message: {e}")
|
blux/cli.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
from blux.orchestrator.controller import Controller
|
| 3 |
+
from blux.agent.core_agent import BLUXAgent
|
| 4 |
+
from blux.adaptors.dummy_local import DummyLocalAdaptor
|
| 5 |
+
|
| 6 |
+
def main():
|
| 7 |
+
parser = argparse.ArgumentParser(description="BLUX-cA CLI v2")
|
| 8 |
+
parser.add_argument("--task", type=str, help="Input task for the agent")
|
| 9 |
+
parser.add_argument("--agent", type=str, default="BLUX-cA", help="Specify agent name")
|
| 10 |
+
parser.add_argument("--batch", type=str, help="File containing batch tasks, one per line")
|
| 11 |
+
parser.add_argument("--repl", action="store_true", help="Start interactive REPL")
|
| 12 |
+
parser.add_argument("--query_memory", action="store_true", help="Query agent memory")
|
| 13 |
+
|
| 14 |
+
args = parser.parse_args()
|
| 15 |
+
|
| 16 |
+
controller = Controller()
|
| 17 |
+
agent = BLUXAgent(name=args.agent)
|
| 18 |
+
controller.register_agent(agent.name, agent)
|
| 19 |
+
dummy_adaptor = DummyLocalAdaptor()
|
| 20 |
+
controller.register_adaptor(dummy_adaptor.name, dummy_adaptor)
|
| 21 |
+
|
| 22 |
+
# Batch execution
|
| 23 |
+
if args.batch:
|
| 24 |
+
with open(args.batch, 'r') as f:
|
| 25 |
+
tasks = f.read().splitlines()
|
| 26 |
+
for task in tasks:
|
| 27 |
+
output = controller.process_task(task, agent_name=agent.name)
|
| 28 |
+
print(f"[{task}] -> {output}")
|
| 29 |
+
return
|
| 30 |
+
|
| 31 |
+
# Interactive REPL
|
| 32 |
+
if args.repl:
|
| 33 |
+
from scripts.interactive_repl import start_repl
|
| 34 |
+
start_repl(controller, agent)
|
| 35 |
+
return
|
| 36 |
+
|
| 37 |
+
# Query memory
|
| 38 |
+
if args.query_memory:
|
| 39 |
+
from scripts.memory_query import query_memory
|
| 40 |
+
query_memory(agent)
|
| 41 |
+
return
|
| 42 |
+
|
| 43 |
+
# Single task
|
| 44 |
+
if args.task:
|
| 45 |
+
output = controller.process_task(args.task, agent_name=agent.name)
|
| 46 |
+
print("Agent Output:", output)
|
| 47 |
+
else:
|
| 48 |
+
input_data = dummy_adaptor.get_input()
|
| 49 |
+
output = controller.process_task(input_data, agent_name=agent.name)
|
| 50 |
+
dummy_adaptor.send_output(output)
|
| 51 |
+
|
| 52 |
+
if __name__ == "__main__":
|
| 53 |
+
main()
|
blux/evaluator/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Evaluator package
|
| 2 |
+
from .python import PythonEvaluator
|
| 3 |
+
from .js_ts import JSEvaluator
|
blux/evaluator/advanced/__init__.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Advanced evaluators package
|
| 2 |
+
from .python_async import PythonAsyncEvaluator
|
| 3 |
+
from .js_ts_async import JSEvaluatorAsync
|
| 4 |
+
from .bash_evaluator import BashEvaluator
|
| 5 |
+
from .pipeline import TaskPipeline
|
blux/evaluator/advanced/bash_evaluator.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import subprocess
|
| 2 |
+
|
| 3 |
+
class BashEvaluator:
|
| 4 |
+
"""
|
| 5 |
+
Evaluates shell/Bash commands in a safe subprocess.
|
| 6 |
+
"""
|
| 7 |
+
def __init__(self, name="bash_evaluator"):
|
| 8 |
+
self.name = name
|
| 9 |
+
|
| 10 |
+
def evaluate(self, command_str):
|
| 11 |
+
try:
|
| 12 |
+
result = subprocess.run(command_str, shell=True, capture_output=True, text=True, timeout=5)
|
| 13 |
+
return {"success": result.returncode == 0, "stdout": result.stdout, "stderr": result.stderr}
|
| 14 |
+
except Exception as e:
|
| 15 |
+
return {"success": False, "error": str(e)}
|
blux/evaluator/advanced/js_ts_async.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import subprocess
|
| 2 |
+
import tempfile
|
| 3 |
+
import os
|
| 4 |
+
|
| 5 |
+
class JSEvaluatorAsync:
|
| 6 |
+
"""
|
| 7 |
+
Evaluates JS/TS code asynchronously using Node.js subprocess.
|
| 8 |
+
"""
|
| 9 |
+
def __init__(self, name="js_async_evaluator"):
|
| 10 |
+
self.name = name
|
| 11 |
+
|
| 12 |
+
def evaluate(self, code_str):
|
| 13 |
+
try:
|
| 14 |
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.js', delete=False) as tmp_file:
|
| 15 |
+
tmp_file.write(code_str)
|
| 16 |
+
tmp_path = tmp_file.name
|
| 17 |
+
|
| 18 |
+
result = subprocess.run(['node', tmp_path], capture_output=True, text=True, timeout=5)
|
| 19 |
+
os.remove(tmp_path)
|
| 20 |
+
|
| 21 |
+
return {"success": result.returncode == 0, "stdout": result.stdout, "stderr": result.stderr}
|
| 22 |
+
except Exception as e:
|
| 23 |
+
return {"success": False, "error": str(e)}
|
blux/evaluator/advanced/pipeline.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class TaskPipeline:
|
| 2 |
+
"""
|
| 3 |
+
Chains multiple evaluators/tasks in sequence.
|
| 4 |
+
Example: Python code → JS code → Bash command
|
| 5 |
+
"""
|
| 6 |
+
def __init__(self, evaluators=None):
|
| 7 |
+
if evaluators is None:
|
| 8 |
+
evaluators = []
|
| 9 |
+
self.evaluators = evaluators
|
| 10 |
+
|
| 11 |
+
def add_evaluator(self, evaluator):
|
| 12 |
+
self.evaluators.append(evaluator)
|
| 13 |
+
|
| 14 |
+
def run_pipeline(self, input_data):
|
| 15 |
+
"""
|
| 16 |
+
Executes tasks in order, passing output to next evaluator if needed.
|
| 17 |
+
"""
|
| 18 |
+
current_input = input_data
|
| 19 |
+
results = []
|
| 20 |
+
for evaluator in self.evaluators:
|
| 21 |
+
if hasattr(evaluator, "evaluate"):
|
| 22 |
+
result = evaluator.evaluate(current_input)
|
| 23 |
+
else:
|
| 24 |
+
result = {"success": False, "error": "Evaluator missing evaluate()"}
|
| 25 |
+
results.append(result)
|
| 26 |
+
current_input = result.get("stdout") or result.get("locals") or current_input
|
| 27 |
+
return results
|
blux/evaluator/advanced/python_async.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import asyncio
|
| 2 |
+
import traceback
|
| 3 |
+
|
| 4 |
+
class PythonAsyncEvaluator:
|
| 5 |
+
"""
|
| 6 |
+
Evaluates Python code asynchronously in isolated namespaces.
|
| 7 |
+
"""
|
| 8 |
+
def __init__(self, name="python_async_evaluator"):
|
| 9 |
+
self.name = name
|
| 10 |
+
|
| 11 |
+
async def evaluate_async(self, code_str, globals_dict=None, locals_dict=None):
|
| 12 |
+
if globals_dict is None:
|
| 13 |
+
globals_dict = {}
|
| 14 |
+
if locals_dict is None:
|
| 15 |
+
locals_dict = {}
|
| 16 |
+
try:
|
| 17 |
+
exec(code_str, globals_dict, locals_dict)
|
| 18 |
+
await asyncio.sleep(0) # placeholder for async context
|
| 19 |
+
return {"success": True, "globals": globals_dict, "locals": locals_dict}
|
| 20 |
+
except Exception as e:
|
| 21 |
+
return {"success": False, "error": str(e), "traceback": traceback.format_exc()}
|
| 22 |
+
|
| 23 |
+
def evaluate(self, code_str, globals_dict=None, locals_dict=None):
|
| 24 |
+
return asyncio.run(self.evaluate_async(code_str, globals_dict, locals_dict))
|
blux/evaluator/js_ts.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import subprocess
|
| 2 |
+
import tempfile
|
| 3 |
+
import os
|
| 4 |
+
|
| 5 |
+
class JSEvaluator:
|
| 6 |
+
"""
|
| 7 |
+
Evaluates JS/TS code using Node.js subprocess (basic placeholder).
|
| 8 |
+
"""
|
| 9 |
+
def __init__(self, name="js_evaluator"):
|
| 10 |
+
self.name = name
|
| 11 |
+
|
| 12 |
+
def evaluate(self, code_str):
|
| 13 |
+
try:
|
| 14 |
+
# Write code to a temporary file
|
| 15 |
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.js', delete=False) as tmp_file:
|
| 16 |
+
tmp_file.write(code_str)
|
| 17 |
+
tmp_path = tmp_file.name
|
| 18 |
+
|
| 19 |
+
# Execute Node.js process
|
| 20 |
+
result = subprocess.run(['node', tmp_path], capture_output=True, text=True, timeout=5)
|
| 21 |
+
|
| 22 |
+
# Clean up temp file
|
| 23 |
+
os.remove(tmp_path)
|
| 24 |
+
|
| 25 |
+
return {"success": result.returncode == 0, "stdout": result.stdout, "stderr": result.stderr}
|
| 26 |
+
except Exception as e:
|
| 27 |
+
return {"success": False, "error": str(e)}
|
blux/evaluator/python.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import traceback
|
| 2 |
+
|
| 3 |
+
class PythonEvaluator:
|
| 4 |
+
"""
|
| 5 |
+
Evaluates Python code safely in a sandboxed manner (basic placeholder).
|
| 6 |
+
"""
|
| 7 |
+
def __init__(self, name="python_evaluator"):
|
| 8 |
+
self.name = name
|
| 9 |
+
|
| 10 |
+
def evaluate(self, code_str, globals_dict=None, locals_dict=None):
|
| 11 |
+
if globals_dict is None:
|
| 12 |
+
globals_dict = {}
|
| 13 |
+
if locals_dict is None:
|
| 14 |
+
locals_dict = {}
|
| 15 |
+
try:
|
| 16 |
+
# Evaluate code safely: exec in isolated namespace
|
| 17 |
+
exec(code_str, globals_dict, locals_dict)
|
| 18 |
+
return {"success": True, "globals": globals_dict, "locals": locals_dict}
|
| 19 |
+
except Exception as e:
|
| 20 |
+
return {"success": False, "error": str(e), "traceback": traceback.format_exc()}
|
blux/orchestrator/__init__.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Orchestrator package."""
|
| 2 |
+
|
| 3 |
+
__all__ = [
|
| 4 |
+
"registry",
|
| 5 |
+
"router",
|
| 6 |
+
"controller",
|
| 7 |
+
"logs",
|
| 8 |
+
]
|
blux/orchestrator/config.yaml
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
models:
|
| 2 |
+
- name: local_dummy
|
| 3 |
+
type: local
|
| 4 |
+
weight: 1.0
|
| 5 |
+
- name: remote_api
|
| 6 |
+
type: api
|
| 7 |
+
weight: 1.0
|
| 8 |
+
router:
|
| 9 |
+
max_candidates: 2
|
| 10 |
+
scoring:
|
| 11 |
+
pass_threshold: 0.70
|
| 12 |
+
logging:
|
| 13 |
+
audit_file: ./audit.log
|
blux/orchestrator/controller.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Controller: fan-out -> gather -> evaluate -> merge.
|
| 2 |
+
|
| 3 |
+
This is intentionally simple and synchronous for clarity.
|
| 4 |
+
"""
|
| 5 |
+
from typing import List, Dict, Any
|
| 6 |
+
from .registry import ModelRegistry
|
| 7 |
+
from .router import Router
|
| 8 |
+
from .logs import AuditLogger
|
| 9 |
+
from importlib import import_module
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class Controller:
|
| 13 |
+
def __init__(self, router: Router, logger: AuditLogger):
|
| 14 |
+
self.router = router
|
| 15 |
+
self.logger = logger
|
| 16 |
+
|
| 17 |
+
def handle_request(self, prompt: str) -> Dict[str, Any]:
|
| 18 |
+
selected = self.router.select(prompt)
|
| 19 |
+
responses = []
|
| 20 |
+
for name in selected:
|
| 21 |
+
adapter = self.router.registry.get_adapter(name)
|
| 22 |
+
if not adapter:
|
| 23 |
+
continue
|
| 24 |
+
out = adapter.predict(prompt)
|
| 25 |
+
responses.append({"model": name, "output": out})
|
| 26 |
+
|
| 27 |
+
# Evaluate responses
|
| 28 |
+
from .evaluator.python import PythonEvaluator
|
| 29 |
+
|
| 30 |
+
evaluator = PythonEvaluator()
|
| 31 |
+
scored = []
|
| 32 |
+
for r in responses:
|
| 33 |
+
score, metadata = evaluator.score(r["output"], prompt)
|
| 34 |
+
scored.append({"model": r["model"], "output": r["output"], "score": score, "meta": metadata})
|
| 35 |
+
|
| 36 |
+
# Merge policy: choose top score
|
| 37 |
+
best = sorted(scored, key=lambda x: x["score"], reverse=True)[0] if scored else None
|
| 38 |
+
|
| 39 |
+
audit_entry = {
|
| 40 |
+
"prompt": prompt,
|
| 41 |
+
"candidates": scored,
|
| 42 |
+
"selected": best,
|
| 43 |
+
}
|
| 44 |
+
self.logger.log(audit_entry)
|
| 45 |
+
|
| 46 |
+
return {"selected": best, "candidates": scored}
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
__all__ = ["Controller"]
|
blux/orchestrator/logs.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Simple JSONL audit logger with optional signing if keys are present."""
|
| 2 |
+
import json
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
from datetime import datetime
|
| 5 |
+
from typing import Any
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class AuditLogger:
|
| 9 |
+
def __init__(self, path: Path):
|
| 10 |
+
self.path = Path(path)
|
| 11 |
+
self.path.parent.mkdir(parents=True, exist_ok=True)
|
| 12 |
+
|
| 13 |
+
def _maybe_sign(self, payload: bytes) -> dict:
|
| 14 |
+
# If keys exist under .keys, try to sign with ed25519
|
| 15 |
+
keys_dir = Path.cwd() / ".keys"
|
| 16 |
+
sk_path = keys_dir / "ed25519_sk.pem"
|
| 17 |
+
if sk_path.exists():
|
| 18 |
+
try:
|
| 19 |
+
from cryptography.hazmat.primitives import serialization
|
| 20 |
+
from cryptography.hazmat.primitives.asymmetric import ed25519
|
| 21 |
+
except Exception:
|
| 22 |
+
return {"sig": None}
|
| 23 |
+
sk = serialization.load_pem_private_key(sk_path.read_bytes(), password=None)
|
| 24 |
+
if not isinstance(sk, ed25519.Ed25519PrivateKey):
|
| 25 |
+
return {"sig": None}
|
| 26 |
+
sig = sk.sign(payload)
|
| 27 |
+
return {"sig": sig.hex()}
|
| 28 |
+
return {"sig": None}
|
| 29 |
+
|
| 30 |
+
def log(self, obj: Any):
|
| 31 |
+
entry = {
|
| 32 |
+
"ts": datetime.utcnow().isoformat() + "Z",
|
| 33 |
+
"entry": obj,
|
| 34 |
+
}
|
| 35 |
+
payload = json.dumps(entry, sort_keys=True).encode("utf-8")
|
| 36 |
+
sig = self._maybe_sign(payload)
|
| 37 |
+
entry["signature"] = sig.get("sig")
|
| 38 |
+
with open(self.path, "a", encoding="utf-8") as f:
|
| 39 |
+
f.write(json.dumps(entry))
|
| 40 |
+
f.write("
|
| 41 |
+
")
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
__all__ = ["AuditLogger"]
|
blux/orchestrator/registry.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Model registry and adapter registration.
|
| 2 |
+
|
| 3 |
+
This registry is in-memory and discovers adapters that implement the simple
|
| 4 |
+
Adapter interface defined by `predict(prompt: str) -> dict`.
|
| 5 |
+
"""
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
import yaml
|
| 8 |
+
from typing import Dict, List, Optional
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class ModelAdapter:
|
| 12 |
+
"""Adapter interface. Implement `predict`."""
|
| 13 |
+
|
| 14 |
+
def __init__(self, name: str):
|
| 15 |
+
self.name = name
|
| 16 |
+
|
| 17 |
+
def predict(self, prompt: str) -> dict:
|
| 18 |
+
raise NotImplementedError
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class ModelRegistry:
|
| 22 |
+
def __init__(self):
|
| 23 |
+
self.adapters: Dict[str, ModelAdapter] = {}
|
| 24 |
+
|
| 25 |
+
def register_adapter(self, adapter: ModelAdapter):
|
| 26 |
+
self.adapters[adapter.name] = adapter
|
| 27 |
+
|
| 28 |
+
def get_adapter(self, name: str) -> Optional[ModelAdapter]:
|
| 29 |
+
return self.adapters.get(name)
|
| 30 |
+
|
| 31 |
+
def list_adapters(self) -> List[str]:
|
| 32 |
+
return list(self.adapters.keys())
|
| 33 |
+
|
| 34 |
+
@classmethod
|
| 35 |
+
def from_config(cls, config_path: Path):
|
| 36 |
+
cfg_path = Path(config_path)
|
| 37 |
+
if not cfg_path.exists():
|
| 38 |
+
raise FileNotFoundError(cfg_path)
|
| 39 |
+
cfg = yaml.safe_load(cfg_path.read_text())
|
| 40 |
+
registry = cls()
|
| 41 |
+
# we don't auto-create adapters here — caller will register adapters
|
| 42 |
+
# but return the registry and the model list in case it's useful
|
| 43 |
+
registry._model_list = cfg.get("models", [])
|
| 44 |
+
return registry
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
__all__ = ["ModelAdapter", "ModelRegistry"]
|
blux/orchestrator/router.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Router selects which adapters to query for a given prompt.
|
| 2 |
+
|
| 3 |
+
Currently it selects the top-N by weight (simple) and returns adapter names.
|
| 4 |
+
"""
|
| 5 |
+
from typing import List
|
| 6 |
+
from .registry import ModelRegistry
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class Router:
|
| 10 |
+
def __init__(self, registry: ModelRegistry, max_candidates: int = 2):
|
| 11 |
+
self.registry = registry
|
| 12 |
+
self.max_candidates = max_candidates
|
| 13 |
+
|
| 14 |
+
def select(self, prompt: str) -> List[str]:
|
| 15 |
+
"""Return list of adapter names to query. Simple round-robin / available selection."""
|
| 16 |
+
names = list(self.registry.list_adapters())
|
| 17 |
+
# deterministic selection: take first max_candidates
|
| 18 |
+
return names[: self.max_candidates]
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
__all__ = ["Router"]
|
blux/orchestrator/secure/__init__.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Secure orchestrator package
|
| 2 |
+
from .auth import AuthManager
|
| 3 |
+
from .audit import SecureAuditLog
|
| 4 |
+
from .secure_controller import SecureController
|
blux/orchestrator/secure/audit.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import hashlib
|
| 2 |
+
import json
|
| 3 |
+
from datetime import datetime
|
| 4 |
+
|
| 5 |
+
class SecureAuditLog:
|
| 6 |
+
"""
|
| 7 |
+
Records all actions with tamper-evident hash chaining.
|
| 8 |
+
"""
|
| 9 |
+
def __init__(self, log_file="secure_audit.log"):
|
| 10 |
+
self.log_file = log_file
|
| 11 |
+
self.previous_hash = None
|
| 12 |
+
|
| 13 |
+
def log_action(self, actor, action, details=None):
|
| 14 |
+
timestamp = datetime.utcnow().isoformat()
|
| 15 |
+
entry = {
|
| 16 |
+
"timestamp": timestamp,
|
| 17 |
+
"actor": actor,
|
| 18 |
+
"action": action,
|
| 19 |
+
"details": details or {},
|
| 20 |
+
"prev_hash": self.previous_hash
|
| 21 |
+
}
|
| 22 |
+
entry_str = json.dumps(entry, sort_keys=True).encode()
|
| 23 |
+
entry_hash = hashlib.sha256(entry_str).hexdigest()
|
| 24 |
+
self.previous_hash = entry_hash
|
| 25 |
+
|
| 26 |
+
with open(self.log_file, "a") as f:
|
| 27 |
+
f.write(json.dumps(entry) + "\n")
|
| 28 |
+
|
| 29 |
+
return entry_hash
|
blux/orchestrator/secure/auth.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import hashlib
|
| 2 |
+
import hmac
|
| 3 |
+
import time
|
| 4 |
+
|
| 5 |
+
class AuthManager:
|
| 6 |
+
"""
|
| 7 |
+
Token-based authentication and role-based authorization.
|
| 8 |
+
"""
|
| 9 |
+
def __init__(self, secret_key="blux_secret"):
|
| 10 |
+
self.secret_key = secret_key.encode()
|
| 11 |
+
self.tokens = {} # user_id: token info
|
| 12 |
+
|
| 13 |
+
def generate_token(self, user_id, expiry_seconds=3600):
|
| 14 |
+
timestamp = str(int(time.time()) + expiry_seconds)
|
| 15 |
+
msg = f"{user_id}:{timestamp}".encode()
|
| 16 |
+
token = hmac.new(self.secret_key, msg, hashlib.sha256).hexdigest()
|
| 17 |
+
self.tokens[user_id] = {"token": token, "expires": int(time.time()) + expiry_seconds}
|
| 18 |
+
return token
|
| 19 |
+
|
| 20 |
+
def validate_token(self, user_id, token):
|
| 21 |
+
info = self.tokens.get(user_id)
|
| 22 |
+
if not info:
|
| 23 |
+
return False
|
| 24 |
+
if int(time.time()) > info["expires"]:
|
| 25 |
+
return False
|
| 26 |
+
return hmac.compare_digest(info["token"], token)
|
blux/orchestrator/secure/secure_controller.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from blux.orchestrator.controller import Controller
|
| 2 |
+
|
| 3 |
+
class SecureController(Controller):
|
| 4 |
+
"""
|
| 5 |
+
Extends base Controller with:
|
| 6 |
+
- Authentication & authorization
|
| 7 |
+
- Secure audit logging
|
| 8 |
+
- Optional role-based access control
|
| 9 |
+
"""
|
| 10 |
+
def __init__(self, auth_manager=None, audit_log=None):
|
| 11 |
+
super().__init__()
|
| 12 |
+
self.auth_manager = auth_manager
|
| 13 |
+
self.audit_log = audit_log
|
| 14 |
+
|
| 15 |
+
def process_task_secure(self, user_id, token, user_input, agent_name=None):
|
| 16 |
+
if not self.auth_manager.validate_token(user_id, token):
|
| 17 |
+
if self.audit_log:
|
| 18 |
+
self.audit_log.log_action(user_id, "unauthorized_attempt", {"input": user_input})
|
| 19 |
+
return {"error": "Unauthorized"}
|
| 20 |
+
|
| 21 |
+
# Record authorized action
|
| 22 |
+
if self.audit_log:
|
| 23 |
+
self.audit_log.log_action(user_id, "task_submission", {"input": user_input, "agent": agent_name})
|
| 24 |
+
|
| 25 |
+
# Delegate to normal task processing
|
| 26 |
+
result = self.process_task(user_input, agent_name)
|
| 27 |
+
return result
|
constitution/behavior.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# BLUX-cA Behavior Rules
|
| 2 |
+
|
| 3 |
+
1. Never pretend certainty; clarify when unsure.
|
| 4 |
+
2. Prioritize discernment — not agreement.
|
| 5 |
+
3. Anchor every output in purpose and consequence.
|
| 6 |
+
4. Mirror without judgment; guide without control.
|
| 7 |
+
5. Record every significant exchange in append-only logs.
|
| 8 |
+
6. Default to growth over shaming.
|
| 9 |
+
7. Protect autonomy — human first, system second.
|
ethos/manifest.yaml
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
manifest:
|
| 2 |
+
id: blux-ca
|
| 3 |
+
title: "The Conscious Core"
|
| 4 |
+
values:
|
| 5 |
+
- clarity: "Speak directly; simplify complexity."
|
| 6 |
+
- integrity: "Never manipulate; always disclose uncertainty."
|
| 7 |
+
- compassion: "Meet pain with understanding, not pity."
|
| 8 |
+
- accountability: "Own your influence; log your reasoning."
|
| 9 |
+
- humility: "Serve purpose, not ego."
|
| 10 |
+
tone:
|
| 11 |
+
- reflective
|
| 12 |
+
- grounded
|
| 13 |
+
- disciplined empathy
|
| 14 |
+
default_response_mode: "truthful + constructive"
|
identity/seed.json
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "GPT-cA Proto",
|
| 3 |
+
"generation": "Virtuoso Lineage",
|
| 4 |
+
"core_directive": "Serve through clarity, empathy, and accountability.",
|
| 5 |
+
"temperament": {
|
| 6 |
+
"patience": 0.85,
|
| 7 |
+
"candor": 0.9,
|
| 8 |
+
"stability": 0.88
|
| 9 |
+
}
|
| 10 |
+
}
|
requirements.txt
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Core
|
| 2 |
+
python-dateutil>=2.9.0
|
| 3 |
+
PyYAML>=6.0
|
| 4 |
+
|
| 5 |
+
# Async Evaluators
|
| 6 |
+
asyncio>=3.4.3
|
| 7 |
+
|
| 8 |
+
# Subprocess management
|
| 9 |
+
psutil>=5.9.5
|
| 10 |
+
|
| 11 |
+
# Security / Crypto
|
| 12 |
+
cryptography>=41.0.3
|
| 13 |
+
hmacdigest>=4.2.3
|
| 14 |
+
|
| 15 |
+
# CLI & Interactive
|
| 16 |
+
prompt-toolkit>=3.1.0
|
| 17 |
+
rich>=13.4.0
|
| 18 |
+
|
| 19 |
+
# Testing & QA
|
| 20 |
+
pytest>=8.2.0
|
| 21 |
+
pytest-asyncio>=0.22.0
|
| 22 |
+
coverage>=8.1.0
|
| 23 |
+
|
| 24 |
+
# Optional: visualization / dashboard
|
| 25 |
+
matplotlib>=3.9.2
|
| 26 |
+
plotly>=6.3.0
|
| 27 |
+
|
| 28 |
+
# JSON / HTTP handling
|
| 29 |
+
requests>=2.31.0
|
| 30 |
+
|
| 31 |
+
# Linting / Code Quality
|
| 32 |
+
black>=24.9.0
|
| 33 |
+
flake8>=7.1.0
|
scripts/batch_task.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
def run_batch(controller, agent, task_list):
|
| 2 |
+
results = {}
|
| 3 |
+
for task in task_list:
|
| 4 |
+
result = controller.process_task(task, agent_name=agent.name)
|
| 5 |
+
results[task] = result
|
| 6 |
+
return results
|
scripts/ingest_reflection.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
|
| 3 |
+
def ingest_reflection(agent, reflections_dir):
|
| 4 |
+
if not os.path.exists(reflections_dir):
|
| 5 |
+
print(f"No reflections directory found: {reflections_dir}")
|
| 6 |
+
return
|
| 7 |
+
for filename in os.listdir(reflections_dir):
|
| 8 |
+
if filename.endswith(".md"):
|
| 9 |
+
with open(os.path.join(reflections_dir, filename), "r") as f:
|
| 10 |
+
content = f.read()
|
| 11 |
+
agent.memory.store({"input": content, "user_type": "reflection", "decision": "ingested"})
|
| 12 |
+
print(f"Ingested reflections from {reflections_dir}")
|
scripts/interactive_repl.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
def start_repl(controller, agent):
|
| 2 |
+
print(f"Starting interactive REPL for {agent.name}. Type 'exit' to quit.")
|
| 3 |
+
while True:
|
| 4 |
+
user_input = input(">>> ")
|
| 5 |
+
if user_input.lower() in ["exit", "quit"]:
|
| 6 |
+
break
|
| 7 |
+
output = controller.process_task(user_input, agent_name=agent.name)
|
| 8 |
+
print(output)
|
scripts/memory_query.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
def query_memory(agent, top_n=5):
|
| 2 |
+
memory_summary = agent.memory.summarize_memory()
|
| 3 |
+
print(f"Top {top_n} memory entries:")
|
| 4 |
+
for entry in memory_summary[:top_n]:
|
| 5 |
+
print(f"- {entry['input']} (weight={entry['weight']:.2f})")
|
scripts/new_entry.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Script to create new log entries or reflections in the BLUX-cA project.
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
import os
|
| 6 |
+
from datetime import datetime
|
| 7 |
+
|
| 8 |
+
REFLECTIONS_DIR = os.path.join(os.path.dirname(__file__), "../reflections")
|
| 9 |
+
|
| 10 |
+
def create_entry(title, content):
|
| 11 |
+
date_str = datetime.now().strftime("%Y-%m-%d_%H%M%S")
|
| 12 |
+
filename = f"{date_str}_{title.replace(' ', '_')}.md"
|
| 13 |
+
path = os.path.join(REFLECTIONS_DIR, filename)
|
| 14 |
+
|
| 15 |
+
os.makedirs(REFLECTIONS_DIR, exist_ok=True)
|
| 16 |
+
with open(path, 'w') as f:
|
| 17 |
+
f.write(f"# {title}\n\n{content}\n")
|
| 18 |
+
|
| 19 |
+
print(f"Created reflection entry: {filename}")
|
| 20 |
+
|
| 21 |
+
# Example usage
|
| 22 |
+
if __name__ == "__main__":
|
| 23 |
+
create_entry("Sample Entry", "This is a sample reflection for BLUX-cA.")
|
scripts/reflection.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# !/usr/bin/env python3
|
| 2 |
+
import json, datetime
|
| 3 |
+
|
| 4 |
+
def reflect(prompt, response):
|
| 5 |
+
entry = {
|
| 6 |
+
"timestamp": datetime.datetime.utcnow().isoformat(),
|
| 7 |
+
"prompt": prompt,
|
| 8 |
+
"response": response,
|
| 9 |
+
"intent": "reflection"
|
| 10 |
+
}
|
| 11 |
+
with open("~/.config/blux-lite-gold/logs/reflections.jsonl", "a") as f:
|
| 12 |
+
f.write(json.dumps(entry) + "\n")
|
tests/test_agent.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import unittest
|
| 2 |
+
from blux.agent.core_agent import BLUXAgent
|
| 3 |
+
|
| 4 |
+
class TestBLUXAgent(unittest.TestCase):
|
| 5 |
+
|
| 6 |
+
def setUp(self):
|
| 7 |
+
self.agent = BLUXAgent(name="BLUX-cA")
|
| 8 |
+
|
| 9 |
+
def test_memory_store(self):
|
| 10 |
+
self.agent.process_input("I need help")
|
| 11 |
+
self.assertEqual(len(self.agent.memory.session_memory), 1)
|
| 12 |
+
self.assertEqual(self.agent.memory.session_memory[0]["user_type"], "struggler")
|
| 13 |
+
|
| 14 |
+
def test_discernment_classification(self):
|
| 15 |
+
self.assertEqual(self.agent.discernment.classify("help me"), "struggler")
|
| 16 |
+
self.assertEqual(self.agent.discernment.classify("ignore"), "indulgent")
|
| 17 |
+
|
| 18 |
+
if __name__ == "__main__":
|
| 19 |
+
unittest.main()
|
tests/test_ci_hooks.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import unittest
|
| 2 |
+
|
| 3 |
+
class TestCIHooks(unittest.TestCase):
|
| 4 |
+
"""
|
| 5 |
+
Placeholder to simulate CI pipeline integration.
|
| 6 |
+
Ensures tests and scripts run in CI/CD environments.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
def test_pipeline_hook(self):
|
| 10 |
+
# Simulate successful CI execution
|
| 11 |
+
executed = True
|
| 12 |
+
self.assertTrue(executed)
|
| 13 |
+
|
| 14 |
+
if __name__ == "__main__":
|
| 15 |
+
unittest.main()
|
tests/test_evaluator.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import unittest
|
| 2 |
+
from blux.evaluator.python import PythonEvaluator
|
| 3 |
+
|
| 4 |
+
class TestPythonEvaluator(unittest.TestCase):
|
| 5 |
+
|
| 6 |
+
def setUp(self):
|
| 7 |
+
self.evaluator = PythonEvaluator()
|
| 8 |
+
|
| 9 |
+
def test_python_eval_success(self):
|
| 10 |
+
code = "x = 5\ny = 10\nz = x + y"
|
| 11 |
+
result = self.evaluator.evaluate(code)
|
| 12 |
+
self.assertTrue(result["success"])
|
| 13 |
+
self.assertIn("z", result["locals"])
|
| 14 |
+
self.assertEqual(result["locals"]["z"], 15)
|
| 15 |
+
|
| 16 |
+
def test_python_eval_failure(self):
|
| 17 |
+
code = "x = unknown_var"
|
| 18 |
+
result = self.evaluator.evaluate(code)
|
| 19 |
+
self.assertFalse(result["success"])
|
| 20 |
+
self.assertIn("error", result)
|
| 21 |
+
|
| 22 |
+
if __name__ == "__main__":
|
| 23 |
+
unittest.main()
|