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