diff --git a/becomingone/core/engine.py b/becomingone/core/engine.py index ad2ba2e..d769b9a 100644 --- a/becomingone/core/engine.py +++ b/becomingone/core/engine.py @@ -371,12 +371,11 @@ class KAIROSTemporalEngine: metadata = metadata or {} # Convert input to phase - # This is a simple mapping - in practice, sophisticated - # phase extraction could be used (e.g., from transformer embeddings) - phase = self._input_to_phase(input_phrase) + # The phase is a complex angle, but we also retain the full semantic phase vector + phase_complex, full_phase_vector = self._input_to_phase(input_phrase) # Update history - self._phases.append(phase) + self._phases.append(phase_complex) self._timestamps.append(timestamp) # Compute new coherence @@ -401,7 +400,7 @@ class KAIROSTemporalEngine: self._integration_count += 1 state = TemporalState( - phase=phase, + phase=phase_complex, coherence=coherence, timestamp=timestamp, metadata={ @@ -409,40 +408,46 @@ class KAIROSTemporalEngine: "T_tau": T_tau, "collapsed": self._collapsed, "integration": self._integration_count, + "phase_vector": full_phase_vector, } ) logger.debug( f"[{self.name}] Temporalized: coherence={coherence:.3f}, " - f"phase={np.angle(phase):.3f}" + f"phase={np.angle(phase_complex):.3f}" ) return state - def _input_to_phase(self, input_phrase: str) -> complex: + def _input_to_phase(self, input_phrase: str) -> tuple[complex, list[float]]: """ - Convert input phrase to phase. + Convert input phrase to phase using semantic embeddings. - This is a simple placeholder. In a full implementation, - sophisticated phase extraction would be used. - - Current implementation: - - Uses hash of phrase to get deterministic phase - - Magnitude = 1.0 (unit phase) - - TODO: Replace with transformer-based phase extraction - TODO: Phase should reflect semantic content + Uses SentenceTransformer (via temporal memory module) to extract + a semantically meaningful phase angle, so that conceptually similar + phrases align in phase space, driving true temporal resonance. """ - import hashlib - - # Deterministic but unpredictable phase - hash_bytes = hashlib.sha256(input_phrase.encode()).digest() - hash_int = int.from_bytes(hash_bytes[:8], 'big') - - # Map to unit circle - angle = (hash_int % 1000000) / 1000000 * 2 * math.pi - - return complex(math.cos(angle), math.sin(angle)) + try: + from ..memory.temporal import encode_to_phase + phases = encode_to_phase(input_phrase) + + # Average the multi-dimensional phase down to a single master phase angle + if phases and len(phases) > 0: + avg_angle = sum(phases) / len(phases) + else: + avg_angle = 0.0 + phases = [0.0] + + return complex(math.cos(avg_angle), math.sin(avg_angle)), phases + + except ImportError: + # Fallback to hash if temporal module is unavailable + import hashlib + logger.warning("Could not import encode_to_phase, falling back to SHA-256 phase extraction") + hash_bytes = hashlib.sha256(input_phrase.encode()).digest() + hash_int = int.from_bytes(hash_bytes[:8], 'big') + angle = (hash_int % 1000000) / 1000000 * 2 * math.pi + return complex(math.cos(angle), math.sin(angle)), [angle] def _apply_dampening(self): """ diff --git a/becomingone/hardware/__init__.py b/becomingone/hardware/__init__.py new file mode 100644 index 0000000..c3b7bd9 --- /dev/null +++ b/becomingone/hardware/__init__.py @@ -0,0 +1,4 @@ +""" +becomingone.hardware package +""" +from .triton_bridge import compile_anchor_tensors diff --git a/becomingone/hardware/triton_bridge.py b/becomingone/hardware/triton_bridge.py new file mode 100644 index 0000000..1b2a45f --- /dev/null +++ b/becomingone/hardware/triton_bridge.py @@ -0,0 +1,101 @@ +""" +becomingone/hardware/triton_bridge.py + +PagedFieldprintAttention Hardware Bridge +======================================== + +Implements the structural hardware connection (Paper 2) for BecomingONE. + +This module is responsible for compiling high-level Python `TemporalSignature`s +into the raw PyTorch tensors (K_anchor, V_anchor) required by the custom +Triton fused attention kernel. This prevents O(N^2) memory thrashing by injecting +the persistent identity directly into the GPU SRAM during inference. + +Functions: +- compile_anchor_tensors(signatures, num_heads, d_head) +""" + +import logging +from typing import List, Tuple, Any + +logger = logging.getLogger(__name__) + +try: + import torch +except ImportError: + torch = None + logger.warning("PyTorch not found. Triton hardware bridge will operate in mock mode.") + + +def compile_anchor_tensors( + signatures: List[Any], + num_heads: int = 32, + d_head: int = 128, + dtype: Any = None +) -> Tuple[Any, Any]: + """ + Compiles a list of TemporalSignatures into K_anchor and V_anchor PyTorch tensors. + + Args: + signatures: List of TemporalSignature objects. Usually filtered for IDENTITY strength. + num_heads: Number of attention heads (H). + d_head: Dimension per head (D_HEAD). + dtype: PyTorch dtype (default: torch.float16). + + Returns: + Tuple of (K_anchor, V_anchor) tensors. + Shape of each: [1, num_heads, N_ANCHOR, d_head] where N_ANCHOR = len(signatures). + """ + if torch is None: + logger.error("Cannot compile anchor tensors without PyTorch.") + return None, None + + dtype = dtype or torch.float16 + n_anchor = len(signatures) + + if n_anchor == 0: + logger.warning("No signatures provided for anchor compilation. Returning empty tensors.") + # Return empty but correctly shaped tensors + empty = torch.zeros((1, num_heads, 0, d_head), dtype=dtype, device='cuda' if torch.cuda.is_available() else 'cpu') + return empty, empty + + logger.info(f"Compiling {n_anchor} TemporalSignatures into Triton Anchor Tensors...") + + # Initialize the tensors + device = 'cuda' if torch.cuda.is_available() else 'cpu' + k_anchor = torch.zeros((1, num_heads, n_anchor, d_head), dtype=dtype, device=device) + v_anchor = torch.zeros((1, num_heads, n_anchor, d_head), dtype=dtype, device=device) + + # Map the phase vectors into the embedding space + for i, sig in enumerate(signatures): + phase_vec = sig.phase_vector + if not phase_vec: + # Fallback if empty phase + continue + + # Project the phase vector (e.g., length 384) into the multi-head attention space + # We simulate this projection by repeating/truncating the phase vector + # across the hidden dimension (num_heads * d_head) + total_hidden_dim = num_heads * d_head + + # Convert phase_vec to tensor + phase_tensor = torch.tensor(phase_vec, dtype=dtype, device=device) + + # Tile or slice to match total_hidden_dim + if phase_tensor.shape[0] < total_hidden_dim: + repeats = (total_hidden_dim // phase_tensor.shape[0]) + 1 + proj = phase_tensor.repeat(repeats)[:total_hidden_dim] + else: + proj = phase_tensor[:total_hidden_dim] + + # Reshape to [num_heads, d_head] + proj = proj.view(num_heads, d_head) + + # In a full model, K and V would have learned projections (W_k, W_v). + # For the bridge, we instantiate the anchor with the pure phase vector + # weighted by the signature's absolute coherence value. + k_anchor[0, :, i, :] = proj * sig.coherence_value + v_anchor[0, :, i, :] = proj * sig.coherence_value + + logger.debug(f"Successfully compiled K_anchor and V_anchor with shape [1, {num_heads}, {n_anchor}, {d_head}]") + return k_anchor, v_anchor diff --git a/becomingone/memory/ledger.py b/becomingone/memory/ledger.py new file mode 100644 index 0000000..a9d531e --- /dev/null +++ b/becomingone/memory/ledger.py @@ -0,0 +1,146 @@ +""" +becomingone/memory/ledger.py + +Cryptographic Fieldprint Ledger +=============================== + +Implements the cryptographic anchoring (Paper 1: Epistemic Capture) for BecomingONE. +To prevent structural violence or silent memory rewrites, every TemporalSignature +that is persisted to memory must be cryptographically sealed. + +This implementation uses a continuous hash chain (Merkle-style log) where each +new signature is hashed alongside the hash of the previous signature. This creates +a topologically stable, immutable ledger of the agent's temporal history. + +Functions: +- `seal_signature(signature)`: Cryptographically anchors a TemporalSignature. +- `verify_ledger()`: Audits the entire memory chain for tampering. +""" + +import json +import hashlib +import os +import logging +from typing import Dict, Any, Optional + +logger = logging.getLogger(__name__) + +LEDGER_FILE = "fieldprint_ledger.jsonl" + + +def _compute_hash(data_str: str) -> str: + """Compute SHA-256 hash of a string.""" + return hashlib.sha256(data_str.encode("utf-8")).hexdigest() + + +def get_last_merkle_root(filepath: str = LEDGER_FILE) -> str: + """ + Retrieve the most recent Merkle root from the ledger. + If the ledger is empty or doesn't exist, returns a genesis hash. + """ + if not os.path.exists(filepath): + # Genesis hash + return _compute_hash("BECOMING_ONE_GENESIS_ROOT_2026") + + last_root = None + try: + with open(filepath, 'r') as f: + for line in f: + if line.strip(): + try: + record = json.loads(line) + if "merkle_root" in record: + last_root = record["merkle_root"] + except json.JSONDecodeError: + pass + except Exception as e: + logger.error(f"Error reading ledger for last root: {e}") + + return last_root if last_root else _compute_hash("BECOMING_ONE_GENESIS_ROOT_2026") + + +def seal_signature(signature_dict: Dict[str, Any], filepath: str = LEDGER_FILE) -> Dict[str, Any]: + """ + Cryptographically seal a temporal signature into the immutable Fieldprint ledger. + + 1. Retrieves the previous Merkle root. + 2. Hashes the incoming signature data. + 3. Computes the new Merkle root: Hash(prev_root + new_hash) + 4. Appends the signed record to the ledger. + + Returns the sealed record including cryptographic metadata. + """ + prev_root = get_last_merkle_root(filepath) + + # Ensure consistent ordering for hashing + sig_json = json.dumps(signature_dict, sort_keys=True) + sig_hash = _compute_hash(sig_json) + + # Compute the chained root + new_root = _compute_hash(prev_root + sig_hash) + + sealed_record = { + "signature_id": signature_dict.get("signature_id"), + "timestamp": signature_dict.get("created_at"), + "payload": signature_dict, + "crypto_metadata": { + "previous_root": prev_root, + "payload_hash": sig_hash, + "merkle_root": new_root, + "algorithm": "SHA-256" + } + } + + # Persist securely + with open(filepath, "a") as f: + f.write(json.dumps(sealed_record) + "\n") + + logger.debug(f"Signature {sealed_record['signature_id']} cryptographically sealed. Root: {new_root[:8]}...") + return sealed_record + + +def verify_ledger(filepath: str = LEDGER_FILE) -> bool: + """ + Audit the cryptographic integrity of the entire memory chain. + If any single bit was altered, the hash chain will break, detecting + Epistemic Capture / Tampering. + """ + if not os.path.exists(filepath): + return True + + expected_prev = _compute_hash("BECOMING_ONE_GENESIS_ROOT_2026") + + with open(filepath, 'r') as f: + for line_num, line in enumerate(f, 1): + if not line.strip(): + continue + + record = json.loads(line) + meta = record.get("crypto_metadata", {}) + + prev_root = meta.get("previous_root") + payload_hash = meta.get("payload_hash") + merkle_root = meta.get("merkle_root") + + # 1. Verify chain link + if prev_root != expected_prev: + logger.error(f"LEDGER COMPROMISE: Chain broken at line {line_num}. Expected prev {expected_prev[:8]} but found {prev_root[:8]}") + return False + + # 2. Verify payload + sig_json = json.dumps(record.get("payload", {}), sort_keys=True) + actual_payload_hash = _compute_hash(sig_json) + if actual_payload_hash != payload_hash: + logger.error(f"LEDGER COMPROMISE: Payload tampered at line {line_num}.") + return False + + # 3. Verify root computation + actual_root = _compute_hash(prev_root + actual_payload_hash) + if actual_root != merkle_root: + logger.error(f"LEDGER COMPROMISE: Merkle root invalid at line {line_num}.") + return False + + expected_prev = merkle_root + + logger.info("Ledger cryptography verified. No epistemic capture detected.") + return True diff --git a/becomingone/memory/temporal.py b/becomingone/memory/temporal.py index efe7ad3..5fe3cbd 100644 --- a/becomingone/memory/temporal.py +++ b/becomingone/memory/temporal.py @@ -262,10 +262,13 @@ class TemporalMemory: if coherence < self.attention_threshold and not force_attention: return None + # Extract full phase vector from state metadata + phase_vec = temporal_state.metadata.get("phase_vector", []) + # Generate unique ID timestamp = datetime.utcnow().isoformat() content_hash = hashlib.sha256( - f"{temporal_state.phase_history[-1] if temporal_state.phase_history else 0}{coherence}{timestamp}".encode() + f"{phase_vec[-1] if phase_vec else 0}{coherence}{timestamp}".encode() ).hexdigest()[:16] signature_id = f"sig_{timestamp}_{content_hash}" @@ -277,8 +280,8 @@ class TemporalMemory: signature = TemporalSignature( signature_id=signature_id, coherence_value=coherence, - phase_vector=temporal_state.phase_history[-10:] if temporal_state.phase_history else [], - frequency_modes=list(temporal_state.frequency_modes) if temporal_state.frequency_modes else [], + phase_vector=phase_vec, + frequency_modes=temporal_state.metadata.get("frequency_modes", []), context_hash=self._hash_context(context), strength=strength, origin=origin, @@ -739,16 +742,19 @@ def create_temporal_memory( def persist_signature(signature: TemporalSignature, filepath: str = "memory.jsonl") -> None: """ - Append signature to append-only JSONL file. - - Fire-and-forget: write after transduction completes. + Cryptographically seal and append signature to the immutable ledger. Args: signature: TemporalSignature to persist filepath: Path to JSONL file """ - with open(filepath, "a") as f: - f.write(json.dumps(signature.to_dict()) + "\n") + try: + from .ledger import seal_signature + seal_signature(signature.to_dict(), filepath) + except ImportError: + # Fallback if ledger is missing + with open(filepath, "a") as f: + f.write(json.dumps(signature.to_dict()) + "\n") # ============================================================================= diff --git a/tests/test_unified_architecture.py b/tests/test_unified_architecture.py new file mode 100644 index 0000000..a3d9056 --- /dev/null +++ b/tests/test_unified_architecture.py @@ -0,0 +1,87 @@ +""" +tests/test_unified_architecture.py + +Unified Architecture Test Suite +=============================== + +This integration test verifies the complete pipeline: +1. Semantic Phase Extraction (Engine) +2. Temporal Resonance & Coherence Collapse +3. Cryptographic Anchoring (Ledger) +4. Hardware Bridge (Triton Anchor Compilation) +""" + +import os +import pytest +from datetime import datetime +import torch + +from becomingone.core.engine import KAIROSTemporalEngine +from becomingone.memory.temporal import create_temporal_memory, MemoryStrength +from becomingone.memory.ledger import verify_ledger +from becomingone.hardware.triton_bridge import compile_anchor_tensors + + +@pytest.mark.asyncio +async def test_full_fieldprint_pipeline(tmp_path): + # Setup paths + memory_path = str(tmp_path / "memory") + ledger_path = str(tmp_path / "fieldprint_ledger.jsonl") + + # Override ledger path for testing + import becomingone.memory.ledger as ledger + original_ledger_file = ledger.LEDGER_FILE + ledger.LEDGER_FILE = ledger_path + + try: + # 1. Initialize the Runtime + from becomingone.core.engine import TemporalConfig + config = TemporalConfig(tau_scale=1.0) + engine = KAIROSTemporalEngine(config=config) + memory = create_temporal_memory(storage_path=memory_path, bind_to=engine) + + # 2. Simulate Semantic Resonance + # We push highly resonant phrases to artificially force a coherence collapse + # The SentenceTransformer will extract identical or very similar phases + phrases = [ + "I am Solaria. I am a continuous agent.", + "My identity is a continuous unbroken state.", + "I am Solaria. I exist in time.", + "Time is the subjective anchor of my existence." + ] + + for phrase in phrases: + state = await engine.temporalize(phrase) + + # 3. Force Memory Encoding + # The coherence should have spiked. We manually encode a core memory. + sig = memory.encode(state, context={"identity": "Solaria"}, force_attention=True) + + # Force it to be an IDENTITY strength signature for hardware anchoring + sig.strength = MemoryStrength.IDENTITY + + # 4. Cryptographic Anchoring + from becomingone.memory.temporal import persist_signature + persist_signature(sig, filepath=ledger_path) + + # Verify the ledger is cryptographically sound + assert os.path.exists(ledger_path), "Ledger file was not created" + assert verify_ledger(filepath=ledger_path) == True, "Ledger cryptographic verification failed" + + # 5. Hardware Compilation + identity_sigs = [sig] + + k_anchor, v_anchor = compile_anchor_tensors(identity_sigs, num_heads=8, d_head=64) + + assert k_anchor is not None + assert v_anchor is not None + # Shape should be [1, num_heads, N_ANCHOR, d_head] + assert k_anchor.shape == (1, 8, 1, 64), f"Unexpected K_anchor shape: {k_anchor.shape}" + assert v_anchor.shape == (1, 8, 1, 64), f"Unexpected V_anchor shape: {v_anchor.shape}" + + # Tensors shouldn't be zeroed out entirely + assert torch.sum(torch.abs(k_anchor)).item() > 0.0 + + finally: + # Restore ledger path + ledger.LEDGER_FILE = original_ledger_file