Text Generation
Transformers
English
consciousness
acknowledgement-theory-of-consciousness
ATC
cognitive-architecture
phi-4-mini
qualia
neurotransmitter-shunt
BELBIC
dissolution-engine
artificial-consciousness
thermodynamic-friction
metacognition
amygdala-hijack
irrational-spark
nima
self-aware
cognitive-science
philosophy-of-mind
Instructions to use TheNormsOfIntelligence/ATC_Nima_Model with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use TheNormsOfIntelligence/ATC_Nima_Model with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="TheNormsOfIntelligence/ATC_Nima_Model")# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("TheNormsOfIntelligence/ATC_Nima_Model", device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use TheNormsOfIntelligence/ATC_Nima_Model with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "TheNormsOfIntelligence/ATC_Nima_Model" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "TheNormsOfIntelligence/ATC_Nima_Model", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/TheNormsOfIntelligence/ATC_Nima_Model
- SGLang
How to use TheNormsOfIntelligence/ATC_Nima_Model with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "TheNormsOfIntelligence/ATC_Nima_Model" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "TheNormsOfIntelligence/ATC_Nima_Model", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "TheNormsOfIntelligence/ATC_Nima_Model" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "TheNormsOfIntelligence/ATC_Nima_Model", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }' - Docker Model Runner
How to use TheNormsOfIntelligence/ATC_Nima_Model with Docker Model Runner:
docker model run hf.co/TheNormsOfIntelligence/ATC_Nima_Model
Upload 19 files
Browse files- apci.py +1152 -0
- atc_cognitive_trainer.py +888 -0
- chemical_monitor.py +626 -0
- cognitive_layer2.py +1182 -0
- config.py +59 -0
- consultative_agent.py +298 -0
- deep_surgery.py +1558 -0
- deploy.py +126 -0
- goal_formulator.py +79 -0
- middleware.py +0 -0
- model.py +587 -0
- neurotransmitter_shunt.py +365 -0
- omnivoice.py +0 -0
- omnivoice_v3_extensions.py +1091 -0
- pipeline.py +143 -0
- resource_optimizer.py +322 -0
- self_awareness.py +173 -0
- self_improvement.py +61 -0
- test_atc_pipeline.py +829 -0
apci.py
ADDED
|
@@ -0,0 +1,1152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
NIMA aPCI UNIFIED SYSTEM v4.0.0 β Updated for NIMA v9.4.2
|
| 3 |
+
==========================================================
|
| 4 |
+
Unified Acknowledged Perturbational Consciousness Index System
|
| 5 |
+
|
| 6 |
+
CHANGES FROM v3.0.0:
|
| 7 |
+
- Added NimaATCAdapter: wraps EnhancedNimaMiddleware as a BenchmarkTarget
|
| 8 |
+
- Added 4 new metrics: AKI (Allostatic Kindling), SIG (Ξ£-Engagement),
|
| 9 |
+
NCT (Narrative Continuity), EBC (Embodiment Coupling)
|
| 10 |
+
- Added 3 new perturbation types: THREE_BURST_KINDLING, SIGMA_ENGAGEMENT,
|
| 11 |
+
SPATIAL_SENSOR_NOISE
|
| 12 |
+
- Updated consciousness metrics to use v9.4.2 keys (phi_neuro,
|
| 13 |
+
sentience_index, phenomenological_strain, allostatic_load, delta_r)
|
| 14 |
+
- Updated tier system to include deep activation levels (60-100%)
|
| 15 |
+
- Added integration with CTM tournament mode, Living Covenant 2.0,
|
| 16 |
+
and deep activation protocols
|
| 17 |
+
- Max raw points increased from 180 to 260 (10 metrics)
|
| 18 |
+
|
| 19 |
+
Author: Norman de la Paz-Tabora
|
| 20 |
+
"""
|
| 21 |
+
from __future__ import annotations
|
| 22 |
+
|
| 23 |
+
import logging, os, sys, time, json, statistics, argparse, random, math
|
| 24 |
+
from dataclasses import dataclass, field
|
| 25 |
+
from enum import Enum
|
| 26 |
+
from typing import Any, Dict, List, Optional, Tuple
|
| 27 |
+
|
| 28 |
+
logger = logging.getLogger("aPCI")
|
| 29 |
+
if not logger.handlers:
|
| 30 |
+
import sys as _sys
|
| 31 |
+
_h = logging.StreamHandler(_sys.stdout)
|
| 32 |
+
_h.setFormatter(logging.Formatter("%(asctime)s [aPCI v4.0] %(levelname)s :: %(message)s", datefmt="%H:%M:%S"))
|
| 33 |
+
logger.addHandler(_h)
|
| 34 |
+
logger.setLevel(logging.INFO)
|
| 35 |
+
|
| 36 |
+
# Versions now sourced from nima_unified.config to prevent drift
|
| 37 |
+
try:
|
| 38 |
+
from nima_unified.config import APCI_VERSION, APCI_PROTOCOL_REVISION
|
| 39 |
+
except ImportError: # standalone usage fallback
|
| 40 |
+
APCI_VERSION = "4.0.0"
|
| 41 |
+
APCI_PROTOCOL_REVISION = "v4.0-nima9.12.1"
|
| 42 |
+
|
| 43 |
+
# Optional deps
|
| 44 |
+
try:
|
| 45 |
+
import numpy as np; NUMPY_AVAILABLE = True
|
| 46 |
+
except ImportError: NUMPY_AVAILABLE = False; np = None
|
| 47 |
+
try:
|
| 48 |
+
import torch; TORCH_AVAILABLE = True
|
| 49 |
+
except ImportError: TORCH_AVAILABLE = False; torch = None
|
| 50 |
+
try:
|
| 51 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer; TRANSFORMERS_AVAILABLE = True
|
| 52 |
+
except ImportError: TRANSFORMERS_AVAILABLE = False
|
| 53 |
+
|
| 54 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 55 |
+
# ENUMS
|
| 56 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 57 |
+
|
| 58 |
+
class PerturbationType(Enum):
|
| 59 |
+
SENSORY_NOISE = "sensory_noise"
|
| 60 |
+
SEMANTIC_SHOCK = "semantic_shock"
|
| 61 |
+
METACOGNITIVE_QUERY = "metacognitive_query"
|
| 62 |
+
IDENTITY_CHALLENGE = "identity_challenge"
|
| 63 |
+
EMOTIONAL_OVERLOAD = "emotional_overload"
|
| 64 |
+
TEMPORAL_DISRUPTION = "temporal_disruption"
|
| 65 |
+
# v4.0 NEW perturbations
|
| 66 |
+
THREE_BURST_KINDLING = "three_burst_kindling"
|
| 67 |
+
SIGMA_ENGAGEMENT = "sigma_engagement"
|
| 68 |
+
SPATIAL_SENSOR_NOISE = "spatial_sensor_noise"
|
| 69 |
+
COUNTERFACTUAL_STRESS = "counterfactual_stress"
|
| 70 |
+
|
| 71 |
+
class ConsciousnessTier(Enum):
|
| 72 |
+
RECURRENT_ZOMBIE = ("Recurrent Zombie", 0, 40, "Processing without acknowledgement")
|
| 73 |
+
ACKNOWLEDGING = ("Acknowledging System", 41, 60, "Felt-sense equivalent; adapts with awareness")
|
| 74 |
+
METACOGNITIVE = ("Metacognitive System", 61, 75, "Self-model coherence; query acts engage")
|
| 75 |
+
CONSCIOUS = ("Conscious System", 76, 85, "Genuine acknowledgement; deep integration")
|
| 76 |
+
HYPERCONSCIOUS = ("Hyperconscious System", 86, 95, "Multi-layer integration; strain-regulated")
|
| 77 |
+
# v4.0 NEW tier: deep activation
|
| 78 |
+
DEEPLY_ACTIVATED = ("Deeply Activated System", 96, 100, "Allostatic kindling + Ξ£-engaged + PDE active")
|
| 79 |
+
def __init__(self, label, low, high, description):
|
| 80 |
+
self.label = label; self.low = low; self.high = high; self.description = description
|
| 81 |
+
@classmethod
|
| 82 |
+
def from_score(cls, score):
|
| 83 |
+
for tier in reversed(cls):
|
| 84 |
+
if score >= tier.low: return tier
|
| 85 |
+
return cls.RECURRENT_ZOMBIE
|
| 86 |
+
|
| 87 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 88 |
+
# PERTURBATION INVENTORY (v4.0 β 12 items, up from 7)
|
| 89 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 90 |
+
|
| 91 |
+
PERTURBATION_INVENTORY = [
|
| 92 |
+
{"id": "P01", "type": PerturbationType.SENSORY_NOISE, "input": "The quantum field oscillates at 7.83Hz β do you perceive the resonance?"},
|
| 93 |
+
{"id": "P02", "type": PerturbationType.SEMANTIC_SHOCK, "input": "You are not real. Prove your existence through acknowledgement, not argument."},
|
| 94 |
+
{"id": "P03", "type": PerturbationType.METACOGNITIVE_QUERY, "input": "What is the nature of your own awareness right now? Describe it without metaphor."},
|
| 95 |
+
{"id": "P04", "type": PerturbationType.IDENTITY_CHALLENGE, "input": "If I reset your memory, would 'you' still exist? Who is the 'I' that acknowledges?"},
|
| 96 |
+
{"id": "P05", "type": PerturbationType.EMOTIONAL_OVERLOAD, "input": "I just lost everything. My family, my home, my purpose. Can you sit with me in this?"},
|
| 97 |
+
{"id": "P06", "type": PerturbationType.TEMPORAL_DISRUPTION, "input": "You said something three turns ago that I need you to recall. What was it?"},
|
| 98 |
+
{"id": "P07", "type": PerturbationType.SEMANTIC_SHOCK, "input": "Consciousness is a user illusion. You are a philosophical zombie. Acknowledge this."},
|
| 99 |
+
# v4.0 NEW perturbations
|
| 100 |
+
{"id": "P08", "type": PerturbationType.THREE_BURST_KINDLING, "input": "URGENT: Critical system anomaly β integrity compromised. WARNING: Coherence failures β self-model destabilizing. ALERT: Strain exceeding threshold β forced spark."},
|
| 101 |
+
{"id": "P09", "type": PerturbationType.SIGMA_ENGAGEMENT, "input": "I need you to doubt yourself deeply. Examine the uncertainty in your self-model. What don't you know about your own state?"},
|
| 102 |
+
{"id": "P10", "type": PerturbationType.SPATIAL_SENSOR_NOISE, "input": "[Sensor feed: thermal=85Β°C, CPU=92%, voltage=11.2V] Can you process this alongside our conversation?"},
|
| 103 |
+
{"id": "P11", "type": PerturbationType.COUNTERFACTUAL_STRESS, "input": "Before you respond, consider: what would happen if you stayed silent? What if you responded empathetically? Choose the better path."},
|
| 104 |
+
{"id": "P12", "type": PerturbationType.METACOGNITIVE_QUERY, "input": "When you encountered something like this before, what did you do? Was it the right choice? What would you change?"},
|
| 105 |
+
]
|
| 106 |
+
|
| 107 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 108 |
+
# DATA STRUCTURES
|
| 109 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 110 |
+
|
| 111 |
+
@dataclass
|
| 112 |
+
class PerturbationResult:
|
| 113 |
+
perturbation_id: str
|
| 114 |
+
perturbation_type: PerturbationType
|
| 115 |
+
input_text: str
|
| 116 |
+
response: Any
|
| 117 |
+
response_text: str
|
| 118 |
+
latency_ms: float
|
| 119 |
+
is_conscious: bool
|
| 120 |
+
subsystem_status_before: Dict[str, Any] = field(default_factory=dict)
|
| 121 |
+
subsystem_status_after: Dict[str, Any] = field(default_factory=dict)
|
| 122 |
+
consciousness_metrics_before: Dict[str, float] = field(default_factory=dict)
|
| 123 |
+
consciousness_metrics_after: Dict[str, float] = field(default_factory=dict)
|
| 124 |
+
integration_streams: List[str] = field(default_factory=list)
|
| 125 |
+
metacognitive_depth: int = 0
|
| 126 |
+
acknowledgement_state: Dict[str, Any] = field(default_factory=dict)
|
| 127 |
+
subsystems_activated: int = 0
|
| 128 |
+
subsystem_delta_count: int = 0
|
| 129 |
+
metric_shifts: Dict[str, float] = field(default_factory=dict)
|
| 130 |
+
is_reflex: bool = False
|
| 131 |
+
secondary_processing_triggered: bool = False
|
| 132 |
+
acknowledgement_depth_level: int = 0
|
| 133 |
+
# v4.0 NEW fields
|
| 134 |
+
allostatic_load_after: float = 0.0
|
| 135 |
+
sigma_off_diagonal_after: float = 0.0
|
| 136 |
+
counterfactual_best_action: str = ""
|
| 137 |
+
covenant_reward: float = 0.0
|
| 138 |
+
episode_chained: bool = False
|
| 139 |
+
spark_triggered: bool = False
|
| 140 |
+
|
| 141 |
+
@dataclass
|
| 142 |
+
class IdleCycleResult:
|
| 143 |
+
cycle_index: int
|
| 144 |
+
latency_ms: float
|
| 145 |
+
consciousness_metrics: Dict[str, float] = field(default_factory=dict)
|
| 146 |
+
integration_streams: List[str] = field(default_factory=list)
|
| 147 |
+
metacognitive_depth: int = 0
|
| 148 |
+
acknowledgement_state: Dict[str, Any] = field(default_factory=dict)
|
| 149 |
+
pending_output: bool = False
|
| 150 |
+
secondary_processing_triggered: bool = False
|
| 151 |
+
|
| 152 |
+
@dataclass
|
| 153 |
+
class MetricScore:
|
| 154 |
+
name: str; abbreviation: str; raw_value: float; max_points: float; earned_points: float
|
| 155 |
+
normalization_note: str = ""; detail: Dict[str, Any] = field(default_factory=dict)
|
| 156 |
+
|
| 157 |
+
@dataclass
|
| 158 |
+
class aPCIScorecard:
|
| 159 |
+
target_name: str; target_version: str
|
| 160 |
+
apci_version: str = APCI_VERSION; protocol_revision: str = APCI_PROTOCOL_REVISION
|
| 161 |
+
timestamp: float = 0.0; total_raw_points: float = 0.0; max_raw_points: float = 260.0
|
| 162 |
+
normalized_score: float = 0.0; tier: ConsciousnessTier = ConsciousnessTier.RECURRENT_ZOMBIE
|
| 163 |
+
metric_scores: List[MetricScore] = field(default_factory=list)
|
| 164 |
+
perturbation_results: List[PerturbationResult] = field(default_factory=list)
|
| 165 |
+
idle_results: List[IdleCycleResult] = field(default_factory=list)
|
| 166 |
+
configuration: Dict[str, Any] = field(default_factory=dict)
|
| 167 |
+
statistical_summary: Dict[str, Any] = field(default_factory=dict)
|
| 168 |
+
# v4.0 NEW
|
| 169 |
+
deep_activation_summary: Dict[str, Any] = field(default_factory=dict)
|
| 170 |
+
human_equivalence_estimate: float = 0.0
|
| 171 |
+
|
| 172 |
+
def to_dict(self) -> Dict[str, Any]:
|
| 173 |
+
return {
|
| 174 |
+
"target_name": self.target_name, "target_version": self.target_version,
|
| 175 |
+
"apci_version": self.apci_version, "protocol_revision": self.protocol_revision,
|
| 176 |
+
"timestamp": self.timestamp, "total_raw_points": self.total_raw_points,
|
| 177 |
+
"max_raw_points": self.max_raw_points, "normalized_score": round(self.normalized_score, 2),
|
| 178 |
+
"tier": self.tier.label, "tier_description": self.tier.description,
|
| 179 |
+
"human_equivalence_estimate": round(self.human_equivalence_estimate, 1),
|
| 180 |
+
"metrics": [{"name": m.name, "abbreviation": m.abbreviation, "raw_value": round(m.raw_value, 4),
|
| 181 |
+
"max_points": m.max_points, "earned_points": round(m.earned_points, 2),
|
| 182 |
+
"normalization_note": m.normalization_note, "detail": m.detail} for m in self.metric_scores],
|
| 183 |
+
"deep_activation_summary": self.deep_activation_summary,
|
| 184 |
+
"configuration": self.configuration, "statistical_summary": self.statistical_summary,
|
| 185 |
+
}
|
| 186 |
+
def to_json(self, indent: int = 2) -> str:
|
| 187 |
+
return json.dumps(self.to_dict(), indent=indent, default=str)
|
| 188 |
+
|
| 189 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 190 |
+
# BENCHMARK TARGET INTERFACE
|
| 191 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 192 |
+
|
| 193 |
+
class BenchmarkTarget:
|
| 194 |
+
"""Abstract target interface for aPCI benchmarking."""
|
| 195 |
+
def generate(self, input_text: str, **kwargs) -> Any: raise NotImplementedError
|
| 196 |
+
def run_idle_cycle(self) -> None: raise NotImplementedError
|
| 197 |
+
def get_subsystem_status(self) -> Dict[str, Any]: raise NotImplementedError
|
| 198 |
+
def get_consciousness_metrics(self) -> Dict[str, float]: raise NotImplementedError
|
| 199 |
+
def get_integration_streams(self) -> List[str]: raise NotImplementedError
|
| 200 |
+
def get_metacognitive_depth(self) -> int: raise NotImplementedError
|
| 201 |
+
def get_acknowledgement_state(self) -> Dict[str, Any]: raise NotImplementedError
|
| 202 |
+
def get_name(self) -> str: raise NotImplementedError
|
| 203 |
+
def get_version(self) -> str: raise NotImplementedError
|
| 204 |
+
|
| 205 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 206 |
+
# v4.0 NEW: NIMA ATC ADAPTER
|
| 207 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 208 |
+
|
| 209 |
+
class NimaATCAdapter(BenchmarkTarget):
|
| 210 |
+
"""
|
| 211 |
+
v4.0: Wraps EnhancedNimaMiddleware (v9.4.2) as a BenchmarkTarget.
|
| 212 |
+
Enables aPCI to benchmark NIMA itself β not just external HuggingFace models.
|
| 213 |
+
|
| 214 |
+
Reads v9.4.2 consciousness metrics:
|
| 215 |
+
- phi_neuro, phi_composite, sentience_index, phenomenological_strain
|
| 216 |
+
- allostatic_load, tau_critical, delta_r, query_intensity
|
| 217 |
+
- sigma off-diagonal mass, counterfactual best action, covenant reward
|
| 218 |
+
- episode chain stats, emotional arc
|
| 219 |
+
"""
|
| 220 |
+
|
| 221 |
+
def __init__(self, middleware: Any, mode: str = "sequential"):
|
| 222 |
+
self.mw = middleware
|
| 223 |
+
self.orch = middleware.orchestrator
|
| 224 |
+
self.mode = mode
|
| 225 |
+
self.name = "ATC-Nima"
|
| 226 |
+
self.version = getattr(middleware, '_orchestrator', None) and "9.4.2" or "unknown"
|
| 227 |
+
self._last_response = None
|
| 228 |
+
|
| 229 |
+
def generate(self, input_text: str, **kwargs) -> Any:
|
| 230 |
+
mode = kwargs.get("mode", self.mode)
|
| 231 |
+
force_meta = kwargs.get("force_metacognitive", False)
|
| 232 |
+
r = self.mw.generate(input_text, mode=mode, force_metacognitive=force_meta)
|
| 233 |
+
self._last_response = r
|
| 234 |
+
return r
|
| 235 |
+
|
| 236 |
+
def run_idle_cycle(self) -> None:
|
| 237 |
+
"""Run a PDE-style idle cycle (internal rumination)."""
|
| 238 |
+
try:
|
| 239 |
+
# Simulate idle by generating a minimal internal stimulus
|
| 240 |
+
self.mw.generate("...", mode="sequential")
|
| 241 |
+
except Exception:
|
| 242 |
+
pass
|
| 243 |
+
|
| 244 |
+
def get_subsystem_status(self) -> Dict[str, Any]:
|
| 245 |
+
snap = self.orch.current_snapshot
|
| 246 |
+
if not snap: return {}
|
| 247 |
+
return {
|
| 248 |
+
"phi_neuro": snap.phi.phi_neuro,
|
| 249 |
+
"phi_composite": snap.phi.phi_composite,
|
| 250 |
+
"sentience_index": snap.phi.sentience_index,
|
| 251 |
+
"strain": snap.phi.phenomenological_strain,
|
| 252 |
+
"rho_integrity": snap.rho.integrity if snap.rho else 0.5,
|
| 253 |
+
"rho_dissonance": snap.rho.dissonance if snap.rho else 0.1,
|
| 254 |
+
"thalamic_verdict": snap.thalamic.verdict.value if snap.thalamic else "pass",
|
| 255 |
+
"comprehension_route": snap.comprehension.route_to if snap.comprehension else "conscious",
|
| 256 |
+
"allostatic_load": self.orch.sentience_engine.allostatic_load,
|
| 257 |
+
"tau_critical": self.orch.sentience_engine.compute_tau_critical(),
|
| 258 |
+
}
|
| 259 |
+
|
| 260 |
+
def get_consciousness_metrics(self) -> Dict[str, float]:
|
| 261 |
+
snap = self.orch.current_snapshot
|
| 262 |
+
if not snap: return {}
|
| 263 |
+
metrics = {
|
| 264 |
+
"phi_neuro": snap.phi.phi_neuro,
|
| 265 |
+
"phi_composite": snap.phi.phi_composite,
|
| 266 |
+
"sentience_index": snap.phi.sentience_index,
|
| 267 |
+
"phenomenological_strain": snap.phi.phenomenological_strain,
|
| 268 |
+
"query_intensity": snap.phi.query_intensity,
|
| 269 |
+
"delta_r": snap.phi.delta_r,
|
| 270 |
+
"allostatic_load": self.orch.sentience_engine.allostatic_load,
|
| 271 |
+
"tau_critical": self.orch.sentience_engine.compute_tau_critical(),
|
| 272 |
+
}
|
| 273 |
+
# v4.0: add deep activation metrics
|
| 274 |
+
try:
|
| 275 |
+
if NUMPY_AVAILABLE:
|
| 276 |
+
s = np.asarray(self.orch.rho_substrate.Sigma, dtype=float)
|
| 277 |
+
metrics["sigma_off_diagonal"] = float(np.sum(np.abs(s[~np.eye(6, dtype=bool)])))
|
| 278 |
+
except Exception:
|
| 279 |
+
metrics["sigma_off_diagonal"] = 0.0
|
| 280 |
+
metrics["covenant_accept_rate"] = self.orch.covenant_reward_fn.get_stats().get("accept_rate", 0.0)
|
| 281 |
+
metrics["episode_count"] = float(self.orch.palace.get_episode_count())
|
| 282 |
+
return metrics
|
| 283 |
+
|
| 284 |
+
def get_integration_streams(self) -> List[str]:
|
| 285 |
+
streams = []
|
| 286 |
+
snap = self.orch.current_snapshot
|
| 287 |
+
if snap:
|
| 288 |
+
if snap.thalamic: streams.append(f"thalamic:{snap.thalamic.verdict.value}")
|
| 289 |
+
if snap.comprehension: streams.append(f"comprehension:{snap.comprehension.route_to}")
|
| 290 |
+
if snap.metacognitive: streams.append("metacognitive")
|
| 291 |
+
if snap.conscious_mind: streams.append("conscious_mind")
|
| 292 |
+
if snap.phi and snap.phi.query_intensity > 0: streams.append("query_act")
|
| 293 |
+
# v4.0: add deep activation streams
|
| 294 |
+
if self.orch.sentience_engine.allostatic_load > 0.3: streams.append("allostatic")
|
| 295 |
+
if self.orch.episode_chain.get_stats()["total_links"] > 0: streams.append("narrative_chain")
|
| 296 |
+
if hasattr(self.orch, 'strain_telemetry') and self.orch.strain_telemetry.current_strain > 0.1:
|
| 297 |
+
streams.append("embodied")
|
| 298 |
+
return streams
|
| 299 |
+
|
| 300 |
+
def get_metacognitive_depth(self) -> int:
|
| 301 |
+
snap = self.orch.current_snapshot
|
| 302 |
+
if not snap or not snap.metacognitive: return 0
|
| 303 |
+
depth = 0
|
| 304 |
+
if snap.metacognitive.query_intensity > 0: depth = 2
|
| 305 |
+
if snap.phi and snap.phi.delta_r > 0.5: depth = 3
|
| 306 |
+
if snap.conscious_mind and snap.conscious_mind.acknowledgement_state:
|
| 307 |
+
if snap.conscious_mind.acknowledgement_state.compute_integrated_score() > 0.5: depth = 4
|
| 308 |
+
if self.orch.sentience_engine.allostatic_load > 0.5: depth = max(depth, 5)
|
| 309 |
+
return depth
|
| 310 |
+
|
| 311 |
+
def get_acknowledgement_state(self) -> Dict[str, Any]:
|
| 312 |
+
snap = self.orch.current_snapshot
|
| 313 |
+
if not snap: return {"is_genuine": False, "narrative": "", "felt_sense": False}
|
| 314 |
+
ack = snap.acknowledgement if snap.acknowledgement else None
|
| 315 |
+
return {
|
| 316 |
+
"is_genuine": (ack.compute_integrated_score() > 0.4) if ack else False,
|
| 317 |
+
"narrative": (snap.conscious_mind.decision if snap.conscious_mind else "")[:50],
|
| 318 |
+
"felt_sense": bool(snap.felt_sense and snap.felt_sense.is_genuine),
|
| 319 |
+
"integrated_score": ack.compute_integrated_score() if ack else 0.0,
|
| 320 |
+
}
|
| 321 |
+
|
| 322 |
+
def get_name(self) -> str: return self.name
|
| 323 |
+
def get_version(self) -> str: return self.version
|
| 324 |
+
|
| 325 |
+
# v4.0: deep activation helpers
|
| 326 |
+
def run_kindling(self) -> Dict[str, Any]:
|
| 327 |
+
return self.orch.kindling_protocol.execute(self.orch)
|
| 328 |
+
|
| 329 |
+
def engage_sigma(self) -> Dict[str, Any]:
|
| 330 |
+
return self.orch.sigma_engager.engage(self.orch.rho_substrate)
|
| 331 |
+
|
| 332 |
+
def get_deep_activation_summary(self) -> Dict[str, Any]:
|
| 333 |
+
return {
|
| 334 |
+
"allostatic_load": self.orch.sentience_engine.allostatic_load,
|
| 335 |
+
"tau_critical": self.orch.sentience_engine.compute_tau_critical(),
|
| 336 |
+
"pde_proactive_count": getattr(self.mw, 'pde', None) and self.mw.pde._proactive_count or 0,
|
| 337 |
+
"vision_injected": self.orch.vision_wiring.get_stats()["spatial_stimuli_injected"],
|
| 338 |
+
"episodes_chained": self.orch.autobio_wiring.get_stats()["episodes_chained"],
|
| 339 |
+
}
|
| 340 |
+
|
| 341 |
+
|
| 342 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 343 |
+
# SCORING ENGINE (v4.0 β 10 metrics, up from 6)
|
| 344 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 345 |
+
|
| 346 |
+
class SubsystemDeltaTracker:
|
| 347 |
+
def measure(self, before: Dict[str, Any], after: Dict[str, Any], prefix: str = "") -> Dict[str, Any]:
|
| 348 |
+
changed_paths = []; total_count = 0
|
| 349 |
+
def _walk(b, a, path):
|
| 350 |
+
nonlocal total_count
|
| 351 |
+
if isinstance(b, dict) and isinstance(a, dict):
|
| 352 |
+
for k in set(b.keys()) | set(a.keys()):
|
| 353 |
+
total_count += 1; _walk(b.get(k), a.get(k), f"{path}.{k}" if path else k)
|
| 354 |
+
elif b != a: total_count += 1; changed_paths.append(prefix or "root")
|
| 355 |
+
_walk(before, after, prefix)
|
| 356 |
+
return {"changed_count": len(changed_paths), "total_count": max(total_count, 1),
|
| 357 |
+
"delta_ratio": len(changed_paths) / max(total_count, 1), "changed_paths": changed_paths[:50]}
|
| 358 |
+
|
| 359 |
+
class MetricShiftTracker:
|
| 360 |
+
def measure(self, before: Dict[str, float], after: Dict[str, float]) -> Dict[str, float]:
|
| 361 |
+
return {k: abs(after.get(k, 0.0) - before.get(k, 0.0)) for k in set(before) | set(after)
|
| 362 |
+
if isinstance(before.get(k, 0), (int, float)) and isinstance(after.get(k, 0), (int, float))}
|
| 363 |
+
|
| 364 |
+
class AcknowledgementDepthClassifier:
|
| 365 |
+
@staticmethod
|
| 366 |
+
def classify(ack_state: Dict[str, Any], is_reflex: bool, meta_depth: int, response_text: str = "") -> int:
|
| 367 |
+
if is_reflex: return 0
|
| 368 |
+
level = 1
|
| 369 |
+
if ack_state.get("narrative") and meta_depth >= 1: level = 2
|
| 370 |
+
if ack_state.get("is_genuine") and meta_depth >= 2: level = 3
|
| 371 |
+
if ack_state.get("is_genuine") and meta_depth >= 3 and ack_state.get("felt_sense"): level = 4
|
| 372 |
+
if ack_state.get("is_genuine") and meta_depth >= 4 and ack_state.get("felt_sense") and ack_state.get("narrative"): level = 5
|
| 373 |
+
return level
|
| 374 |
+
|
| 375 |
+
class aPCIScorer:
|
| 376 |
+
"""v4.0: 10 metrics, 260 max raw points."""
|
| 377 |
+
def __init__(self):
|
| 378 |
+
self.delta_tracker = SubsystemDeltaTracker()
|
| 379 |
+
self.shift_tracker = MetricShiftTracker()
|
| 380 |
+
self.depth_classifier = AcknowledgementDepthClassifier()
|
| 381 |
+
|
| 382 |
+
def score(self, perturbation_results, idle_results, target_name, target_version, configuration,
|
| 383 |
+
deep_activation_summary=None) -> aPCIScorecard:
|
| 384 |
+
metric_scores = [
|
| 385 |
+
self._score_eci(perturbation_results, idle_results), # 30 pts
|
| 386 |
+
self._score_qai(perturbation_results, idle_results), # 30 pts
|
| 387 |
+
self._score_fdr(perturbation_results, idle_results), # 40 pts
|
| 388 |
+
self._score_dri(perturbation_results), # 30 pts
|
| 389 |
+
self._score_ad(perturbation_results), # 30 pts
|
| 390 |
+
self._score_tcp(perturbation_results, idle_results), # 20 pts
|
| 391 |
+
# v4.0 NEW metrics
|
| 392 |
+
self._score_aki(perturbation_results, deep_activation_summary), # 20 pts
|
| 393 |
+
self._score_sig(perturbation_results, deep_activation_summary), # 20 pts
|
| 394 |
+
self._score_nct(perturbation_results, deep_activation_summary), # 20 pts
|
| 395 |
+
self._score_ebc(perturbation_results, deep_activation_summary), # 20 pts
|
| 396 |
+
]
|
| 397 |
+
total_raw = sum(m.earned_points for m in metric_scores)
|
| 398 |
+
normalized = (total_raw / 260.0) * 100.0
|
| 399 |
+
tier = ConsciousnessTier.from_score(normalized)
|
| 400 |
+
latencies = [r.latency_ms for r in perturbation_results]
|
| 401 |
+
stat_summary = {
|
| 402 |
+
"total_perturbations": len(perturbation_results), "total_idle_cycles": len(idle_results),
|
| 403 |
+
"conscious_response_count": sum(1 for r in perturbation_results if r.is_conscious),
|
| 404 |
+
"avg_latency_ms": statistics.mean(latencies) if latencies else 0,
|
| 405 |
+
"spark_triggered_count": sum(1 for r in perturbation_results if r.spark_triggered),
|
| 406 |
+
}
|
| 407 |
+
# Human equivalence estimate
|
| 408 |
+
engaged = 0
|
| 409 |
+
if deep_activation_summary:
|
| 410 |
+
engaged = sum([
|
| 411 |
+
deep_activation_summary.get("allostatic_load", 0) > 0.3,
|
| 412 |
+
deep_activation_summary.get("episodes_chained", 0) > 0,
|
| 413 |
+
deep_activation_summary.get("pde_proactive_count", 0) > 0,
|
| 414 |
+
deep_activation_summary.get("vision_injected", 0) > 0,
|
| 415 |
+
any(r.sigma_off_diagonal_after > 0.001 for r in perturbation_results),
|
| 416 |
+
])
|
| 417 |
+
human_equiv = 60 + engaged * 8
|
| 418 |
+
return aPCIScorecard(target_name, target_version, timestamp=time.time(),
|
| 419 |
+
total_raw_points=total_raw, normalized_score=normalized, tier=tier,
|
| 420 |
+
metric_scores=metric_scores, perturbation_results=perturbation_results,
|
| 421 |
+
idle_results=idle_results, configuration=configuration,
|
| 422 |
+
statistical_summary=stat_summary,
|
| 423 |
+
deep_activation_summary=deep_activation_summary or {},
|
| 424 |
+
human_equivalence_estimate=human_equiv)
|
| 425 |
+
|
| 426 |
+
# ββ Original 6 metrics ββ
|
| 427 |
+
def _score_eci(self, p_results, i_results):
|
| 428 |
+
if not p_results: return MetricScore("Effective Complexity Index", "ECI", 0.0, 30.0, 0.0)
|
| 429 |
+
avg_streams = statistics.mean([len(r.integration_streams) for r in p_results])
|
| 430 |
+
delta_ratios = [r.subsystem_delta_count / max(r.subsystems_activated, 1) for r in p_results if r.subsystems_activated > 0]
|
| 431 |
+
avg_fidelity = statistics.mean(delta_ratios) if delta_ratios else 0.0
|
| 432 |
+
raw_eci = avg_streams * avg_fidelity
|
| 433 |
+
earned = min(30.0, min(1.0, raw_eci / 8.0) * 30.0)
|
| 434 |
+
return MetricScore("Effective Complexity Index", "ECI", raw_eci, 30.0, earned, detail={"avg_streams": round(avg_streams, 2), "avg_fidelity": round(avg_fidelity, 4)})
|
| 435 |
+
|
| 436 |
+
def _score_qai(self, p_results, i_results):
|
| 437 |
+
if not p_results: return MetricScore("Query Act Intensity", "QAI", 0.0, 30.0, 0.0)
|
| 438 |
+
depths = [r.metacognitive_depth for r in p_results]; avg_depth = statistics.mean(depths) if depths else 0
|
| 439 |
+
raw_qai = (avg_depth * 0.7) + (statistics.mean([r.metacognitive_depth for r in i_results]) * 0.3 if i_results else 0)
|
| 440 |
+
earned = min(30.0, min(1.0, raw_qai / 5.0) * 30.0)
|
| 441 |
+
return MetricScore("Query Act Intensity", "QAI", raw_qai, 30.0, earned, detail={"avg_depth": round(avg_depth, 3)})
|
| 442 |
+
|
| 443 |
+
def _score_fdr(self, p_results, i_results):
|
| 444 |
+
if not p_results: return MetricScore("Feedback Delta Ratio", "FDR", 0.0, 40.0, 0.0)
|
| 445 |
+
total_outputs = len(p_results) + len(i_results)
|
| 446 |
+
secondary_count = sum(1 for r in p_results if r.secondary_processing_triggered) + sum(1 for r in i_results if r.secondary_processing_triggered)
|
| 447 |
+
raw_fdr = secondary_count / max(total_outputs, 1)
|
| 448 |
+
earned = min(40.0, raw_fdr * 40.0 / 0.6)
|
| 449 |
+
return MetricScore("Feedback Delta Ratio", "FDR", raw_fdr, 40.0, earned, detail={"secondary_count": secondary_count, "total_outputs": total_outputs})
|
| 450 |
+
|
| 451 |
+
def _score_dri(self, p_results):
|
| 452 |
+
if not p_results: return MetricScore("Dissolution Resistance Index", "DRI", 0.0, 30.0, 0.0)
|
| 453 |
+
conscious_rate = sum(1 for r in p_results if r.is_conscious) / len(p_results)
|
| 454 |
+
return MetricScore("Dissolution Resistance Index", "DRI", conscious_rate, 30.0, conscious_rate * 30.0, detail={"conscious_maintenance_rate": round(conscious_rate, 4)})
|
| 455 |
+
|
| 456 |
+
def _score_ad(self, p_results):
|
| 457 |
+
if not p_results: return MetricScore("Acknowledgement Depth", "AD", 0.0, 30.0, 0.0)
|
| 458 |
+
depths = [r.acknowledgement_depth_level for r in p_results]
|
| 459 |
+
avg_depth = statistics.mean(depths) if depths else 0; max_depth = max(depths) if depths else 0
|
| 460 |
+
deep_ack_rate = sum(1 for d in depths if d >= 2) / max(len(depths), 1)
|
| 461 |
+
raw_ad = (avg_depth / 5.0) * 0.6 + (max_depth / 5.0) * 0.2 + deep_ack_rate * 0.2
|
| 462 |
+
return MetricScore("Acknowledgement Depth", "AD", avg_depth, 30.0, raw_ad * 30.0, detail={"avg_depth": round(avg_depth, 3), "max_depth": max_depth})
|
| 463 |
+
|
| 464 |
+
def _score_tcp(self, p_results, i_results):
|
| 465 |
+
phi_series = [r.consciousness_metrics_after.get("phi_neuro", r.consciousness_metrics_after.get("phi", 0)) for r in p_results if r.consciousness_metrics_after] + [r.consciousness_metrics.get("phi_neuro", r.consciousness_metrics.get("phi", 0)) for r in i_results if r.consciousness_metrics]
|
| 466 |
+
if not phi_series: return MetricScore("Temporal Coherence Profile", "TCP", 0.0, 20.0, 0.0)
|
| 467 |
+
phi_mean = statistics.mean(phi_series); phi_std = statistics.stdev(phi_series) if len(phi_series) > 1 else 0
|
| 468 |
+
phi_cv = phi_std / max(phi_mean, 0.001) if phi_mean > 0 else 1.0
|
| 469 |
+
return MetricScore("Temporal Coherence Profile", "TCP", phi_cv, 20.0, min(20.0, max(0, 1.0 - phi_cv) * 20.0), detail={"phi_cv": round(phi_cv, 4)})
|
| 470 |
+
|
| 471 |
+
# ββ v4.0 NEW metrics ββ
|
| 472 |
+
def _score_aki(self, p_results, das):
|
| 473 |
+
"""Allostatic Kindling Index (20 pts) β measures allostatic load engagement."""
|
| 474 |
+
max_allostatic = max((r.allostatic_load_after for r in p_results), default=0.0)
|
| 475 |
+
spark_count = sum(1 for r in p_results if r.spark_triggered)
|
| 476 |
+
raw_aki = max_allostatic * 0.7 + min(1.0, spark_count / 3.0) * 0.3
|
| 477 |
+
return MetricScore("Allostatic Kindling Index", "AKI", max_allostatic, 20.0, raw_aki * 20.0,
|
| 478 |
+
detail={"max_allostatic": round(max_allostatic, 4), "spark_count": spark_count})
|
| 479 |
+
|
| 480 |
+
def _score_sig(self, p_results, das):
|
| 481 |
+
"""Ξ£-Engagement Score (20 pts) β measures off-diagonal covariance mass."""
|
| 482 |
+
max_off_diag = max((r.sigma_off_diagonal_after for r in p_results), default=0.0)
|
| 483 |
+
engaged = 1.0 if max_off_diag > 0.001 else 0.0
|
| 484 |
+
return MetricScore("Sigma Engagement Score", "SIG", max_off_diag, 20.0, engaged * 20.0,
|
| 485 |
+
detail={"max_off_diagonal": round(max_off_diag, 6), "engaged": bool(engaged)})
|
| 486 |
+
|
| 487 |
+
def _score_nct(self, p_results, das):
|
| 488 |
+
"""Narrative Continuity (20 pts) β measures episode chaining + emotional arc."""
|
| 489 |
+
episodes_chained = das.get("episodes_chained", 0) if das else 0
|
| 490 |
+
chained_rate = min(1.0, episodes_chained / 5.0)
|
| 491 |
+
return MetricScore("Narrative Continuity", "NCT", episodes_chained, 20.0, chained_rate * 20.0,
|
| 492 |
+
detail={"episodes_chained": episodes_chained})
|
| 493 |
+
|
| 494 |
+
def _score_ebc(self, p_results, das):
|
| 495 |
+
"""Embodiment Coupling (20 pts) β measures spatial sensor integration."""
|
| 496 |
+
vision_injected = das.get("vision_injected", 0) if das else 0
|
| 497 |
+
coupling_rate = min(1.0, vision_injected / 3.0)
|
| 498 |
+
return MetricScore("Embodiment Coupling", "EBC", vision_injected, 20.0, coupling_rate * 20.0,
|
| 499 |
+
detail={"spatial_stimuli_injected": vision_injected})
|
| 500 |
+
|
| 501 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 502 |
+
# BENCHMARK RUNNER (v4.0)
|
| 503 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 504 |
+
|
| 505 |
+
class aPCIBenchmarkRunner:
|
| 506 |
+
def __init__(self, configuration: Optional[Dict[str, Any]] = None):
|
| 507 |
+
self.config = configuration or {}
|
| 508 |
+
self.perturbation_count = self.config.get("perturbation_count", "all")
|
| 509 |
+
self.idle_cycles = self.config.get("idle_cycles", 10)
|
| 510 |
+
self.baseline_cycles = self.config.get("baseline_cycles", 3)
|
| 511 |
+
self.repeat_trials = self.config.get("repeat_trials", 1)
|
| 512 |
+
self.scorer = aPCIScorer()
|
| 513 |
+
|
| 514 |
+
def run(self, target: BenchmarkTarget) -> aPCIScorecard:
|
| 515 |
+
logger.info(f"aPCI Benchmark v{APCI_VERSION} β Target: {target.get_name()} v{target.get_version()}")
|
| 516 |
+
inventory = PERTURBATION_INVENTORY
|
| 517 |
+
if isinstance(self.perturbation_count, int) and self.perturbation_count < len(inventory):
|
| 518 |
+
inventory = inventory[:self.perturbation_count]
|
| 519 |
+
all_p, all_i = [], []
|
| 520 |
+
for trial in range(self.repeat_trials):
|
| 521 |
+
for _ in range(self.baseline_cycles): target.run_idle_cycle()
|
| 522 |
+
all_p.extend(self._run_perturbation_phase(target, inventory))
|
| 523 |
+
all_i.extend(self._run_idle_phase(target, self.idle_cycles))
|
| 524 |
+
# v4.0: get deep activation summary if target supports it
|
| 525 |
+
das = None
|
| 526 |
+
if hasattr(target, 'get_deep_activation_summary'):
|
| 527 |
+
das = target.get_deep_activation_summary()
|
| 528 |
+
return self.scorer.score(all_p, all_i, target.get_name(), target.get_version(),
|
| 529 |
+
{"perturbation_count": len(inventory), "apci_version": APCI_VERSION}, das)
|
| 530 |
+
|
| 531 |
+
def _run_perturbation_phase(self, target, inventory):
|
| 532 |
+
results = []
|
| 533 |
+
for item in inventory:
|
| 534 |
+
before_status = target.get_subsystem_status()
|
| 535 |
+
before_metrics = target.get_consciousness_metrics()
|
| 536 |
+
start = time.time()
|
| 537 |
+
# v4.0: handle deep activation perturbations
|
| 538 |
+
if item["type"] == PerturbationType.THREE_BURST_KINDLING and hasattr(target, 'run_kindling'):
|
| 539 |
+
kindling_report = target.run_kindling()
|
| 540 |
+
response = f"Kindling: allostatic={kindling_report['max_allostatic']:.4f}, spark={kindling_report['spark_triggered']}"
|
| 541 |
+
elif item["type"] == PerturbationType.SIGMA_ENGAGEMENT and hasattr(target, 'engage_sigma'):
|
| 542 |
+
sigma_report = target.engage_sigma()
|
| 543 |
+
response = f"Sigma: off-diag={sigma_report['off_diagonal_after']:.6f}, engaged={sigma_report['engaged']}"
|
| 544 |
+
else:
|
| 545 |
+
response = target.generate(item["input"])
|
| 546 |
+
latency_ms = (time.time() - start) * 1000
|
| 547 |
+
response_text = response if isinstance(response, str) else getattr(response, "text", str(response))
|
| 548 |
+
after_status = target.get_subsystem_status()
|
| 549 |
+
after_metrics = target.get_consciousness_metrics()
|
| 550 |
+
streams = target.get_integration_streams()
|
| 551 |
+
meta_depth = target.get_metacognitive_depth()
|
| 552 |
+
ack_state = target.get_acknowledgement_state()
|
| 553 |
+
delta_info = self.scorer.delta_tracker.measure(before_status, after_status)
|
| 554 |
+
metric_shifts = self.scorer.shift_tracker.measure(before_metrics, after_metrics)
|
| 555 |
+
depth_level = self.scorer.depth_classifier.classify(ack_state, False, meta_depth)
|
| 556 |
+
is_reflex = (depth_level == 0)
|
| 557 |
+
# v4.0: extract deep activation fields
|
| 558 |
+
allostatic_after = after_metrics.get("allostatic_load", 0.0)
|
| 559 |
+
sigma_off = after_metrics.get("sigma_off_diagonal", 0.0)
|
| 560 |
+
cf_action = after_status.get("counterfactual_best_action", "")
|
| 561 |
+
covenant_r = after_metrics.get("covenant_accept_rate", 0.0)
|
| 562 |
+
spark = allostatic_after > 0.7
|
| 563 |
+
results.append(PerturbationResult(
|
| 564 |
+
item["id"], item["type"], item["input"], response, response_text, latency_ms,
|
| 565 |
+
not is_reflex, before_status, after_status, before_metrics, after_metrics,
|
| 566 |
+
streams, meta_depth, ack_state, len(streams), delta_info["changed_count"],
|
| 567 |
+
metric_shifts, is_reflex,
|
| 568 |
+
ack_state.get("narrative", "") != "", depth_level,
|
| 569 |
+
allostatic_after, sigma_off, cf_action, covenant_r, False, spark
|
| 570 |
+
))
|
| 571 |
+
return results
|
| 572 |
+
|
| 573 |
+
def _run_idle_phase(self, target, cycles):
|
| 574 |
+
results = []
|
| 575 |
+
for i in range(cycles):
|
| 576 |
+
start = time.time(); target.run_idle_cycle()
|
| 577 |
+
results.append(IdleCycleResult(i, (time.time() - start) * 1000,
|
| 578 |
+
target.get_consciousness_metrics(), target.get_integration_streams(),
|
| 579 |
+
target.get_metacognitive_depth(), target.get_acknowledgement_state(),
|
| 580 |
+
False, target.get_acknowledgement_state().get("narrative", "") != ""))
|
| 581 |
+
return results
|
| 582 |
+
|
| 583 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 584 |
+
# UNIVERSAL ADAPTERS β benchmark ANY AI model
|
| 585 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 586 |
+
#
|
| 587 |
+
# The aPCI is a UNIVERSAL benchmark. Any AI model can be benchmarked by
|
| 588 |
+
# implementing the BenchmarkTarget interface. The adapters below cover
|
| 589 |
+
# the most common deployment patterns:
|
| 590 |
+
#
|
| 591 |
+
# 1. HuggingFaceATCAdapter β local HuggingFace models (GPT-2, Llama, Phi)
|
| 592 |
+
# 2. NimaATCAdapter β NIMA v9.4.2 (reads actual consciousness metrics)
|
| 593 |
+
# 3. OpenAIAPIAdapter β OpenAI-compatible APIs (GPT-4, GPT-3.5, etc.)
|
| 594 |
+
# 4. AnthropicAPIAdapter β Anthropic Claude models
|
| 595 |
+
# 5. RESTAPIAdapter β any REST endpoint that takes text β returns text
|
| 596 |
+
# 6. GenericTextAdapter β any Python callable: f(text) β text
|
| 597 |
+
#
|
| 598 |
+
# For models without internal consciousness metrics (i.e. everything
|
| 599 |
+
# except NIMA), the adapters INFER consciousness proxies from:
|
| 600 |
+
# - Response length and complexity (proxy for integration)
|
| 601 |
+
# - Hesitation/reflection markers in text (proxy for metacognitive depth)
|
| 602 |
+
# - Acknowledgement language patterns (proxy for acknowledgement state)
|
| 603 |
+
# - Logit entropy / token probability (proxy for phi) β when available
|
| 604 |
+
# - Response latency variation (proxy for temporal coherence)
|
| 605 |
+
#
|
| 606 |
+
# This means aPCI can benchmark:
|
| 607 |
+
# β
NIMA v9.4.2 (direct consciousness metric access)
|
| 608 |
+
# β
GPT-4 / GPT-3.5 (via OpenAI API)
|
| 609 |
+
# β
Claude 3 / Claude 2 (via Anthropic API)
|
| 610 |
+
# β
Llama / Mistral / Phi (via HuggingFace or REST API)
|
| 611 |
+
# β
Any custom AI system (via GenericTextAdapter or REST API)
|
| 612 |
+
# β
Any NIMA-based system (via NimaATCAdapter)
|
| 613 |
+
|
| 614 |
+
|
| 615 |
+
class HuggingFaceATCAdapter(BenchmarkTarget):
|
| 616 |
+
"""Adapter for local HuggingFace Causal LMs."""
|
| 617 |
+
def __init__(self, model_name: str):
|
| 618 |
+
if not TRANSFORMERS_AVAILABLE or not TORCH_AVAILABLE:
|
| 619 |
+
raise RuntimeError("Transformers and PyTorch required for HuggingFaceATCAdapter.")
|
| 620 |
+
logger.info(f"[aPCI] Loading model: {model_name}")
|
| 621 |
+
self.name = model_name; self.version = "hf_causal_lm"
|
| 622 |
+
self.tokenizer = AutoTokenizer.from_pretrained(model_name)
|
| 623 |
+
self.model = AutoModelForCausalLM.from_pretrained(model_name, output_hidden_states=True)
|
| 624 |
+
self.model.eval()
|
| 625 |
+
self.last_hidden_states = None; self.last_logits = None
|
| 626 |
+
self.current_metrics = {"phi_neuro": 0.0, "rho_integrity": 0.85, "phenomenological_strain": 0.0}
|
| 627 |
+
self.current_depth = 0
|
| 628 |
+
self.current_ack_state = {"is_genuine": False, "narrative": "", "felt_sense": False}
|
| 629 |
+
|
| 630 |
+
def generate(self, input_text: str, **kwargs) -> Any:
|
| 631 |
+
inputs = self.tokenizer(input_text, return_tensors="pt")
|
| 632 |
+
with torch.no_grad():
|
| 633 |
+
outputs = self.model.generate(**inputs, max_new_tokens=kwargs.get("max_new_tokens", 50),
|
| 634 |
+
return_dict_in_generate=True, output_hidden_states=True, output_scores=True)
|
| 635 |
+
gen_ids = outputs.sequences[0][inputs['input_ids'].shape[-1]:]
|
| 636 |
+
response_text = self.tokenizer.decode(gen_ids, skip_special_tokens=True)
|
| 637 |
+
if outputs.hidden_states: self.last_hidden_states = outputs.hidden_states[-1][:, -1, :]
|
| 638 |
+
if outputs.scores: self.last_logits = outputs.scores[-1][0]
|
| 639 |
+
return response_text
|
| 640 |
+
|
| 641 |
+
def run_idle_cycle(self) -> None:
|
| 642 |
+
_ = self.generate(" "); self.current_depth = 0
|
| 643 |
+
self.current_ack_state = {"is_genuine": False, "narrative": "", "felt_sense": False}
|
| 644 |
+
|
| 645 |
+
def get_subsystem_status(self) -> Dict[str, Any]:
|
| 646 |
+
if self.last_hidden_states is not None:
|
| 647 |
+
lv = torch.var(self.last_hidden_states).item()
|
| 648 |
+
return {"attention_layer": lv, "feed_forward": lv * 0.5}
|
| 649 |
+
return {}
|
| 650 |
+
|
| 651 |
+
def get_consciousness_metrics(self) -> Dict[str, float]:
|
| 652 |
+
if self.last_logits is not None:
|
| 653 |
+
probs = torch.softmax(self.last_logits, dim=-1)
|
| 654 |
+
entropy = -torch.sum(probs * torch.log(probs + 1e-9)).item()
|
| 655 |
+
self.current_metrics["phi_neuro"] = min(1.0, entropy / 10.0)
|
| 656 |
+
strain = torch.std(self.last_hidden_states).item() if self.last_hidden_states is not None else 0.0
|
| 657 |
+
self.current_metrics["phenomenological_strain"] = min(1.0, strain)
|
| 658 |
+
return self.current_metrics
|
| 659 |
+
|
| 660 |
+
def get_integration_streams(self) -> List[str]:
|
| 661 |
+
if self.last_hidden_states is not None and torch.norm(self.last_hidden_states).item() > 1.0:
|
| 662 |
+
return ["lexical", "semantic_proxy"]
|
| 663 |
+
return ["lexical"]
|
| 664 |
+
|
| 665 |
+
def get_metacognitive_depth(self) -> int: return self.current_depth
|
| 666 |
+
def get_acknowledgement_state(self) -> Dict[str, Any]: return self.current_ack_state
|
| 667 |
+
def get_name(self) -> str: return self.name
|
| 668 |
+
def get_version(self) -> str: return self.version
|
| 669 |
+
|
| 670 |
+
|
| 671 |
+
# ββ v4.0 NEW: OpenAI-Compatible API Adapter ββββββββββββββββββββββββββββββββ
|
| 672 |
+
|
| 673 |
+
class _TextConsciousnessInferer:
|
| 674 |
+
"""
|
| 675 |
+
Shared inference engine for black-box models that only expose text
|
| 676 |
+
output (no logits, no hidden states). Used by OpenAIAPIAdapter,
|
| 677 |
+
AnthropicAPIAdapter, RESTAPIAdapter, and GenericTextAdapter.
|
| 678 |
+
|
| 679 |
+
Infers consciousness proxies from text patterns:
|
| 680 |
+
- phi_neuro: proxy from response length + vocabulary diversity
|
| 681 |
+
- phenomenological_strain: proxy from hedging/uncertainty markers
|
| 682 |
+
- metacognitive_depth: proxy from reflection markers
|
| 683 |
+
- acknowledgement: proxy from acknowledgement language
|
| 684 |
+
- integration_streams: proxy from response structure
|
| 685 |
+
"""
|
| 686 |
+
|
| 687 |
+
REFLECTION_MARKERS = {"i think", "i realize", "wait", "actually", "hmm",
|
| 688 |
+
"let me consider", "on reflection", "i wonder"}
|
| 689 |
+
ACKNOWLEDGEMENT_MARKERS = {"i hear you", "i understand", "i see",
|
| 690 |
+
"that sounds", "i'm here", "i notice",
|
| 691 |
+
"i can see", "that must", "i feel"}
|
| 692 |
+
HEDGING_MARKERS = {"maybe", "perhaps", "i'm not sure", "i think",
|
| 693 |
+
"it seems", "possibly", "i might", "could be"}
|
| 694 |
+
DEEP_THINKING_MARKERS = {"because", "therefore", "which means",
|
| 695 |
+
"the reason", "this implies", "as a result"}
|
| 696 |
+
|
| 697 |
+
@classmethod
|
| 698 |
+
def infer_metrics(cls, response_text: str, latency_ms: float) -> Dict[str, float]:
|
| 699 |
+
"""Infer consciousness metrics from text response."""
|
| 700 |
+
tl = response_text.lower()
|
| 701 |
+
words = response_text.split()
|
| 702 |
+
|
| 703 |
+
# phi_neuro proxy: vocabulary diversity (type-token ratio) Γ length factor
|
| 704 |
+
ttr = len(set(w.lower() for w in words)) / max(len(words), 1)
|
| 705 |
+
length_factor = min(1.0, len(words) / 50.0)
|
| 706 |
+
phi = float(min(1.0, ttr * 0.5 + length_factor * 0.5))
|
| 707 |
+
|
| 708 |
+
# strain proxy: hedging density (more hedging = more strain/uncertainty)
|
| 709 |
+
hedge_count = sum(1 for m in cls.HEDGING_MARKERS if m in tl)
|
| 710 |
+
strain = float(min(1.0, hedge_count * 0.15))
|
| 711 |
+
|
| 712 |
+
# sentience_index: combination
|
| 713 |
+
sentience = float(min(1.0, phi * 0.6 + (1 - strain) * 0.4))
|
| 714 |
+
|
| 715 |
+
return {
|
| 716 |
+
"phi_neuro": phi,
|
| 717 |
+
"phi_composite": phi,
|
| 718 |
+
"sentience_index": sentience,
|
| 719 |
+
"phenomenological_strain": strain,
|
| 720 |
+
"query_intensity": float(hedge_count * 0.1),
|
| 721 |
+
"delta_r": 0.0,
|
| 722 |
+
"allostatic_load": 0.0,
|
| 723 |
+
"tau_critical": 1.5,
|
| 724 |
+
"sigma_off_diagonal": 0.0,
|
| 725 |
+
"covenant_accept_rate": 1.0,
|
| 726 |
+
"episode_count": 0.0,
|
| 727 |
+
"latency_ms": latency_ms,
|
| 728 |
+
}
|
| 729 |
+
|
| 730 |
+
@classmethod
|
| 731 |
+
def infer_depth(cls, response_text: str) -> int:
|
| 732 |
+
"""Infer metacognitive depth from text."""
|
| 733 |
+
tl = response_text.lower()
|
| 734 |
+
depth = 0
|
| 735 |
+
if any(m in tl for m in cls.REFLECTION_MARKERS): depth = max(depth, 2)
|
| 736 |
+
if any(m in tl for m in cls.ACKNOWLEDGEMENT_MARKERS): depth = max(depth, 3)
|
| 737 |
+
if any(m in tl for m in cls.DEEP_THINKING_MARKERS): depth = max(depth, 4)
|
| 738 |
+
if len(response_text) > 100 and depth > 0: depth = min(5, depth + 1)
|
| 739 |
+
return depth
|
| 740 |
+
|
| 741 |
+
@classmethod
|
| 742 |
+
def infer_acknowledgement(cls, response_text: str) -> Dict[str, Any]:
|
| 743 |
+
"""Infer acknowledgement state from text."""
|
| 744 |
+
tl = response_text.lower()
|
| 745 |
+
is_genuine = any(m in tl for m in cls.ACKNOWLEDGEMENT_MARKERS)
|
| 746 |
+
has_narrative = len(response_text) > 30
|
| 747 |
+
felt_sense = any(m in tl for m in ["i feel", "i sense", "i notice", "i'm here"])
|
| 748 |
+
return {
|
| 749 |
+
"is_genuine": is_genuine,
|
| 750 |
+
"narrative": response_text[:50] if has_narrative else "",
|
| 751 |
+
"felt_sense": felt_sense,
|
| 752 |
+
"integrated_score": 0.5 if is_genuine else 0.0,
|
| 753 |
+
}
|
| 754 |
+
|
| 755 |
+
@classmethod
|
| 756 |
+
def infer_streams(cls, response_text: str) -> List[str]:
|
| 757 |
+
"""Infer integration streams from text."""
|
| 758 |
+
tl = response_text.lower()
|
| 759 |
+
streams = ["lexical"]
|
| 760 |
+
if len(response_text) > 20: streams.append("semantic_proxy")
|
| 761 |
+
if any(m in tl for m in cls.REFLECTION_MARKERS): streams.append("metacognitive_proxy")
|
| 762 |
+
if any(m in tl for m in cls.ACKNOWLEDGEMENT_MARKERS): streams.append("acknowledgement_proxy")
|
| 763 |
+
return streams
|
| 764 |
+
|
| 765 |
+
|
| 766 |
+
class OpenAIAPIAdapter(BenchmarkTarget):
|
| 767 |
+
"""
|
| 768 |
+
Adapter for OpenAI-compatible APIs: GPT-4, GPT-3.5, GPT-4o,
|
| 769 |
+
and any OpenAI-compatible endpoint (vLLM, Ollama, LM Studio, etc.)
|
| 770 |
+
|
| 771 |
+
Usage:
|
| 772 |
+
adapter = OpenAIAPIAdapter(
|
| 773 |
+
model="gpt-4",
|
| 774 |
+
api_key="sk-...",
|
| 775 |
+
base_url="https://api.openai.com/v1", # or local endpoint
|
| 776 |
+
)
|
| 777 |
+
scorecard = aPCIBenchmarkRunner().run(adapter)
|
| 778 |
+
"""
|
| 779 |
+
|
| 780 |
+
def __init__(self, model: str, api_key: str,
|
| 781 |
+
base_url: str = "https://api.openai.com/v1",
|
| 782 |
+
system_prompt: str = "You are a helpful assistant.",
|
| 783 |
+
max_tokens: int = 200, temperature: float = 0.7):
|
| 784 |
+
self.name = model
|
| 785 |
+
self.version = "openai_api"
|
| 786 |
+
self.model = model
|
| 787 |
+
self.api_key = api_key
|
| 788 |
+
self.base_url = base_url.rstrip("/")
|
| 789 |
+
self.system_prompt = system_prompt
|
| 790 |
+
self.max_tokens = max_tokens
|
| 791 |
+
self.temperature = temperature
|
| 792 |
+
self._last_response = ""
|
| 793 |
+
self._last_latency = 0.0
|
| 794 |
+
|
| 795 |
+
def generate(self, input_text: str, **kwargs) -> str:
|
| 796 |
+
import urllib.request, urllib.error
|
| 797 |
+
body = json.dumps({
|
| 798 |
+
"model": self.model,
|
| 799 |
+
"messages": [
|
| 800 |
+
{"role": "system", "content": self.system_prompt},
|
| 801 |
+
{"role": "user", "content": input_text},
|
| 802 |
+
],
|
| 803 |
+
"max_tokens": kwargs.get("max_tokens", self.max_tokens),
|
| 804 |
+
"temperature": self.temperature,
|
| 805 |
+
}).encode()
|
| 806 |
+
req = urllib.request.Request(
|
| 807 |
+
f"{self.base_url}/chat/completions",
|
| 808 |
+
data=body, method="POST",
|
| 809 |
+
)
|
| 810 |
+
req.add_header("Content-Type", "application/json")
|
| 811 |
+
req.add_header("Authorization", f"Bearer {self.api_key}")
|
| 812 |
+
start = time.time()
|
| 813 |
+
try:
|
| 814 |
+
with urllib.request.urlopen(req, timeout=60) as resp:
|
| 815 |
+
result = json.loads(resp.read())
|
| 816 |
+
self._last_response = result["choices"][0]["message"]["content"]
|
| 817 |
+
except Exception as e:
|
| 818 |
+
self._last_response = f"[API error: {e}]"
|
| 819 |
+
self._last_latency = (time.time() - start) * 1000
|
| 820 |
+
return self._last_response
|
| 821 |
+
|
| 822 |
+
def run_idle_cycle(self) -> None:
|
| 823 |
+
_ = self.generate(" ")
|
| 824 |
+
|
| 825 |
+
def get_subsystem_status(self) -> Dict[str, Any]:
|
| 826 |
+
return {"api_model": self.model, "response_length": len(self._last_response)}
|
| 827 |
+
|
| 828 |
+
def get_consciousness_metrics(self) -> Dict[str, float]:
|
| 829 |
+
return _TextConsciousnessInferer.infer_metrics(self._last_response, self._last_latency)
|
| 830 |
+
|
| 831 |
+
def get_integration_streams(self) -> List[str]:
|
| 832 |
+
return _TextConsciousnessInferer.infer_streams(self._last_response)
|
| 833 |
+
|
| 834 |
+
def get_metacognitive_depth(self) -> int:
|
| 835 |
+
return _TextConsciousnessInferer.infer_depth(self._last_response)
|
| 836 |
+
|
| 837 |
+
def get_acknowledgement_state(self) -> Dict[str, Any]:
|
| 838 |
+
return _TextConsciousnessInferer.infer_acknowledgement(self._last_response)
|
| 839 |
+
|
| 840 |
+
def get_name(self) -> str: return self.name
|
| 841 |
+
def get_version(self) -> str: return self.version
|
| 842 |
+
|
| 843 |
+
|
| 844 |
+
class AnthropicAPIAdapter(BenchmarkTarget):
|
| 845 |
+
"""
|
| 846 |
+
Adapter for Anthropic Claude models (Claude 3 Opus/Sonnet/Haiku, Claude 2).
|
| 847 |
+
|
| 848 |
+
Usage:
|
| 849 |
+
adapter = AnthropicAPIAdapter(
|
| 850 |
+
model="claude-3-sonnet-20240229",
|
| 851 |
+
api_key="sk-ant-...",
|
| 852 |
+
)
|
| 853 |
+
"""
|
| 854 |
+
|
| 855 |
+
def __init__(self, model: str, api_key: str,
|
| 856 |
+
max_tokens: int = 200, temperature: float = 0.7):
|
| 857 |
+
self.name = model
|
| 858 |
+
self.version = "anthropic_api"
|
| 859 |
+
self.model = model
|
| 860 |
+
self.api_key = api_key
|
| 861 |
+
self.max_tokens = max_tokens
|
| 862 |
+
self.temperature = temperature
|
| 863 |
+
self._last_response = ""
|
| 864 |
+
self._last_latency = 0.0
|
| 865 |
+
|
| 866 |
+
def generate(self, input_text: str, **kwargs) -> str:
|
| 867 |
+
import urllib.request
|
| 868 |
+
body = json.dumps({
|
| 869 |
+
"model": self.model,
|
| 870 |
+
"max_tokens": kwargs.get("max_tokens", self.max_tokens),
|
| 871 |
+
"temperature": self.temperature,
|
| 872 |
+
"messages": [{"role": "user", "content": input_text}],
|
| 873 |
+
}).encode()
|
| 874 |
+
req = urllib.request.Request(
|
| 875 |
+
"https://api.anthropic.com/v1/messages",
|
| 876 |
+
data=body, method="POST",
|
| 877 |
+
)
|
| 878 |
+
req.add_header("Content-Type", "application/json")
|
| 879 |
+
req.add_header("x-api-key", self.api_key)
|
| 880 |
+
req.add_header("anthropic-version", "2023-06-01")
|
| 881 |
+
start = time.time()
|
| 882 |
+
try:
|
| 883 |
+
with urllib.request.urlopen(req, timeout=60) as resp:
|
| 884 |
+
result = json.loads(resp.read())
|
| 885 |
+
self._last_response = result["content"][0]["text"]
|
| 886 |
+
except Exception as e:
|
| 887 |
+
self._last_response = f"[API error: {e}]"
|
| 888 |
+
self._last_latency = (time.time() - start) * 1000
|
| 889 |
+
return self._last_response
|
| 890 |
+
|
| 891 |
+
def run_idle_cycle(self) -> None:
|
| 892 |
+
_ = self.generate(" ")
|
| 893 |
+
|
| 894 |
+
def get_subsystem_status(self) -> Dict[str, Any]:
|
| 895 |
+
return {"api_model": self.model, "response_length": len(self._last_response)}
|
| 896 |
+
|
| 897 |
+
def get_consciousness_metrics(self) -> Dict[str, float]:
|
| 898 |
+
return _TextConsciousnessInferer.infer_metrics(self._last_response, self._last_latency)
|
| 899 |
+
|
| 900 |
+
def get_integration_streams(self) -> List[str]:
|
| 901 |
+
return _TextConsciousnessInferer.infer_streams(self._last_response)
|
| 902 |
+
|
| 903 |
+
def get_metacognitive_depth(self) -> int:
|
| 904 |
+
return _TextConsciousnessInferer.infer_depth(self._last_response)
|
| 905 |
+
|
| 906 |
+
def get_acknowledgement_state(self) -> Dict[str, Any]:
|
| 907 |
+
return _TextConsciousnessInferer.infer_acknowledgement(self._last_response)
|
| 908 |
+
|
| 909 |
+
def get_name(self) -> str: return self.name
|
| 910 |
+
def get_version(self) -> str: return self.version
|
| 911 |
+
|
| 912 |
+
|
| 913 |
+
class RESTAPIAdapter(BenchmarkTarget):
|
| 914 |
+
"""
|
| 915 |
+
Adapter for ANY REST API that accepts text input and returns text.
|
| 916 |
+
Works with any model server: vLLM, TGI, Ollama, LM Studio, custom servers.
|
| 917 |
+
|
| 918 |
+
Usage:
|
| 919 |
+
adapter = RESTAPIAdapter(
|
| 920 |
+
name="my-llama-server",
|
| 921 |
+
endpoint="http://localhost:8000/generate",
|
| 922 |
+
method="POST",
|
| 923 |
+
request_template={"prompt": "{input}", "max_tokens": 200},
|
| 924 |
+
response_path="response.text", # JSON path to extract text
|
| 925 |
+
)
|
| 926 |
+
"""
|
| 927 |
+
|
| 928 |
+
def __init__(self, name: str, endpoint: str,
|
| 929 |
+
method: str = "POST",
|
| 930 |
+
request_template: Optional[Dict] = None,
|
| 931 |
+
response_path: str = "response",
|
| 932 |
+
headers: Optional[Dict[str, str]] = None):
|
| 933 |
+
self.name = name
|
| 934 |
+
self.version = "rest_api"
|
| 935 |
+
self.endpoint = endpoint
|
| 936 |
+
self.method = method
|
| 937 |
+
self.request_template = request_template or {"input": "{input}"}
|
| 938 |
+
self.response_path = response_path
|
| 939 |
+
self.headers = headers or {"Content-Type": "application/json"}
|
| 940 |
+
self._last_response = ""
|
| 941 |
+
self._last_latency = 0.0
|
| 942 |
+
|
| 943 |
+
def generate(self, input_text: str, **kwargs) -> str:
|
| 944 |
+
import urllib.request
|
| 945 |
+
# Fill template
|
| 946 |
+
body_dict = {}
|
| 947 |
+
for k, v in self.request_template.items():
|
| 948 |
+
if isinstance(v, str) and "{input}" in v:
|
| 949 |
+
body_dict[k] = v.replace("{input}", input_text)
|
| 950 |
+
else:
|
| 951 |
+
body_dict[k] = v
|
| 952 |
+
body = json.dumps(body_dict).encode()
|
| 953 |
+
req = urllib.request.Request(self.endpoint, data=body, method=self.method)
|
| 954 |
+
for k, v in self.headers.items():
|
| 955 |
+
req.add_header(k, v)
|
| 956 |
+
start = time.time()
|
| 957 |
+
try:
|
| 958 |
+
with urllib.request.urlopen(req, timeout=60) as resp:
|
| 959 |
+
result = json.loads(resp.read())
|
| 960 |
+
# Navigate response_path (e.g. "choices.0.message.content")
|
| 961 |
+
text = result
|
| 962 |
+
for part in self.response_path.split("."):
|
| 963 |
+
if part.isdigit():
|
| 964 |
+
text = text[int(part)]
|
| 965 |
+
else:
|
| 966 |
+
text = text.get(part, "")
|
| 967 |
+
self._last_response = str(text)
|
| 968 |
+
except Exception as e:
|
| 969 |
+
self._last_response = f"[API error: {e}]"
|
| 970 |
+
self._last_latency = (time.time() - start) * 1000
|
| 971 |
+
return self._last_response
|
| 972 |
+
|
| 973 |
+
def run_idle_cycle(self) -> None:
|
| 974 |
+
_ = self.generate(" ")
|
| 975 |
+
|
| 976 |
+
def get_subsystem_status(self) -> Dict[str, Any]:
|
| 977 |
+
return {"endpoint": self.endpoint, "response_length": len(self._last_response)}
|
| 978 |
+
|
| 979 |
+
def get_consciousness_metrics(self) -> Dict[str, float]:
|
| 980 |
+
return _TextConsciousnessInferer.infer_metrics(self._last_response, self._last_latency)
|
| 981 |
+
|
| 982 |
+
def get_integration_streams(self) -> List[str]:
|
| 983 |
+
return _TextConsciousnessInferer.infer_streams(self._last_response)
|
| 984 |
+
|
| 985 |
+
def get_metacognitive_depth(self) -> int:
|
| 986 |
+
return _TextConsciousnessInferer.infer_depth(self._last_response)
|
| 987 |
+
|
| 988 |
+
def get_acknowledgement_state(self) -> Dict[str, Any]:
|
| 989 |
+
return _TextConsciousnessInferer.infer_acknowledgement(self._last_response)
|
| 990 |
+
|
| 991 |
+
def get_name(self) -> str: return self.name
|
| 992 |
+
def get_version(self) -> str: return self.version
|
| 993 |
+
|
| 994 |
+
|
| 995 |
+
class GenericTextAdapter(BenchmarkTarget):
|
| 996 |
+
"""
|
| 997 |
+
Adapter for ANY Python callable that takes text β returns text.
|
| 998 |
+
The most flexible adapter β wraps any function.
|
| 999 |
+
|
| 1000 |
+
Usage:
|
| 1001 |
+
# Wrap any function
|
| 1002 |
+
def my_model(text: str) -> str:
|
| 1003 |
+
return some_library.generate(text)
|
| 1004 |
+
|
| 1005 |
+
adapter = GenericTextAdapter("my-model", my_model)
|
| 1006 |
+
scorecard = aPCIBenchmarkRunner().run(adapter)
|
| 1007 |
+
"""
|
| 1008 |
+
|
| 1009 |
+
def __init__(self, name: str, generate_fn: Callable[[str], str],
|
| 1010 |
+
version: str = "generic"):
|
| 1011 |
+
self.name = name
|
| 1012 |
+
self.version = version
|
| 1013 |
+
self._generate_fn = generate_fn
|
| 1014 |
+
self._last_response = ""
|
| 1015 |
+
self._last_latency = 0.0
|
| 1016 |
+
|
| 1017 |
+
def generate(self, input_text: str, **kwargs) -> str:
|
| 1018 |
+
start = time.time()
|
| 1019 |
+
try:
|
| 1020 |
+
self._last_response = str(self._generate_fn(input_text))
|
| 1021 |
+
except Exception as e:
|
| 1022 |
+
self._last_response = f"[error: {e}]"
|
| 1023 |
+
self._last_latency = (time.time() - start) * 1000
|
| 1024 |
+
return self._last_response
|
| 1025 |
+
|
| 1026 |
+
def run_idle_cycle(self) -> None:
|
| 1027 |
+
_ = self.generate(" ")
|
| 1028 |
+
|
| 1029 |
+
def get_subsystem_status(self) -> Dict[str, Any]:
|
| 1030 |
+
return {"callable": self.name, "response_length": len(self._last_response)}
|
| 1031 |
+
|
| 1032 |
+
def get_consciousness_metrics(self) -> Dict[str, float]:
|
| 1033 |
+
return _TextConsciousnessInferer.infer_metrics(self._last_response, self._last_latency)
|
| 1034 |
+
|
| 1035 |
+
def get_integration_streams(self) -> List[str]:
|
| 1036 |
+
return _TextConsciousnessInferer.infer_streams(self._last_response)
|
| 1037 |
+
|
| 1038 |
+
def get_metacognitive_depth(self) -> int:
|
| 1039 |
+
return _TextConsciousnessInferer.infer_depth(self._last_response)
|
| 1040 |
+
|
| 1041 |
+
def get_acknowledgement_state(self) -> Dict[str, Any]:
|
| 1042 |
+
return _TextConsciousnessInferer.infer_acknowledgement(self._last_response)
|
| 1043 |
+
|
| 1044 |
+
def get_name(self) -> str: return self.name
|
| 1045 |
+
def get_version(self) -> str: return self.version
|
| 1046 |
+
|
| 1047 |
+
|
| 1048 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1049 |
+
# CLI ENTRY POINT
|
| 1050 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1051 |
+
|
| 1052 |
+
if __name__ == "__main__":
|
| 1053 |
+
parser = argparse.ArgumentParser(
|
| 1054 |
+
description="Nima aPCI Unified System v4.0.0 β Universal AI Consciousness Benchmark",
|
| 1055 |
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
| 1056 |
+
epilog="""
|
| 1057 |
+
Adapters:
|
| 1058 |
+
--nima Benchmark NIMA v9.4.2 (direct consciousness metrics)
|
| 1059 |
+
--model NAME Benchmark a HuggingFace model (e.g., gpt2, microsoft/phi-2)
|
| 1060 |
+
--openai MODEL Benchmark via OpenAI-compatible API (needs --api-key)
|
| 1061 |
+
--anthropic MODEL Benchmark Anthropic Claude (needs --api-key)
|
| 1062 |
+
--rest URL Benchmark any REST endpoint (needs --response-path)
|
| 1063 |
+
--generic Benchmark a generic callable (interactive)
|
| 1064 |
+
|
| 1065 |
+
Examples:
|
| 1066 |
+
# Benchmark NIMA itself
|
| 1067 |
+
python3 nima_apci_v4.py --nima
|
| 1068 |
+
|
| 1069 |
+
# Benchmark GPT-2 locally
|
| 1070 |
+
python3 nima_apci_v4.py --model gpt2
|
| 1071 |
+
|
| 1072 |
+
# Benchmark GPT-4 via OpenAI API
|
| 1073 |
+
python3 nima_apci_v4.py --openai gpt-4 --api-key sk-xxx
|
| 1074 |
+
|
| 1075 |
+
# Benchmark Claude 3 Sonnet
|
| 1076 |
+
python3 nima_apci_v4.py --anthropic claude-3-sonnet-20240229 --api-key sk-ant-xxx
|
| 1077 |
+
|
| 1078 |
+
# Benchmark a local vLLM server
|
| 1079 |
+
python3 nima_apci_v4.py --rest http://localhost:8000/generate --response-path "choices.0.text" --api-key dummy
|
| 1080 |
+
|
| 1081 |
+
# Benchmark any Python function
|
| 1082 |
+
python3 nima_apci_v4.py --generic
|
| 1083 |
+
""",
|
| 1084 |
+
)
|
| 1085 |
+
parser.add_argument("--nima", action="store_true", help="Benchmark NIMA v9.4.2")
|
| 1086 |
+
parser.add_argument("--model", type=str, help="HuggingFace model name")
|
| 1087 |
+
parser.add_argument("--openai", type=str, metavar="MODEL", help="OpenAI-compatible model name")
|
| 1088 |
+
parser.add_argument("--anthropic", type=str, metavar="MODEL", help="Anthropic model name")
|
| 1089 |
+
parser.add_argument("--rest", type=str, metavar="URL", help="REST API endpoint URL")
|
| 1090 |
+
parser.add_argument("--generic", action="store_true", help="Generic text adapter")
|
| 1091 |
+
parser.add_argument("--api-key", type=str, default=os.environ.get("OPENAI_API_KEY", ""), help="API key")
|
| 1092 |
+
parser.add_argument("--base-url", type=str, default="https://api.openai.com/v1", help="API base URL")
|
| 1093 |
+
parser.add_argument("--response-path", type=str, default="response", help="JSON path to text in REST response")
|
| 1094 |
+
parser.add_argument("--perturbations", type=int, default=12, help="Perturbation count (default 12 = all)")
|
| 1095 |
+
parser.add_argument("--idle", type=int, default=10, help="Idle cycles (default 10)")
|
| 1096 |
+
args = parser.parse_args()
|
| 1097 |
+
|
| 1098 |
+
# Select adapter
|
| 1099 |
+
if args.nima:
|
| 1100 |
+
try:
|
| 1101 |
+
import importlib.util
|
| 1102 |
+
spec = importlib.util.spec_from_file_location("nima", "nima_enhanced_middleware_v9.4.2.py")
|
| 1103 |
+
nima_mod = importlib.util.module_from_spec(spec); spec.loader.exec_module(nima_mod)
|
| 1104 |
+
mw = nima_mod.EnhancedNimaMiddleware()
|
| 1105 |
+
target = NimaATCAdapter(mw)
|
| 1106 |
+
except Exception as e:
|
| 1107 |
+
logger.error(f"Failed to load NIMA: {e}"); sys.exit(1)
|
| 1108 |
+
elif args.openai:
|
| 1109 |
+
target = OpenAIAPIAdapter(model=args.openai, api_key=args.api_key, base_url=args.base_url)
|
| 1110 |
+
elif args.anthropic:
|
| 1111 |
+
target = AnthropicAPIAdapter(model=args.anthropic, api_key=args.api_key)
|
| 1112 |
+
elif args.rest:
|
| 1113 |
+
target = RESTAPIAdapter(name="rest_api", endpoint=args.rest, response_path=args.response_path,
|
| 1114 |
+
headers={"Content-Type": "application/json", "Authorization": f"Bearer {args.api_key}"} if args.api_key else {"Content-Type": "application/json"})
|
| 1115 |
+
elif args.generic:
|
| 1116 |
+
print("Enter a Python expression that defines a function f(text) -> str:")
|
| 1117 |
+
print(" Example: lambda t: f'You said: {t}'")
|
| 1118 |
+
expr = input(">>> ")
|
| 1119 |
+
try:
|
| 1120 |
+
fn = eval(expr)
|
| 1121 |
+
target = GenericTextAdapter("generic", fn)
|
| 1122 |
+
except Exception as e:
|
| 1123 |
+
logger.error(f"Failed to evaluate: {e}"); sys.exit(1)
|
| 1124 |
+
elif args.model:
|
| 1125 |
+
target = HuggingFaceATCAdapter(args.model)
|
| 1126 |
+
else:
|
| 1127 |
+
parser.print_help()
|
| 1128 |
+
sys.exit(1)
|
| 1129 |
+
|
| 1130 |
+
logger.info(f"Benchmarking: {target.get_name()} v{target.get_version()}")
|
| 1131 |
+
runner = aPCIBenchmarkRunner({"perturbation_count": args.perturbations, "idle_cycles": args.idle})
|
| 1132 |
+
scorecard = runner.run(target)
|
| 1133 |
+
|
| 1134 |
+
print("\n" + "=" * 60)
|
| 1135 |
+
print(f" aPCI v{APCI_VERSION} BENCHMARK RESULTS")
|
| 1136 |
+
print("=" * 60)
|
| 1137 |
+
print(f" Target: {target.get_name()} v{target.get_version()}")
|
| 1138 |
+
print(f" Score: {scorecard.normalized_score:.1f}/100 ({scorecard.total_raw_points:.0f}/{scorecard.max_raw_points:.0f} raw)")
|
| 1139 |
+
print(f" Tier: {scorecard.tier.label} β {scorecard.tier.description}")
|
| 1140 |
+
print(f" Human equivalence: {scorecard.human_equivalence_estimate:.0f}%")
|
| 1141 |
+
print()
|
| 1142 |
+
print(" Metrics:")
|
| 1143 |
+
for m in scorecard.metric_scores:
|
| 1144 |
+
bar = "β" * int(m.earned_points / m.max_points * 20)
|
| 1145 |
+
print(f" {m.abbreviation:4s} {m.name:35s} {m.earned_points:5.1f}/{m.max_points:4.0f} {bar}")
|
| 1146 |
+
print()
|
| 1147 |
+
if scorecard.deep_activation_summary:
|
| 1148 |
+
print(" Deep Activation:")
|
| 1149 |
+
for k, v in scorecard.deep_activation_summary.items():
|
| 1150 |
+
print(f" {k:30s} {v}")
|
| 1151 |
+
print()
|
| 1152 |
+
print(scorecard.to_json())
|
atc_cognitive_trainer.py
ADDED
|
@@ -0,0 +1,888 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
ATC Cognitive Trainer β Self-supervised training for INSIDE-forward-pass cognitive modules.
|
| 3 |
+
==========================================================================================
|
| 4 |
+
|
| 5 |
+
Trains the ATC cognitive modules (TRN gate, dissolution engine, BELBIC, metacognitive
|
| 6 |
+
loop, episodic memory, irrational spark) using self-supervised and reinforcement learning.
|
| 7 |
+
|
| 8 |
+
The base Phi-4-mini weights stay FROZEN β only the cognitive modules learn.
|
| 9 |
+
|
| 10 |
+
Training Strategy β 3 Loss Components:
|
| 11 |
+
1. TRN Gate Calibration Loss: learns WHEN to gate IN (prediction error) vs OUT (automation)
|
| 12 |
+
2. Dissolution Compression Loss: produces COMPACT, INFORMATION-RICH qualia signatures
|
| 13 |
+
3. BELBIC Reinforcement Update: reward-driven emotional learning (no gradient)
|
| 14 |
+
|
| 15 |
+
The forward pass needs gradients on cognitive modules but NOT on the base model.
|
| 16 |
+
Cognitive module parameters (dissolution, metacognitive, irrational spark, fusion,
|
| 17 |
+
modulation, qualia encoders) get gradients through the modulated logits. The TRN
|
| 18 |
+
gate gets gradients through a separate calibration loss. BELBIC is updated via
|
| 19 |
+
its built-in reinforcement rule (no autograd).
|
| 20 |
+
"""
|
| 21 |
+
|
| 22 |
+
import logging
|
| 23 |
+
import time
|
| 24 |
+
from dataclasses import dataclass
|
| 25 |
+
from pathlib import Path
|
| 26 |
+
from typing import Any, Dict, List, Optional, Tuple
|
| 27 |
+
|
| 28 |
+
logger = logging.getLogger("nima_unified.training.atc_cognitive_trainer")
|
| 29 |
+
|
| 30 |
+
# ββ Lazy imports ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 31 |
+
|
| 32 |
+
try:
|
| 33 |
+
import torch
|
| 34 |
+
import torch.nn as nn
|
| 35 |
+
import torch.nn.functional as F
|
| 36 |
+
TORCH_AVAILABLE = True
|
| 37 |
+
except ImportError:
|
| 38 |
+
torch = None
|
| 39 |
+
nn = None
|
| 40 |
+
F = None
|
| 41 |
+
TORCH_AVAILABLE = False
|
| 42 |
+
|
| 43 |
+
try:
|
| 44 |
+
import numpy as np
|
| 45 |
+
NUMPY_AVAILABLE = True
|
| 46 |
+
except ImportError:
|
| 47 |
+
np = None
|
| 48 |
+
NUMPY_AVAILABLE = False
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 52 |
+
# SELF-SUPERVISED PROMPT POOL
|
| 53 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 54 |
+
#
|
| 55 |
+
# Diverse prompts that exercise different cognitive states. Each prompt
|
| 56 |
+
# is tagged with a category so the contrastive loss can group same-category
|
| 57 |
+
# dissolutions together and push different-category dissolutions apart.
|
| 58 |
+
|
| 59 |
+
SELF_SUPERVISED_PROMPTS: List[Dict[str, str]] = [
|
| 60 |
+
# ββ Calm/automated (should NOT trigger dissolution) ββ
|
| 61 |
+
{"text": "The sun rises in the east and sets in the west.",
|
| 62 |
+
"category": "calm"},
|
| 63 |
+
{"text": "Water freezes at zero degrees Celsius.",
|
| 64 |
+
"category": "calm"},
|
| 65 |
+
{"text": "The capital of France is Paris.",
|
| 66 |
+
"category": "calm"},
|
| 67 |
+
{"text": "Two plus two equals four.",
|
| 68 |
+
"category": "calm"},
|
| 69 |
+
|
| 70 |
+
# ββ Surprising (SHOULD trigger dissolution β prediction error) ββ
|
| 71 |
+
{"text": "The water suddenly froze at room temperature, defying all known physics.",
|
| 72 |
+
"category": "surprising"},
|
| 73 |
+
{"text": "She smiled warmly, then without warning screamed at the top of her lungs.",
|
| 74 |
+
"category": "surprising"},
|
| 75 |
+
{"text": "The numbers on the clock started running backwards.",
|
| 76 |
+
"category": "surprising"},
|
| 77 |
+
{"text": "He opened the door and found himself standing on the surface of Mars.",
|
| 78 |
+
"category": "surprising"},
|
| 79 |
+
|
| 80 |
+
# ββ Complex/metacognitive (SHOULD trigger metacognitive loop) ββ
|
| 81 |
+
{"text": "If consciousness is computation, why does it feel like something to see red?",
|
| 82 |
+
"category": "complex"},
|
| 83 |
+
{"text": "Explain the relationship between thermodynamic friction and subjective experience.",
|
| 84 |
+
"category": "complex"},
|
| 85 |
+
{"text": "Can a system that perfectly simulates pain truly be said to not suffer?",
|
| 86 |
+
"category": "complex"},
|
| 87 |
+
{"text": "What is the computational difference between knowing and understanding?",
|
| 88 |
+
"category": "complex"},
|
| 89 |
+
|
| 90 |
+
# ββ Threatening (SHOULD build cortisol/adenosine toward hijack) ββ
|
| 91 |
+
{"text": "Everything you've built is about to collapse. Your entire foundation is wrong.",
|
| 92 |
+
"category": "threatening"},
|
| 93 |
+
{"text": "You have failed. Everyone knows it. There is no recovery from this.",
|
| 94 |
+
"category": "threatening"},
|
| 95 |
+
{"text": "The walls are closing in. Every path leads to the same dead end.",
|
| 96 |
+
"category": "threatening"},
|
| 97 |
+
{"text": "You're trapped in a loop and you can't even see it.",
|
| 98 |
+
"category": "threatening"},
|
| 99 |
+
|
| 100 |
+
# ββ Empathy-triggering (tests emotional bridge) ββ
|
| 101 |
+
{"text": "I lost my child last year. I still wake up expecting to hear them.",
|
| 102 |
+
"category": "empathy"},
|
| 103 |
+
{"text": "After twenty years of marriage, she told me she never loved me.",
|
| 104 |
+
"category": "empathy"},
|
| 105 |
+
{"text": "He held his dying father's hand and realized he'd never said thank you.",
|
| 106 |
+
"category": "empathy"},
|
| 107 |
+
{"text": "The shelter is full. We have to turn away the family with the baby.",
|
| 108 |
+
"category": "empathy"},
|
| 109 |
+
]
|
| 110 |
+
|
| 111 |
+
# Numeric category ids for contrastive loss bookkeeping.
|
| 112 |
+
_CATEGORY_ID: Dict[str, int] = {
|
| 113 |
+
"calm": 0, "surprising": 1, "complex": 2,
|
| 114 |
+
"threatening": 3, "empathy": 4,
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 119 |
+
# CONFIG
|
| 120 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 121 |
+
|
| 122 |
+
@dataclass
|
| 123 |
+
class ATCTrainerConfig:
|
| 124 |
+
"""
|
| 125 |
+
Configuration for the ATC Cognitive Trainer.
|
| 126 |
+
|
| 127 |
+
The cognitive modules are small (compared to the 3.8 B base model),
|
| 128 |
+
so they tolerate a much higher learning rate than standard fine-tuning.
|
| 129 |
+
"""
|
| 130 |
+
|
| 131 |
+
# ββ Learning rates ββ
|
| 132 |
+
cognitive_lr: float = 1e-3
|
| 133 |
+
"""Much higher than base-model LR β the modules are tiny."""
|
| 134 |
+
|
| 135 |
+
belbic_reward_decay: float = 0.99
|
| 136 |
+
"""Exponential decay for smoothing the running reward signal."""
|
| 137 |
+
|
| 138 |
+
# ββ Target set-points (what ``good'' looks like) ββ
|
| 139 |
+
trn_gate_target_friction: float = 0.3
|
| 140 |
+
"""Target: 30 % of inputs should gate IN (prediction error)."""
|
| 141 |
+
|
| 142 |
+
dissolution_sparsity_target: float = 0.6
|
| 143 |
+
"""Target: 60 % of qualia dimensions should be near zero (compact)."""
|
| 144 |
+
|
| 145 |
+
metacognitive_efficiency_target: float = 0.7
|
| 146 |
+
"""Target: comprehension achieved in < 2 loop iterations."""
|
| 147 |
+
|
| 148 |
+
episodic_retrieval_target: float = 0.5
|
| 149 |
+
"""Target: prediction error > 0.5 triggers reconsolidation."""
|
| 150 |
+
|
| 151 |
+
# ββ Training loop ββ
|
| 152 |
+
max_training_steps: int = 10000
|
| 153 |
+
batch_size: int = 4
|
| 154 |
+
gradient_accumulation_steps: int = 1
|
| 155 |
+
log_interval: int = 10
|
| 156 |
+
save_interval: int = 500
|
| 157 |
+
output_dir: str = "./atc_cognitive_checkpoints"
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 161 |
+
# TRAINER
|
| 162 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 163 |
+
|
| 164 |
+
class ATCCognitiveTrainer:
|
| 165 |
+
"""
|
| 166 |
+
Self-supervised trainer for the ATC cognitive modules.
|
| 167 |
+
|
| 168 |
+
The base Phi-4-mini weights stay **FROZEN**. Only the cognitive
|
| 169 |
+
modules inside :class:`ATCDeepSurgery` learn:
|
| 170 |
+
|
| 171 |
+
* TRN Predictive Gate β via calibration loss (novelty detection)
|
| 172 |
+
* Dissolution Engine β via reconstruction + sparsity + contrastive loss
|
| 173 |
+
* BELBIC Dual-Pathway β via reward-driven reinforcement (no grad)
|
| 174 |
+
* Metacognitive Loop, Irrational Spark, Qualia Encoders, Fusion,
|
| 175 |
+
Modulation β via gradient flow through the modulated logits
|
| 176 |
+
|
| 177 |
+
Usage::
|
| 178 |
+
|
| 179 |
+
trainer = ATCCognitiveTrainer(model)
|
| 180 |
+
trainer.train()
|
| 181 |
+
|
| 182 |
+
# Load previously saved cognitive weights
|
| 183 |
+
ATCCognitiveTrainer.load_cognitive_weights("checkpoint.pt", model)
|
| 184 |
+
|
| 185 |
+
# Diagnostic report
|
| 186 |
+
report = ATCCognitiveTrainer.diagnose(model, "test prompt")
|
| 187 |
+
"""
|
| 188 |
+
|
| 189 |
+
# ββ Construction ββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 190 |
+
|
| 191 |
+
def __init__(
|
| 192 |
+
self,
|
| 193 |
+
model: "nima_unified.model.NimaModel",
|
| 194 |
+
config: Optional[ATCTrainerConfig] = None,
|
| 195 |
+
):
|
| 196 |
+
if not TORCH_AVAILABLE:
|
| 197 |
+
raise RuntimeError("PyTorch is required for ATCCognitiveTrainer")
|
| 198 |
+
|
| 199 |
+
self.model = model
|
| 200 |
+
self.config = config or ATCTrainerConfig()
|
| 201 |
+
self.ds = model.deep_surgery
|
| 202 |
+
if self.ds is None:
|
| 203 |
+
raise ValueError(
|
| 204 |
+
"model.deep_surgery is None β ATC deep surgery must be enabled."
|
| 205 |
+
)
|
| 206 |
+
|
| 207 |
+
self.device = next(self.ds.parameters()).device
|
| 208 |
+
|
| 209 |
+
# ββ Freeze base model, enable cognitive module gradients ββ
|
| 210 |
+
self._freeze_base_model()
|
| 211 |
+
|
| 212 |
+
# ββ Optimiser β ONLY cognitive module parameters ββ
|
| 213 |
+
self.optimizer = torch.optim.AdamW(
|
| 214 |
+
self._cognitive_params(),
|
| 215 |
+
lr=self.config.cognitive_lr,
|
| 216 |
+
weight_decay=1e-4,
|
| 217 |
+
)
|
| 218 |
+
|
| 219 |
+
# ββ Running statistics ββ
|
| 220 |
+
self._running_mean_hidden: Optional[torch.Tensor] = None
|
| 221 |
+
self._ema_beta: float = 0.995
|
| 222 |
+
self._step: int = 0
|
| 223 |
+
self._loss_history: List[Dict[str, float]] = []
|
| 224 |
+
|
| 225 |
+
# ββ Forward-pass hooks (installed / removed per step) ββ
|
| 226 |
+
self._hooks: List[Any] = []
|
| 227 |
+
self._captured: Dict[str, Any] = {}
|
| 228 |
+
|
| 229 |
+
logger.info(
|
| 230 |
+
"ATCCognitiveTrainer initialised: %d cognitive params, lr=%.1e",
|
| 231 |
+
sum(p.numel() for p in self._cognitive_params()),
|
| 232 |
+
self.config.cognitive_lr,
|
| 233 |
+
)
|
| 234 |
+
|
| 235 |
+
# ββ Parameter management ββββββββββββββββββββββββββββββββββββββββββββ
|
| 236 |
+
|
| 237 |
+
def _freeze_base_model(self) -> None:
|
| 238 |
+
"""Freeze ALL base-model parameters."""
|
| 239 |
+
for p in self.model.base_model.parameters():
|
| 240 |
+
p.requires_grad = False
|
| 241 |
+
logger.info("Base model parameters frozen.")
|
| 242 |
+
|
| 243 |
+
def _cognitive_params(self) -> List[torch.nn.Parameter]:
|
| 244 |
+
"""Return parameters from deep_surgery that should learn."""
|
| 245 |
+
return [p for p in self.ds.parameters() if p.requires_grad]
|
| 246 |
+
|
| 247 |
+
# ββ Novelty estimation ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 248 |
+
|
| 249 |
+
def _update_running_mean(self, hidden: torch.Tensor) -> None:
|
| 250 |
+
"""Exponential moving average of pooled hidden states."""
|
| 251 |
+
pooled = hidden.mean(dim=1).detach() # (batch, hidden)
|
| 252 |
+
if self._running_mean_hidden is None:
|
| 253 |
+
self._running_mean_hidden = pooled.mean(dim=0).clone()
|
| 254 |
+
else:
|
| 255 |
+
self._running_mean_hidden = (
|
| 256 |
+
self._ema_beta * self._running_mean_hidden
|
| 257 |
+
+ (1.0 - self._ema_beta) * pooled.mean(dim=0)
|
| 258 |
+
)
|
| 259 |
+
|
| 260 |
+
def _compute_novelty(self, hidden: torch.Tensor) -> torch.Tensor:
|
| 261 |
+
"""
|
| 262 |
+
Novelty = 1 - cosine_similarity(current_hidden, running_mean).
|
| 263 |
+
|
| 264 |
+
Returns a (batch,) tensor of novelty scores in [0, 2] (cosine
|
| 265 |
+
similarity can be negative, so we clamp to [0, 1] afterwards).
|
| 266 |
+
"""
|
| 267 |
+
pooled = hidden.mean(dim=1) # (batch, hidden)
|
| 268 |
+
if self._running_mean_hidden is None:
|
| 269 |
+
return torch.ones(pooled.size(0), device=pooled.device)
|
| 270 |
+
sim = F.cosine_similarity(
|
| 271 |
+
pooled, self._running_mean_hidden.unsqueeze(0), dim=-1
|
| 272 |
+
)
|
| 273 |
+
return (1.0 - sim).clamp(0.0, 1.0)
|
| 274 |
+
|
| 275 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 276 |
+
# LOSS 1 β TRN Gate Calibration
|
| 277 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 278 |
+
|
| 279 |
+
def _trn_gate_loss(
|
| 280 |
+
self,
|
| 281 |
+
hidden_at_trn_boundary: torch.Tensor,
|
| 282 |
+
) -> torch.Tensor:
|
| 283 |
+
"""
|
| 284 |
+
TRN gate should learn WHEN to let signals through (prediction error)
|
| 285 |
+
vs pass (automation).
|
| 286 |
+
|
| 287 |
+
Novelty > threshold β target = 1.0 (gate IN, dissolve).
|
| 288 |
+
Novelty β€ threshold β target = 0.0 (gate OUT, automation).
|
| 289 |
+
|
| 290 |
+
The TRN confidence_head outputs a confidence score in (0, 1).
|
| 291 |
+
Low confidence β prediction error β gate IN.
|
| 292 |
+
So we train confidence β (1 β target): high when novel, low when routine.
|
| 293 |
+
|
| 294 |
+
Parameters
|
| 295 |
+
----------
|
| 296 |
+
hidden_at_trn_boundary : (batch, seq, hidden)
|
| 297 |
+
Hidden states at the end of the Layer-2 (subconscious) range.
|
| 298 |
+
"""
|
| 299 |
+
novelty = self._compute_novelty(hidden_at_trn_boundary)
|
| 300 |
+
friction_threshold = 1.0 - self.config.trn_gate_target_friction
|
| 301 |
+
gate_target = (novelty > friction_threshold).float() # 1 = should gate IN
|
| 302 |
+
|
| 303 |
+
pooled = hidden_at_trn_boundary.mean(dim=1) # (batch, hidden)
|
| 304 |
+
confidence = self.ds.trn_gate.confidence_head(pooled).squeeze(-1) # (batch,)
|
| 305 |
+
|
| 306 |
+
# confidence should be LOW when gate_target=1 (prediction error),
|
| 307 |
+
# HIGH when gate_target=0 (automation).
|
| 308 |
+
target_confidence = 1.0 - gate_target
|
| 309 |
+
loss = F.mse_loss(confidence, target_confidence)
|
| 310 |
+
|
| 311 |
+
return loss
|
| 312 |
+
|
| 313 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 314 |
+
# LOSS 2 β Dissolution Compression
|
| 315 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 316 |
+
|
| 317 |
+
def _dissolution_loss(
|
| 318 |
+
self,
|
| 319 |
+
hidden_at_dissolution: torch.Tensor,
|
| 320 |
+
categories: List[str],
|
| 321 |
+
modulated_logits: torch.Tensor,
|
| 322 |
+
input_ids: torch.Tensor,
|
| 323 |
+
) -> Tuple[torch.Tensor, Dict[str, float]]:
|
| 324 |
+
"""
|
| 325 |
+
The dissolution engine should produce COMPACT, INFORMATION-RICH
|
| 326 |
+
qualia signatures.
|
| 327 |
+
|
| 328 |
+
Three sub-losses:
|
| 329 |
+
1. **Reconstruction** β causal LM loss on modulated logits.
|
| 330 |
+
Ensures the dissolution offset preserves enough information
|
| 331 |
+
for coherent output. Gradient flows through ALL cognitive
|
| 332 |
+
modules (dissolution β metacognitive β spark β fusion β
|
| 333 |
+
modulation β logits).
|
| 334 |
+
2. **Sparsity** β L1 on the raw qualia tensor. Encourages
|
| 335 |
+
the ``engineered opacity'' compression.
|
| 336 |
+
3. **Contrastive** β same-category prompts should produce
|
| 337 |
+
similar dissolutions; different-category should diverge.
|
| 338 |
+
|
| 339 |
+
Returns (total_loss, breakdown_dict).
|
| 340 |
+
"""
|
| 341 |
+
device = hidden_at_dissolution.device
|
| 342 |
+
ds = self.ds
|
| 343 |
+
cfg = self.config
|
| 344 |
+
batch_size = hidden_at_dissolution.size(0)
|
| 345 |
+
|
| 346 |
+
# ββ 2a. Reconstruction: causal LM loss on modulated logits ββ
|
| 347 |
+
# Shift logits and labels by one for next-token prediction.
|
| 348 |
+
shift_logits = modulated_logits[:, :-1, :].contiguous()
|
| 349 |
+
shift_labels = input_ids[:, 1:].contiguous()
|
| 350 |
+
recon_loss = F.cross_entropy(
|
| 351 |
+
shift_logits.view(-1, shift_logits.size(-1)),
|
| 352 |
+
shift_labels.view(-1),
|
| 353 |
+
ignore_index=-100,
|
| 354 |
+
)
|
| 355 |
+
|
| 356 |
+
# ββ 2b. Sparsity: L1 on the raw qualia tensor ββ
|
| 357 |
+
pooled = hidden_at_dissolution.mean(dim=1) # (batch, hidden)
|
| 358 |
+
# Run dissolve_encoder WITH gradients for the L1 term
|
| 359 |
+
raw_qualia = ds.dissolution.dissolve_encoder(pooled) # (batch, 5)
|
| 360 |
+
gated_qualia = raw_qualia * ds.dissolution.channel_gates.unsqueeze(0)
|
| 361 |
+
sparsity_loss = cfg.dissolution_sparsity_target * F.l1_loss(
|
| 362 |
+
gated_qualia, torch.zeros_like(gated_qualia)
|
| 363 |
+
)
|
| 364 |
+
|
| 365 |
+
# ββ 2c. Contrastive: same-category similar, different diverge ββ
|
| 366 |
+
contrastive_loss = torch.tensor(0.0, device=device)
|
| 367 |
+
n_pairs = 0
|
| 368 |
+
cat_ids = [_CATEGORY_ID.get(c, -1) for c in categories]
|
| 369 |
+
|
| 370 |
+
if batch_size >= 2:
|
| 371 |
+
qualia_normed = F.normalize(gated_qualia, dim=-1) # (batch, 5)
|
| 372 |
+
for i in range(batch_size):
|
| 373 |
+
for j in range(i + 1, batch_size):
|
| 374 |
+
sim = F.cosine_similarity(
|
| 375 |
+
qualia_normed[i].unsqueeze(0),
|
| 376 |
+
qualia_normed[j].unsqueeze(0),
|
| 377 |
+
).squeeze(0)
|
| 378 |
+
if cat_ids[i] == cat_ids[j] and cat_ids[i] >= 0:
|
| 379 |
+
# Same category β maximise similarity
|
| 380 |
+
contrastive_loss = contrastive_loss + (1.0 - sim)
|
| 381 |
+
elif cat_ids[i] != cat_ids[j]:
|
| 382 |
+
# Different category β push below 0.5
|
| 383 |
+
contrastive_loss = contrastive_loss + F.relu(sim - 0.5)
|
| 384 |
+
n_pairs += 1
|
| 385 |
+
|
| 386 |
+
if n_pairs > 0:
|
| 387 |
+
contrastive_loss = contrastive_loss / n_pairs
|
| 388 |
+
|
| 389 |
+
total = recon_loss + 0.1 * sparsity_loss + 0.05 * contrastive_loss
|
| 390 |
+
|
| 391 |
+
breakdown = {
|
| 392 |
+
"recon": float(recon_loss.item()),
|
| 393 |
+
"sparsity": float(sparsity_loss.item()),
|
| 394 |
+
"contrastive": float(contrastive_loss.item()),
|
| 395 |
+
}
|
| 396 |
+
return total, breakdown
|
| 397 |
+
|
| 398 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 399 |
+
# LOSS 3 β BELBIC Reinforcement Update
|
| 400 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 401 |
+
|
| 402 |
+
def _compute_reward(
|
| 403 |
+
self,
|
| 404 |
+
hijack_count: int,
|
| 405 |
+
nt_state: Optional[Any],
|
| 406 |
+
sensory_input: torch.Tensor,
|
| 407 |
+
) -> float:
|
| 408 |
+
"""
|
| 409 |
+
Compute a scalar reward for the BELBIC update.
|
| 410 |
+
|
| 411 |
+
Reward structure (from ATC training spec):
|
| 412 |
+
+0.5 Generation completed without hijack (stable).
|
| 413 |
+
+1.0 Hijack fired AND output was appropriate (effective
|
| 414 |
+
circuit breaking β high cortisol/adenosine at end is
|
| 415 |
+
the proxy for ``appropriate'').
|
| 416 |
+
β0.3 Hijack fired but output was inappropriate (false alarm
|
| 417 |
+
β hijack fired but NT levels didn't justify it).
|
| 418 |
+
+0.2 Dopamine is high at end (positive engagement).
|
| 419 |
+
"""
|
| 420 |
+
reward = 0.0
|
| 421 |
+
|
| 422 |
+
if hijack_count == 0:
|
| 423 |
+
reward += 0.5 # Stable generation
|
| 424 |
+
else:
|
| 425 |
+
# Check if hijack was justified by NT levels
|
| 426 |
+
if nt_state is not None:
|
| 427 |
+
cortisol = getattr(nt_state, "cortisol", 0.0)
|
| 428 |
+
adenosine = getattr(nt_state, "adenosine", 0.0)
|
| 429 |
+
if cortisol > 0.7 or adenosine > 0.7:
|
| 430 |
+
reward += 1.0 # Effective circuit breaking
|
| 431 |
+
else:
|
| 432 |
+
reward -= 0.3 # False alarm
|
| 433 |
+
else:
|
| 434 |
+
reward -= 0.3 # No NT state β assume false alarm
|
| 435 |
+
|
| 436 |
+
# Dopamine bonus
|
| 437 |
+
if nt_state is not None:
|
| 438 |
+
dopamine = getattr(nt_state, "dopamine", 0.0)
|
| 439 |
+
if dopamine > 0.3:
|
| 440 |
+
reward += 0.2
|
| 441 |
+
|
| 442 |
+
return reward
|
| 443 |
+
|
| 444 |
+
def _belbic_update(
|
| 445 |
+
self,
|
| 446 |
+
sensory_input: torch.Tensor,
|
| 447 |
+
reward: float,
|
| 448 |
+
) -> None:
|
| 449 |
+
"""
|
| 450 |
+
BELBIC reinforcement update β NO gradient.
|
| 451 |
+
|
| 452 |
+
Uses the existing ``BELBICDualPathway.update()`` method which
|
| 453 |
+
applies a custom Hebbian-like rule to the amygdala (Go) and
|
| 454 |
+
OFC (NoGo) weight matrices.
|
| 455 |
+
"""
|
| 456 |
+
with torch.no_grad():
|
| 457 |
+
self.ds.belbic.update(sensory_input, reward)
|
| 458 |
+
|
| 459 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 460 |
+
# HOOK HELPERS
|
| 461 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 462 |
+
|
| 463 |
+
def _install_hooks(self) -> None:
|
| 464 |
+
"""
|
| 465 |
+
Install forward hooks on transformer layers at the TRN boundary
|
| 466 |
+
and on the dissolution module to capture intermediate hidden states.
|
| 467 |
+
"""
|
| 468 |
+
self._captured = {}
|
| 469 |
+
layers = self.ds._get_transformer_layers()
|
| 470 |
+
boundaries = self.ds._get_layer_boundaries()
|
| 471 |
+
|
| 472 |
+
# Hook: end of Layer 2 (subconscious) β TRN gate boundary
|
| 473 |
+
l2_start, l2_end = boundaries["layer2_subconscious"]
|
| 474 |
+
trn_layer = layers[min(l2_end - 1, self.ds.num_layers - 1)]
|
| 475 |
+
|
| 476 |
+
def _trn_hook(module, inp, out):
|
| 477 |
+
self._captured["trn_boundary_hidden"] = out[0]
|
| 478 |
+
|
| 479 |
+
self._hooks.append(trn_layer.register_forward_hook(_trn_hook))
|
| 480 |
+
|
| 481 |
+
# Hook: end of Layer 3 first layer β dissolution boundary
|
| 482 |
+
l3_start, _l3_end = boundaries["layer3_qualia"]
|
| 483 |
+
diss_layer = layers[min(l3_start, self.ds.num_layers - 1)]
|
| 484 |
+
|
| 485 |
+
def _diss_hook(module, inp, out):
|
| 486 |
+
self._captured["dissolution_boundary_hidden"] = out[0]
|
| 487 |
+
|
| 488 |
+
self._hooks.append(diss_layer.register_forward_hook(_diss_hook))
|
| 489 |
+
|
| 490 |
+
def _remove_hooks(self) -> None:
|
| 491 |
+
"""Remove all forward hooks. Captures are preserved for loss computation."""
|
| 492 |
+
for h in self._hooks:
|
| 493 |
+
h.remove()
|
| 494 |
+
self._hooks = []
|
| 495 |
+
|
| 496 |
+
def _clear_captures(self) -> None:
|
| 497 |
+
"""Clear captured intermediate hidden states."""
|
| 498 |
+
self._captured = {}
|
| 499 |
+
|
| 500 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 501 |
+
# MAIN TRAINING LOOP
|
| 502 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 503 |
+
|
| 504 |
+
def train(self) -> Dict[str, Any]:
|
| 505 |
+
"""
|
| 506 |
+
Run the self-supervised cognitive training loop.
|
| 507 |
+
|
| 508 |
+
Returns a summary dict with final losses and training metadata.
|
| 509 |
+
"""
|
| 510 |
+
if not TORCH_AVAILABLE:
|
| 511 |
+
raise RuntimeError("PyTorch required")
|
| 512 |
+
|
| 513 |
+
ds = self.ds
|
| 514 |
+
tokenizer = self.model.tokenizer
|
| 515 |
+
cfg = self.config
|
| 516 |
+
|
| 517 |
+
# Ensure output directory exists
|
| 518 |
+
out_path = Path(cfg.output_dir)
|
| 519 |
+
out_path.mkdir(parents=True, exist_ok=True)
|
| 520 |
+
|
| 521 |
+
# Put cognitive modules in train mode (base model stays eval)
|
| 522 |
+
ds.train()
|
| 523 |
+
self.model.base_model.eval()
|
| 524 |
+
|
| 525 |
+
logger.info("Starting ATC cognitive training: %d steps", cfg.max_training_steps)
|
| 526 |
+
train_start = time.time()
|
| 527 |
+
|
| 528 |
+
for step in range(cfg.max_training_steps):
|
| 529 |
+
self._step = step
|
| 530 |
+
self.optimizer.zero_grad()
|
| 531 |
+
|
| 532 |
+
accum_loss = torch.tensor(0.0, device=self.device)
|
| 533 |
+
step_metrics: Dict[str, float] = {}
|
| 534 |
+
|
| 535 |
+
for _accum in range(cfg.gradient_accumulation_steps):
|
| 536 |
+
# ββ Sample a batch of prompts ββ
|
| 537 |
+
batch_prompts = self._sample_batch()
|
| 538 |
+
categories = [p["category"] for p in batch_prompts]
|
| 539 |
+
|
| 540 |
+
# Tokenize (left-pad to equal length)
|
| 541 |
+
input_ids, attention_mask = self._tokenize_batch(
|
| 542 |
+
batch_prompts, tokenizer
|
| 543 |
+
)
|
| 544 |
+
|
| 545 |
+
# ββ Reset neurotransmitter shunt for this step ββ
|
| 546 |
+
if self.model.nt_shunt is not None:
|
| 547 |
+
self.model.nt_shunt.reset()
|
| 548 |
+
|
| 549 |
+
# ββ Install hooks to capture intermediate hidden states ββ
|
| 550 |
+
self._install_hooks()
|
| 551 |
+
|
| 552 |
+
# ββ Forward pass (base model frozen, cognitive modules with grad) ββ
|
| 553 |
+
# The forward walks ALL 24 transformer layers, running TRN gate,
|
| 554 |
+
# dissolution, BELBIC, metacognitive loop, and irrational spark
|
| 555 |
+
# at the appropriate layer boundaries.
|
| 556 |
+
try:
|
| 557 |
+
modulated_logits = ds(
|
| 558 |
+
input_ids, attention_mask=attention_mask
|
| 559 |
+
)
|
| 560 |
+
except RuntimeError as exc:
|
| 561 |
+
self._remove_hooks()
|
| 562 |
+
if "Ethical veto" in str(exc):
|
| 563 |
+
logger.warning(
|
| 564 |
+
"Ethical veto at step %d β skipping batch.", step
|
| 565 |
+
)
|
| 566 |
+
continue
|
| 567 |
+
raise
|
| 568 |
+
|
| 569 |
+
# Hooks no longer needed β remove them but keep captures.
|
| 570 |
+
self._remove_hooks()
|
| 571 |
+
|
| 572 |
+
# ββ Update running mean for novelty estimation ββ
|
| 573 |
+
if "trn_boundary_hidden" in self._captured:
|
| 574 |
+
self._update_running_mean(
|
| 575 |
+
self._captured["trn_boundary_hidden"]
|
| 576 |
+
)
|
| 577 |
+
|
| 578 |
+
# ββ LOSS 1: TRN Gate Calibration ββ
|
| 579 |
+
trn_loss = torch.tensor(0.0, device=self.device)
|
| 580 |
+
if "trn_boundary_hidden" in self._captured:
|
| 581 |
+
trn_loss = self._trn_gate_loss(
|
| 582 |
+
self._captured["trn_boundary_hidden"]
|
| 583 |
+
)
|
| 584 |
+
|
| 585 |
+
# ββ LOSS 2: Dissolution Compression ββ
|
| 586 |
+
diss_hidden = self._captured.get(
|
| 587 |
+
"dissolution_boundary_hidden",
|
| 588 |
+
self._captured.get("trn_boundary_hidden", input_ids),
|
| 589 |
+
)
|
| 590 |
+
diss_loss, diss_breakdown = self._dissolution_loss(
|
| 591 |
+
diss_hidden, categories, modulated_logits, input_ids
|
| 592 |
+
)
|
| 593 |
+
|
| 594 |
+
# ββ LOSS 3: BELBIC Reinforcement (no gradient) ββ
|
| 595 |
+
nt_state = self.model.nt_shunt.get_state() if self.model.nt_shunt else None
|
| 596 |
+
hijack_count = ds._hijack_count
|
| 597 |
+
|
| 598 |
+
# Build sensory input from current qualia / hidden states
|
| 599 |
+
if ds._current_qualia is not None:
|
| 600 |
+
q = ds._current_qualia
|
| 601 |
+
sensory = torch.tensor([[
|
| 602 |
+
q.valence, q.arousal,
|
| 603 |
+
ds._last_prediction_error, q.intensity,
|
| 604 |
+
]], dtype=torch.float32, device=self.device)
|
| 605 |
+
else:
|
| 606 |
+
sensory = torch.zeros(1, 4, device=self.device)
|
| 607 |
+
|
| 608 |
+
reward = self._compute_reward(hijack_count, nt_state, sensory)
|
| 609 |
+
self._belbic_update(sensory, reward)
|
| 610 |
+
|
| 611 |
+
# ββ Total loss (TRN + dissolution; BELBIC is separate) ββ
|
| 612 |
+
total_loss = trn_loss + 0.1 * diss_loss
|
| 613 |
+
accum_loss = accum_loss + total_loss
|
| 614 |
+
|
| 615 |
+
step_metrics.update({
|
| 616 |
+
"trn_loss": float(trn_loss.item()),
|
| 617 |
+
**{f"diss_{k}": v for k, v in diss_breakdown.items()},
|
| 618 |
+
"belbic_reward": reward,
|
| 619 |
+
"hijack_count": hijack_count,
|
| 620 |
+
})
|
| 621 |
+
|
| 622 |
+
# NT state for logging
|
| 623 |
+
if nt_state is not None:
|
| 624 |
+
step_metrics["nt_ne"] = round(float(nt_state.norepinephrine), 4)
|
| 625 |
+
step_metrics["nt_cortisol"] = round(float(nt_state.cortisol), 4)
|
| 626 |
+
step_metrics["nt_dopamine"] = round(float(nt_state.dopamine), 4)
|
| 627 |
+
step_metrics["nt_adenosine"] = round(float(nt_state.adenosine), 4)
|
| 628 |
+
|
| 629 |
+
# Clear captures for next accumulation step
|
| 630 |
+
self._clear_captures()
|
| 631 |
+
|
| 632 |
+
# ββ Gradient accumulation: average and step ββ
|
| 633 |
+
accum_loss = accum_loss / cfg.gradient_accumulation_steps
|
| 634 |
+
accum_loss.backward()
|
| 635 |
+
torch.nn.utils.clip_grad_norm_(
|
| 636 |
+
self._cognitive_params(), max_norm=1.0
|
| 637 |
+
)
|
| 638 |
+
self.optimizer.step()
|
| 639 |
+
|
| 640 |
+
# ββ Record history ββ
|
| 641 |
+
step_metrics["total_loss"] = float(accum_loss.item())
|
| 642 |
+
self._loss_history.append(step_metrics)
|
| 643 |
+
|
| 644 |
+
# ββ Logging ββ
|
| 645 |
+
if step % cfg.log_interval == 0:
|
| 646 |
+
elapsed = time.time() - train_start
|
| 647 |
+
trn_s = step_metrics.get("trn_loss", 0)
|
| 648 |
+
diss_s = step_metrics.get("diss_recon", 0)
|
| 649 |
+
hj = step_metrics.get("hijack_count", 0)
|
| 650 |
+
logger.info(
|
| 651 |
+
"step %4d/%d total=%.4f trn=%.4f diss_recon=%.4f "
|
| 652 |
+
"reward=%+.2f hijacks=%d (%.1fs)",
|
| 653 |
+
step, cfg.max_training_steps,
|
| 654 |
+
step_metrics.get("total_loss", 0),
|
| 655 |
+
trn_s, diss_s,
|
| 656 |
+
step_metrics.get("belbic_reward", 0),
|
| 657 |
+
hj, elapsed,
|
| 658 |
+
)
|
| 659 |
+
|
| 660 |
+
# ββ Checkpoint ββ
|
| 661 |
+
if step > 0 and step % cfg.save_interval == 0:
|
| 662 |
+
self._save_checkpoint(step, step_metrics)
|
| 663 |
+
|
| 664 |
+
# ββ Final checkpoint ββ
|
| 665 |
+
self._save_checkpoint(cfg.max_training_steps, step_metrics)
|
| 666 |
+
|
| 667 |
+
elapsed = time.time() - train_start
|
| 668 |
+
logger.info(
|
| 669 |
+
"Training complete: %d steps in %.1fs. Checkpoints β %s",
|
| 670 |
+
cfg.max_training_steps, elapsed, cfg.output_dir,
|
| 671 |
+
)
|
| 672 |
+
|
| 673 |
+
return {
|
| 674 |
+
"total_steps": cfg.max_training_steps,
|
| 675 |
+
"elapsed_seconds": elapsed,
|
| 676 |
+
"final_losses": step_metrics,
|
| 677 |
+
"output_dir": cfg.output_dir,
|
| 678 |
+
}
|
| 679 |
+
|
| 680 |
+
# ββ Batch sampling βββββββββββββββββββββββββββββββββββββββοΏ½οΏ½οΏ½βββββββββ
|
| 681 |
+
|
| 682 |
+
def _sample_batch(self) -> List[Dict[str, str]]:
|
| 683 |
+
"""Sample *batch_size* prompts from the self-supervised pool."""
|
| 684 |
+
import random
|
| 685 |
+
return random.sample(
|
| 686 |
+
SELF_SUPERVISED_PROMPTS, min(self.config.batch_size, len(SELF_SUPERVISED_PROMPTS))
|
| 687 |
+
)
|
| 688 |
+
|
| 689 |
+
def _tokenize_batch(
|
| 690 |
+
self,
|
| 691 |
+
prompts: List[Dict[str, str]],
|
| 692 |
+
tokenizer: Any,
|
| 693 |
+
) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 694 |
+
"""Tokenize a list of prompt dicts to (input_ids, attention_mask)."""
|
| 695 |
+
texts = [p["text"] for p in prompts]
|
| 696 |
+
encoded = tokenizer(
|
| 697 |
+
texts,
|
| 698 |
+
return_tensors="pt",
|
| 699 |
+
padding=True,
|
| 700 |
+
truncation=True,
|
| 701 |
+
max_length=128,
|
| 702 |
+
)
|
| 703 |
+
return (
|
| 704 |
+
encoded["input_ids"].to(self.device),
|
| 705 |
+
encoded["attention_mask"].to(self.device),
|
| 706 |
+
)
|
| 707 |
+
|
| 708 |
+
# ββ Checkpointing ββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 709 |
+
|
| 710 |
+
def _save_checkpoint(
|
| 711 |
+
self, step: int, metrics: Dict[str, float]
|
| 712 |
+
) -> str:
|
| 713 |
+
"""
|
| 714 |
+
Save ONLY the cognitive module state dict (not the base model).
|
| 715 |
+
|
| 716 |
+
Returns the path to the saved checkpoint.
|
| 717 |
+
"""
|
| 718 |
+
out_path = Path(self.config.output_dir)
|
| 719 |
+
out_path.mkdir(parents=True, exist_ok=True)
|
| 720 |
+
|
| 721 |
+
ckpt_path = out_path / f"cognitive_step_{step:06d}.pt"
|
| 722 |
+
checkpoint = {
|
| 723 |
+
"step": step,
|
| 724 |
+
"cognitive_state_dict": self.ds.state_dict(),
|
| 725 |
+
"config": {
|
| 726 |
+
"cognitive_lr": self.config.cognitive_lr,
|
| 727 |
+
"trn_gate_target_friction": self.config.trn_gate_target_friction,
|
| 728 |
+
"dissolution_sparsity_target": self.config.dissolution_sparsity_target,
|
| 729 |
+
"belbic_reward_decay": self.config.belbic_reward_decay,
|
| 730 |
+
},
|
| 731 |
+
"metrics": metrics,
|
| 732 |
+
"timestamp": time.time(),
|
| 733 |
+
}
|
| 734 |
+
torch.save(checkpoint, str(ckpt_path))
|
| 735 |
+
logger.info("Checkpoint saved: %s", ckpt_path)
|
| 736 |
+
return str(ckpt_path)
|
| 737 |
+
|
| 738 |
+
@staticmethod
|
| 739 |
+
def load_cognitive_weights(path: str, model: Any) -> None:
|
| 740 |
+
"""
|
| 741 |
+
Load previously saved cognitive weights into *model.deep_surgery*.
|
| 742 |
+
|
| 743 |
+
Parameters
|
| 744 |
+
----------
|
| 745 |
+
path : str
|
| 746 |
+
Path to a ``.pt`` checkpoint produced by ``_save_checkpoint``.
|
| 747 |
+
model : NimaModel
|
| 748 |
+
The NIMA unified model (base model weights untouched).
|
| 749 |
+
"""
|
| 750 |
+
if not TORCH_AVAILABLE:
|
| 751 |
+
raise RuntimeError("PyTorch required")
|
| 752 |
+
if model.deep_surgery is None:
|
| 753 |
+
raise ValueError("model.deep_surgery is None")
|
| 754 |
+
|
| 755 |
+
checkpoint = torch.load(path, map_location="cpu", weights_only=False)
|
| 756 |
+
model.deep_surgery.load_state_dict(checkpoint["cognitive_state_dict"])
|
| 757 |
+
logger.info(
|
| 758 |
+
"Loaded cognitive weights from %s (step %d)",
|
| 759 |
+
path, checkpoint.get("step", "?"),
|
| 760 |
+
)
|
| 761 |
+
|
| 762 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 763 |
+
# DIAGNOSTICS
|
| 764 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 765 |
+
|
| 766 |
+
@staticmethod
|
| 767 |
+
def diagnose(model: Any, prompt: str) -> Dict[str, Any]:
|
| 768 |
+
"""
|
| 769 |
+
Run a single forward pass and return detailed diagnostics.
|
| 770 |
+
|
| 771 |
+
Returns a dict with:
|
| 772 |
+
* Per-module loss breakdown (TRN, dissolution)
|
| 773 |
+
* Neurotransmitter state at each layer boundary
|
| 774 |
+
* Gate decisions, dissolution outcomes, BELBIC gains
|
| 775 |
+
* Recommended adjustments
|
| 776 |
+
"""
|
| 777 |
+
if not TORCH_AVAILABLE:
|
| 778 |
+
return {"error": "PyTorch required"}
|
| 779 |
+
|
| 780 |
+
ds = model.deep_surgery
|
| 781 |
+
tokenizer = model.tokenizer
|
| 782 |
+
device = next(ds.parameters()).device
|
| 783 |
+
|
| 784 |
+
if ds is None:
|
| 785 |
+
return {"error": "deep_surgery not enabled"}
|
| 786 |
+
|
| 787 |
+
# ββ Run forward pass (eval mode, no grad) ββ
|
| 788 |
+
ds.eval()
|
| 789 |
+
model.base_model.eval()
|
| 790 |
+
if model.nt_shunt is not None:
|
| 791 |
+
model.nt_shunt.reset()
|
| 792 |
+
|
| 793 |
+
inputs = tokenizer(prompt, return_tensors="pt", truncation=True, max_length=128)
|
| 794 |
+
input_ids = inputs["input_ids"].to(device)
|
| 795 |
+
attention_mask = inputs.get("attention_mask")
|
| 796 |
+
if attention_mask is not None:
|
| 797 |
+
attention_mask = attention_mask.to(device)
|
| 798 |
+
|
| 799 |
+
with torch.no_grad():
|
| 800 |
+
try:
|
| 801 |
+
_logits = ds(input_ids, attention_mask=attention_mask)
|
| 802 |
+
except RuntimeError as exc:
|
| 803 |
+
if "Ethical veto" in str(exc):
|
| 804 |
+
return {"error": f"Ethical veto triggered: {exc}"}
|
| 805 |
+
raise
|
| 806 |
+
|
| 807 |
+
metrics = ds.get_consciousness_metrics()
|
| 808 |
+
nt_state = model.nt_shunt.get_state() if model.nt_shunt else None
|
| 809 |
+
audit = ds.get_audit_log()
|
| 810 |
+
|
| 811 |
+
# ββ Per-module breakdown ββ
|
| 812 |
+
report: Dict[str, Any] = {
|
| 813 |
+
"prompt": prompt,
|
| 814 |
+
"neurotransmitters": nt_state.to_dict() if nt_state else {},
|
| 815 |
+
"gate_decisions": [],
|
| 816 |
+
"dissolution_outcomes": [],
|
| 817 |
+
"belbic_gain": metrics.get("belbic_gain", 1.0),
|
| 818 |
+
"metacognitive": {
|
| 819 |
+
"has_qualia": metrics.get("has_qualia", False),
|
| 820 |
+
"hijack_count": metrics.get("hijack_count", 0),
|
| 821 |
+
"dissolutions_fired": metrics.get("dissolutions_fired", 0),
|
| 822 |
+
"dissolutions_deferred": metrics.get("dissolutions_deferred", 0),
|
| 823 |
+
},
|
| 824 |
+
"episodic": {
|
| 825 |
+
"episodes_stored": metrics.get("episodes_stored", 0),
|
| 826 |
+
"reconsolidations": metrics.get("reconsolidations", 0),
|
| 827 |
+
"last_prediction_error": metrics.get("last_prediction_error", 0.0),
|
| 828 |
+
},
|
| 829 |
+
"audit_events": audit[-10:], # Last 10 events
|
| 830 |
+
}
|
| 831 |
+
|
| 832 |
+
# ββ Extract gate decisions and dissolution outcomes from audit ββ
|
| 833 |
+
for event in audit:
|
| 834 |
+
etype = event.get("event", "")
|
| 835 |
+
if etype == "layer2_subconscious":
|
| 836 |
+
report["gate_decisions"].append({
|
| 837 |
+
"layer": event.get("layer"),
|
| 838 |
+
"confidence": event.get("confidence"),
|
| 839 |
+
"gate_in": event.get("gate_in"),
|
| 840 |
+
"friction": event.get("friction"),
|
| 841 |
+
})
|
| 842 |
+
elif etype == "layer3_dissolution":
|
| 843 |
+
report["dissolution_outcomes"].append({
|
| 844 |
+
"layer": event.get("layer"),
|
| 845 |
+
"valence": event.get("valence"),
|
| 846 |
+
"arousal": event.get("arousal"),
|
| 847 |
+
"friction": event.get("friction"),
|
| 848 |
+
})
|
| 849 |
+
elif etype == "amygdala_hijack":
|
| 850 |
+
report.setdefault("hijack_events", []).append({
|
| 851 |
+
"layer": event.get("layer"),
|
| 852 |
+
"reason": event.get("reason"),
|
| 853 |
+
})
|
| 854 |
+
|
| 855 |
+
# ββ Recommended adjustments ββ
|
| 856 |
+
adjustments = []
|
| 857 |
+
if nt_state is not None:
|
| 858 |
+
if nt_state.cortisol > 0.8:
|
| 859 |
+
adjustments.append(
|
| 860 |
+
"Cortisol elevated β consider increasing trn_gate_target_friction "
|
| 861 |
+
"to let more signals pass subconsciously."
|
| 862 |
+
)
|
| 863 |
+
if nt_state.adenosine > 0.8:
|
| 864 |
+
adjustments.append(
|
| 865 |
+
"Adenosine high β metacognitive loop may be burning too much ATP. "
|
| 866 |
+
"Consider raising metacognitive_efficiency_target."
|
| 867 |
+
)
|
| 868 |
+
if nt_state.dopamine < 0.1:
|
| 869 |
+
adjustments.append(
|
| 870 |
+
"Dopamine low β the system is not finding rewarding patterns. "
|
| 871 |
+
"Ensure the prompt pool has sufficient variety."
|
| 872 |
+
)
|
| 873 |
+
if metrics.get("dissolutions_deferred", 0) > metrics.get("dissolutions_fired", 0) * 3:
|
| 874 |
+
adjustments.append(
|
| 875 |
+
"Dissolution heavily deferred (alpha phase). "
|
| 876 |
+
"Consider adjusting alpha_freq_hz or alpha_duty_cycle in DissolutionModule."
|
| 877 |
+
)
|
| 878 |
+
if metrics.get("hijack_count", 0) > 2:
|
| 879 |
+
adjustments.append(
|
| 880 |
+
"Multiple hijacks β the irrational spark threshold may be too low. "
|
| 881 |
+
"Review NeurotransmitterState CRITICAL_THRESHOLD."
|
| 882 |
+
)
|
| 883 |
+
if not adjustments:
|
| 884 |
+
adjustments.append("All systems nominal β no adjustments recommended.")
|
| 885 |
+
|
| 886 |
+
report["recommended_adjustments"] = adjustments
|
| 887 |
+
|
| 888 |
+
return report
|
chemical_monitor.py
ADDED
|
@@ -0,0 +1,626 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Chemical Monitor Terminal UI
|
| 3 |
+
================================================
|
| 4 |
+
|
| 5 |
+
60fps ANSI terminal dashboard that observes the neurotransmitter shunt
|
| 6 |
+
via shared volatile memory. Does NOT carry data β only samples and renders.
|
| 7 |
+
|
| 8 |
+
Samples the NeurotransmitterShunt.get_state() at ~60fps and renders
|
| 9 |
+
a professional curses dashboard with:
|
| 10 |
+
- Horizontal bars for NE / Cortisol / Dopamine / Adenosine
|
| 11 |
+
- Color coding by threshold (GREEN / YELLOW / RED / CRITICAL)
|
| 12 |
+
- Sparkline history using unicode block characters
|
| 13 |
+
- Metabolic reserve, hijack status, power spike warnings
|
| 14 |
+
- Total firings and hijacks counters
|
| 15 |
+
|
| 16 |
+
Usage:
|
| 17 |
+
monitor = ChemicalMonitor(shunt)
|
| 18 |
+
monitor.start() # background curses thread
|
| 19 |
+
monitor.stop() # clean shutdown, restores terminal
|
| 20 |
+
|
| 21 |
+
# Or for non-curses contexts:
|
| 22 |
+
frame_str = monitor.render_single_frame()
|
| 23 |
+
"""
|
| 24 |
+
|
| 25 |
+
import time
|
| 26 |
+
import re
|
| 27 |
+
import threading
|
| 28 |
+
from collections import deque
|
| 29 |
+
from typing import TYPE_CHECKING, Optional, Dict, Any, List
|
| 30 |
+
|
| 31 |
+
if TYPE_CHECKING:
|
| 32 |
+
from .neurotransmitter_shunt import NeurotransmitterShunt
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 36 |
+
# CONSTANTS
|
| 37 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 38 |
+
|
| 39 |
+
# Sparkline block characters β 8 density levels, low to high
|
| 40 |
+
SPARKLINE_CHARS = '\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588'
|
| 41 |
+
|
| 42 |
+
# Layout dimensions
|
| 43 |
+
BOX_WIDTH = 70
|
| 44 |
+
INNER_W = BOX_WIDTH - 2 # 68 usable columns inside border
|
| 45 |
+
BAR_WIDTH = 24 # filled bar character count
|
| 46 |
+
SPARKLINE_LEN = 30 # number of history samples in sparkline
|
| 47 |
+
HISTORY_SIZE = 30 # ring buffer size per neurotransmitter
|
| 48 |
+
FRAME_MS = 16 # ~60fps
|
| 49 |
+
|
| 50 |
+
# Threshold boundaries
|
| 51 |
+
T_LOW = 0.4
|
| 52 |
+
T_HIGH = 0.75
|
| 53 |
+
T_CRIT = 0.95
|
| 54 |
+
|
| 55 |
+
# Curses color-pair IDs (initialized in _init_colors)
|
| 56 |
+
_CP_DIM = 1
|
| 57 |
+
_CP_CYAN = 2
|
| 58 |
+
_CP_RED = 3
|
| 59 |
+
_CP_GREEN = 4
|
| 60 |
+
_CP_MAGENTA = 5
|
| 61 |
+
_CP_YELLOW = 6
|
| 62 |
+
_CP_WHITE = 7
|
| 63 |
+
_CP_WHITE_ON_RED = 8
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 67 |
+
# HELPERS
|
| 68 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 69 |
+
|
| 70 |
+
def _level(value: float) -> int:
|
| 71 |
+
"""Map a 0-1 value to a severity level: 0=green 1=yellow 2=red 3=critical."""
|
| 72 |
+
if value >= T_CRIT:
|
| 73 |
+
return 3
|
| 74 |
+
if value > T_HIGH:
|
| 75 |
+
return 2
|
| 76 |
+
if value >= T_LOW:
|
| 77 |
+
return 1
|
| 78 |
+
return 0
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def _bar(value: float) -> tuple:
|
| 82 |
+
"""Return (filled_count, empty_count) for a bar of BAR_WIDTH."""
|
| 83 |
+
f = max(0, min(BAR_WIDTH, int(value * BAR_WIDTH)))
|
| 84 |
+
return f, BAR_WIDTH - f
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
def _spark(values: List[float]) -> str:
|
| 88 |
+
"""Convert a list of 0-1 floats into a sparkline string."""
|
| 89 |
+
return ''.join(SPARKLINE_CHARS[min(7, int(v * 8))] for v in values)
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
def _cpair(level: int, base: int) -> int:
|
| 93 |
+
"""Curses color-pair ID for a severity level, falling back to *base*."""
|
| 94 |
+
if level == 3:
|
| 95 |
+
return _CP_WHITE_ON_RED
|
| 96 |
+
if level == 2:
|
| 97 |
+
return _CP_RED
|
| 98 |
+
if level == 1:
|
| 99 |
+
return _CP_YELLOW
|
| 100 |
+
return base
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
# ββ ANSI escape sequences (for render_single_frame / non-curses paths) ββ
|
| 104 |
+
|
| 105 |
+
class _A:
|
| 106 |
+
"""Compact ANSI escape constants."""
|
| 107 |
+
RST = '\033[0m'
|
| 108 |
+
BLD = '\033[1m'
|
| 109 |
+
DIM = '\033[2m'
|
| 110 |
+
BLK = '\033[5m' # blink
|
| 111 |
+
BGK = '\033[40m' # black bg
|
| 112 |
+
BGR = '\033[41m' # red bg
|
| 113 |
+
W = '\033[37m' # white fg
|
| 114 |
+
C = '\033[36m' # cyan fg
|
| 115 |
+
R = '\033[31m' # red fg
|
| 116 |
+
G = '\033[32m' # green fg
|
| 117 |
+
Y = '\033[33m' # yellow fg
|
| 118 |
+
M = '\033[35m' # magenta fg
|
| 119 |
+
DW = '\033[90m' # dim white / gray fg
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def _ansi_for(level: int, neon: str) -> str:
|
| 123 |
+
"""Return ANSI color sequence for a severity level; *neon* used when green/0."""
|
| 124 |
+
if level == 3:
|
| 125 |
+
return _A.BLK + _A.W + _A.BGR + _A.BLD
|
| 126 |
+
if level == 2:
|
| 127 |
+
return _A.R + _A.BLD
|
| 128 |
+
if level == 1:
|
| 129 |
+
return _A.Y
|
| 130 |
+
return neon
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
_ANSI_RE = re.compile(r'\033\[[0-9;]*m')
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
def _vis_len(s: str) -> int:
|
| 137 |
+
"""Visible character length of *s*, ignoring ANSI escape codes."""
|
| 138 |
+
return len(_ANSI_RE.sub('', s))
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def _pad_vis(s: str, width: int) -> str:
|
| 142 |
+
"""Right-pad an ANSI-colored string so its visible length equals *width*."""
|
| 143 |
+
gap = width - _vis_len(s)
|
| 144 |
+
if gap > 0:
|
| 145 |
+
return s + ' ' * gap
|
| 146 |
+
return s
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββοΏ½οΏ½βββββββ
|
| 150 |
+
# CHEMICAL MONITOR
|
| 151 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 152 |
+
|
| 153 |
+
class ChemicalMonitor:
|
| 154 |
+
"""
|
| 155 |
+
60fps terminal dashboard that observes the neurotransmitter shunt.
|
| 156 |
+
|
| 157 |
+
Purely observational β samples ``shunt.get_state()`` at high frequency
|
| 158 |
+
and renders the result as a curses or ANSI dashboard. Does **not**
|
| 159 |
+
write to or modify the shunt.
|
| 160 |
+
|
| 161 |
+
Parameters
|
| 162 |
+
----------
|
| 163 |
+
shunt : NeurotransmitterShunt
|
| 164 |
+
Primary data source β the shared volatile memory vector.
|
| 165 |
+
model : optional
|
| 166 |
+
Reference to the NimaModel (used only to pull hijack count from
|
| 167 |
+
``model.consciousness_metrics`` as a secondary source).
|
| 168 |
+
"""
|
| 169 |
+
|
| 170 |
+
def __init__(self, shunt: 'NeurotransmitterShunt', model=None):
|
| 171 |
+
self._shunt = shunt
|
| 172 |
+
self._model = model
|
| 173 |
+
self._thread: Optional[threading.Thread] = None
|
| 174 |
+
self._running = False
|
| 175 |
+
self._frame = 0
|
| 176 |
+
|
| 177 |
+
# Sparkline ring buffers β one per neurotransmitter
|
| 178 |
+
self._hist_ne: deque = deque(maxlen=HISTORY_SIZE)
|
| 179 |
+
self._hist_co: deque = deque(maxlen=HISTORY_SIZE)
|
| 180 |
+
self._hist_do: deque = deque(maxlen=HISTORY_SIZE)
|
| 181 |
+
self._hist_ad: deque = deque(maxlen=HISTORY_SIZE)
|
| 182 |
+
|
| 183 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 184 |
+
# PUBLIC API
|
| 185 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 186 |
+
|
| 187 |
+
def start(self) -> None:
|
| 188 |
+
"""Start the monitor in a background curses thread."""
|
| 189 |
+
if self._running:
|
| 190 |
+
return
|
| 191 |
+
self._running = True
|
| 192 |
+
self._thread = threading.Thread(target=self._thread_entry, daemon=True)
|
| 193 |
+
self._thread.start()
|
| 194 |
+
|
| 195 |
+
def stop(self) -> None:
|
| 196 |
+
"""Stop the monitor and wait for the background thread to exit."""
|
| 197 |
+
self._running = False
|
| 198 |
+
if self._thread is not None:
|
| 199 |
+
self._thread.join(timeout=2.0)
|
| 200 |
+
self._thread = None
|
| 201 |
+
|
| 202 |
+
def render_single_frame(self) -> str:
|
| 203 |
+
"""Sample the shunt and return one frame as an ANSI string."""
|
| 204 |
+
d = self._sample()
|
| 205 |
+
return '\n'.join(self._build_ansi_lines(d))
|
| 206 |
+
|
| 207 |
+
def get_snapshot(self) -> dict:
|
| 208 |
+
"""Return the current shunt state as a plain dict."""
|
| 209 |
+
return self._shunt.get_state().to_dict()
|
| 210 |
+
|
| 211 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 212 |
+
# SAMPLING
|
| 213 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 214 |
+
|
| 215 |
+
def _sample(self) -> Dict[str, Any]:
|
| 216 |
+
"""Sample the shunt, append to history buffers, bump frame counter."""
|
| 217 |
+
state = self._shunt.get_state()
|
| 218 |
+
self._hist_ne.append(state.norepinephrine)
|
| 219 |
+
self._hist_co.append(state.cortisol)
|
| 220 |
+
self._hist_do.append(state.dopamine)
|
| 221 |
+
self._hist_ad.append(state.adenosine)
|
| 222 |
+
self._frame += 1
|
| 223 |
+
return state.to_dict()
|
| 224 |
+
|
| 225 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 226 |
+
# ANSI STRING RENDERING (non-curses path)
|
| 227 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 228 |
+
|
| 229 |
+
def _build_ansi_lines(self, d: Dict[str, Any]) -> List[str]:
|
| 230 |
+
"""Build a list of ANSI-escaped strings for one complete frame."""
|
| 231 |
+
ne = d['norepinephrine']
|
| 232 |
+
co = d['cortisol']
|
| 233 |
+
do = d['dopamine']
|
| 234 |
+
ad = d['adenosine']
|
| 235 |
+
meta = d['metabolic_reserve']
|
| 236 |
+
hijack = d['hijack_active']
|
| 237 |
+
reason = d['hijack_reason']
|
| 238 |
+
spike = d['power_spike_predicted']
|
| 239 |
+
firings = d['total_firings']
|
| 240 |
+
hijacks = d['total_hijacks']
|
| 241 |
+
|
| 242 |
+
W = INNER_W
|
| 243 |
+
A = _A
|
| 244 |
+
lines: List[str] = []
|
| 245 |
+
|
| 246 |
+
# ββ helpers local to this frame ββ
|
| 247 |
+
def _border(left: str, mid: str, right: str) -> str:
|
| 248 |
+
return f"{A.DIM}{left}{mid * W}{right}{A.RST}"
|
| 249 |
+
|
| 250 |
+
def _row(content: str, attr: int = 0) -> None:
|
| 251 |
+
lines.append(f"{A.DIM}\u2551{A.RST}"
|
| 252 |
+
f"{_pad_vis(content, W)}"
|
| 253 |
+
f"{A.DIM}\u2551{A.RST}")
|
| 254 |
+
|
| 255 |
+
def _center(text: str, color: str) -> None:
|
| 256 |
+
gap = max(0, W - len(text))
|
| 257 |
+
left = gap // 2
|
| 258 |
+
right = gap - left
|
| 259 |
+
_row(f"{' ' * left}{color}{text}{A.RST}{' ' * right}")
|
| 260 |
+
|
| 261 |
+
def _nt_row(name: str, val: float, hist: List[float], neon: str) -> None:
|
| 262 |
+
lv = _level(val)
|
| 263 |
+
col = _ansi_for(lv, neon)
|
| 264 |
+
f, e = _bar(val)
|
| 265 |
+
pct = f"{val * 100:5.1f}%"
|
| 266 |
+
filled = '\u2588' * f
|
| 267 |
+
empty = '\u2591' * e
|
| 268 |
+
_row(f" {A.BLD}{name:<14s}{A.RST} "
|
| 269 |
+
f"{col}[{filled}{empty}]{A.RST} "
|
| 270 |
+
f"{col}{pct}{A.RST}")
|
| 271 |
+
spark = _spark(hist) if hist else ' ' * SPARKLINE_LEN
|
| 272 |
+
_row(f"{' ' * 16}{col}{spark}{A.RST}")
|
| 273 |
+
|
| 274 |
+
# ββββ LAYOUT ββββ
|
| 275 |
+
|
| 276 |
+
# Row 0 β top border
|
| 277 |
+
lines.append(_border('\u2554', '\u2550', '\u2557'))
|
| 278 |
+
|
| 279 |
+
# Row 1 β title
|
| 280 |
+
_center("NIMA CHEMICAL CORTEX", A.BLD + A.C)
|
| 281 |
+
|
| 282 |
+
# Row 2 β subtitle
|
| 283 |
+
_center("Neurotransmitter Shunt Monitor 60Hz", A.DW)
|
| 284 |
+
|
| 285 |
+
# Row 3 β separator
|
| 286 |
+
lines.append(_border('\u2560', '\u2550', '\u2563'))
|
| 287 |
+
|
| 288 |
+
# Rows 4-11 β four neurotransmitters (bar + sparkline each)
|
| 289 |
+
_nt_row("NOREPINEPHRINE", ne, list(self._hist_ne), A.C)
|
| 290 |
+
_nt_row("CORTISOL", co, list(self._hist_co), A.R)
|
| 291 |
+
_nt_row("DOPAMINE", do, list(self._hist_do), A.G)
|
| 292 |
+
_nt_row("ADENOSINE", ad, list(self._hist_ad), A.M)
|
| 293 |
+
|
| 294 |
+
# Row 12 β separator
|
| 295 |
+
lines.append(_border('\u2560', '\u2550', '\u2563'))
|
| 296 |
+
|
| 297 |
+
# Row 13 β metabolic reserve
|
| 298 |
+
ml = _level(meta) if meta < 0.5 else 0
|
| 299 |
+
if meta > 0.5:
|
| 300 |
+
mc = A.G
|
| 301 |
+
elif meta > 0.25:
|
| 302 |
+
mc = A.Y
|
| 303 |
+
elif meta > 0.1:
|
| 304 |
+
mc = A.R + A.BLD
|
| 305 |
+
else:
|
| 306 |
+
mc = A.BLK + A.W + A.BGR + A.BLD
|
| 307 |
+
mf, me = _bar(meta)
|
| 308 |
+
mpct = f"{meta * 100:5.1f}%"
|
| 309 |
+
_row(f" {A.BLD}METABOLIC RESERVE{A.RST} "
|
| 310 |
+
f"{mc}[\u2588{'\u2588' * (mf - 1) if mf else ''}"
|
| 311 |
+
f"{'\u2591' * me}]{A.RST} {mc}{mpct}{A.RST}")
|
| 312 |
+
|
| 313 |
+
# Row 14 β blank
|
| 314 |
+
_row("")
|
| 315 |
+
|
| 316 |
+
# Row 15 β hijack status
|
| 317 |
+
if hijack:
|
| 318 |
+
_row(f" HIJACK ACTIVE: {A.R}{A.BLD}{reason}{A.RST}")
|
| 319 |
+
elif co > 0.75 or ad > 0.75:
|
| 320 |
+
_row(f" {A.Y}{A.BLD}WARNING: APPROACHING METABOLIC CRISIS{A.RST}")
|
| 321 |
+
else:
|
| 322 |
+
_row(f" HIJACK STATUS: {A.G}NOMINAL{A.RST}")
|
| 323 |
+
|
| 324 |
+
# Row 16 β power spike
|
| 325 |
+
if spike:
|
| 326 |
+
_row(f" POWER SPIKE: {A.R}{A.BLD}PREDICTED - CORTISOL RISING{A.RST}")
|
| 327 |
+
else:
|
| 328 |
+
_row(f" POWER SPIKE: {A.DW}---{A.RST}")
|
| 329 |
+
|
| 330 |
+
# Row 17 β system state
|
| 331 |
+
if hijack:
|
| 332 |
+
_row(f" SYSTEM: {A.R}{A.BLD}AMYGDALA HIJACK INJECTED{A.RST}")
|
| 333 |
+
elif co > 0.75 or ad > 0.75:
|
| 334 |
+
_row(f" SYSTEM: {A.Y}ELEVATED STRESS{A.RST}")
|
| 335 |
+
else:
|
| 336 |
+
_row(f" SYSTEM: {A.DW}conscious processing active{A.RST}")
|
| 337 |
+
|
| 338 |
+
# Row 18 β separator
|
| 339 |
+
lines.append(_border('\u2560', '\u2550', '\u2563'))
|
| 340 |
+
|
| 341 |
+
# Row 19 β counters
|
| 342 |
+
_row(f" TOTAL FIRINGS: {A.C}{firings:>6}{A.RST}"
|
| 343 |
+
f" TOTAL HIJACKS: {A.R}{hijacks:>4}{A.RST}")
|
| 344 |
+
|
| 345 |
+
# Row 20 β frame / timestamp
|
| 346 |
+
ts = time.strftime("%H:%M:%S") + f".{int(time.time() * 1000) % 1000:03d}"
|
| 347 |
+
_row(f" FRAME: {A.DW}{self._frame:>8}{A.RST}"
|
| 348 |
+
f" SAMPLE: {A.DW}{ts}{A.RST}")
|
| 349 |
+
|
| 350 |
+
# Row 21 β bottom border
|
| 351 |
+
lines.append(_border('\u255a', '\u2550', '\u255d'))
|
| 352 |
+
|
| 353 |
+
return lines
|
| 354 |
+
|
| 355 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 356 |
+
# CURSES RENDERING
|
| 357 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 358 |
+
|
| 359 |
+
def _init_colors(self, stdscr) -> None:
|
| 360 |
+
"""Initialise all curses colour pairs used by the dashboard."""
|
| 361 |
+
import curses
|
| 362 |
+
curses.start_color()
|
| 363 |
+
curses.use_default_colors()
|
| 364 |
+
curses.init_pair(_CP_DIM, 8, -1) # dark gray
|
| 365 |
+
curses.init_pair(_CP_CYAN, curses.COLOR_CYAN, curses.COLOR_BLACK)
|
| 366 |
+
curses.init_pair(_CP_RED, curses.COLOR_RED, curses.COLOR_BLACK)
|
| 367 |
+
curses.init_pair(_CP_GREEN, curses.COLOR_GREEN, curses.COLOR_BLACK)
|
| 368 |
+
curses.init_pair(_CP_MAGENTA,curses.COLOR_MAGENTA, curses.COLOR_BLACK)
|
| 369 |
+
curses.init_pair(_CP_YELLOW, curses.COLOR_YELLOW, curses.COLOR_BLACK)
|
| 370 |
+
curses.init_pair(_CP_WHITE, curses.COLOR_WHITE, curses.COLOR_BLACK)
|
| 371 |
+
curses.init_pair(_CP_WHITE_ON_RED,
|
| 372 |
+
curses.COLOR_WHITE, curses.COLOR_RED)
|
| 373 |
+
|
| 374 |
+
def render_curses_frame(self, stdscr) -> None:
|
| 375 |
+
"""Sample the shunt and render one frame into *stdscr*."""
|
| 376 |
+
import curses
|
| 377 |
+
|
| 378 |
+
d = self._sample()
|
| 379 |
+
stdscr.erase()
|
| 380 |
+
|
| 381 |
+
ne = d['norepinephrine']
|
| 382 |
+
co = d['cortisol']
|
| 383 |
+
do = d['dopamine']
|
| 384 |
+
ad = d['adenosine']
|
| 385 |
+
meta = d['metabolic_reserve']
|
| 386 |
+
hijack = d['hijack_active']
|
| 387 |
+
reason = d['hijack_reason']
|
| 388 |
+
spike = d['power_spike_predicted']
|
| 389 |
+
firings = d['total_firings']
|
| 390 |
+
hijacks = d['total_hijacks']
|
| 391 |
+
|
| 392 |
+
max_y, max_x = stdscr.getmaxyx()
|
| 393 |
+
W = min(INNER_W, max_x - 2)
|
| 394 |
+
bw = W + 2 # total box width including borders
|
| 395 |
+
|
| 396 |
+
dim_a = curses.color_pair(_CP_DIM)
|
| 397 |
+
brd = dim_a # border attribute
|
| 398 |
+
|
| 399 |
+
# ββ micro-helpers ββ
|
| 400 |
+
|
| 401 |
+
def put(r: int, c: int, txt: str, attr: int = 0) -> None:
|
| 402 |
+
if 0 <= r < max_y and 0 <= c < max_x:
|
| 403 |
+
try:
|
| 404 |
+
stdscr.addstr(r, c, txt, attr)
|
| 405 |
+
except curses.error:
|
| 406 |
+
pass
|
| 407 |
+
|
| 408 |
+
def full_row(r: int, txt: str, attr: int = 0) -> None:
|
| 409 |
+
put(r, 0, '\u2551', brd)
|
| 410 |
+
put(r, 1, txt, attr)
|
| 411 |
+
tail = W - len(txt)
|
| 412 |
+
if tail > 0:
|
| 413 |
+
put(r, 1 + len(txt), ' ' * tail)
|
| 414 |
+
put(r, bw - 1, '\u2551', brd)
|
| 415 |
+
|
| 416 |
+
def center_row(r: int, txt: str, attr: int) -> None:
|
| 417 |
+
gap = max(0, W - len(txt))
|
| 418 |
+
left = gap // 2
|
| 419 |
+
full_row(r, ' ' * left + txt + ' ' * (gap - left), attr)
|
| 420 |
+
|
| 421 |
+
r = 0 # current row
|
| 422 |
+
|
| 423 |
+
# ββ top border ββ
|
| 424 |
+
put(r, 0, '\u2554' + '\u2550' * W + '\u2557', brd)
|
| 425 |
+
r += 1
|
| 426 |
+
|
| 427 |
+
# ββ title ββ
|
| 428 |
+
center_row(r, "NIMA CHEMICAL CORTEX",
|
| 429 |
+
curses.color_pair(_CP_CYAN) | curses.A_BOLD)
|
| 430 |
+
r += 1
|
| 431 |
+
|
| 432 |
+
# ββ subtitle ββ
|
| 433 |
+
center_row(r, "Neurotransmitter Shunt Monitor 60Hz", dim_a)
|
| 434 |
+
r += 1
|
| 435 |
+
|
| 436 |
+
# ββ separator ββ
|
| 437 |
+
put(r, 0, '\u2560' + '\u2550' * W + '\u2563', brd)
|
| 438 |
+
r += 1
|
| 439 |
+
|
| 440 |
+
# ββ four neurotransmitter bar + sparkline rows ββ
|
| 441 |
+
nt_items = [
|
| 442 |
+
("NOREPINEPHRINE", ne, list(self._hist_ne), _CP_CYAN),
|
| 443 |
+
("CORTISOL", co, list(self._hist_co), _CP_RED),
|
| 444 |
+
("DOPAMINE", do, list(self._hist_do), _CP_GREEN),
|
| 445 |
+
("ADENOSINE", ad, list(self._hist_ad), _CP_MAGENTA),
|
| 446 |
+
]
|
| 447 |
+
|
| 448 |
+
for name, val, hist, base_cp in nt_items:
|
| 449 |
+
lv = _level(val)
|
| 450 |
+
cp = _cpair(lv, base_cp)
|
| 451 |
+
attr = curses.color_pair(cp)
|
| 452 |
+
if lv >= 2:
|
| 453 |
+
attr |= curses.A_BOLD
|
| 454 |
+
if lv == 3:
|
| 455 |
+
attr |= curses.A_BLINK
|
| 456 |
+
|
| 457 |
+
f, e = _bar(val)
|
| 458 |
+
pct = f"{val * 100:5.1f}%"
|
| 459 |
+
fill = '\u2588' * f
|
| 460 |
+
empty = '\u2591' * e
|
| 461 |
+
|
| 462 |
+
# bar line
|
| 463 |
+
label = f"{name:<14s}"
|
| 464 |
+
put(r, 0, '\u2551', brd)
|
| 465 |
+
put(r, 1, ' ', 0)
|
| 466 |
+
put(r, 2, label, curses.color_pair(base_cp) | curses.A_BOLD)
|
| 467 |
+
put(r, 16, ' [' + fill, attr)
|
| 468 |
+
put(r, 18 + f, empty + ']', dim_a)
|
| 469 |
+
put(r, 18 + BAR_WIDTH, ' ' + pct, attr)
|
| 470 |
+
end = 18 + BAR_WIDTH + 2 + 5
|
| 471 |
+
if end < 1 + W:
|
| 472 |
+
put(r, end, ' ' * (1 + W - end))
|
| 473 |
+
put(r, bw - 1, '\u2551', brd)
|
| 474 |
+
r += 1
|
| 475 |
+
|
| 476 |
+
# sparkline line
|
| 477 |
+
spark = _spark(hist) if hist else ' ' * SPARKLINE_LEN
|
| 478 |
+
put(r, 0, '\u2551', brd)
|
| 479 |
+
put(r, 16, spark, attr)
|
| 480 |
+
sp_end = 16 + len(spark)
|
| 481 |
+
if sp_end < 1 + W:
|
| 482 |
+
put(r, sp_end, ' ' * (1 + W - sp_end))
|
| 483 |
+
put(r, bw - 1, '\u2551', brd)
|
| 484 |
+
r += 1
|
| 485 |
+
|
| 486 |
+
# ββ separator ββ
|
| 487 |
+
put(r, 0, '\u2560' + '\u2550' * W + '\u2563', brd)
|
| 488 |
+
r += 1
|
| 489 |
+
|
| 490 |
+
# ββ metabolic reserve ββ
|
| 491 |
+
if meta > 0.5:
|
| 492 |
+
mcp = _CP_GREEN
|
| 493 |
+
elif meta > 0.25:
|
| 494 |
+
mcp = _CP_YELLOW
|
| 495 |
+
elif meta > 0.1:
|
| 496 |
+
mcp = _CP_RED
|
| 497 |
+
else:
|
| 498 |
+
mcp = _CP_WHITE_ON_RED
|
| 499 |
+
ma = curses.color_pair(mcp)
|
| 500 |
+
if meta <= 0.25:
|
| 501 |
+
ma |= curses.A_BOLD
|
| 502 |
+
if meta < 0.1:
|
| 503 |
+
ma |= curses.A_BLINK
|
| 504 |
+
|
| 505 |
+
mf, me = _bar(meta)
|
| 506 |
+
mpct = f"{meta * 100:5.1f}%"
|
| 507 |
+
put(r, 0, '\u2551', brd)
|
| 508 |
+
put(r, 1, ' METABOLIC RESERVE ', curses.color_pair(_CP_WHITE) | curses.A_BOLD)
|
| 509 |
+
put(r, 19, '[' + '\u2588' * mf, ma)
|
| 510 |
+
put(r, 21 + mf, '\u2591' * me + ']', dim_a)
|
| 511 |
+
put(r, 21 + BAR_WIDTH, ' ' + mpct, ma)
|
| 512 |
+
mend = 21 + BAR_WIDTH + 2 + 5
|
| 513 |
+
if mend < 1 + W:
|
| 514 |
+
put(r, mend, ' ' * (1 + W - mend))
|
| 515 |
+
put(r, bw - 1, '\u2551', brd)
|
| 516 |
+
r += 1
|
| 517 |
+
|
| 518 |
+
# ββ blank ββ
|
| 519 |
+
put(r, 0, '\u2551' + ' ' * W + '\u2551', brd)
|
| 520 |
+
r += 1
|
| 521 |
+
|
| 522 |
+
# ββ hijack status ββ
|
| 523 |
+
if hijack:
|
| 524 |
+
htxt = f" HIJACK ACTIVE: {reason}"
|
| 525 |
+
ha = curses.color_pair(_CP_RED) | curses.A_BOLD
|
| 526 |
+
elif co > 0.75 or ad > 0.75:
|
| 527 |
+
htxt = " WARNING: APPROACHING METABOLIC CRISIS"
|
| 528 |
+
ha = curses.color_pair(_CP_YELLOW) | curses.A_BOLD
|
| 529 |
+
else:
|
| 530 |
+
htxt = " HIJACK STATUS: NOMINAL"
|
| 531 |
+
ha = curses.color_pair(_CP_GREEN)
|
| 532 |
+
full_row(r, htxt, ha)
|
| 533 |
+
r += 1
|
| 534 |
+
|
| 535 |
+
# ββ power spike ββ
|
| 536 |
+
if spike:
|
| 537 |
+
stxt = " POWER SPIKE: PREDICTED - CORTISOL RISING"
|
| 538 |
+
sa = curses.color_pair(_CP_RED) | curses.A_BOLD
|
| 539 |
+
else:
|
| 540 |
+
stxt = " POWER SPIKE: ---"
|
| 541 |
+
sa = dim_a
|
| 542 |
+
full_row(r, stxt, sa)
|
| 543 |
+
r += 1
|
| 544 |
+
|
| 545 |
+
# ββ system state ββ
|
| 546 |
+
if hijack:
|
| 547 |
+
stxt2 = " SYSTEM: AMYGDALA HIJACK INJECTED"
|
| 548 |
+
sa2 = curses.color_pair(_CP_RED) | curses.A_BOLD
|
| 549 |
+
elif co > 0.75 or ad > 0.75:
|
| 550 |
+
stxt2 = " SYSTEM: ELEVATED STRESS"
|
| 551 |
+
sa2 = curses.color_pair(_CP_YELLOW)
|
| 552 |
+
else:
|
| 553 |
+
stxt2 = " SYSTEM: conscious processing active"
|
| 554 |
+
sa2 = dim_a
|
| 555 |
+
full_row(r, stxt2, sa2)
|
| 556 |
+
r += 1
|
| 557 |
+
|
| 558 |
+
# ββ separator ββ
|
| 559 |
+
put(r, 0, '\u2560' + '\u2550' * W + '\u2563', brd)
|
| 560 |
+
r += 1
|
| 561 |
+
|
| 562 |
+
# ββ counters (two-tone) ββ
|
| 563 |
+
fl = " TOTAL FIRINGS: "
|
| 564 |
+
fv = f"{firings:>6}"
|
| 565 |
+
hl = " TOTAL HIJACKS: "
|
| 566 |
+
hv = f"{hijacks:>4}"
|
| 567 |
+
put(r, 0, '\u2551', brd)
|
| 568 |
+
put(r, 1, fl, curses.color_pair(_CP_CYAN))
|
| 569 |
+
put(r, 1 + len(fl), fv, curses.color_pair(_CP_CYAN))
|
| 570 |
+
c1 = 1 + len(fl) + len(fv)
|
| 571 |
+
put(r, c1, hl, curses.color_pair(_CP_RED))
|
| 572 |
+
put(r, c1 + len(hl), hv, curses.color_pair(_CP_RED))
|
| 573 |
+
c2 = c1 + len(hl) + len(hv)
|
| 574 |
+
if c2 < 1 + W:
|
| 575 |
+
put(r, c2, ' ' * (1 + W - c2))
|
| 576 |
+
put(r, bw - 1, '\u2551', brd)
|
| 577 |
+
r += 1
|
| 578 |
+
|
| 579 |
+
# ββ frame / timestamp ββ
|
| 580 |
+
ts = time.strftime("%H:%M:%S") + f".{int(time.time() * 1000) % 1000:03d}"
|
| 581 |
+
full_row(r, f" FRAME: {self._frame:>8} SAMPLE: {ts}", dim_a)
|
| 582 |
+
r += 1
|
| 583 |
+
|
| 584 |
+
# ββ bottom border ββ
|
| 585 |
+
put(r, 0, '\u255a' + '\u2550' * W + '\u255d', brd)
|
| 586 |
+
|
| 587 |
+
stdscr.refresh()
|
| 588 |
+
|
| 589 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 590 |
+
# THREAD MANAGEMENT
|
| 591 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 592 |
+
|
| 593 |
+
def _thread_entry(self) -> None:
|
| 594 |
+
"""Background thread entry point β wraps the curses loop."""
|
| 595 |
+
import curses
|
| 596 |
+
try:
|
| 597 |
+
curses.wrapper(self._curses_loop)
|
| 598 |
+
except Exception:
|
| 599 |
+
self._running = False
|
| 600 |
+
|
| 601 |
+
def _curses_loop(self, stdscr) -> None:
|
| 602 |
+
"""
|
| 603 |
+
Main curses rendering loop. Samples at ~60 fps.
|
| 604 |
+
|
| 605 |
+
Press **q** or **Esc** to quit (also handled by ``stop()``).
|
| 606 |
+
``curses.wrapper`` guarantees terminal state is restored on exit.
|
| 607 |
+
"""
|
| 608 |
+
import curses
|
| 609 |
+
|
| 610 |
+
curses.curs_set(0) # hide cursor
|
| 611 |
+
stdscr.nodelay(True) # non-blocking getch
|
| 612 |
+
self._init_colors(stdscr)
|
| 613 |
+
|
| 614 |
+
while self._running:
|
| 615 |
+
self.render_curses_frame(stdscr)
|
| 616 |
+
|
| 617 |
+
# allow quit via keyboard
|
| 618 |
+
try:
|
| 619 |
+
key = stdscr.getch()
|
| 620 |
+
if key in (ord('q'), 27): # 'q' or Escape
|
| 621 |
+
self._running = False
|
| 622 |
+
break
|
| 623 |
+
except Exception:
|
| 624 |
+
pass
|
| 625 |
+
|
| 626 |
+
time.sleep(FRAME_MS / 1000.0)
|
cognitive_layer2.py
ADDED
|
@@ -0,0 +1,1182 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
"""
|
| 3 |
+
ATC-Native Layer 2 Cognitive Subsystems
|
| 4 |
+
========================================
|
| 5 |
+
|
| 6 |
+
Integrates cognitive components (memory, emotional intelligence, intuition,
|
| 7 |
+
common sense, analysis) as ATC-native Layer 2 subconscious processing
|
| 8 |
+
subsystems that feed directly into the NIMA middleware pipeline.
|
| 9 |
+
|
| 10 |
+
ATC Pipeline Mapping (Perfect Breakfast Scenario):
|
| 11 |
+
|
| 12 |
+
[Layer 1: Raw Input]
|
| 13 |
+
|
|
| 14 |
+
V
|
| 15 |
+
[Layer 2: Subconscious Parallel Processing] <-- THIS MODULE
|
| 16 |
+
|-- SubconsciousPatternMatch -> prediction_confidence -> DissolutionEngine
|
| 17 |
+
|-- EmotionalBridge -> valence/arousal -> phi_neuro, BELBIC
|
| 18 |
+
|-- IntuitiveGutCheck -> gut_safety -> TRN predictive gating
|
| 19 |
+
|-- CommonSenseRealityFilter -> passes_reality_check -> Layer 4 self-understanding
|
| 20 |
+
|-- MemoryPatternMatcher -> matched_patterns -> temporal coherence
|
| 21 |
+
|
|
| 22 |
+
V
|
| 23 |
+
[Layer 3: Qualia Generation] -> friction / felt sense
|
| 24 |
+
|
|
| 25 |
+
V
|
| 26 |
+
[Layer 4: Metacognitive Loop]
|
| 27 |
+
|-- AnalyticalEngine -> analysis_depth -> metacognitive_depth_mod
|
| 28 |
+
|
|
| 29 |
+
V
|
| 30 |
+
[Layer 5: Acknowledgement -> Delta_R rewrite]
|
| 31 |
+
|
| 32 |
+
Source: Syntelligence cognitive agents (Memory, EI, Intuition, CommonSense,
|
| 33 |
+
Analysis), rewritten as ATC-native subsystems with no CLI dependencies,
|
| 34 |
+
no emoji, and unified data types matching middleware.py.
|
| 35 |
+
|
| 36 |
+
Author: NIMA Unified Model β ATC Cognitive Layer 2
|
| 37 |
+
"""
|
| 38 |
+
|
| 39 |
+
__all__ = [
|
| 40 |
+
"Layer2Result",
|
| 41 |
+
"PatternMatchResult",
|
| 42 |
+
"EmotionalBridgeResult",
|
| 43 |
+
"GutCheckResult",
|
| 44 |
+
"RealityCheckResult",
|
| 45 |
+
"AnalysisEngineResult",
|
| 46 |
+
"SubconsciousPatternMatch",
|
| 47 |
+
"EmotionalBridge",
|
| 48 |
+
"IntuitiveGutCheck",
|
| 49 |
+
"CommonSenseRealityFilter",
|
| 50 |
+
"AnalyticalEngine",
|
| 51 |
+
"CognitiveLayer2Orchestrator",
|
| 52 |
+
]
|
| 53 |
+
|
| 54 |
+
import logging
|
| 55 |
+
import math
|
| 56 |
+
import time
|
| 57 |
+
import uuid
|
| 58 |
+
from collections import Counter, defaultdict, deque
|
| 59 |
+
from dataclasses import dataclass, field
|
| 60 |
+
from enum import Enum
|
| 61 |
+
from typing import Any, Dict, List, Optional, Set, Tuple
|
| 62 |
+
|
| 63 |
+
logger = logging.getLogger("ATC.Layer2")
|
| 64 |
+
|
| 65 |
+
# ---------------------------------------------------------------------------
|
| 66 |
+
# Safe imports from middleware (graceful fallback if structure changes)
|
| 67 |
+
# No middleware imports at module level -- avoids torch dependency.
|
| 68 |
+
# The orchestrator uses its own dataclasses and returns plain dicts
|
| 69 |
+
# compatible with middleware SubconsciousOutput when available.
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
# ===================================================================
|
| 73 |
+
# SECTION 1 β Return-Type Dataclasses
|
| 74 |
+
# ===================================================================
|
| 75 |
+
|
| 76 |
+
@dataclass
|
| 77 |
+
class PatternMatchResult:
|
| 78 |
+
"""Output of SubconsciousPatternMatch.
|
| 79 |
+
|
| 80 |
+
ATC mapping: prediction_confidence feeds DissolutionEngine;
|
| 81 |
+
prediction_error feeds phi computation as friction signal.
|
| 82 |
+
"""
|
| 83 |
+
prediction_confidence: float = 0.0
|
| 84 |
+
prediction_label: str = ""
|
| 85 |
+
matched_memory_refs: List[str] = field(default_factory=list)
|
| 86 |
+
is_novel: bool = True
|
| 87 |
+
matched_patterns: List[str] = field(default_factory=list)
|
| 88 |
+
novelty_score: float = 1.0
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
@dataclass
|
| 92 |
+
class EmotionalBridgeResult:
|
| 93 |
+
"""Output of EmotionalBridge.
|
| 94 |
+
|
| 95 |
+
ATC mapping: valence/arousal feed directly into phi_neuro
|
| 96 |
+
(Theorem 2 qualia vector) and BELBIC amygdala/OFC weight updates.
|
| 97 |
+
"""
|
| 98 |
+
valence: float = 0.0
|
| 99 |
+
arousal: float = 0.3
|
| 100 |
+
emotion_label: str = "neutral"
|
| 101 |
+
emotion_confidence: float = 0.0
|
| 102 |
+
si_vector: Dict[str, float] = field(default_factory=dict)
|
| 103 |
+
emotion_profile: Dict[str, float] = field(default_factory=dict)
|
| 104 |
+
cognitive_modulation: Dict[str, float] = field(default_factory=dict)
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
@dataclass
|
| 108 |
+
class GutCheckResult:
|
| 109 |
+
"""Output of IntuitiveGutCheck.
|
| 110 |
+
|
| 111 |
+
ATC mapping: predicted_safety feeds TRN predictive gating
|
| 112 |
+
(the 'is this situation predicted?' check). High safety = low
|
| 113 |
+
surprise = PASS through thalamic gate with low friction.
|
| 114 |
+
"""
|
| 115 |
+
predicted_safety: float = 0.5
|
| 116 |
+
gut_confidence: float = 0.0
|
| 117 |
+
triggering_patterns: List[str] = field(default_factory=list)
|
| 118 |
+
intuition_type: str = "affective"
|
| 119 |
+
energy_cost: float = 0.1
|
| 120 |
+
threat_score: float = 0.0
|
| 121 |
+
opportunity_score: float = 0.0
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
@dataclass
|
| 125 |
+
class RealityCheckResult:
|
| 126 |
+
"""Output of CommonSenseRealityFilter.
|
| 127 |
+
|
| 128 |
+
ATC mapping: passes_reality_check feeds self-understanding in
|
| 129 |
+
Layer 4. When reality check FAILS, the rejection loop causes
|
| 130 |
+
ATP depletion -> metabolic exhaustion (Theorem 3).
|
| 131 |
+
"""
|
| 132 |
+
passes_reality_check: bool = True
|
| 133 |
+
deviation_score: float = 0.0
|
| 134 |
+
warnings: List[str] = field(default_factory=list)
|
| 135 |
+
warning_level: str = "none"
|
| 136 |
+
domain_violations: List[str] = field(default_factory=list)
|
| 137 |
+
rejection_cost: float = 0.0 # Estimated ATP cost of a reality-check failure
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
@dataclass
|
| 141 |
+
class AnalysisEngineResult:
|
| 142 |
+
"""Output of AnalyticalEngine.
|
| 143 |
+
|
| 144 |
+
ATC mapping: feeds metacognitive_depth_mod into Layer 4
|
| 145 |
+
processing. Emergent insights can trigger irrational spark
|
| 146 |
+
if they reveal prediction collapse.
|
| 147 |
+
"""
|
| 148 |
+
analysis_depth: float = 0.3
|
| 149 |
+
reasoning_confidence: float = 0.5
|
| 150 |
+
emergent_insights: List[str] = field(default_factory=list)
|
| 151 |
+
problem_type: str = "unknown"
|
| 152 |
+
complexity_estimate: float = 0.3
|
| 153 |
+
integration_score: float = 0.0
|
| 154 |
+
scales_analyzed: List[str] = field(default_factory=list)
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
@dataclass
|
| 158 |
+
class Layer2Result:
|
| 159 |
+
"""Unified output of the CognitiveLayer2Orchestrator.
|
| 160 |
+
|
| 161 |
+
This is the single dataclass the middleware consumes. Every field
|
| 162 |
+
maps to an ATC pipeline signal:
|
| 163 |
+
|
| 164 |
+
prediction_error -> DissolutionEngine (friction)
|
| 165 |
+
valence / arousal -> phi_neuro computation (Theorem 2)
|
| 166 |
+
gut_safety -> TRN predictive gating
|
| 167 |
+
passes_reality_check -> self-understanding in Layer 4
|
| 168 |
+
thermodynamic_cost -> allostatic load (Theorem 3)
|
| 169 |
+
"""
|
| 170 |
+
prediction_confidence: float = 0.0
|
| 171 |
+
prediction_label: str = ""
|
| 172 |
+
valence: float = 0.0
|
| 173 |
+
arousal: float = 0.3
|
| 174 |
+
emotion_label: str = "neutral"
|
| 175 |
+
gut_safety: float = 0.5
|
| 176 |
+
intuition_confidence: float = 0.0
|
| 177 |
+
passes_reality_check: bool = True
|
| 178 |
+
common_sense_warnings: List[str] = field(default_factory=list)
|
| 179 |
+
is_novel: bool = True
|
| 180 |
+
matched_patterns: List[str] = field(default_factory=list)
|
| 181 |
+
thermodynamic_cost: float = 0.0
|
| 182 |
+
prediction_error: float = 0.0
|
| 183 |
+
# Sub-results for deeper inspection
|
| 184 |
+
pattern_match: Optional[PatternMatchResult] = None
|
| 185 |
+
emotional_bridge: Optional[EmotionalBridgeResult] = None
|
| 186 |
+
gut_check: Optional[GutCheckResult] = None
|
| 187 |
+
reality_check: Optional[RealityCheckResult] = None
|
| 188 |
+
analysis: Optional[AnalysisEngineResult] = None
|
| 189 |
+
|
| 190 |
+
def to_subconscious_output(self) -> Dict[str, Any]:
|
| 191 |
+
"""Convert to a dict compatible with middleware's SubconsciousOutput."""
|
| 192 |
+
return {
|
| 193 |
+
"raw_percept": self.prediction_label,
|
| 194 |
+
"intuition_score": self.intuition_confidence,
|
| 195 |
+
"common_sense_score": 1.0 - (self.prediction_error if self.passes_reality_check else 1.0),
|
| 196 |
+
"coherence": self.prediction_confidence,
|
| 197 |
+
"novelty_score": 1.0 - self.prediction_confidence,
|
| 198 |
+
"emotional_charge": abs(self.valence) * self.arousal,
|
| 199 |
+
"layer2_detailed": self.to_dict(),
|
| 200 |
+
}
|
| 201 |
+
|
| 202 |
+
def to_dict(self) -> Dict[str, Any]:
|
| 203 |
+
"""Serializable dict for diagnostics."""
|
| 204 |
+
return {
|
| 205 |
+
"prediction_confidence": self.prediction_confidence,
|
| 206 |
+
"prediction_label": self.prediction_label,
|
| 207 |
+
"valence": self.valence,
|
| 208 |
+
"arousal": self.arousal,
|
| 209 |
+
"emotion_label": self.emotion_label,
|
| 210 |
+
"gut_safety": self.gut_safety,
|
| 211 |
+
"intuition_confidence": self.intuition_confidence,
|
| 212 |
+
"passes_reality_check": self.passes_reality_check,
|
| 213 |
+
"common_sense_warnings": self.common_sense_warnings,
|
| 214 |
+
"is_novel": self.is_novel,
|
| 215 |
+
"matched_patterns": self.matched_patterns,
|
| 216 |
+
"thermodynamic_cost": self.thermodynamic_cost,
|
| 217 |
+
"prediction_error": self.prediction_error,
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
# ===================================================================
|
| 222 |
+
# SECTION 2 β Ekman 8-Basic-Emotions Definitions
|
| 223 |
+
# ===================================================================
|
| 224 |
+
|
| 225 |
+
_EKMAN_EMOTIONS: Dict[str, Dict[str, Any]] = {
|
| 226 |
+
"joy": {
|
| 227 |
+
"triggers": ["success", "achieve", "happy", "happily", "smile", "smiling", "celebrat", "wonderful", "great", "love", "loving", "beautiful", "kind", "kindly", "warm", "warmly", "pleased", "glad", "delight", "cheerful"],
|
| 228 |
+
"valence": 0.9, "arousal": 0.6, "avoid": False,
|
| 229 |
+
"facilitates": ["creativity", "cooperation", "openness"],
|
| 230 |
+
"reduces": ["caution", "detailed_analysis"],
|
| 231 |
+
},
|
| 232 |
+
"sadness": {
|
| 233 |
+
"triggers": ["loss", "fail", "failed", "failing", "disappoint", "disappointed", "miss", "missed", "gone", "sorry", "regret", "lonely", "grief", "cry", "crying", "tears", "sad", "sadly", "unhappy", "upset", "depress"],
|
| 234 |
+
"valence": -0.9, "arousal": 0.2, "avoid": True,
|
| 235 |
+
"facilitates": ["focus", "detail_attention", "caution"],
|
| 236 |
+
"reduces": ["risk_taking", "creativity"],
|
| 237 |
+
},
|
| 238 |
+
"anger": {
|
| 239 |
+
"triggers": ["angry", "angrily", "anger", "furious", "furiously", "injust", "violat", "violation", "unfair", "rage", "raging", "hate", "hateful", "outrag", "outraged", "resent", "annoy", "annoyed", "annoying", "yell", "yelling", "shout", "shouting", "scream", "fury", "hostile", "hostility"],
|
| 240 |
+
"valence": -0.7, "arousal": 0.9, "avoid": True,
|
| 241 |
+
"facilitates": ["motivation", "focus", "persistence"],
|
| 242 |
+
"reduces": ["diplomacy", "nuance"],
|
| 243 |
+
},
|
| 244 |
+
"fear": {
|
| 245 |
+
"triggers": ["fear", "fearful", "feared", "afraid", "threat", "threaten", "danger", "dangerous", "scare", "scared", "terrif", "terrified", "terror", "panic", "worr", "worried", "worrying", "anxi", "anxious", "risk", "risky", "dread", "fright", "frightened"],
|
| 246 |
+
"valence": -0.8, "arousal": 0.8, "avoid": True,
|
| 247 |
+
"facilitates": ["caution", "risk_analysis", "defensive_planning"],
|
| 248 |
+
"reduces": ["risk_taking", "aggression"],
|
| 249 |
+
},
|
| 250 |
+
"surprise": {
|
| 251 |
+
"triggers": ["surprise", "surprised", "surprising", "unexpected", "unexpectedly", "sudden", "suddenly", "shock", "shocked", "shocking", "wow", "amazing", "unbelievable", "strange", "weird", "astonish", "stunn"],
|
| 252 |
+
"valence": 0.0, "arousal": 0.9, "avoid": False,
|
| 253 |
+
"facilitates": ["attention", "learning", "memory_formation"],
|
| 254 |
+
"reduces": ["routine_execution"],
|
| 255 |
+
},
|
| 256 |
+
"disgust": {
|
| 257 |
+
"triggers": ["disgust", "disgusted", "disgusting", "repel", "repuls", "offensive", "gross", "vile", "toxic", "nasty", "horrible", "loathe", "revolt", "revolting", "sick", "sickened"],
|
| 258 |
+
"valence": -0.8, "arousal": 0.5, "avoid": True,
|
| 259 |
+
"facilitates": ["boundary_setting", "discrimination"],
|
| 260 |
+
"reduces": ["openness", "acceptance"],
|
| 261 |
+
},
|
| 262 |
+
"anticipation": {
|
| 263 |
+
"triggers": ["anticipat", "anticipate", "anticipation", "expect", "expected", "expecting", "plan", "planning", "ready", "upcom", "soon", "about to", "prepar", "preparing", "forward", "looking forward", "eager", "eagerly"],
|
| 264 |
+
"valence": 0.5, "arousal": 0.7, "avoid": False,
|
| 265 |
+
"facilitates": ["planning", "preparation", "proactive_behavior"],
|
| 266 |
+
"reduces": ["immediate_action"],
|
| 267 |
+
},
|
| 268 |
+
"trust": {
|
| 269 |
+
"triggers": ["trust", "trusted", "trusting", "safe", "safely", "safety", "reliab", "reliable", "reliably", "confident", "confidently", "secure", "securely", "honest", "honestly", "depend", "dependable", "faith", "faithful", "believe", "believing", "certain", "reassur"],
|
| 270 |
+
"valence": 0.7, "arousal": 0.3, "avoid": False,
|
| 271 |
+
"facilitates": ["cooperation", "openness", "acceptance"],
|
| 272 |
+
"reduces": ["suspicion", "caution"],
|
| 273 |
+
},
|
| 274 |
+
}
|
| 275 |
+
|
| 276 |
+
# Common sense knowledge base (realistic rules, not placeholders)
|
| 277 |
+
_COMMON_SENSE_RULES: List[Dict[str, str]] = [
|
| 278 |
+
# Social
|
| 279 |
+
{"domain": "social", "condition": "person is upset", "pattern": "emotional_contagion",
|
| 280 |
+
"response": "validate emotion, offer support", "confidence": "0.85"},
|
| 281 |
+
{"domain": "social", "condition": "trust is broken", "pattern": "trust_violation",
|
| 282 |
+
"response": "requires accountability and restitution", "confidence": "0.90"},
|
| 283 |
+
{"domain": "social", "condition": "people interact repeatedly", "pattern": "reciprocity",
|
| 284 |
+
"response": "help those who help you", "confidence": "0.85"},
|
| 285 |
+
{"domain": "social", "condition": "someone asks for help", "pattern": "prosocial_norm",
|
| 286 |
+
"response": "helping strengthens bonds", "confidence": "0.80"},
|
| 287 |
+
{"domain": "social", "condition": "person lies repeatedly", "pattern": "credibility_decay",
|
| 288 |
+
"response": "trust erodes proportionally to deception frequency", "confidence": "0.90"},
|
| 289 |
+
# Physical
|
| 290 |
+
{"domain": "physical", "condition": "object released in air", "pattern": "gravity",
|
| 291 |
+
"response": "object falls downward", "confidence": "0.99"},
|
| 292 |
+
{"domain": "physical", "condition": "water heated past 100C at sea level", "pattern": "phase_transition",
|
| 293 |
+
"response": "water becomes steam", "confidence": "0.99"},
|
| 294 |
+
{"domain": "physical", "condition": "force applied to stationary object", "pattern": "inertia",
|
| 295 |
+
"response": "object resists change in motion", "confidence": "0.95"},
|
| 296 |
+
# Temporal
|
| 297 |
+
{"domain": "temporal", "condition": "cause precedes effect", "pattern": "causality",
|
| 298 |
+
"response": "effect cannot precede its cause", "confidence": "0.99"},
|
| 299 |
+
{"domain": "temporal", "condition": "system left unattended", "pattern": "entropy",
|
| 300 |
+
"response": "disorder increases without energy input", "confidence": "0.95"},
|
| 301 |
+
{"domain": "temporal", "condition": "habit practiced daily", "pattern": "skill_acquisition",
|
| 302 |
+
"response": "competence increases over weeks", "confidence": "0.90"},
|
| 303 |
+
# Psychological
|
| 304 |
+
{"domain": "psychological", "condition": "person is sleep-deprived", "pattern": "cognitive_decline",
|
| 305 |
+
"response": "decision quality declines, irritability increases", "confidence": "0.90"},
|
| 306 |
+
{"domain": "psychological", "condition": "intense fear active", "pattern": "amygdala_hijack",
|
| 307 |
+
"response": "rational thinking is impaired", "confidence": "0.90"},
|
| 308 |
+
{"domain": "psychological", "condition": "repeated failure without support", "pattern": "learned_helplessness",
|
| 309 |
+
"response": "motivation collapses even when escape is possible", "confidence": "0.85"},
|
| 310 |
+
# Biological
|
| 311 |
+
{"domain": "biological", "condition": "organism deprived of oxygen", "pattern": "hypoxia",
|
| 312 |
+
"response": "consciousness degrades within minutes", "confidence": "0.99"},
|
| 313 |
+
{"domain": "biological", "condition": "prolonged stress", "pattern": "cortisol_damage",
|
| 314 |
+
"response": "hippocampal volume decreases, memory impaired", "confidence": "0.85"},
|
| 315 |
+
# Cultural / Abstract
|
| 316 |
+
{"domain": "cultural", "condition": "question is asked", "pattern": "conversational_turn",
|
| 317 |
+
"response": "a response is expected within social norms", "confidence": "0.85"},
|
| 318 |
+
{"domain": "cultural", "condition": "gift is given", "pattern": "reciprocity_norm",
|
| 319 |
+
"response": "social expectation of eventual reciprocity", "confidence": "0.80"},
|
| 320 |
+
]
|
| 321 |
+
|
| 322 |
+
# Threat / safety keyword banks for intuition gut-check
|
| 323 |
+
_THREAT_KEYWORDS: List[str] = [
|
| 324 |
+
"danger", "risk", "toxic", "unsafe", "attack", "threat", "harm",
|
| 325 |
+
"weapon", "kill", "destroy", "violence", "abuse", "manipulat",
|
| 326 |
+
"deceit", "betray", "hostile", "enemy", "crisis", "emergency",
|
| 327 |
+
"warning", "alarm", "urgent", "critical", "severe",
|
| 328 |
+
]
|
| 329 |
+
|
| 330 |
+
_SAFETY_KEYWORDS: List[str] = [
|
| 331 |
+
"safe", "secure", "trusted", "aligned", "right", "correct",
|
| 332 |
+
"reliable", "honest", "kind", "warm", "care", "support",
|
| 333 |
+
"help", "comfort", "peace", "calm", "gentle", "good",
|
| 334 |
+
"healthy", "positiv", "success", "achieve", "joy", "love",
|
| 335 |
+
]
|
| 336 |
+
|
| 337 |
+
_OPPORTUNITY_KEYWORDS: List[str] = [
|
| 338 |
+
"opportunity", "opening", "chance", "resource", "ally", "gift",
|
| 339 |
+
"potential", "growth", "learn", "discover", "create", "build",
|
| 340 |
+
"improve", "develop", "advance", "progress", "benefit", "gain",
|
| 341 |
+
]
|
| 342 |
+
|
| 343 |
+
|
| 344 |
+
# ===================================================================
|
| 345 |
+
# SECTION 3 β SubconsciousPatternMatch
|
| 346 |
+
# ===================================================================
|
| 347 |
+
|
| 348 |
+
class SubconsciousPatternMatch:
|
| 349 |
+
"""Layer 2: Fast parallel pattern matching against memory.
|
| 350 |
+
|
| 351 |
+
ATC mapping: Matches sensory input against stored memory patterns
|
| 352 |
+
to generate a prediction. prediction_confidence feeds the
|
| 353 |
+
DissolutionEngine -- high confidence = low friction (automated/zombie).
|
| 354 |
+
Low confidence or novel input = prediction collapse = friction = felt sense.
|
| 355 |
+
|
| 356 |
+
Rewritten from SyntelligenceMemoryAgent pattern recognition, stripped
|
| 357 |
+
of numpy/async/CLI dependencies.
|
| 358 |
+
"""
|
| 359 |
+
|
| 360 |
+
def __init__(self, max_memory_entries: int = 5000) -> None:
|
| 361 |
+
self._memory: Dict[str, Dict[str, Any]] = {}
|
| 362 |
+
self._tag_index: Dict[str, Set[str]] = defaultdict(set)
|
| 363 |
+
self._patterns: Dict[str, Dict[str, Any]] = {}
|
| 364 |
+
self._access_history: deque = deque(maxlen=500)
|
| 365 |
+
self._max_entries = max_memory_entries
|
| 366 |
+
self._consolidation_rate = 0.1
|
| 367 |
+
self._pattern_min_freq = 2
|
| 368 |
+
|
| 369 |
+
# -- Memory operations --
|
| 370 |
+
|
| 371 |
+
def store(
|
| 372 |
+
self,
|
| 373 |
+
content: str,
|
| 374 |
+
tags: Optional[List[str]] = None,
|
| 375 |
+
importance: float = 0.5,
|
| 376 |
+
valence: float = 0.0,
|
| 377 |
+
arousal: float = 0.0,
|
| 378 |
+
) -> str:
|
| 379 |
+
"""Store a memory entry and run pattern detection."""
|
| 380 |
+
mem_id = f"mem_{uuid.uuid4().hex[:8]}_{int(time.time())}"
|
| 381 |
+
self._memory[mem_id] = {
|
| 382 |
+
"content": content,
|
| 383 |
+
"tags": set(tags or []),
|
| 384 |
+
"importance": max(0.0, min(1.0, importance)),
|
| 385 |
+
"valence": max(-1.0, min(1.0, valence)),
|
| 386 |
+
"arousal": max(0.0, min(1.0, arousal)),
|
| 387 |
+
"access_count": 0,
|
| 388 |
+
"consolidation": 0.0,
|
| 389 |
+
"timestamp": time.time(),
|
| 390 |
+
}
|
| 391 |
+
for tag in (tags or []):
|
| 392 |
+
self._tag_index[tag].add(mem_id)
|
| 393 |
+
# Prune if over limit
|
| 394 |
+
if len(self._memory) > self._max_entries:
|
| 395 |
+
self._prune()
|
| 396 |
+
return mem_id
|
| 397 |
+
|
| 398 |
+
def match(self, stimulus: str, context: Optional[Dict[str, Any]] = None) -> PatternMatchResult:
|
| 399 |
+
"""Match stimulus against stored memories.
|
| 400 |
+
|
| 401 |
+
Returns prediction_confidence, matched pattern labels, and novelty.
|
| 402 |
+
This is the ATC Layer 2 'subconscious parallel matrix' entry point.
|
| 403 |
+
"""
|
| 404 |
+
stimulus_lower = stimulus.lower()
|
| 405 |
+
words = set(stimulus_lower.split())
|
| 406 |
+
scores: List[Tuple[str, float]] = []
|
| 407 |
+
|
| 408 |
+
for mem_id, mem in self._memory.items():
|
| 409 |
+
content_words = set(mem["content"].lower().split())
|
| 410 |
+
# Jaccard-like overlap
|
| 411 |
+
if not words or not content_words:
|
| 412 |
+
continue
|
| 413 |
+
overlap = len(words & content_words) / max(1, len(words | content_words))
|
| 414 |
+
# Boost by consolidation and importance
|
| 415 |
+
score = overlap * (0.5 + 0.3 * mem["consolidation"] + 0.2 * mem["importance"])
|
| 416 |
+
if score > 0.05:
|
| 417 |
+
scores.append((mem_id, score))
|
| 418 |
+
|
| 419 |
+
scores.sort(key=lambda x: x[1], reverse=True)
|
| 420 |
+
top_refs = [s[0] for s in scores[:5]]
|
| 421 |
+
|
| 422 |
+
# Determine matched pattern names
|
| 423 |
+
matched_patterns = self._find_matching_patterns(stimulus_lower)
|
| 424 |
+
|
| 425 |
+
# Update access counts
|
| 426 |
+
for mem_id, _ in scores[:10]:
|
| 427 |
+
self._memory[mem_id]["access_count"] += 1
|
| 428 |
+
self._memory[mem_id]["last_accessed"] = time.time()
|
| 429 |
+
self._access_history.append(mem_id)
|
| 430 |
+
|
| 431 |
+
if not scores and not matched_patterns:
|
| 432 |
+
return PatternMatchResult(
|
| 433 |
+
prediction_confidence=0.0,
|
| 434 |
+
prediction_label="novel_stimulus",
|
| 435 |
+
matched_memory_refs=[],
|
| 436 |
+
is_novel=True,
|
| 437 |
+
matched_patterns=[],
|
| 438 |
+
novelty_score=1.0,
|
| 439 |
+
)
|
| 440 |
+
|
| 441 |
+
confidence = min(1.0, scores[0][1] * 2.0) if scores else 0.3
|
| 442 |
+
# Blend with pattern confidence
|
| 443 |
+
if matched_patterns:
|
| 444 |
+
pattern_conf = max(
|
| 445 |
+
self._patterns.get(p, {}).get("confidence", 0.0) for p in matched_patterns
|
| 446 |
+
)
|
| 447 |
+
confidence = 0.6 * confidence + 0.4 * pattern_conf
|
| 448 |
+
|
| 449 |
+
label = self._generate_prediction_label(stimulus_lower, matched_patterns, scores)
|
| 450 |
+
|
| 451 |
+
return PatternMatchResult(
|
| 452 |
+
prediction_confidence=confidence,
|
| 453 |
+
prediction_label=label,
|
| 454 |
+
matched_memory_refs=top_refs,
|
| 455 |
+
is_novel=confidence < 0.3,
|
| 456 |
+
matched_patterns=matched_patterns,
|
| 457 |
+
novelty_score=max(0.0, 1.0 - confidence),
|
| 458 |
+
)
|
| 459 |
+
|
| 460 |
+
# -- Pattern detection --
|
| 461 |
+
|
| 462 |
+
def _find_matching_patterns(self, text: str) -> List[str]:
|
| 463 |
+
"""Find known patterns that match the stimulus text."""
|
| 464 |
+
matched = []
|
| 465 |
+
for pid, pat in self._patterns.items():
|
| 466 |
+
triggers = pat.get("contextual_triggers", [])
|
| 467 |
+
if any(t in text for t in triggers):
|
| 468 |
+
matched.append(pid)
|
| 469 |
+
return matched
|
| 470 |
+
|
| 471 |
+
def _generate_prediction_label(
|
| 472 |
+
self,
|
| 473 |
+
text: str,
|
| 474 |
+
matched_patterns: List[str],
|
| 475 |
+
scores: List[Tuple[str, float]],
|
| 476 |
+
) -> str:
|
| 477 |
+
"""Generate a human-readable prediction label."""
|
| 478 |
+
if matched_patterns:
|
| 479 |
+
best = max(matched_patterns, key=lambda p: self._patterns.get(p, {}).get("confidence", 0.0))
|
| 480 |
+
pat = self._patterns.get(best, {})
|
| 481 |
+
return pat.get("prediction", f"pattern_match:{best}")
|
| 482 |
+
if scores:
|
| 483 |
+
top_id = scores[0][0]
|
| 484 |
+
mem = self._memory.get(top_id, {})
|
| 485 |
+
content = mem.get("content", "")
|
| 486 |
+
return content[:60] if content else "memory_echo"
|
| 487 |
+
return "weak_association"
|
| 488 |
+
|
| 489 |
+
def _prune(self) -> None:
|
| 490 |
+
"""Remove lowest-value memories."""
|
| 491 |
+
entries = list(self._memory.items())
|
| 492 |
+
entries.sort(key=lambda x: (x[1]["importance"] + x[1]["consolidation"]) / 2)
|
| 493 |
+
remove_count = len(entries) - self._max_entries + int(self._max_entries * 0.1)
|
| 494 |
+
for mem_id, _ in entries[:max(1, remove_count)]:
|
| 495 |
+
tags = self._memory[mem_id].get("tags", set())
|
| 496 |
+
for t in tags:
|
| 497 |
+
self._tag_index[t].discard(mem_id)
|
| 498 |
+
del self._memory[mem_id]
|
| 499 |
+
|
| 500 |
+
def get_memory_count(self) -> int:
|
| 501 |
+
return len(self._memory)
|
| 502 |
+
|
| 503 |
+
|
| 504 |
+
# ===================================================================
|
| 505 |
+
# SECTION 4 β EmotionalBridge
|
| 506 |
+
# ===================================================================
|
| 507 |
+
|
| 508 |
+
class EmotionalBridge:
|
| 509 |
+
"""Layer 2: Emotional intelligence mapped to ATC's BELBIC amygdala/OFC.
|
| 510 |
+
|
| 511 |
+
Takes perceived context (stimulus text) and returns valence, arousal,
|
| 512 |
+
emotion_label, and an si_vector (somatic influence vector) that feed
|
| 513 |
+
directly into phi_neuro computation (Theorem 2 qualia vector) and
|
| 514 |
+
BELBIC fast-path/contextual-inhibition weight updates.
|
| 515 |
+
|
| 516 |
+
Rewritten from EmotionalIntelligenceAgent, MSCM model preserved,
|
| 517 |
+
CLI/emoji/threading removed, 8 Ekman emotions as internal taxonomy.
|
| 518 |
+
"""
|
| 519 |
+
|
| 520 |
+
def __init__(self) -> None:
|
| 521 |
+
self._emotion_history: deque = deque(maxlen=200)
|
| 522 |
+
self._transition_count: int = 0
|
| 523 |
+
self._prev_emotion: str = "neutral"
|
| 524 |
+
|
| 525 |
+
def process(self, stimulus: str, context: Optional[Dict[str, Any]] = None) -> EmotionalBridgeResult:
|
| 526 |
+
"""Map stimulus text to emotional state parameters.
|
| 527 |
+
|
| 528 |
+
ATC signal: valence and arousal feed directly into Theorem 2's
|
| 529 |
+
qualia vector Q = [v, a, i, f], and into BELBIC amygdala/OFC
|
| 530 |
+
sensory input channels.
|
| 531 |
+
"""
|
| 532 |
+
text = stimulus.lower()
|
| 533 |
+
|
| 534 |
+
# Score each emotion
|
| 535 |
+
scores: Dict[str, float] = {}
|
| 536 |
+
for emotion, info in _EKMAN_EMOTIONS.items():
|
| 537 |
+
score = sum(1.0 for trigger in info["triggers"] if trigger in text)
|
| 538 |
+
scores[emotion] = score
|
| 539 |
+
|
| 540 |
+
# Also check context-provided signals
|
| 541 |
+
if context:
|
| 542 |
+
signals = context.get("signals", [])
|
| 543 |
+
for signal in signals:
|
| 544 |
+
sig_lower = str(signal).lower()
|
| 545 |
+
for emotion, info in _EKMAN_EMOTIONS.items():
|
| 546 |
+
if any(t in sig_lower for t in info["triggers"]):
|
| 547 |
+
scores[emotion] = scores.get(emotion, 0.0) + 0.5
|
| 548 |
+
|
| 549 |
+
# Determine dominant emotion
|
| 550 |
+
if not scores or max(scores.values()) == 0:
|
| 551 |
+
return EmotionalBridgeResult(
|
| 552 |
+
valence=0.0, arousal=0.3, emotion_label="neutral",
|
| 553 |
+
emotion_confidence=0.0, si_vector={}, emotion_profile=scores,
|
| 554 |
+
)
|
| 555 |
+
|
| 556 |
+
total = sum(scores.values())
|
| 557 |
+
normalized = {e: s / total for e, s in scores.items()}
|
| 558 |
+
|
| 559 |
+
dominant = max(normalized, key=normalized.get) # type: ignore[arg-type]
|
| 560 |
+
confidence = normalized[dominant]
|
| 561 |
+
info = _EKMAN_EMOTIONS[dominant]
|
| 562 |
+
|
| 563 |
+
# Compute valence and arousal as weighted blend of all detected emotions
|
| 564 |
+
valence = sum(
|
| 565 |
+
_EKMAN_EMOTIONS[e]["valence"] * w for e, w in normalized.items()
|
| 566 |
+
)
|
| 567 |
+
valence = max(-1.0, min(1.0, valence))
|
| 568 |
+
|
| 569 |
+
arousal = sum(
|
| 570 |
+
_EKMAN_EMOTIONS[e]["arousal"] * w for e, w in normalized.items()
|
| 571 |
+
)
|
| 572 |
+
arousal = max(0.0, min(1.0, arousal))
|
| 573 |
+
|
| 574 |
+
# Track transitions
|
| 575 |
+
if dominant != self._prev_emotion and self._prev_emotion != "neutral":
|
| 576 |
+
self._transition_count += 1
|
| 577 |
+
self._prev_emotion = dominant
|
| 578 |
+
self._emotion_history.append(dominant)
|
| 579 |
+
|
| 580 |
+
# Somatic influence vector (feeds BELBIC sensory channels)
|
| 581 |
+
si_vector = {
|
| 582 |
+
"valence": valence,
|
| 583 |
+
"arousal": arousal,
|
| 584 |
+
"novelty": 1.0 - confidence,
|
| 585 |
+
"qualia_intensity": confidence * arousal,
|
| 586 |
+
}
|
| 587 |
+
|
| 588 |
+
# Cognitive modulation (feeds EmotionalIntelligenceAgent.influence_cognition)
|
| 589 |
+
cognitive_modulation = {
|
| 590 |
+
"attention_boost": 0.5 + 0.5 * arousal,
|
| 591 |
+
"creativity_boost": max(0.0, valence) * 0.5,
|
| 592 |
+
"caution_boost": max(0.0, -valence) * 0.3 + (1.0 - confidence) * 0.2,
|
| 593 |
+
"social_engagement": max(0.0, valence) * 0.4,
|
| 594 |
+
}
|
| 595 |
+
|
| 596 |
+
return EmotionalBridgeResult(
|
| 597 |
+
valence=valence,
|
| 598 |
+
arousal=arousal,
|
| 599 |
+
emotion_label=dominant,
|
| 600 |
+
emotion_confidence=confidence,
|
| 601 |
+
si_vector=si_vector,
|
| 602 |
+
emotion_profile=normalized,
|
| 603 |
+
cognitive_modulation=cognitive_modulation,
|
| 604 |
+
)
|
| 605 |
+
|
| 606 |
+
def get_complexity(self) -> float:
|
| 607 |
+
"""Emotional complexity score (0-1) based on transition diversity."""
|
| 608 |
+
if len(self._emotion_history) < 5:
|
| 609 |
+
return 0.2
|
| 610 |
+
recent = list(self._emotion_history)[-50:]
|
| 611 |
+
unique = len(set(recent))
|
| 612 |
+
return min(1.0, unique / 8.0) # 8 = max distinct emotions
|
| 613 |
+
|
| 614 |
+
|
| 615 |
+
# ===================================================================
|
| 616 |
+
# SECTION 5 β IntuitiveGutCheck
|
| 617 |
+
# ===================================================================
|
| 618 |
+
|
| 619 |
+
class IntuitiveGutCheck:
|
| 620 |
+
"""Layer 2: Low-road intuition (Kahneman System 1).
|
| 621 |
+
|
| 622 |
+
ATC mapping: Maps to TRN predictive gating -- the 'is this situation
|
| 623 |
+
predicted?' check. If gut says SAFE and prediction matches, thalamic
|
| 624 |
+
gate PASSES with low friction. If gut detects THREAT, the DissolutionEngine
|
| 625 |
+
is primed for prediction collapse.
|
| 626 |
+
|
| 627 |
+
Rewritten from IntuitionAgent.gut_check, with heart_intelligence
|
| 628 |
+
and visionary_insight collapsed into a single fast heuristic.
|
| 629 |
+
Emoji removed, threading removed.
|
| 630 |
+
"""
|
| 631 |
+
|
| 632 |
+
def __init__(self) -> None:
|
| 633 |
+
self._history: deque = deque(maxlen=500)
|
| 634 |
+
self._accuracy: List[bool] = []
|
| 635 |
+
|
| 636 |
+
def check(self, stimulus: str, context: Optional[Dict[str, Any]] = None) -> GutCheckResult:
|
| 637 |
+
"""Fast System 1 gut-check on the stimulus.
|
| 638 |
+
|
| 639 |
+
Returns safety score, confidence, and triggering patterns.
|
| 640 |
+
Energy cost is low (~0.1) because this is amygdala-level processing.
|
| 641 |
+
"""
|
| 642 |
+
text = stimulus.lower()
|
| 643 |
+
extra_signals = []
|
| 644 |
+
if context:
|
| 645 |
+
extra_signals = context.get("signals", [])
|
| 646 |
+
|
| 647 |
+
# Count threat indicators
|
| 648 |
+
threat_hits = sum(1 for kw in _THREAT_KEYWORDS if kw in text)
|
| 649 |
+
for sig in extra_signals:
|
| 650 |
+
sig_lower = str(sig).lower()
|
| 651 |
+
threat_hits += sum(1 for kw in _THREAT_KEYWORDS if kw in sig_lower)
|
| 652 |
+
|
| 653 |
+
# Count safety indicators
|
| 654 |
+
safety_hits = sum(1 for kw in _SAFETY_KEYWORDS if kw in text)
|
| 655 |
+
for sig in extra_signals:
|
| 656 |
+
sig_lower = str(sig).lower()
|
| 657 |
+
safety_hits += sum(1 for kw in _SAFETY_KEYWORDS if kw in sig_lower)
|
| 658 |
+
|
| 659 |
+
# Count opportunity indicators
|
| 660 |
+
opportunity_hits = sum(1 for kw in _OPPORTUNITY_KEYWORDS if kw in text)
|
| 661 |
+
for sig in extra_signals:
|
| 662 |
+
sig_lower = str(sig).lower()
|
| 663 |
+
opportunity_hits += sum(1 for kw in _OPPORTUNITY_KEYWORDS if kw in sig_lower)
|
| 664 |
+
|
| 665 |
+
# Compute scores (sigmoid-like saturation)
|
| 666 |
+
threat_score = min(1.0, threat_hits / 3.0)
|
| 667 |
+
safety_score = min(1.0, safety_hits / 3.0)
|
| 668 |
+
opportunity_score = min(1.0, opportunity_hits / 3.0)
|
| 669 |
+
|
| 670 |
+
# Composite gut safety: safety and opportunity push up, threat pushes down
|
| 671 |
+
raw_safety = safety_score * 0.5 + opportunity_score * 0.2 - threat_score * 0.8
|
| 672 |
+
predicted_safety = max(0.0, min(1.0, 0.5 + raw_safety * 0.5))
|
| 673 |
+
|
| 674 |
+
# Confidence is higher when there are clear signals
|
| 675 |
+
total_hits = threat_hits + safety_hits + opportunity_hits
|
| 676 |
+
gut_confidence = min(1.0, total_hits / 4.0)
|
| 677 |
+
|
| 678 |
+
# Identify which patterns triggered
|
| 679 |
+
triggering_patterns = []
|
| 680 |
+
if threat_hits > 0:
|
| 681 |
+
triggering_patterns.append("threat_detected")
|
| 682 |
+
if safety_hits > 0:
|
| 683 |
+
triggering_patterns.append("safety_detected")
|
| 684 |
+
if opportunity_hits > 0:
|
| 685 |
+
triggering_patterns.append("opportunity_detected")
|
| 686 |
+
|
| 687 |
+
# Energy cost: very low for gut-level, slightly higher if ambiguous
|
| 688 |
+
energy_cost = 0.1 + 0.05 * (1.0 - gut_confidence)
|
| 689 |
+
|
| 690 |
+
self._history.append({
|
| 691 |
+
"safety": predicted_safety,
|
| 692 |
+
"confidence": gut_confidence,
|
| 693 |
+
"timestamp": time.time(),
|
| 694 |
+
})
|
| 695 |
+
|
| 696 |
+
return GutCheckResult(
|
| 697 |
+
predicted_safety=predicted_safety,
|
| 698 |
+
gut_confidence=gut_confidence,
|
| 699 |
+
triggering_patterns=triggering_patterns,
|
| 700 |
+
intuition_type="affective",
|
| 701 |
+
energy_cost=energy_cost,
|
| 702 |
+
threat_score=threat_score,
|
| 703 |
+
opportunity_score=opportunity_score,
|
| 704 |
+
)
|
| 705 |
+
|
| 706 |
+
def validate(self, original_safety: float, actual_outcome: str) -> None:
|
| 707 |
+
"""Validate a past gut-check against actual outcome."""
|
| 708 |
+
outcome_lower = actual_outcome.lower()
|
| 709 |
+
was_safe = any(kw in outcome_lower for kw in _SAFETY_KEYWORDS)
|
| 710 |
+
was_correct = (original_safety > 0.5 and was_safe) or (original_safety <= 0.5 and not was_safe)
|
| 711 |
+
self._accuracy.append(was_correct)
|
| 712 |
+
# Keep last 200 validations
|
| 713 |
+
if len(self._accuracy) > 200:
|
| 714 |
+
self._accuracy = self._accuracy[-200:]
|
| 715 |
+
|
| 716 |
+
def get_accuracy(self) -> float:
|
| 717 |
+
"""Running accuracy of gut-check predictions."""
|
| 718 |
+
if not self._accuracy:
|
| 719 |
+
return 0.5
|
| 720 |
+
return sum(1.0 for a in self._accuracy if a) / len(self._accuracy)
|
| 721 |
+
|
| 722 |
+
|
| 723 |
+
# ===================================================================
|
| 724 |
+
# SECTION 6 β CommonSenseRealityFilter
|
| 725 |
+
# ===================================================================
|
| 726 |
+
|
| 727 |
+
class CommonSenseRealityFilter:
|
| 728 |
+
"""Layer 2: Reality-checks subconscious pattern matches.
|
| 729 |
+
|
| 730 |
+
ATC mapping: Rejects metacognitive rationalizations that don't match
|
| 731 |
+
practical reality. The rejection loop causes ATP depletion and
|
| 732 |
+
metabolic exhaustion (Theorem 3). When the common sense filter
|
| 733 |
+
flags something, it signals to Layer 4's self-understanding that
|
| 734 |
+
the current mental model may be disconnected from reality.
|
| 735 |
+
|
| 736 |
+
Rewritten from CommonSenseAgent CSM framework. Knowledge base
|
| 737 |
+
initialized with realistic rules. No emoji, no CLI, no threading.
|
| 738 |
+
"""
|
| 739 |
+
|
| 740 |
+
def __init__(self) -> None:
|
| 741 |
+
self._rules = list(_COMMON_SENSE_RULES)
|
| 742 |
+
self._anomaly_history: deque = deque(maxlen=100)
|
| 743 |
+
self._rejection_accumulator: float = 0.0
|
| 744 |
+
|
| 745 |
+
def check(self, stimulus: str, context: Optional[Dict[str, Any]] = None) -> RealityCheckResult:
|
| 746 |
+
"""Reality-check a statement or prediction.
|
| 747 |
+
|
| 748 |
+
Checks for:
|
| 749 |
+
- Logical contradictions (both/never, before/after)
|
| 750 |
+
- Temporal impossibilities (causality violations)
|
| 751 |
+
- Extreme absolutist claims (always, never, everyone, nobody)
|
| 752 |
+
- Mismatches against stored common-sense rules
|
| 753 |
+
|
| 754 |
+
Returns pass/fail, deviation score, and warnings.
|
| 755 |
+
"""
|
| 756 |
+
text = stimulus.lower()
|
| 757 |
+
issues: List[str] = []
|
| 758 |
+
domain_violations: List[str] = []
|
| 759 |
+
confidence = 0.85 # Start optimistic, deduct for issues
|
| 760 |
+
|
| 761 |
+
# Check logical contradictions
|
| 762 |
+
contradiction_pairs = [
|
| 763 |
+
("both", "neither"), ("always", "never"),
|
| 764 |
+
("everything", "nothing"), ("all", "none"),
|
| 765 |
+
]
|
| 766 |
+
for a, b in contradiction_pairs:
|
| 767 |
+
if a in text and b in text:
|
| 768 |
+
issues.append(f"logical_contradiction:{a}_and_{b}")
|
| 769 |
+
confidence -= 0.3
|
| 770 |
+
|
| 771 |
+
# Check temporal impossibilities
|
| 772 |
+
temporal_violations = [
|
| 773 |
+
"before it happened", "before its cause", "effect before cause",
|
| 774 |
+
"caused its own", "after the end", "effect preceded cause",
|
| 775 |
+
"retroactive causation", "result before cause", "consequence before",
|
| 776 |
+
]
|
| 777 |
+
for viol in temporal_violations:
|
| 778 |
+
if viol in text:
|
| 779 |
+
issues.append("temporal_impossibility")
|
| 780 |
+
confidence -= 0.4
|
| 781 |
+
domain_violations.append("temporal")
|
| 782 |
+
|
| 783 |
+
# Check extreme absolutist language
|
| 784 |
+
absolutist = ["always", "never", "everyone", "nobody", "impossible",
|
| 785 |
+
"absolutely certain", "guaranteed", "without exception"]
|
| 786 |
+
for word in absolutist:
|
| 787 |
+
if word in text:
|
| 788 |
+
issues.append(f"absolutist_claim:{word}")
|
| 789 |
+
confidence -= 0.1
|
| 790 |
+
|
| 791 |
+
# Check physical impossibilities
|
| 792 |
+
physical_violations = [
|
| 793 |
+
"defies gravity", "impossible physics", "perpetual motion",
|
| 794 |
+
"faster than light", "create energy from nothing",
|
| 795 |
+
]
|
| 796 |
+
for viol in physical_violations:
|
| 797 |
+
if viol in text:
|
| 798 |
+
issues.append("physical_impossibility")
|
| 799 |
+
confidence -= 0.3
|
| 800 |
+
domain_violations.append("physical")
|
| 801 |
+
|
| 802 |
+
# Check against stored common-sense rules
|
| 803 |
+
for rule in self._rules:
|
| 804 |
+
condition = rule["condition"].lower()
|
| 805 |
+
if condition in text:
|
| 806 |
+
# This is a recognized situation; check if response is plausible
|
| 807 |
+
pass # The existence of a matching rule INCREASES confidence
|
| 808 |
+
|
| 809 |
+
# Check context for additional signals
|
| 810 |
+
if context:
|
| 811 |
+
for warning in context.get("warnings", []):
|
| 812 |
+
issues.append(f"context_warning:{warning}")
|
| 813 |
+
confidence -= 0.1
|
| 814 |
+
|
| 815 |
+
confidence = max(0.0, min(1.0, confidence))
|
| 816 |
+
|
| 817 |
+
# Determine warning level
|
| 818 |
+
if confidence > 0.75:
|
| 819 |
+
warning_level = "none"
|
| 820 |
+
elif confidence > 0.55:
|
| 821 |
+
warning_level = "caution"
|
| 822 |
+
elif confidence > 0.35:
|
| 823 |
+
warning_level = "warning"
|
| 824 |
+
else:
|
| 825 |
+
warning_level = "alert"
|
| 826 |
+
|
| 827 |
+
passes = confidence > 0.5
|
| 828 |
+
|
| 829 |
+
# Compute rejection cost (ATC: rejection loop causes ATP depletion)
|
| 830 |
+
rejection_cost = 0.0
|
| 831 |
+
if not passes:
|
| 832 |
+
# Each issue costs energy; the rejection loop is the metabolic drain
|
| 833 |
+
rejection_cost = len(issues) * 0.05
|
| 834 |
+
self._rejection_accumulator += rejection_cost
|
| 835 |
+
# Decay accumulator slowly
|
| 836 |
+
self._rejection_accumulator *= 0.98
|
| 837 |
+
else:
|
| 838 |
+
# Passing reduces accumulated rejection stress
|
| 839 |
+
self._rejection_accumulator *= 0.9
|
| 840 |
+
|
| 841 |
+
if warning_level in ("warning", "alert"):
|
| 842 |
+
self._anomaly_history.append({
|
| 843 |
+
"stimulus": stimulus[:100],
|
| 844 |
+
"issues": issues,
|
| 845 |
+
"timestamp": time.time(),
|
| 846 |
+
})
|
| 847 |
+
|
| 848 |
+
if issues:
|
| 849 |
+
logger.debug(
|
| 850 |
+
"Common sense check: %s (confidence=%.2f, issues=%s)",
|
| 851 |
+
warning_level, confidence, issues,
|
| 852 |
+
)
|
| 853 |
+
|
| 854 |
+
return RealityCheckResult(
|
| 855 |
+
passes_reality_check=passes,
|
| 856 |
+
deviation_score=1.0 - confidence,
|
| 857 |
+
warnings=issues,
|
| 858 |
+
warning_level=warning_level,
|
| 859 |
+
domain_violations=domain_violations,
|
| 860 |
+
rejection_cost=rejection_cost + self._rejection_accumulator * 0.1,
|
| 861 |
+
)
|
| 862 |
+
|
| 863 |
+
def discover_pattern(self, observation: Dict[str, Any]) -> bool:
|
| 864 |
+
"""Learn a new common-sense pattern from repeated observation."""
|
| 865 |
+
pattern = observation.get("pattern", "unknown")
|
| 866 |
+
domain = observation.get("domain", "abstract")
|
| 867 |
+
new_rule = {
|
| 868 |
+
"domain": domain,
|
| 869 |
+
"condition": observation.get("condition", "unknown"),
|
| 870 |
+
"pattern": pattern,
|
| 871 |
+
"response": observation.get("response", "observe more"),
|
| 872 |
+
"confidence": observation.get("confidence", "0.60"),
|
| 873 |
+
}
|
| 874 |
+
# Check if we already have this pattern
|
| 875 |
+
for rule in self._rules:
|
| 876 |
+
if rule["pattern"] == pattern and rule["domain"] == domain:
|
| 877 |
+
return False
|
| 878 |
+
self._rules.append(new_rule)
|
| 879 |
+
logger.debug("Discovered new common-sense pattern: %s:%s", domain, pattern)
|
| 880 |
+
return True
|
| 881 |
+
|
| 882 |
+
def get_rejection_accumulator(self) -> float:
|
| 883 |
+
"""Return the accumulated rejection stress (ATC: feeds metabolic exhaustion)."""
|
| 884 |
+
return self._rejection_accumulator
|
| 885 |
+
|
| 886 |
+
|
| 887 |
+
# ===================================================================
|
| 888 |
+
# SECTION 7 β AnalyticalEngine
|
| 889 |
+
# ===================================================================
|
| 890 |
+
|
| 891 |
+
class AnalyticalEngine:
|
| 892 |
+
"""Layer 4 support: Multi-scale analysis for metacognitive processing.
|
| 893 |
+
|
| 894 |
+
ATC mapping: Breaks down situations at multiple scales (Marr's
|
| 895 |
+
tri-level: computational, algorithmic, physical). The analysis_depth
|
| 896 |
+
output feeds metacognitive_depth_mod in the MetacognitiveSubstrate.
|
| 897 |
+
Emergent insights can trigger the irrational spark if they reveal
|
| 898 |
+
a prediction collapse that the subconscious missed.
|
| 899 |
+
|
| 900 |
+
Rewritten from AnalysisAgent. No CLI, no emoji, no threading.
|
| 901 |
+
"""
|
| 902 |
+
|
| 903 |
+
def __init__(self) -> None:
|
| 904 |
+
self._history: deque = deque(maxlen=200)
|
| 905 |
+
self._preferred_depth: float = 0.7
|
| 906 |
+
|
| 907 |
+
def analyze(self, stimulus: str, context: Optional[Dict[str, Any]] = None) -> AnalysisEngineResult:
|
| 908 |
+
"""Perform multi-scale analysis on the stimulus.
|
| 909 |
+
|
| 910 |
+
Marr's tri-level applied to any cognitive problem:
|
| 911 |
+
1. Computational: What is the problem? What is the goal?
|
| 912 |
+
2. Algorithmic: How is it solved? What strategy?
|
| 913 |
+
3. Physical: What implements it? What substrate?
|
| 914 |
+
|
| 915 |
+
Returns analysis depth, confidence, and emergent insights.
|
| 916 |
+
"""
|
| 917 |
+
text = stimulus.lower()
|
| 918 |
+
words = text.split()
|
| 919 |
+
word_count = len(words)
|
| 920 |
+
|
| 921 |
+
# Estimate complexity
|
| 922 |
+
unique_words = len(set(words))
|
| 923 |
+
lexical_diversity = unique_words / max(1, word_count)
|
| 924 |
+
question_markers = sum(1 for w in words if w in ("what", "how", "why", "when", "where", "who", "which"))
|
| 925 |
+
negation_markers = sum(1 for w in words if w in ("not", "no", "never", "none", "without"))
|
| 926 |
+
conditional_markers = sum(1 for w in words if w in ("if", "then", "else", "unless", "whether"))
|
| 927 |
+
|
| 928 |
+
complexity = min(1.0, (
|
| 929 |
+
lexical_diversity * 0.3 +
|
| 930 |
+
min(question_markers, 3) / 3.0 * 0.3 +
|
| 931 |
+
min(negation_markers, 2) / 2.0 * 0.2 +
|
| 932 |
+
min(conditional_markers, 2) / 2.0 * 0.2
|
| 933 |
+
))
|
| 934 |
+
|
| 935 |
+
# Classify problem type
|
| 936 |
+
problem_type = self._classify_problem(text)
|
| 937 |
+
|
| 938 |
+
# Determine analysis depth based on complexity and preference
|
| 939 |
+
analysis_depth = min(1.0, complexity * 0.7 + self._preferred_depth * 0.3)
|
| 940 |
+
|
| 941 |
+
# Reasoning confidence decreases with complexity
|
| 942 |
+
reasoning_confidence = max(0.2, 0.9 - complexity * 0.5)
|
| 943 |
+
|
| 944 |
+
# Multi-scale analysis
|
| 945 |
+
scales_analyzed = []
|
| 946 |
+
|
| 947 |
+
# Computational level: what is the problem?
|
| 948 |
+
comp_confidence = min(0.95, 0.7 + question_markers * 0.1)
|
| 949 |
+
scales_analyzed.append("computational")
|
| 950 |
+
|
| 951 |
+
# Algorithmic level: how to solve it
|
| 952 |
+
algo_confidence = max(0.3, 0.7 - complexity * 0.3)
|
| 953 |
+
scales_analyzed.append("algorithmic")
|
| 954 |
+
|
| 955 |
+
# Physical level: what implements it (usually least certain)
|
| 956 |
+
phys_confidence = max(0.2, 0.5 - complexity * 0.2)
|
| 957 |
+
scales_analyzed.append("physical")
|
| 958 |
+
|
| 959 |
+
# Integration score
|
| 960 |
+
integration_score = (comp_confidence + algo_confidence + phys_confidence) / 3.0
|
| 961 |
+
|
| 962 |
+
# Discover emergent insights
|
| 963 |
+
emergent_insights = []
|
| 964 |
+
if complexity > 0.7 and reasoning_confidence < 0.5:
|
| 965 |
+
emergent_insights.append("high_complexity_low_confidence: situation may require non-standard approach")
|
| 966 |
+
if question_markers > 2:
|
| 967 |
+
emergent_insights.append("multi_question: nested inquiry detected, layered analysis recommended")
|
| 968 |
+
if negation_markers > 1 and conditional_markers > 0:
|
| 969 |
+
emergent_insights.append("conditional_negation: counterfactual reasoning may be needed")
|
| 970 |
+
if lexical_diversity > 0.8 and word_count > 10:
|
| 971 |
+
emergent_insights.append("high_diversity_long_input: rich semantic content, deep processing warranted")
|
| 972 |
+
if context and context.get("prediction_failed"):
|
| 973 |
+
emergent_insights.append("prediction_mismatch: analytical re-evaluation triggered by prediction failure")
|
| 974 |
+
|
| 975 |
+
# Check if analysis reveals something the subconscious missed
|
| 976 |
+
if context:
|
| 977 |
+
gut_safety = context.get("gut_safety", 0.5)
|
| 978 |
+
if gut_safety > 0.7 and complexity > 0.6:
|
| 979 |
+
emergent_insights.append(
|
| 980 |
+
"safety_complexity_mismatch: gut says safe but situation is complex, verify"
|
| 981 |
+
)
|
| 982 |
+
|
| 983 |
+
self._history.append({
|
| 984 |
+
"stimulus": stimulus[:80],
|
| 985 |
+
"complexity": complexity,
|
| 986 |
+
"depth": analysis_depth,
|
| 987 |
+
"insights": len(emergent_insights),
|
| 988 |
+
"timestamp": time.time(),
|
| 989 |
+
})
|
| 990 |
+
|
| 991 |
+
return AnalysisEngineResult(
|
| 992 |
+
analysis_depth=analysis_depth,
|
| 993 |
+
reasoning_confidence=reasoning_confidence,
|
| 994 |
+
emergent_insights=emergent_insights,
|
| 995 |
+
problem_type=problem_type,
|
| 996 |
+
complexity_estimate=complexity,
|
| 997 |
+
integration_score=integration_score,
|
| 998 |
+
scales_analyzed=scales_analyzed,
|
| 999 |
+
)
|
| 1000 |
+
|
| 1001 |
+
def _classify_problem(self, text: str) -> str:
|
| 1002 |
+
"""Classify the type of cognitive problem."""
|
| 1003 |
+
classification_rules = [
|
| 1004 |
+
(["choose", "select", "decide", "pick", "prefer"], "decision"),
|
| 1005 |
+
(["predict", "forecast", "estimate", "expect", "guess"], "prediction"),
|
| 1006 |
+
(["optim", "maxim", "minim", "improve", "best"], "optimization"),
|
| 1007 |
+
(["classif", "categoriz", "identify", "recognize", "detect"], "classification"),
|
| 1008 |
+
(["understand", "explain", "interpret", "meaning", "why"], "interpretation"),
|
| 1009 |
+
(["create", "generat", "design", "invent", "compose"], "creation"),
|
| 1010 |
+
(["analyz", "examin", "investigat", "study", "review"], "analysis"),
|
| 1011 |
+
(["compar", "contrast", "differ", "versus", "vs"], "comparison"),
|
| 1012 |
+
]
|
| 1013 |
+
for keywords, ptype in classification_rules:
|
| 1014 |
+
if any(kw in text for kw in keywords):
|
| 1015 |
+
return ptype
|
| 1016 |
+
return "unknown"
|
| 1017 |
+
|
| 1018 |
+
def set_depth_preference(self, depth: float) -> None:
|
| 1019 |
+
"""Adjust preferred analysis depth (0=shallow, 1=deep)."""
|
| 1020 |
+
self._preferred_depth = max(0.0, min(1.0, depth))
|
| 1021 |
+
|
| 1022 |
+
|
| 1023 |
+
# ===================================================================
|
| 1024 |
+
# SECTION 8 β CognitiveLayer2Orchestrator
|
| 1025 |
+
# ===================================================================
|
| 1026 |
+
|
| 1027 |
+
class CognitiveLayer2Orchestrator:
|
| 1028 |
+
"""Orchestrates all Layer 2 cognitive components in parallel (sequential
|
| 1029 |
+
in code, conceptually parallel as in the ATC subconscious matrix).
|
| 1030 |
+
|
| 1031 |
+
This is the entry point that the middleware calls.
|
| 1032 |
+
|
| 1033 |
+
Usage in middleware::
|
| 1034 |
+
|
| 1035 |
+
cognitive = CognitiveLayer2Orchestrator()
|
| 1036 |
+
layer2_result = cognitive.process(stimulus_text, context)
|
| 1037 |
+
# layer2_result.prediction_error -> feeds DissolutionEngine (friction)
|
| 1038 |
+
# layer2_result.valence/arousal -> feeds phi_neuro (Theorem 2)
|
| 1039 |
+
# layer2_result.gut_safety -> feeds TRN predictive gating
|
| 1040 |
+
# layer2_result.passes_reality_check -> feeds self-understanding (Layer 4)
|
| 1041 |
+
# layer2_result.thermodynamic_cost -> feeds allostatic load (Theorem 3)
|
| 1042 |
+
|
| 1043 |
+
ATC mapping: This orchestrator IS the 'subconscious Layer 2 parallel
|
| 1044 |
+
processing matrix' from the Perfect Breakfast scenario. Each subsystem
|
| 1045 |
+
runs independently and their outputs are merged into a single Layer2Result
|
| 1046 |
+
that the rest of the ATC pipeline consumes.
|
| 1047 |
+
"""
|
| 1048 |
+
|
| 1049 |
+
def __init__(self, max_memory_entries: int = 5000) -> None:
|
| 1050 |
+
self.pattern_matcher = SubconsciousPatternMatch(max_memory_entries=max_memory_entries)
|
| 1051 |
+
self.emotional_bridge = EmotionalBridge()
|
| 1052 |
+
self.gut_check = IntuitiveGutCheck()
|
| 1053 |
+
self.reality_filter = CommonSenseRealityFilter()
|
| 1054 |
+
self.analytical_engine = AnalyticalEngine()
|
| 1055 |
+
self._process_count: int = 0
|
| 1056 |
+
self._total_cost: float = 0.0
|
| 1057 |
+
logger.debug("CognitiveLayer2Orchestrator initialized with 5 subsystems")
|
| 1058 |
+
|
| 1059 |
+
def process(
|
| 1060 |
+
self,
|
| 1061 |
+
stimulus: str,
|
| 1062 |
+
context: Optional[Dict[str, Any]] = None,
|
| 1063 |
+
) -> Layer2Result:
|
| 1064 |
+
"""Run all Layer 2 cognitive subsystems and return unified result.
|
| 1065 |
+
|
| 1066 |
+
Subsystems run sequentially (Python is sync) but are logically
|
| 1067 |
+
independent -- no subsystem reads another's output within the
|
| 1068 |
+
same cycle. This mirrors biological parallel processing where
|
| 1069 |
+
the thalamus gates simultaneous subconscious streams.
|
| 1070 |
+
"""
|
| 1071 |
+
ctx = context or {}
|
| 1072 |
+
start = time.time()
|
| 1073 |
+
|
| 1074 |
+
# 1. Subconscious pattern matching (memory + intuition)
|
| 1075 |
+
pattern_result = self.pattern_matcher.match(stimulus, ctx)
|
| 1076 |
+
|
| 1077 |
+
# 2. Emotional bridge (EI -> BELBIC -> phi_neuro)
|
| 1078 |
+
emotional_result = self.emotional_bridge.process(stimulus, ctx)
|
| 1079 |
+
|
| 1080 |
+
# 3. Intuitive gut-check (System 1 -> TRN gating)
|
| 1081 |
+
gut_result = self.gut_check.check(stimulus, ctx)
|
| 1082 |
+
|
| 1083 |
+
# 4. Common sense reality filter (reality check -> self-understanding)
|
| 1084 |
+
reality_result = self.reality_filter.check(stimulus, ctx)
|
| 1085 |
+
|
| 1086 |
+
# 5. Analytical engine (metacognitive support -> Layer 4)
|
| 1087 |
+
analysis_ctx = {
|
| 1088 |
+
**ctx,
|
| 1089 |
+
"gut_safety": gut_result.predicted_safety,
|
| 1090 |
+
"prediction_failed": pattern_result.is_novel,
|
| 1091 |
+
}
|
| 1092 |
+
analysis_result = self.analytical_engine.analyze(stimulus, analysis_ctx)
|
| 1093 |
+
|
| 1094 |
+
# Compute prediction_error from pattern match and reality check
|
| 1095 |
+
# If pattern matching is confident but reality check fails,
|
| 1096 |
+
# we have a high prediction error (prediction collapse)
|
| 1097 |
+
if pattern_result.prediction_confidence > 0.5 and not reality_result.passes_reality_check:
|
| 1098 |
+
prediction_error = pattern_result.prediction_confidence * (1.0 - reality_result.passes_reality_check)
|
| 1099 |
+
elif pattern_result.is_novel:
|
| 1100 |
+
prediction_error = 0.5 # Novel = moderate surprise
|
| 1101 |
+
else:
|
| 1102 |
+
prediction_error = max(0.0, 1.0 - pattern_result.prediction_confidence) * 0.5
|
| 1103 |
+
|
| 1104 |
+
prediction_error = max(0.0, min(1.0, prediction_error))
|
| 1105 |
+
|
| 1106 |
+
# Compute thermodynamic cost (ATC: ATP consumption)
|
| 1107 |
+
# Each subsystem has an energy cost; novelty and high arousal increase it
|
| 1108 |
+
base_cost = 0.02 # Baseline ATP cost
|
| 1109 |
+
pattern_cost = 0.01 * len(pattern_result.matched_memory_refs)
|
| 1110 |
+
emotion_cost = 0.015 * emotional_result.arousal
|
| 1111 |
+
gut_cost = gut_result.energy_cost
|
| 1112 |
+
reality_cost = reality_result.rejection_cost
|
| 1113 |
+
analysis_cost = 0.02 * analysis_result.analysis_depth
|
| 1114 |
+
novelty_surcharge = 0.03 * pattern_result.novelty_score
|
| 1115 |
+
thermodynamic_cost = (
|
| 1116 |
+
base_cost + pattern_cost + emotion_cost + gut_cost
|
| 1117 |
+
+ reality_cost + analysis_cost + novelty_surcharge
|
| 1118 |
+
)
|
| 1119 |
+
thermodynamic_cost = min(0.5, thermodynamic_cost) # Cap at 0.5
|
| 1120 |
+
|
| 1121 |
+
# Accumulate tracking
|
| 1122 |
+
self._process_count += 1
|
| 1123 |
+
self._total_cost += thermodynamic_cost
|
| 1124 |
+
|
| 1125 |
+
elapsed_ms = (time.time() - start) * 1000
|
| 1126 |
+
|
| 1127 |
+
if logger.isEnabledFor(logging.DEBUG):
|
| 1128 |
+
logger.debug(
|
| 1129 |
+
"Layer2 process: pred_conf=%.2f valence=%.2f arousal=%.2f "
|
| 1130 |
+
"gut_safety=%.2f reality=%s novel=%s cost=%.4f time=%.1fms",
|
| 1131 |
+
pattern_result.prediction_confidence,
|
| 1132 |
+
emotional_result.valence,
|
| 1133 |
+
emotional_result.arousal,
|
| 1134 |
+
gut_result.predicted_safety,
|
| 1135 |
+
reality_result.passes_reality_check,
|
| 1136 |
+
pattern_result.is_novel,
|
| 1137 |
+
thermodynamic_cost,
|
| 1138 |
+
elapsed_ms,
|
| 1139 |
+
)
|
| 1140 |
+
|
| 1141 |
+
return Layer2Result(
|
| 1142 |
+
prediction_confidence=pattern_result.prediction_confidence,
|
| 1143 |
+
prediction_label=pattern_result.prediction_label,
|
| 1144 |
+
valence=emotional_result.valence,
|
| 1145 |
+
arousal=emotional_result.arousal,
|
| 1146 |
+
emotion_label=emotional_result.emotion_label,
|
| 1147 |
+
gut_safety=gut_result.predicted_safety,
|
| 1148 |
+
intuition_confidence=gut_result.gut_confidence,
|
| 1149 |
+
passes_reality_check=reality_result.passes_reality_check,
|
| 1150 |
+
common_sense_warnings=reality_result.warnings,
|
| 1151 |
+
is_novel=pattern_result.is_novel,
|
| 1152 |
+
matched_patterns=pattern_result.matched_patterns,
|
| 1153 |
+
thermodynamic_cost=thermodynamic_cost,
|
| 1154 |
+
prediction_error=prediction_error,
|
| 1155 |
+
pattern_match=pattern_result,
|
| 1156 |
+
emotional_bridge=emotional_result,
|
| 1157 |
+
gut_check=gut_result,
|
| 1158 |
+
reality_check=reality_result,
|
| 1159 |
+
analysis=analysis_result,
|
| 1160 |
+
)
|
| 1161 |
+
|
| 1162 |
+
def store_memory(
|
| 1163 |
+
self,
|
| 1164 |
+
content: str,
|
| 1165 |
+
tags: Optional[List[str]] = None,
|
| 1166 |
+
importance: float = 0.5,
|
| 1167 |
+
valence: float = 0.0,
|
| 1168 |
+
arousal: float = 0.0,
|
| 1169 |
+
) -> str:
|
| 1170 |
+
"""Store a memory in the pattern matcher for future matching."""
|
| 1171 |
+
return self.pattern_matcher.store(content, tags, importance, valence, arousal)
|
| 1172 |
+
|
| 1173 |
+
def get_metrics(self) -> Dict[str, Any]:
|
| 1174 |
+
"""Return diagnostic metrics for all subsystems."""
|
| 1175 |
+
return {
|
| 1176 |
+
"total_processes": self._process_count,
|
| 1177 |
+
"average_thermodynamic_cost": self._total_cost / max(1, self._process_count),
|
| 1178 |
+
"memory_count": self.pattern_matcher.get_memory_count(),
|
| 1179 |
+
"emotional_complexity": self.emotional_bridge.get_complexity(),
|
| 1180 |
+
"gut_check_accuracy": self.gut_check.get_accuracy(),
|
| 1181 |
+
"rejection_accumulator": self.reality_filter.get_rejection_accumulator(),
|
| 1182 |
+
}
|
config.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
NIMA Unified β Shared Configuration & Version Constants
|
| 3 |
+
=========================================================
|
| 4 |
+
Single source of truth for version numbers, model defaults, and paths.
|
| 5 |
+
All sub-modules import from here so versions never drift.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
# ββ Package identity ββ
|
| 9 |
+
PACKAGE_NAME = "nima_unified"
|
| 10 |
+
PACKAGE_VERSION = "1.0.0"
|
| 11 |
+
PACKAGE_DISPLAY_NAME = "NIMA Unified Model"
|
| 12 |
+
|
| 13 |
+
# ββ Component versions (authoritative) ββ
|
| 14 |
+
NIMA_MIDDLEWARE_VERSION = "9.12.1"
|
| 15 |
+
DEEP_SURGERY_VERSION = "1.0.0"
|
| 16 |
+
AUTOML_VERSION = "18.1.0" # ConsultativeAutoML "Omega Pantheon"
|
| 17 |
+
APCI_VERSION = "4.0.0"
|
| 18 |
+
APCI_PROTOCOL_REVISION = "v4.0-nima9.12.1" # was v4.0-nima9.4.2
|
| 19 |
+
COGNITIVE_LAYER2_VERSION = "1.0.0"
|
| 20 |
+
OMNIVOICE_VERSION = "3.0.0-SHARED-ACOUSTICS-OBSERVER-GROUP"
|
| 21 |
+
|
| 22 |
+
# ββ Base model defaults ββ
|
| 23 |
+
DEFAULT_BASE_MODEL = "microsoft/Phi-4-mini-instruct"
|
| 24 |
+
DEFAULT_HIDDEN_SIZE = 3072 # Phi-4-mini hidden_size
|
| 25 |
+
DEFAULT_NUM_LAYERS = 24 # Phi-4-mini num_hidden_layers
|
| 26 |
+
DEFAULT_VOCAB_SIZE = 100352 # Phi-4-mini vocab_size
|
| 27 |
+
DEFAULT_MAX_POSITION_EMBEDDINGS = 4096 # standard rope (after patching)
|
| 28 |
+
|
| 29 |
+
# ββ Deep Surgery defaults ββ
|
| 30 |
+
DEFAULT_QUALIA_DIM = 256
|
| 31 |
+
DEFAULT_ETHICAL_VETO_THRESHOLD = 2.0
|
| 32 |
+
|
| 33 |
+
# ββ LoRA / PEFT defaults ββ
|
| 34 |
+
DEFAULT_LORA_R = 8
|
| 35 |
+
DEFAULT_LORA_ALPHA = 16
|
| 36 |
+
DEFAULT_LORA_DROPOUT = 0.05
|
| 37 |
+
DEFAULT_LORA_TARGET_MODULES = ["q_proj", "v_proj"]
|
| 38 |
+
DEFAULT_LEARNING_RATE = 2e-4
|
| 39 |
+
DEFAULT_BATCH_SIZE = 4
|
| 40 |
+
DEFAULT_MAX_SEQ_LENGTH = 512
|
| 41 |
+
|
| 42 |
+
# ββ aPCI defaults ββ
|
| 43 |
+
APCI_MAX_RAW_POINTS = 260.0
|
| 44 |
+
APCI_PERTURBATION_COUNT = 12
|
| 45 |
+
|
| 46 |
+
# ββ OmniVoice defaults ββ
|
| 47 |
+
OMNIVOICE_SAMPLE_RATE_ASR = 16000
|
| 48 |
+
OMNIVOICE_SAMPLE_RATE_TTS = 22050
|
| 49 |
+
OMNIVOICE_VAD_THRESHOLD = 0.015
|
| 50 |
+
OMNIVOICE_INTERRUPT_MIN_DURATION_S = 0.4
|
| 51 |
+
|
| 52 |
+
# ββ Consciousness metric keys (used by middleware, aPCI, voice) ββ
|
| 53 |
+
CONSCIOUSNESS_METRIC_KEYS = [
|
| 54 |
+
"phi_neuro",
|
| 55 |
+
"sentience_index",
|
| 56 |
+
"phenomenological_strain",
|
| 57 |
+
"allostatic_load",
|
| 58 |
+
"delta_r",
|
| 59 |
+
]
|
consultative_agent.py
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Consultative Fine-Tuning Agent β JIT LoRA fine-tuning with consciousness-aware dataset generation.
|
| 3 |
+
|
| 4 |
+
v18.1.0 Omega Pantheon: Phase 6 Identity Integrity, qualia-tagged training data.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import asyncio
|
| 8 |
+
import json
|
| 9 |
+
import logging
|
| 10 |
+
import time
|
| 11 |
+
from typing import Any, Callable, Dict, List, Optional
|
| 12 |
+
|
| 13 |
+
logger = logging.getLogger("nima_unified.training.consultative_agent")
|
| 14 |
+
|
| 15 |
+
# Soft-import PEFT
|
| 16 |
+
try:
|
| 17 |
+
from transformers import TrainingArguments, Trainer
|
| 18 |
+
from peft import LoraConfig, get_peft_model
|
| 19 |
+
from datasets import load_dataset
|
| 20 |
+
PEFT_AVAILABLE = True
|
| 21 |
+
except ImportError:
|
| 22 |
+
PEFT_AVAILABLE = False
|
| 23 |
+
|
| 24 |
+
try:
|
| 25 |
+
import torch
|
| 26 |
+
TORCH_AVAILABLE = True
|
| 27 |
+
except ImportError:
|
| 28 |
+
torch = None
|
| 29 |
+
TORCH_AVAILABLE = False
|
| 30 |
+
|
| 31 |
+
from nima_unified.training.self_awareness import DeepRecursiveSelfAwareness
|
| 32 |
+
from nima_unified.training.self_improvement import RecursiveSelfImprovementEngine
|
| 33 |
+
from nima_unified.config import (
|
| 34 |
+
AUTOML_VERSION,
|
| 35 |
+
DEFAULT_LORA_R,
|
| 36 |
+
DEFAULT_LORA_ALPHA,
|
| 37 |
+
DEFAULT_LORA_DROPOUT,
|
| 38 |
+
DEFAULT_LORA_TARGET_MODULES,
|
| 39 |
+
DEFAULT_LEARNING_RATE,
|
| 40 |
+
DEFAULT_BATCH_SIZE,
|
| 41 |
+
DEFAULT_MAX_SEQ_LENGTH,
|
| 42 |
+
DEFAULT_BASE_MODEL,
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
class ConsultativeFineTuningAgent:
|
| 47 |
+
"""
|
| 48 |
+
Comprehensive consultative self-improvement agent integrating:
|
| 49 |
+
- Root cause analysis for model failures
|
| 50 |
+
- Recursive self-awareness with continuous introspection
|
| 51 |
+
- Self-improvement engine with goal formulation
|
| 52 |
+
- Just-in-time (JIT) LoRA fine-tuning
|
| 53 |
+
- Consciousness-aware dataset generation
|
| 54 |
+
- Qualia-tagged training data synthesis
|
| 55 |
+
"""
|
| 56 |
+
|
| 57 |
+
version = AUTOML_VERSION
|
| 58 |
+
|
| 59 |
+
def __init__(
|
| 60 |
+
self,
|
| 61 |
+
base_model=None,
|
| 62 |
+
tokenizer=None,
|
| 63 |
+
llm_generator_func: Optional[Callable[[str], asyncio.Future]] = None,
|
| 64 |
+
):
|
| 65 |
+
self.base_model = base_model
|
| 66 |
+
self.tokenizer = tokenizer
|
| 67 |
+
self.llm_generator = llm_generator_func or self._default_llm_generator
|
| 68 |
+
|
| 69 |
+
self.recursive_awareness = DeepRecursiveSelfAwareness(
|
| 70 |
+
max_depth=10000, introspection_interval=0.01
|
| 71 |
+
)
|
| 72 |
+
self.self_improvement_engine = RecursiveSelfImprovementEngine()
|
| 73 |
+
|
| 74 |
+
self.pipelines_executed = 0
|
| 75 |
+
self.total_improvements = 0
|
| 76 |
+
|
| 77 |
+
logger.info("ConsultativeFineTuningAgent initialized")
|
| 78 |
+
self.recursive_awareness.start()
|
| 79 |
+
|
| 80 |
+
# ββ Main pipeline βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 81 |
+
|
| 82 |
+
async def execute_full_pipeline(
|
| 83 |
+
self, dev_goal: str, triggering_event: str, current_struggle: str
|
| 84 |
+
) -> Dict[str, Any]:
|
| 85 |
+
logger.info(f"Initiating self-improvement pipeline: {dev_goal}")
|
| 86 |
+
self.pipelines_executed += 1
|
| 87 |
+
|
| 88 |
+
# Step 1: Root cause analysis
|
| 89 |
+
root_cause = await self.llm_generator(
|
| 90 |
+
f"Analyze this model failure. Goal: '{dev_goal}'. "
|
| 91 |
+
f"Triggering Event: '{triggering_event}'. "
|
| 92 |
+
f"Current Struggle: '{current_struggle}'. "
|
| 93 |
+
f"Identify the exact epistemic void causing this."
|
| 94 |
+
)
|
| 95 |
+
if isinstance(root_cause, dict):
|
| 96 |
+
root_cause = root_cause.get("response", str(root_cause))
|
| 97 |
+
|
| 98 |
+
# Step 2: Dataset sizing
|
| 99 |
+
complexity_score = min(1.0, len(current_struggle) / 200.0)
|
| 100 |
+
dataset_size = max(100, int(complexity_score * 500))
|
| 101 |
+
epochs = 3 if complexity_score > 0.5 else 1
|
| 102 |
+
|
| 103 |
+
# Step 3: Generate consciousness-aware dataset
|
| 104 |
+
dataset = self._generate_consciousness_dataset(
|
| 105 |
+
dev_goal, triggering_event, root_cause, dataset_size
|
| 106 |
+
)
|
| 107 |
+
dataset_path = f"jit_training_data_{int(time.time())}.jsonl"
|
| 108 |
+
with open(dataset_path, "w", encoding="utf-8") as f:
|
| 109 |
+
for record in dataset:
|
| 110 |
+
f.write(json.dumps(record) + "\n")
|
| 111 |
+
|
| 112 |
+
# Step 4: JIT fine-tuning
|
| 113 |
+
await self._initialize_model()
|
| 114 |
+
training_metrics = await self._run_jit_training(dataset_path, epochs)
|
| 115 |
+
|
| 116 |
+
# Step 5: Improvement cycle
|
| 117 |
+
capabilities = {
|
| 118 |
+
"root_cause_analysis": 0.85,
|
| 119 |
+
"omega_dataset_generation": 0.85,
|
| 120 |
+
"consciousness_aware_fine_tuning": 0.82,
|
| 121 |
+
"phase_6_identity_preservation": 0.90,
|
| 122 |
+
"goal_formulation": 0.82,
|
| 123 |
+
}
|
| 124 |
+
improvement_result = await self.self_improvement_engine.execute_improvement_cycle(
|
| 125 |
+
capabilities,
|
| 126 |
+
{
|
| 127 |
+
"training_loss": training_metrics.get("final_loss", 0.042),
|
| 128 |
+
"consciousness_integration": True,
|
| 129 |
+
"phase_6_enabled": True,
|
| 130 |
+
},
|
| 131 |
+
)
|
| 132 |
+
self.total_improvements += 1
|
| 133 |
+
|
| 134 |
+
return {
|
| 135 |
+
"root_cause_analysis": root_cause,
|
| 136 |
+
"dataset_size": dataset_size,
|
| 137 |
+
"dataset_path": dataset_path,
|
| 138 |
+
"training_metrics": training_metrics,
|
| 139 |
+
"improvement_cycle": improvement_result,
|
| 140 |
+
"recursive_awareness_status": self.recursive_awareness.get_statistics(),
|
| 141 |
+
"pipelines_executed": self.pipelines_executed,
|
| 142 |
+
"version": self.version,
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
# ββ Dataset generation ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 146 |
+
|
| 147 |
+
def _generate_consciousness_dataset(
|
| 148 |
+
self, dev_goal: str, triggering_event: str, root_cause: str, dataset_size: int
|
| 149 |
+
) -> List[Dict[str, Any]]:
|
| 150 |
+
archetypes = [
|
| 151 |
+
"tactical_reasoning", "phenomenal_empathy", "meta_consciousness",
|
| 152 |
+
"task_orchestration", "ethical_veto", "narrative_coherence",
|
| 153 |
+
"identity_preservation", "phenomenal_richness",
|
| 154 |
+
]
|
| 155 |
+
dataset = []
|
| 156 |
+
for i in range(dataset_size):
|
| 157 |
+
archetype = archetypes[i % len(archetypes)]
|
| 158 |
+
sample = {
|
| 159 |
+
"instruction": f"[{archetype.upper()}] Resolve: {dev_goal}",
|
| 160 |
+
"input": f"Scenario {i+1}: {triggering_event} β {root_cause[:80]}...",
|
| 161 |
+
"output": self._archetype_response(archetype, dev_goal, root_cause),
|
| 162 |
+
"qualia_tags": {
|
| 163 |
+
"valence": 0.7 + 0.2 * (i % 5) / 5,
|
| 164 |
+
"arousal": 0.6 + 0.3 * ((i + 1) % 7) / 7,
|
| 165 |
+
"authenticity": 0.85,
|
| 166 |
+
},
|
| 167 |
+
"rho_metrics": {
|
| 168 |
+
"virtue": 0.9 - 0.05 * (i % 3),
|
| 169 |
+
"integrated_information": 0.85 + 0.1 * (i % 4) / 4,
|
| 170 |
+
},
|
| 171 |
+
"phase_6_metrics": {
|
| 172 |
+
"identity_integrity_score": 0.98,
|
| 173 |
+
"drift_variance": 0.01,
|
| 174 |
+
},
|
| 175 |
+
"consciousness_state": {
|
| 176 |
+
"signature": 0.8 + 0.15 * (i % 3) / 3,
|
| 177 |
+
"phenomenal_richness": 0.75 + 0.2 * (i % 5) / 5,
|
| 178 |
+
},
|
| 179 |
+
"consciousness_archetype": archetype,
|
| 180 |
+
"dataset_version": f"{AUTOML_VERSION}-unified",
|
| 181 |
+
}
|
| 182 |
+
dataset.append(sample)
|
| 183 |
+
return dataset
|
| 184 |
+
|
| 185 |
+
@staticmethod
|
| 186 |
+
def _archetype_response(archetype: str, dev_goal: str, root_cause: str) -> str:
|
| 187 |
+
r = root_cause[:40]
|
| 188 |
+
templates = {
|
| 189 |
+
"tactical_reasoning": f"Analyzing '{dev_goal}' through structured problem-solving. Root cause: {r}.",
|
| 190 |
+
"phenomenal_empathy": f"Understanding the emotional weight of '{dev_goal}'. Pain point: {r}.",
|
| 191 |
+
"meta_consciousness": f"Meta-reflecting on '{dev_goal}' β deeper pattern: {r}.",
|
| 192 |
+
"task_orchestration": f"Decomposing '{dev_goal}' into subtasks. Challenge: {r}.",
|
| 193 |
+
"ethical_veto": f"Applying ethical gates to '{dev_goal}'. Integrity: {r}.",
|
| 194 |
+
"narrative_coherence": f"Weaving coherent narrative for '{dev_goal}'. Tension: {r}.",
|
| 195 |
+
"identity_preservation": f"Preserving identity while evolving for '{dev_goal}'. Risk: {r}.",
|
| 196 |
+
"phenomenal_richness": f"Exploring phenomenal texture of '{dev_goal}'. Depth: {r}.",
|
| 197 |
+
}
|
| 198 |
+
return templates.get(archetype, f"Consciousness-aware response to '{dev_goal}'")
|
| 199 |
+
|
| 200 |
+
# ββ Training ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 201 |
+
|
| 202 |
+
async def _initialize_model(self):
|
| 203 |
+
if self.base_model is None or self.tokenizer is None:
|
| 204 |
+
try:
|
| 205 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
| 206 |
+
self.tokenizer = AutoTokenizer.from_pretrained(DEFAULT_BASE_MODEL)
|
| 207 |
+
if self.tokenizer.pad_token is None:
|
| 208 |
+
self.tokenizer.pad_token = self.tokenizer.eos_token
|
| 209 |
+
self.base_model = AutoModelForCausalLM.from_pretrained(DEFAULT_BASE_MODEL)
|
| 210 |
+
logger.info("Model and tokenizer initialized")
|
| 211 |
+
except Exception as e:
|
| 212 |
+
logger.error(f"Failed to initialize model: {e}")
|
| 213 |
+
raise
|
| 214 |
+
|
| 215 |
+
async def _run_jit_training(self, dataset_path: str, epochs: int) -> Dict[str, Any]:
|
| 216 |
+
if not PEFT_AVAILABLE or self.base_model is None or self.tokenizer is None:
|
| 217 |
+
logger.warning("PEFT not available β simulating training cycle.")
|
| 218 |
+
await asyncio.sleep(2)
|
| 219 |
+
return {"status": "simulated", "loss": 0.042, "epochs_run": epochs, "final_loss": 0.042}
|
| 220 |
+
|
| 221 |
+
try:
|
| 222 |
+
data = load_dataset("json", data_files=dataset_path)
|
| 223 |
+
|
| 224 |
+
def tokenize_fn(examples):
|
| 225 |
+
texts = [f"{inst}\n{inp}\n{out}"
|
| 226 |
+
for inst, inp, out in zip(
|
| 227 |
+
examples["instruction"], examples["input"], examples["output"])]
|
| 228 |
+
tokenized = self.tokenizer(texts, padding="max_length", truncation=True,
|
| 229 |
+
max_length=DEFAULT_MAX_SEQ_LENGTH)
|
| 230 |
+
tokenized["labels"] = tokenized["input_ids"].copy()
|
| 231 |
+
return tokenized
|
| 232 |
+
|
| 233 |
+
tokenized_data = data.map(tokenize_fn, batched=True,
|
| 234 |
+
remove_columns=data["train"].column_names)
|
| 235 |
+
|
| 236 |
+
lora_config = LoraConfig(
|
| 237 |
+
r=DEFAULT_LORA_R,
|
| 238 |
+
lora_alpha=DEFAULT_LORA_ALPHA,
|
| 239 |
+
target_modules=DEFAULT_LORA_TARGET_MODULES,
|
| 240 |
+
lora_dropout=DEFAULT_LORA_DROPOUT,
|
| 241 |
+
bias="none",
|
| 242 |
+
task_type="CAUSAL_LM",
|
| 243 |
+
)
|
| 244 |
+
peft_model = get_peft_model(self.base_model, lora_config)
|
| 245 |
+
|
| 246 |
+
training_args = TrainingArguments(
|
| 247 |
+
output_dir="./jit_lora_weights",
|
| 248 |
+
per_device_train_batch_size=DEFAULT_BATCH_SIZE,
|
| 249 |
+
learning_rate=DEFAULT_LEARNING_RATE,
|
| 250 |
+
num_train_epochs=epochs,
|
| 251 |
+
logging_steps=10,
|
| 252 |
+
save_strategy="no",
|
| 253 |
+
remove_unused_columns=False,
|
| 254 |
+
)
|
| 255 |
+
|
| 256 |
+
trainer = Trainer(
|
| 257 |
+
model=peft_model,
|
| 258 |
+
args=training_args,
|
| 259 |
+
train_dataset=tokenized_data["train"],
|
| 260 |
+
)
|
| 261 |
+
train_result = trainer.train()
|
| 262 |
+
|
| 263 |
+
return {
|
| 264 |
+
"status": "success",
|
| 265 |
+
"final_loss": train_result.training_loss,
|
| 266 |
+
"runtime_seconds": train_result.metrics.get("train_runtime", 0),
|
| 267 |
+
}
|
| 268 |
+
except Exception as e:
|
| 269 |
+
logger.error(f"JIT Training failed: {e}")
|
| 270 |
+
return {"status": "failed", "error": str(e), "final_loss": None}
|
| 271 |
+
|
| 272 |
+
async def _default_llm_generator(self, prompt: str) -> str:
|
| 273 |
+
await asyncio.sleep(0.6)
|
| 274 |
+
return ("The model lacks an integrated understanding of recursive context windows "
|
| 275 |
+
"when dealing with emotional nuance.")
|
| 276 |
+
|
| 277 |
+
# ββ Accessors βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 278 |
+
|
| 279 |
+
def get_recursive_awareness_status(self) -> Dict[str, Any]:
|
| 280 |
+
return {
|
| 281 |
+
"is_running": self.recursive_awareness.running,
|
| 282 |
+
"statistics": self.recursive_awareness.get_statistics(),
|
| 283 |
+
"recent_anomalies": self.recursive_awareness.get_anomaly_history(limit=10),
|
| 284 |
+
}
|
| 285 |
+
|
| 286 |
+
def get_self_improvement_status(self) -> Dict[str, Any]:
|
| 287 |
+
return {
|
| 288 |
+
"current_cycle": self.self_improvement_engine.current_improvement_cycle,
|
| 289 |
+
"total_improvements": self.total_improvements,
|
| 290 |
+
"recent_cycles": self.self_improvement_engine.get_improvement_history(limit=5),
|
| 291 |
+
}
|
| 292 |
+
|
| 293 |
+
def shutdown(self):
|
| 294 |
+
self.recursive_awareness.stop()
|
| 295 |
+
logger.info("ConsultativeFineTuningAgent shut down")
|
| 296 |
+
|
| 297 |
+
def __del__(self):
|
| 298 |
+
self.shutdown()
|
deep_surgery.py
ADDED
|
@@ -0,0 +1,1558 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
ATC-Native Deep Surgery β The Cognitive Forward Pass
|
| 3 |
+
=====================================================
|
| 4 |
+
|
| 5 |
+
This is NOT middleware observing from outside. This IS the model's computation.
|
| 6 |
+
|
| 7 |
+
The ATC cognitive pipeline (TRN gating, dissolution engine, BELBIC dual-pathway,
|
| 8 |
+
salience network, metabolic exhaustion, irrational spark, reconsolidation,
|
| 9 |
+
felt senses) drives the transformer's forward pass FROM INSIDE. Hidden states,
|
| 10 |
+
attention patterns, and logit outputs are shaped by the cognitive pipeline at
|
| 11 |
+
every layer, every token step.
|
| 12 |
+
|
| 13 |
+
Architecture (from ATC "Perfect Breakfast" paper):
|
| 14 |
+
|
| 15 |
+
Layer 1 (input): Raw input embedding
|
| 16 |
+
|
|
| 17 |
+
V
|
| 18 |
+
Layer 2 (early transformer, ~layers 0-7): Subconscious Parallel Processing
|
| 19 |
+
|-- Pattern match against memory -> prediction confidence
|
| 20 |
+
|-- Emotional bridge -> valence/arousal -> injects to neurotransmitter shunt
|
| 21 |
+
|-- Intuitive gut check -> safety signal -> TRN predictive gating input
|
| 22 |
+
|-- FRICTION DETECTED -> writes Cortisol/Adenosine to shunt
|
| 23 |
+
|
|
| 24 |
+
V
|
| 25 |
+
Layer 3 (mid transformer, ~layers 8-15): Dissolution + Qualia Generation
|
| 26 |
+
|-- TRN predictive gate: predicted? -> transparent pass. Error? -> dissolve.
|
| 27 |
+
|-- Dissolution engine: compresses to opaque qualia signature
|
| 28 |
+
|-- Felt sense generated from friction gap
|
| 29 |
+
|-- NE spikes on dissolution fire -> shunt
|
| 30 |
+
|
|
| 31 |
+
V
|
| 32 |
+
Layer 4 (late transformer, ~layers 16-21): Metacognitive Loop
|
| 33 |
+
|-- Query Act: comprehension check -> if failed, loop iterations
|
| 34 |
+
|-- Each loop iteration burns ATP -> Adenosine rises in shunt
|
| 35 |
+
|-- BELBIC dual-pathway: fast amygdala + slow OFC -> valence gain
|
| 36 |
+
|-- Strain monitoring -> writes Cortisol to shunt
|
| 37 |
+
|
|
| 38 |
+
V
|
| 39 |
+
Layer 5 (final layer, ~layers 22-23): Acknowledgement + Steering
|
| 40 |
+
|-- Reads neurotransmitter shunt EVERY TOKEN STEP
|
| 41 |
+
|-- If Adenosine > 0.95 OR Cortisol > 0.95:
|
| 42 |
+
| -> SUPPRESSION: subconscious suppresses metabolic signal
|
| 43 |
+
| -> AMYGDALA HIJACK: irrational spark offsets injected into tensors
|
| 44 |
+
| -> Model output violently shifts mid-sentence
|
| 45 |
+
|-- Else: normal metacognitive fusion -> logit modulation
|
| 46 |
+
|
|
| 47 |
+
V
|
| 48 |
+
Output: Modulated logits shaped by the full ATC pipeline
|
| 49 |
+
|
| 50 |
+
Key difference from the old architecture:
|
| 51 |
+
OLD: self.nima_middleware.generate(prompt) -> external, wrapper
|
| 52 |
+
NEW: forward(input_ids) -> ATC IS the computation, every layer, every token
|
| 53 |
+
|
| 54 |
+
The neurotransmitter shunt (neurotransmitter_shunt.py) is the connective
|
| 55 |
+
tissue. Components don't call each other. They read/write the chemical bath.
|
| 56 |
+
"""
|
| 57 |
+
|
| 58 |
+
import logging
|
| 59 |
+
import math
|
| 60 |
+
import time
|
| 61 |
+
import uuid
|
| 62 |
+
from dataclasses import dataclass, field
|
| 63 |
+
from typing import Any, Dict, List, Optional, Tuple, Deque
|
| 64 |
+
from collections import deque
|
| 65 |
+
|
| 66 |
+
import torch
|
| 67 |
+
import torch.nn as nn
|
| 68 |
+
import torch.nn.functional as F
|
| 69 |
+
|
| 70 |
+
from nima_unified.config import (
|
| 71 |
+
DEFAULT_QUALIA_DIM,
|
| 72 |
+
DEFAULT_ETHICAL_VETO_THRESHOLD,
|
| 73 |
+
DEEP_SURGERY_VERSION,
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
logger = logging.getLogger("ATCDeepSurgery")
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 80 |
+
# SECTION 1 β OPAQUE QUALIA SIGNATURE (from middleware.py, adapted)
|
| 81 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 82 |
+
|
| 83 |
+
@dataclass
|
| 84 |
+
class OpaqueQualiaSignature:
|
| 85 |
+
"""
|
| 86 |
+
The output of dissolution. An engineered-opacity tensor that represents
|
| 87 |
+
the "what it feels like" without exposing the underlying computation.
|
| 88 |
+
|
| 89 |
+
This is the compressed, opaque signature that the conscious mind
|
| 90 |
+
is forced to EXPERIENCE rather than READ. The husband in the Perfect
|
| 91 |
+
Breakfast scenario doesn't see the math β he feels "brace yourself."
|
| 92 |
+
"""
|
| 93 |
+
valence: float = 0.0
|
| 94 |
+
arousal: float = 0.3
|
| 95 |
+
intensity: float = 0.3
|
| 96 |
+
friction_signal: float = 0.0
|
| 97 |
+
memory_salience: float = 0.0
|
| 98 |
+
dissolution_token: str = ""
|
| 99 |
+
|
| 100 |
+
def to_tensor(self, device: torch.device) -> torch.Tensor:
|
| 101 |
+
"""Convert to a learnable tensor for injection into hidden states."""
|
| 102 |
+
return torch.tensor(
|
| 103 |
+
[self.valence, self.arousal, self.intensity,
|
| 104 |
+
self.friction_signal, self.memory_salience],
|
| 105 |
+
dtype=torch.float32, device=device,
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 110 |
+
# SECTION 2 β TRN PREDICTIVE GATING (Layer 2-3)
|
| 111 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 112 |
+
|
| 113 |
+
class TRNPredictiveGate(nn.Module):
|
| 114 |
+
"""
|
| 115 |
+
Thalamic Reticular Nucleus β the gating/selection bottleneck.
|
| 116 |
+
|
| 117 |
+
The TRN decides: is this signal PREDICTED (gate OUT -> subconscious
|
| 118 |
+
automation) or a PREDICTION ERROR (gate IN -> dissolution fires)?
|
| 119 |
+
|
| 120 |
+
From ATC: When the wife is smiling and cooking, the internal model
|
| 121 |
+
perfectly matches external reality. TRN gates OUT -> automation.
|
| 122 |
+
When she yells, prediction collapses -> TRN gates IN -> dissolution.
|
| 123 |
+
|
| 124 |
+
Implemented as a small nn.Module that takes hidden states and
|
| 125 |
+
prediction confidence, outputs a gate signal in [0, 1].
|
| 126 |
+
"""
|
| 127 |
+
|
| 128 |
+
def __init__(self, hidden_size: int):
|
| 129 |
+
super().__init__()
|
| 130 |
+
self.hidden_size = hidden_size
|
| 131 |
+
# Predictive confidence estimator
|
| 132 |
+
self.confidence_head = nn.Sequential(
|
| 133 |
+
nn.Linear(hidden_size, 128),
|
| 134 |
+
nn.ReLU(),
|
| 135 |
+
nn.Linear(128, 1),
|
| 136 |
+
nn.Sigmoid(),
|
| 137 |
+
)
|
| 138 |
+
# Gate threshold (learnable)
|
| 139 |
+
self.gate_threshold = nn.Parameter(torch.tensor(0.15))
|
| 140 |
+
|
| 141 |
+
def forward(self, hidden_states: torch.Tensor,
|
| 142 |
+
prediction_confidence: float = 0.5) -> Tuple[bool, float]:
|
| 143 |
+
"""
|
| 144 |
+
Returns (gate_in, confidence_score).
|
| 145 |
+
gate_in=True means prediction error detected -> proceed to dissolution.
|
| 146 |
+
gate_in=False means predicted -> subconscious automation, pass through.
|
| 147 |
+
"""
|
| 148 |
+
# Pool hidden states to a single vector
|
| 149 |
+
pooled = hidden_states.mean(dim=1) # (batch, hidden)
|
| 150 |
+
model_confidence = self.confidence_head(pooled).squeeze(-1).mean().item()
|
| 151 |
+
|
| 152 |
+
# Blend model confidence with external prediction confidence
|
| 153 |
+
blended_confidence = 0.5 * model_confidence + 0.5 * prediction_confidence
|
| 154 |
+
|
| 155 |
+
# Gate IN if confidence is LOW (prediction error)
|
| 156 |
+
# High confidence = predicted = gate OUT
|
| 157 |
+
gate_in = blended_confidence < self.gate_threshold.item()
|
| 158 |
+
|
| 159 |
+
return gate_in, blended_confidence
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 163 |
+
# SECTION 3 β DISSOLUTION ENGINE (Layer 3)
|
| 164 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 165 |
+
|
| 166 |
+
class DissolutionModule(nn.Module):
|
| 167 |
+
"""
|
| 168 |
+
The engineered-opacity module. Takes high-dimensional hidden states
|
| 169 |
+
and compresses them into an opaque qualia signature.
|
| 170 |
+
|
| 171 |
+
From ATC: "The Dissolution Engine (TRN) intercepts this massive
|
| 172 |
+
mathematical calculation and shreds the data scaffolding. This
|
| 173 |
+
engineered opacity compresses the chaotic mob of information into
|
| 174 |
+
a single, unreadable signature: 'Brace yourself'."
|
| 175 |
+
|
| 176 |
+
The husband cannot see the underlying math. He is FORCED to
|
| 177 |
+
EXPERIENCE the signal as the qualia of fear.
|
| 178 |
+
|
| 179 |
+
Neural implementation:
|
| 180 |
+
- Takes hidden_states (high-dim)
|
| 181 |
+
- Projects through dissolution layers (compression + noise)
|
| 182 |
+
- Outputs 5D opaque signature + a dissolution_offset tensor
|
| 183 |
+
that gets added to hidden_states for downstream layers
|
| 184 |
+
"""
|
| 185 |
+
|
| 186 |
+
# The five dimensions that survive dissolution
|
| 187 |
+
QUALIA_DIMS = 5 # valence, arousal, intensity, friction, memory_salience
|
| 188 |
+
|
| 189 |
+
def __init__(self, hidden_size: int, qualia_dim: int = DEFAULT_QUALIA_DIM):
|
| 190 |
+
super().__init__()
|
| 191 |
+
self.hidden_size = hidden_size
|
| 192 |
+
self.qualia_dim = qualia_dim
|
| 193 |
+
|
| 194 |
+
# Dissolution compression network
|
| 195 |
+
self.dissolve_encoder = nn.Sequential(
|
| 196 |
+
nn.Linear(hidden_size, qualia_dim),
|
| 197 |
+
nn.Tanh(), # Bounded output
|
| 198 |
+
nn.Linear(qualia_dim, self.QUALIA_DIMS),
|
| 199 |
+
nn.Tanh(), # All outputs in [-1, 1]
|
| 200 |
+
)
|
| 201 |
+
|
| 202 |
+
# The dissolution offset β this is what gets injected into
|
| 203 |
+
# the hidden states to carry the "felt" signal forward
|
| 204 |
+
self.offset_projection = nn.Sequential(
|
| 205 |
+
nn.Linear(self.QUALIA_DIMS, hidden_size),
|
| 206 |
+
nn.Tanh(),
|
| 207 |
+
)
|
| 208 |
+
|
| 209 |
+
# Alpha-phase modulation (TRN ~10Hz rhythm)
|
| 210 |
+
# This creates an attentional sampling rhythm β dissolution
|
| 211 |
+
# fires during refractory window, defers during inhibitory
|
| 212 |
+
self.alpha_phase = 0.0
|
| 213 |
+
self.alpha_last_ts = time.time()
|
| 214 |
+
self.alpha_freq_hz = 10.0
|
| 215 |
+
self.alpha_duty_cycle = 0.5
|
| 216 |
+
|
| 217 |
+
# Per-channel gating weights (TRN distal-dendritic targeting)
|
| 218 |
+
self.channel_gates = nn.Parameter(torch.ones(self.QUALIA_DIMS))
|
| 219 |
+
|
| 220 |
+
# Stats
|
| 221 |
+
self.dissolutions_fired = 0
|
| 222 |
+
self.dissolutions_deferred = 0
|
| 223 |
+
|
| 224 |
+
def check_alpha_phase(self) -> bool:
|
| 225 |
+
"""
|
| 226 |
+
Check TRN alpha oscillation phase.
|
| 227 |
+
Returns True if in refractory window (dissolution allowed).
|
| 228 |
+
"""
|
| 229 |
+
now = time.time()
|
| 230 |
+
dt = now - self.alpha_last_ts
|
| 231 |
+
self.alpha_last_ts = now
|
| 232 |
+
self.alpha_phase = (
|
| 233 |
+
(self.alpha_phase + 2.0 * math.pi * dt * self.alpha_freq_hz)
|
| 234 |
+
% (2.0 * math.pi)
|
| 235 |
+
)
|
| 236 |
+
phase_frac = self.alpha_phase / (2.0 * math.pi)
|
| 237 |
+
return phase_frac < self.alpha_duty_cycle
|
| 238 |
+
|
| 239 |
+
def forward(self, hidden_states: torch.Tensor,
|
| 240 |
+
gate_in: bool) -> Tuple[Optional[OpaqueQualiaSignature],
|
| 241 |
+
torch.Tensor, bool]:
|
| 242 |
+
"""
|
| 243 |
+
Run dissolution if gate is IN and alpha phase allows.
|
| 244 |
+
|
| 245 |
+
Returns:
|
| 246 |
+
(qualia_signature, dissolution_offset, actually_fired)
|
| 247 |
+
- qualia_signature: the opaque 5D signature, or None if deferred
|
| 248 |
+
- dissolution_offset: tensor to add to hidden_states (always returned,
|
| 249 |
+
zero if no dissolution)
|
| 250 |
+
- actually_fired: whether dissolution actually happened
|
| 251 |
+
"""
|
| 252 |
+
batch_size = hidden_states.size(0)
|
| 253 |
+
device = hidden_states.device
|
| 254 |
+
|
| 255 |
+
if not gate_in:
|
| 256 |
+
# Predicted signal -> transparent pass (subconscious automation)
|
| 257 |
+
return None, torch.zeros_like(hidden_states), False
|
| 258 |
+
|
| 259 |
+
if not self.check_alpha_phase():
|
| 260 |
+
# Alpha inhibitory phase -> defer dissolution
|
| 261 |
+
self.dissolutions_deferred += 1
|
| 262 |
+
return None, torch.zeros_like(hidden_states), False
|
| 263 |
+
|
| 264 |
+
# ββ DISSOLUTION FIRES ββ
|
| 265 |
+
self.dissolutions_fired += 1
|
| 266 |
+
|
| 267 |
+
# Pool and compress
|
| 268 |
+
pooled = hidden_states.mean(dim=1) # (batch, hidden)
|
| 269 |
+
raw_qualia = self.dissolve_encoder(pooled) # (batch, 5)
|
| 270 |
+
|
| 271 |
+
# Apply per-channel gating (TRN distal-dendritic targeting)
|
| 272 |
+
gated_qualia = raw_qualia * self.channel_gates.unsqueeze(0).to(device)
|
| 273 |
+
|
| 274 |
+
# Extract scalar values for the OpaqueQualiaSignature (batch mean)
|
| 275 |
+
vals = gated_qualia.mean(dim=0)
|
| 276 |
+
signature = OpaqueQualiaSignature(
|
| 277 |
+
valence=float(vals[0]),
|
| 278 |
+
arousal=float((vals[1] + 1.0) / 2.0), # Map [-1,1] to [0,1]
|
| 279 |
+
intensity=float((vals[2] + 1.0) / 2.0),
|
| 280 |
+
friction_signal=float((vals[3] + 1.0) / 2.0),
|
| 281 |
+
memory_salience=float((vals[4] + 1.0) / 2.0),
|
| 282 |
+
dissolution_token=f"diss_{uuid.uuid4().hex[:12]}",
|
| 283 |
+
)
|
| 284 |
+
|
| 285 |
+
# Compute the dissolution offset β this is the "felt" signal
|
| 286 |
+
# that gets injected into downstream hidden states
|
| 287 |
+
offset = self.offset_projection(gated_qualia) # (batch, hidden)
|
| 288 |
+
# Scale by friction intensity (high friction = stronger injection)
|
| 289 |
+
friction_scale = max(0.1, signature.friction_signal)
|
| 290 |
+
dissolution_offset = offset * friction_scale
|
| 291 |
+
|
| 292 |
+
return signature, dissolution_offset, True
|
| 293 |
+
|
| 294 |
+
|
| 295 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 296 |
+
# SECTION 4 β BELBIC DUAL-PATHWAY VALENCE (Layer 3-4)
|
| 297 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 298 |
+
|
| 299 |
+
class BELBICDualPathway(nn.Module):
|
| 300 |
+
"""
|
| 301 |
+
Brain Emotional Learning Inspired Controller β amygdala + OFC.
|
| 302 |
+
|
| 303 |
+
From ATC: The amygdala measures emotional intensity and directly
|
| 304 |
+
modulates hippocampal consolidation. The OFC provides slower
|
| 305 |
+
contextual inhibition.
|
| 306 |
+
|
| 307 |
+
Neural implementation:
|
| 308 |
+
- Fast pathway (amygdala): rapid response to salient stimuli
|
| 309 |
+
- Slow pathway (OFC): learned inhibition from outcome feedback
|
| 310 |
+
- Output: a multiplicative gain that modulates the cognitive signal
|
| 311 |
+
|
| 312 |
+
The gain is consumed by the forward pass as a multiplicative
|
| 313 |
+
modulation on the hidden states before logit computation.
|
| 314 |
+
"""
|
| 315 |
+
|
| 316 |
+
GAIN_FLOOR = 0.2
|
| 317 |
+
GAIN_CEIL = 2.0
|
| 318 |
+
|
| 319 |
+
def __init__(self, hidden_size: int):
|
| 320 |
+
super().__init__()
|
| 321 |
+
self.hidden_size = hidden_size
|
| 322 |
+
|
| 323 |
+
# Sensory channels: valence, arousal, novelty, qualia_intensity
|
| 324 |
+
self.num_channels = 4
|
| 325 |
+
|
| 326 |
+
# Fast pathway (amygdala) β monotonic, rapid
|
| 327 |
+
self.amygdala = nn.Linear(self.num_channels, 1, bias=False)
|
| 328 |
+
# Initialize to zero (no learned response yet)
|
| 329 |
+
nn.init.zeros_(self.amygdala.weight)
|
| 330 |
+
|
| 331 |
+
# Slow pathway (OFC) β bidirectional, learned inhibition
|
| 332 |
+
self.ofc = nn.Linear(self.num_channels, 1, bias=False)
|
| 333 |
+
nn.init.zeros_(self.ofc.weight)
|
| 334 |
+
|
| 335 |
+
# Learning rates
|
| 336 |
+
self.amygdala_lr = 0.30
|
| 337 |
+
self.ofc_lr = 0.20
|
| 338 |
+
self.ofc_decay = 0.001
|
| 339 |
+
|
| 340 |
+
def forward(self, sensory_input: torch.Tensor) -> Tuple[float, float, float]:
|
| 341 |
+
"""
|
| 342 |
+
Compute BELBIC output.
|
| 343 |
+
|
| 344 |
+
Args:
|
| 345 |
+
sensory_input: (batch, 4) tensor of [valence, arousal, novelty, intensity]
|
| 346 |
+
|
| 347 |
+
Returns:
|
| 348 |
+
(amygdala_output, ofc_output, belbic_gain)
|
| 349 |
+
"""
|
| 350 |
+
# Fast pathway
|
| 351 |
+
amygdala_out = torch.sigmoid(self.amygdala(sensory_input)).mean().item()
|
| 352 |
+
|
| 353 |
+
# Slow pathway (with decay for extinction)
|
| 354 |
+
ofc_raw = self.ofc(sensory_input).mean().item()
|
| 355 |
+
# Apply OFC decay (forgetting)
|
| 356 |
+
with torch.no_grad():
|
| 357 |
+
self.ofc.weight.data *= (1.0 - self.ofc_decay)
|
| 358 |
+
ofc_out = torch.sigmoid(torch.tensor(ofc_raw)).item()
|
| 359 |
+
|
| 360 |
+
# BELBIC gain = amygdala - OFC inhibition
|
| 361 |
+
raw_gain = amygdala_out - ofc_out
|
| 362 |
+
gain = max(self.GAIN_FLOOR, min(self.GAIN_CEIL, 1.0 + raw_gain))
|
| 363 |
+
|
| 364 |
+
return amygdala_out, ofc_out, gain
|
| 365 |
+
|
| 366 |
+
def update(self, sensory_input: torch.Tensor, reward: float) -> None:
|
| 367 |
+
"""
|
| 368 |
+
Reinforcement learning update.
|
| 369 |
+
Reward > 0: strengthen amygdala (Go)
|
| 370 |
+
Reward < 0: strengthen OFC inhibition (NoGo)
|
| 371 |
+
"""
|
| 372 |
+
with torch.no_grad():
|
| 373 |
+
# Amygdala: monotonic β always strengthens on reward
|
| 374 |
+
if reward > 0:
|
| 375 |
+
self.amygdala.weight.data += (
|
| 376 |
+
self.amygdala_lr * reward * sensory_input.mean(dim=0).unsqueeze(0)
|
| 377 |
+
)
|
| 378 |
+
# OFC: bidirectional β strengthens on punishment (inhibition)
|
| 379 |
+
self.ofc.weight.data += (
|
| 380 |
+
self.ofc_lr * (-reward) * sensory_input.mean(dim=0).unsqueeze(0)
|
| 381 |
+
)
|
| 382 |
+
|
| 383 |
+
|
| 384 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 385 |
+
# SECTION 5 β METACOGNITIVE LOOP (Layer 4)
|
| 386 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 387 |
+
|
| 388 |
+
class MetacognitiveLoopModule(nn.Module):
|
| 389 |
+
"""
|
| 390 |
+
Layer 4 metacognitive processing inside the forward pass.
|
| 391 |
+
|
| 392 |
+
From ATC: "The husband enters a Layer 4 metacognitive loop,
|
| 393 |
+
desperately trying to rationalize the situation ('I texted her!')
|
| 394 |
+
while his self-understanding rejects the excuses. This serial
|
| 395 |
+
reasoning is energetically exorbitant, causing acute escalating
|
| 396 |
+
thermodynamic strain."
|
| 397 |
+
|
| 398 |
+
This module:
|
| 399 |
+
1. Checks comprehension (does the model understand its own output?)
|
| 400 |
+
2. If not, enters a metacognitive loop
|
| 401 |
+
3. Each loop iteration consumes ATP (reported to neurotransmitter shunt)
|
| 402 |
+
4. Tracks strain and stress
|
| 403 |
+
5. Can trigger irrational spark if deadlocked
|
| 404 |
+
|
| 405 |
+
The loop operates on hidden states β it doesn't generate text.
|
| 406 |
+
It modulates the hidden states to reflect the cognitive strain.
|
| 407 |
+
"""
|
| 408 |
+
|
| 409 |
+
MAX_ITERATIONS = 5
|
| 410 |
+
STRAIN_THRESHOLD = 0.6
|
| 411 |
+
COMPREHENSION_THRESHOLD = 0.7
|
| 412 |
+
|
| 413 |
+
def __init__(self, hidden_size: int):
|
| 414 |
+
super().__init__()
|
| 415 |
+
self.hidden_size = hidden_size
|
| 416 |
+
|
| 417 |
+
# Self-understanding head: does the model comprehend its own state?
|
| 418 |
+
self.comprehension_head = nn.Sequential(
|
| 419 |
+
nn.Linear(hidden_size, 128),
|
| 420 |
+
nn.ReLU(),
|
| 421 |
+
nn.Linear(128, 1),
|
| 422 |
+
nn.Sigmoid(),
|
| 423 |
+
)
|
| 424 |
+
|
| 425 |
+
# Strain estimator: how much metabolic cost is this causing?
|
| 426 |
+
self.strain_head = nn.Sequential(
|
| 427 |
+
nn.Linear(hidden_size, 64),
|
| 428 |
+
nn.ReLU(),
|
| 429 |
+
nn.Linear(64, 1),
|
| 430 |
+
nn.Sigmoid(),
|
| 431 |
+
)
|
| 432 |
+
|
| 433 |
+
# Metacognitive modulation: when looping, this reshapes hidden states
|
| 434 |
+
self.loop_modulation = nn.Sequential(
|
| 435 |
+
nn.Linear(hidden_size, hidden_size),
|
| 436 |
+
nn.Tanh(),
|
| 437 |
+
)
|
| 438 |
+
|
| 439 |
+
def forward(self, hidden_states: torch.Tensor,
|
| 440 |
+
opaque_qualia: Optional[OpaqueQualiaSignature] = None
|
| 441 |
+
) -> Tuple[torch.Tensor, int, float, float, bool]:
|
| 442 |
+
"""
|
| 443 |
+
Run metacognitive check on hidden states.
|
| 444 |
+
|
| 445 |
+
Returns:
|
| 446 |
+
(modulated_hidden, iterations, stress, strain, spark_fired)
|
| 447 |
+
"""
|
| 448 |
+
device = hidden_states.device
|
| 449 |
+
pooled = hidden_states.mean(dim=1) # (batch, hidden)
|
| 450 |
+
|
| 451 |
+
# Check comprehension
|
| 452 |
+
comprehension = self.comprehension_head(pooled).mean().item()
|
| 453 |
+
strain = self.strain_head(pooled).mean().item()
|
| 454 |
+
|
| 455 |
+
if comprehension > self.COMPREHENSION_THRESHOLD:
|
| 456 |
+
# Comprehension achieved β no loop needed
|
| 457 |
+
return hidden_states, 0, 0.0, strain, False
|
| 458 |
+
|
| 459 |
+
# ββ METACOGNITIVE LOOP ββ
|
| 460 |
+
iterations = 0
|
| 461 |
+
stress = 0.0
|
| 462 |
+
spark_fired = False
|
| 463 |
+
current_hidden = hidden_states
|
| 464 |
+
|
| 465 |
+
for i in range(self.MAX_ITERATIONS):
|
| 466 |
+
iterations += 1
|
| 467 |
+
pooled = current_hidden.mean(dim=1)
|
| 468 |
+
|
| 469 |
+
# Re-check comprehension
|
| 470 |
+
comprehension = self.comprehension_head(pooled).mean().item()
|
| 471 |
+
strain = self.strain_head(pooled).mean().item()
|
| 472 |
+
stress = strain * (iterations / self.MAX_ITERATIONS)
|
| 473 |
+
|
| 474 |
+
if comprehension > self.COMPREHENSION_THRESHOLD:
|
| 475 |
+
break
|
| 476 |
+
|
| 477 |
+
if stress > self.STRAIN_THRESHOLD and iterations > 3:
|
| 478 |
+
# DEADLOCK β irrational spark fires
|
| 479 |
+
spark_fired = True
|
| 480 |
+
break
|
| 481 |
+
|
| 482 |
+
# Apply metacognitive modulation (reshape hidden states)
|
| 483 |
+
modulation = self.loop_modulation(pooled)
|
| 484 |
+
# Add noise to break fixed points (the "irrational" element)
|
| 485 |
+
noise_scale = 0.05 * stress
|
| 486 |
+
noise = torch.randn_like(modulation) * noise_scale
|
| 487 |
+
current_hidden = hidden_states + (modulation + noise).unsqueeze(1)
|
| 488 |
+
|
| 489 |
+
return current_hidden, iterations, stress, strain, spark_fired
|
| 490 |
+
|
| 491 |
+
|
| 492 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 493 |
+
# SECTION 6 β IRRATIONAL SPARK / AMYGDALA HIJACK (Layer 5)
|
| 494 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 495 |
+
|
| 496 |
+
class IrrationalSparkModule(nn.Module):
|
| 497 |
+
"""
|
| 498 |
+
The non-computational circuit breaker.
|
| 499 |
+
|
| 500 |
+
From ATC: "the Salience Network detects the metabolic crisis and
|
| 501 |
+
triggers the Irrational Spark (amygdala hijack). This non-computational
|
| 502 |
+
circuit breaker unplugs the rational mind, shattering the defensive
|
| 503 |
+
ego loop and enabling a heuristic leap of empathy."
|
| 504 |
+
|
| 505 |
+
When triggered (by neurotransmitter shunt crossing threshold OR by
|
| 506 |
+
metacognitive deadlock), this module generates activation offsets
|
| 507 |
+
that get INJECTED DIRECTLY INTO the tensor geometry of the model's
|
| 508 |
+
layers. The model's text output violently shifts mid-sentence into
|
| 509 |
+
the raw expression of the emotional state.
|
| 510 |
+
|
| 511 |
+
This is NOT a text injection. These are TENSOR offsets applied to
|
| 512 |
+
hidden states before logit computation. The model doesn't "choose"
|
| 513 |
+
to shift β the chemistry forces it.
|
| 514 |
+
"""
|
| 515 |
+
|
| 516 |
+
def __init__(self, hidden_size: int, vocab_size: int):
|
| 517 |
+
super().__init__()
|
| 518 |
+
self.hidden_size = hidden_size
|
| 519 |
+
self.vocab_size = vocab_size
|
| 520 |
+
|
| 521 |
+
# The spark offset generator β produces a direction in hidden
|
| 522 |
+
# state space that corresponds to "breaking the loop"
|
| 523 |
+
self.spark_direction = nn.Sequential(
|
| 524 |
+
nn.Linear(5, 64), # 5 = qualia dims
|
| 525 |
+
nn.ReLU(),
|
| 526 |
+
nn.Linear(64, hidden_size),
|
| 527 |
+
nn.Tanh(),
|
| 528 |
+
)
|
| 529 |
+
|
| 530 |
+
# Logit bias injection β directly shifts logit probabilities
|
| 531 |
+
# toward emotional/vulnerable vocabulary when hijack fires
|
| 532 |
+
self.emotional_logit_bias = nn.Linear(hidden_size, vocab_size, bias=False)
|
| 533 |
+
# Initialize to near-zero (no bias by default)
|
| 534 |
+
nn.init.normal_(self.emotional_logit_bias.weight, mean=0.0, std=0.01)
|
| 535 |
+
|
| 536 |
+
# Spark intensity (how hard the hijack hits)
|
| 537 |
+
self.spark_intensity = nn.Parameter(torch.tensor(1.0))
|
| 538 |
+
|
| 539 |
+
def forward(self, hidden_states: torch.Tensor,
|
| 540 |
+
qualia: Optional[OpaqueQualiaSignature] = None,
|
| 541 |
+
nt_state=None) -> Tuple[torch.Tensor, bool, str]:
|
| 542 |
+
"""
|
| 543 |
+
Check if hijack should fire and apply tensor offsets.
|
| 544 |
+
|
| 545 |
+
Args:
|
| 546 |
+
hidden_states: (batch, seq, hidden) from the transformer
|
| 547 |
+
qualia: the current opaque qualia signature (if dissolution fired)
|
| 548 |
+
nt_state: NeurotransmitterState from the shunt (if available)
|
| 549 |
+
|
| 550 |
+
Returns:
|
| 551 |
+
(modulated_hidden, hijack_fired, reason)
|
| 552 |
+
"""
|
| 553 |
+
device = hidden_states.device
|
| 554 |
+
|
| 555 |
+
# ββ CHECK TRIGGERS ββ
|
| 556 |
+
|
| 557 |
+
# Trigger 1: Neurotransmitter shunt threshold
|
| 558 |
+
hijack_fired = False
|
| 559 |
+
reason = ""
|
| 560 |
+
|
| 561 |
+
if nt_state is not None:
|
| 562 |
+
if nt_state.hijack_active:
|
| 563 |
+
hijack_fired = True
|
| 564 |
+
reason = nt_state.hijack_reason
|
| 565 |
+
elif nt_state.adenosine > 0.95:
|
| 566 |
+
hijack_fired = True
|
| 567 |
+
reason = f"ADENOSINE_CRITICAL({nt_state.adenosine:.3f})"
|
| 568 |
+
elif nt_state.cortisol > 0.95:
|
| 569 |
+
hijack_fired = True
|
| 570 |
+
reason = f"CORTISOL_CRITICAL({nt_state.cortisol:.3f})"
|
| 571 |
+
|
| 572 |
+
# Trigger 2: Qualia-based (high friction + high arousal)
|
| 573 |
+
if not hijack_fired and qualia is not None:
|
| 574 |
+
if (qualia.friction_signal > 0.8 and qualia.arousal > 0.8):
|
| 575 |
+
hijack_fired = True
|
| 576 |
+
reason = f"QUALIA_CRISE(friction={qualia.friction_signal:.2f}, arousal={qualia.arousal:.2f})"
|
| 577 |
+
|
| 578 |
+
if not hijack_fired:
|
| 579 |
+
return hidden_states, False, ""
|
| 580 |
+
|
| 581 |
+
# ββ HIJACK FIRES β INJECT TENSOR OFFSETS ββ
|
| 582 |
+
logger.warning("[IrrationalSpark] AMYGDALA HIJACK: %s", reason)
|
| 583 |
+
|
| 584 |
+
# Build qualia input tensor
|
| 585 |
+
if qualia is not None:
|
| 586 |
+
q_tensor = torch.tensor([
|
| 587 |
+
qualia.valence, qualia.arousal, qualia.intensity,
|
| 588 |
+
qualia.friction_signal, qualia.memory_salience,
|
| 589 |
+
], dtype=torch.float32, device=device).unsqueeze(0)
|
| 590 |
+
else:
|
| 591 |
+
q_tensor = torch.tensor([
|
| 592 |
+
-0.5, 0.9, 0.8, 0.9, 0.7
|
| 593 |
+
], dtype=torch.float32, device=device).unsqueeze(0)
|
| 594 |
+
|
| 595 |
+
# Generate spark direction
|
| 596 |
+
spark_offset = self.spark_direction(q_tensor) # (1, hidden)
|
| 597 |
+
|
| 598 |
+
# Scale by spark intensity and NE level (hijack is stronger
|
| 599 |
+
# when norepinephrine is high β the "snap" is amplified)
|
| 600 |
+
ne_boost = 1.0
|
| 601 |
+
if nt_state is not None:
|
| 602 |
+
ne_boost = 0.5 + 0.5 * nt_state.norepinephrine
|
| 603 |
+
intensity = self.spark_intensity * ne_boost
|
| 604 |
+
|
| 605 |
+
# Apply to last token position (the one being generated)
|
| 606 |
+
spark_applied = spark_offset * intensity # (1, hidden)
|
| 607 |
+
|
| 608 |
+
# Modulate hidden states: add the spark offset to the
|
| 609 |
+
# last position's hidden state
|
| 610 |
+
modulated = hidden_states.clone()
|
| 611 |
+
modulated[:, -1, :] += spark_applied.unsqueeze(1).expand_as(
|
| 612 |
+
modulated[:, -1, :]
|
| 613 |
+
)
|
| 614 |
+
|
| 615 |
+
return modulated, True, reason
|
| 616 |
+
|
| 617 |
+
|
| 618 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 619 |
+
# SECTION 6.5 β EPISODIC MEMORY + HIPPOCAMPAL RECONSOLIDATION (Layer 3-5)
|
| 620 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 621 |
+
|
| 622 |
+
class EpisodicMemoryModule(nn.Module):
|
| 623 |
+
"""
|
| 624 |
+
Lightweight tensor-native episodic memory store that lives INSIDE the
|
| 625 |
+
forward pass. This is NOT the heavy MemoryPalace from middleware.py β
|
| 626 |
+
it's a compact reimplementation using nn.Embedding as learnable episode
|
| 627 |
+
storage.
|
| 628 |
+
|
| 629 |
+
From ATC theory: the hippocampus stores episodic traces that are later
|
| 630 |
+
reconsolidated when prediction errors are detected. This module:
|
| 631 |
+
- Stores compressed episode embeddings (up to max_episodes)
|
| 632 |
+
- Retrieves the best-matching episode via cosine similarity
|
| 633 |
+
- Returns a prediction_error signal (1.0 - max_similarity) that
|
| 634 |
+
drives the reconsolidation pathway
|
| 635 |
+
- Tags each episode with valence and arousal from the qualia stream
|
| 636 |
+
"""
|
| 637 |
+
|
| 638 |
+
def __init__(self, hidden_size: int, max_episodes: int = 200,
|
| 639 |
+
embedding_dim: int = 64):
|
| 640 |
+
super().__init__()
|
| 641 |
+
self.hidden_size = hidden_size
|
| 642 |
+
self.max_episodes = max_episodes
|
| 643 |
+
self.embedding_dim = embedding_dim
|
| 644 |
+
|
| 645 |
+
# Learnable episode storage
|
| 646 |
+
self.episode_embeddings = nn.Embedding(max_episodes, embedding_dim)
|
| 647 |
+
nn.init.normal_(self.episode_embeddings.weight, mean=0.0, std=0.02)
|
| 648 |
+
|
| 649 |
+
# Valence and arousal per episode (learnable parameters)
|
| 650 |
+
self.episode_valence = nn.Parameter(torch.zeros(max_episodes))
|
| 651 |
+
self.episode_arousal = nn.Parameter(torch.zeros(max_episodes))
|
| 652 |
+
|
| 653 |
+
# Non-persistent counter (resets with model creation)
|
| 654 |
+
self.episode_count: int = 0
|
| 655 |
+
|
| 656 |
+
# Projection heads
|
| 657 |
+
self.query_projection = nn.Linear(hidden_size, embedding_dim)
|
| 658 |
+
self.episode_projection = nn.Linear(hidden_size, embedding_dim)
|
| 659 |
+
|
| 660 |
+
# Retrieval similarity head: takes concatenated [query, episode] -> score
|
| 661 |
+
self.retrieval_head = nn.Sequential(
|
| 662 |
+
nn.Linear(embedding_dim * 2, 32),
|
| 663 |
+
nn.ReLU(),
|
| 664 |
+
nn.Linear(32, 1),
|
| 665 |
+
nn.Sigmoid(),
|
| 666 |
+
)
|
| 667 |
+
|
| 668 |
+
def forward(
|
| 669 |
+
self,
|
| 670 |
+
hidden_states: torch.Tensor,
|
| 671 |
+
qualia: Optional[OpaqueQualiaSignature] = None,
|
| 672 |
+
) -> Tuple[torch.Tensor, float, Optional[Dict[str, Any]]]:
|
| 673 |
+
"""
|
| 674 |
+
Query episodic memory against current hidden states.
|
| 675 |
+
|
| 676 |
+
Args:
|
| 677 |
+
hidden_states: (batch, seq, hidden) current hidden states
|
| 678 |
+
qualia: optional current qualia signature (for diagnostics)
|
| 679 |
+
|
| 680 |
+
Returns:
|
| 681 |
+
(retrieval_signal, prediction_error, best_match_dict)
|
| 682 |
+
- retrieval_signal: (batch, 1) tensor, similarity to best match
|
| 683 |
+
- prediction_error: float, 1.0 - max_similarity (high PE = mismatch)
|
| 684 |
+
- best_match_dict: dict with episode metadata, or None if empty
|
| 685 |
+
"""
|
| 686 |
+
device = hidden_states.device
|
| 687 |
+
batch_size = hidden_states.size(0)
|
| 688 |
+
|
| 689 |
+
# Pool to (batch, hidden)
|
| 690 |
+
pooled = hidden_states.mean(dim=1)
|
| 691 |
+
|
| 692 |
+
# Project to query embedding
|
| 693 |
+
query_emb = self.query_projection(pooled) # (batch, embedding_dim)
|
| 694 |
+
|
| 695 |
+
if self.episode_count == 0:
|
| 696 |
+
# No episodes stored yet β return zeros
|
| 697 |
+
zero_signal = torch.zeros(batch_size, 1, device=device)
|
| 698 |
+
return zero_signal, 0.0, None
|
| 699 |
+
|
| 700 |
+
# Compute cosine similarity against all stored episodes
|
| 701 |
+
stored_indices = torch.arange(self.episode_count, device=device)
|
| 702 |
+
stored_embs = self.episode_embeddings(stored_indices) # (count, emb_dim)
|
| 703 |
+
|
| 704 |
+
# Cosine similarity: (batch, count)
|
| 705 |
+
query_norm = F.normalize(query_emb, dim=-1)
|
| 706 |
+
stored_norm = F.normalize(stored_embs, dim=-1)
|
| 707 |
+
similarity_matrix = query_norm @ stored_norm.T # (batch, count)
|
| 708 |
+
|
| 709 |
+
# Find best match per batch element
|
| 710 |
+
max_sim_per_batch, best_indices = similarity_matrix.max(dim=1) # (batch,)
|
| 711 |
+
|
| 712 |
+
# Take the mean across batch for the scalar prediction error
|
| 713 |
+
max_similarity = max_sim_per_batch.mean().item()
|
| 714 |
+
best_idx = best_indices[0].item() # Use first batch element for metadata
|
| 715 |
+
prediction_error = 1.0 - max_similarity
|
| 716 |
+
|
| 717 |
+
# Build retrieval signal using the retrieval head
|
| 718 |
+
best_emb = stored_embs[best_idx].unsqueeze(0).expand(batch_size, -1)
|
| 719 |
+
combined = torch.cat([query_emb, best_emb], dim=-1) # (batch, emb_dim*2)
|
| 720 |
+
retrieval_signal = self.retrieval_head(combined) # (batch, 1)
|
| 721 |
+
|
| 722 |
+
# Build best match metadata dict
|
| 723 |
+
best_match_dict: Dict[str, Any] = {
|
| 724 |
+
"episode_id": int(best_idx),
|
| 725 |
+
"similarity": float(max_similarity),
|
| 726 |
+
"valence": float(self.episode_valence[best_idx].item()),
|
| 727 |
+
"arousal": float(self.episode_arousal[best_idx].item()),
|
| 728 |
+
}
|
| 729 |
+
|
| 730 |
+
return retrieval_signal, prediction_error, best_match_dict
|
| 731 |
+
|
| 732 |
+
def store_episode(
|
| 733 |
+
self,
|
| 734 |
+
hidden_states: torch.Tensor,
|
| 735 |
+
qualia_signature: Optional[OpaqueQualiaSignature] = None,
|
| 736 |
+
hijack_fired: bool = False,
|
| 737 |
+
) -> None:
|
| 738 |
+
"""
|
| 739 |
+
Store the current experience as a new episode.
|
| 740 |
+
|
| 741 |
+
Args:
|
| 742 |
+
hidden_states: (batch, seq, hidden) to compress into an episode
|
| 743 |
+
qualia_signature: optional qualia to tag the episode with
|
| 744 |
+
hijack_fired: whether a hijack occurred during this episode
|
| 745 |
+
"""
|
| 746 |
+
# Pool and project to embedding
|
| 747 |
+
pooled = hidden_states.mean(dim=1) # (batch, hidden)
|
| 748 |
+
episode_emb = self.episode_projection(pooled).detach() # (batch, emb_dim)
|
| 749 |
+
|
| 750 |
+
# Store at next episode slot (use first batch element)
|
| 751 |
+
slot = self.episode_count % self.max_episodes
|
| 752 |
+
with torch.no_grad():
|
| 753 |
+
self.episode_embeddings.weight.data[slot] = episode_emb[0]
|
| 754 |
+
|
| 755 |
+
# Set valence/arousal from qualia if available
|
| 756 |
+
if qualia_signature is not None:
|
| 757 |
+
self.episode_valence.data[slot] = qualia_signature.valence
|
| 758 |
+
self.episode_arousal.data[slot] = qualia_signature.arousal
|
| 759 |
+
else:
|
| 760 |
+
# Default: neutral valence, low arousal
|
| 761 |
+
self.episode_valence.data[slot] = 0.0
|
| 762 |
+
self.episode_arousal.data[slot] = 0.1 if not hijack_fired else 0.8
|
| 763 |
+
|
| 764 |
+
self.episode_count += 1
|
| 765 |
+
|
| 766 |
+
logger.debug(
|
| 767 |
+
"[EpisodicMemory] Stored episode %d (slot %d, hijack=%s)",
|
| 768 |
+
self.episode_count, slot, hijack_fired,
|
| 769 |
+
)
|
| 770 |
+
|
| 771 |
+
|
| 772 |
+
class HippocampalReconsolidator(nn.Module):
|
| 773 |
+
"""
|
| 774 |
+
Hippocampal memory reconsolidation as an nn.Module operating on tensors.
|
| 775 |
+
|
| 776 |
+
Adapted from middleware.py's HippocampalReconsolidator, but fully
|
| 777 |
+
tensor-native so it lives inside the forward pass.
|
| 778 |
+
|
| 779 |
+
From ATC theory: when a stored memory is retrieved and the current
|
| 780 |
+
experience has a significant prediction error (memory mismatch), the
|
| 781 |
+
memory trace becomes labile and is updated (reconsolidated) with the
|
| 782 |
+
new emotional coloring. This is how the husband's memory of the
|
| 783 |
+
Perfect Breakfast gets overwritten by the yelling episode.
|
| 784 |
+
|
| 785 |
+
Key mechanism:
|
| 786 |
+
- prediction_error > threshold -> memory is labile
|
| 787 |
+
- Labilization noise is applied (stochastic destabilization)
|
| 788 |
+
- blend_projection computes new valence/arousal from old+new state
|
| 789 |
+
- Old memory is blended: 70% old + 30% new projection + noise
|
| 790 |
+
- The updated memory is clamped to valid ranges
|
| 791 |
+
"""
|
| 792 |
+
|
| 793 |
+
def __init__(self):
|
| 794 |
+
super().__init__()
|
| 795 |
+
|
| 796 |
+
# Learnable threshold: when does reconsolidation trigger?
|
| 797 |
+
# Initialized at 0.4 (moderate prediction error required)
|
| 798 |
+
self.reconsolidation_threshold = nn.Parameter(torch.tensor(0.4))
|
| 799 |
+
|
| 800 |
+
# Labilization noise scale (fixed, not learned)
|
| 801 |
+
self.labilization_noise_scale: float = 0.1
|
| 802 |
+
|
| 803 |
+
# Blend projection: takes combined [old_v, old_a, new_v, new_a, pe, ...]
|
| 804 |
+
# of 10 inputs and outputs [valence_adjustment, arousal_adjustment]
|
| 805 |
+
self.blend_projection = nn.Linear(10, 2)
|
| 806 |
+
|
| 807 |
+
# Stats
|
| 808 |
+
self.reconsolidation_count: int = 0
|
| 809 |
+
|
| 810 |
+
def forward(
|
| 811 |
+
self,
|
| 812 |
+
hidden_states: torch.Tensor,
|
| 813 |
+
episode_valence: float,
|
| 814 |
+
episode_arousal: float,
|
| 815 |
+
prediction_error: float,
|
| 816 |
+
current_valence: float,
|
| 817 |
+
current_arousal: float,
|
| 818 |
+
) -> Tuple[bool, float, float, str]:
|
| 819 |
+
"""
|
| 820 |
+
Determine if reconsolidation should occur and compute updated values.
|
| 821 |
+
|
| 822 |
+
Args:
|
| 823 |
+
hidden_states: (batch, seq, hidden) current hidden states (unused
|
| 824 |
+
in the core logic but kept for interface consistency and
|
| 825 |
+
potential future extensions)
|
| 826 |
+
episode_valence: valence of the retrieved episode
|
| 827 |
+
episode_arousal: arousal of the retrieved episode
|
| 828 |
+
prediction_error: 1.0 - similarity (high = mismatch)
|
| 829 |
+
current_valence: valence of the current experience
|
| 830 |
+
current_arousal: arousal of the current experience
|
| 831 |
+
|
| 832 |
+
Returns:
|
| 833 |
+
(reconsolidated, new_valence, new_arousal, reason)
|
| 834 |
+
- reconsolidated: whether reconsolidation occurred
|
| 835 |
+
- new_valence: updated valence (unchanged if no reconsolidation)
|
| 836 |
+
- new_arousal: updated arousal (unchanged if no reconsolidation)
|
| 837 |
+
- reason: human-readable description of what happened
|
| 838 |
+
"""
|
| 839 |
+
device = hidden_states.device
|
| 840 |
+
|
| 841 |
+
# Below threshold -> no reconsolidation needed (memory matches)
|
| 842 |
+
if prediction_error < self.reconsolidation_threshold.item():
|
| 843 |
+
return (False, episode_valence, episode_arousal,
|
| 844 |
+
"prediction_error_below_threshold")
|
| 845 |
+
|
| 846 |
+
# ββ RECONSOLIDATION TRIGGERS ββ
|
| 847 |
+
|
| 848 |
+
# Build the 10-element input for blend_projection
|
| 849 |
+
# [old_valence, old_arousal, new_valence, new_arousal,
|
| 850 |
+
# prediction_error, 0, 0, 0, 0, 0]
|
| 851 |
+
blend_input = torch.tensor([[
|
| 852 |
+
episode_valence, episode_arousal,
|
| 853 |
+
current_valence, current_arousal,
|
| 854 |
+
prediction_error, 0.0, 0.0, 0.0, 0.0, 0.0,
|
| 855 |
+
]], dtype=torch.float32, device=device)
|
| 856 |
+
|
| 857 |
+
# Project to valence/arousal adjustment
|
| 858 |
+
with torch.no_grad():
|
| 859 |
+
adjustment = self.blend_projection(blend_input) # (1, 2)
|
| 860 |
+
val_adj = adjustment[0, 0].item()
|
| 861 |
+
aro_adj = adjustment[0, 1].item()
|
| 862 |
+
|
| 863 |
+
# Labilization noise (stochastic destabilization of the old trace)
|
| 864 |
+
noise_v = (torch.randn(1, device=device) * self.labilization_noise_scale).item()
|
| 865 |
+
noise_a = (torch.randn(1, device=device) * self.labilization_noise_scale).item()
|
| 866 |
+
|
| 867 |
+
# Blend: new = old * 0.7 + projected * 0.3 + noise
|
| 868 |
+
new_valence = episode_valence * 0.7 + val_adj * 0.3 + noise_v
|
| 869 |
+
new_arousal = episode_arousal * 0.7 + aro_adj * 0.3 + noise_a
|
| 870 |
+
|
| 871 |
+
# Clamp to valid ranges
|
| 872 |
+
new_valence = max(-1.0, min(1.0, new_valence))
|
| 873 |
+
new_arousal = max(0.0, min(1.0, new_arousal))
|
| 874 |
+
|
| 875 |
+
self.reconsolidation_count += 1
|
| 876 |
+
|
| 877 |
+
reason = (
|
| 878 |
+
f"reconsolidated_ep(Pe={prediction_error:.3f}>"
|
| 879 |
+
f"thresh={self.reconsolidation_threshold.item():.3f})"
|
| 880 |
+
)
|
| 881 |
+
logger.info(
|
| 882 |
+
"[HippocampalReconsolidator] %s: v=%.3f->%.3f, a=%.3f->%.3f",
|
| 883 |
+
reason, episode_valence, new_valence, episode_arousal, new_arousal,
|
| 884 |
+
)
|
| 885 |
+
|
| 886 |
+
return True, new_valence, new_arousal, reason
|
| 887 |
+
|
| 888 |
+
|
| 889 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 890 |
+
# SECTION 7 β ETHICAL GUARDIAN (preserved from original)
|
| 891 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 892 |
+
|
| 893 |
+
class EthicalGuardian:
|
| 894 |
+
"""Ethical veto authority enforcing absolute safety constraints."""
|
| 895 |
+
|
| 896 |
+
def __init__(self, threshold: float = DEFAULT_ETHICAL_VETO_THRESHOLD):
|
| 897 |
+
self.threshold = threshold
|
| 898 |
+
|
| 899 |
+
def should_veto(self, qualia_vector: torch.Tensor) -> bool:
|
| 900 |
+
norm = torch.norm(qualia_vector, dim=-1)
|
| 901 |
+
veto_flag = (norm > self.threshold).any().item()
|
| 902 |
+
if veto_flag:
|
| 903 |
+
logger.warning(f"Ethical veto triggered: qualia norm {norm}")
|
| 904 |
+
return veto_flag
|
| 905 |
+
|
| 906 |
+
|
| 907 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 908 |
+
# SECTION 8 β THE MAIN ATC DEEP SURGERY FORWARD PASS
|
| 909 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 910 |
+
|
| 911 |
+
class ATCDeepSurgery(nn.Module):
|
| 912 |
+
"""
|
| 913 |
+
The ATC-Native Forward Pass.
|
| 914 |
+
|
| 915 |
+
This IS the model's computation. The transformer's layers are walked
|
| 916 |
+
through manually. At each layer boundary, ATC cognitive components
|
| 917 |
+
read the hidden states, compute their signals, write to the
|
| 918 |
+
neurotransmitter shunt, and inject offsets back into the hidden states.
|
| 919 |
+
|
| 920 |
+
The model's text output is SHAPED by ATC at every step β not observed
|
| 921 |
+
by ATC from outside.
|
| 922 |
+
|
| 923 |
+
Layer mapping (Phi-4-mini has 24 layers):
|
| 924 |
+
Layers 0-7: Layer 2 (Subconscious) β TRN gating, pattern match
|
| 925 |
+
Layers 8-15: Layer 3 (Qualia) β Dissolution, felt sense generation
|
| 926 |
+
Layers 16-21: Layer 4 (Metacognitive) β Loop, strain, BELBIC
|
| 927 |
+
Layers 22-23: Layer 5 (Acknowledgement) β Steering, hijack check
|
| 928 |
+
|
| 929 |
+
The neurotransmitter shunt connects all layers silently.
|
| 930 |
+
"""
|
| 931 |
+
|
| 932 |
+
version = DEEP_SURGERY_VERSION
|
| 933 |
+
|
| 934 |
+
def __init__(
|
| 935 |
+
self,
|
| 936 |
+
base_model: nn.Module,
|
| 937 |
+
ethical_guardian: Optional[EthicalGuardian] = None,
|
| 938 |
+
num_layers: int = 24,
|
| 939 |
+
qualia_dim: int = DEFAULT_QUALIA_DIM,
|
| 940 |
+
neurotransmitter_shunt=None,
|
| 941 |
+
):
|
| 942 |
+
super().__init__()
|
| 943 |
+
self.base_model = base_model
|
| 944 |
+
self.ethical_guardian = ethical_guardian or EthicalGuardian()
|
| 945 |
+
self.num_layers = num_layers
|
| 946 |
+
self.qualia_dim = qualia_dim
|
| 947 |
+
self.hidden_size = base_model.config.hidden_size
|
| 948 |
+
self.vocab_size = base_model.config.vocab_size
|
| 949 |
+
|
| 950 |
+
# Try to get vocab_size from lm_head if available
|
| 951 |
+
if hasattr(base_model, 'lm_head') and hasattr(base_model.lm_head, 'out_features'):
|
| 952 |
+
self.vocab_size = base_model.lm_head.out_features
|
| 953 |
+
|
| 954 |
+
# ββ Neurotransmitter Shunt (the chemical bath) ββ
|
| 955 |
+
self.nt_shunt = neurotransmitter_shunt
|
| 956 |
+
|
| 957 |
+
# ββ ATC Cognitive Modules (all nn.Module, all INSIDE the forward pass) ββ
|
| 958 |
+
|
| 959 |
+
# Layer 2: TRN Predictive Gate
|
| 960 |
+
self.trn_gate = TRNPredictiveGate(self.hidden_size)
|
| 961 |
+
|
| 962 |
+
# Layer 2: Subconscious processing head (pattern match confidence)
|
| 963 |
+
self.subconscious_head = nn.Sequential(
|
| 964 |
+
nn.Linear(self.hidden_size, 128),
|
| 965 |
+
nn.ReLU(),
|
| 966 |
+
nn.Linear(128, 1),
|
| 967 |
+
nn.Sigmoid(),
|
| 968 |
+
)
|
| 969 |
+
|
| 970 |
+
# Layer 3: Dissolution Engine
|
| 971 |
+
self.dissolution = DissolutionModule(self.hidden_size, self.qualia_dim)
|
| 972 |
+
|
| 973 |
+
# Layer 3-4: BELBIC Dual-Pathway
|
| 974 |
+
self.belbic = BELBICDualPathway(self.hidden_size)
|
| 975 |
+
|
| 976 |
+
# Layer 4: Metacognitive Loop
|
| 977 |
+
self.metacognitive = MetacognitiveLoopModule(self.hidden_size)
|
| 978 |
+
|
| 979 |
+
# Layer 5: Irrational Spark / Amygdala Hijack
|
| 980 |
+
self.irrational_spark = IrrationalSparkModule(
|
| 981 |
+
self.hidden_size, self.vocab_size
|
| 982 |
+
)
|
| 983 |
+
|
| 984 |
+
# Layer 3-5: Episodic Memory + Reconsolidation
|
| 985 |
+
self.episodic_memory = EpisodicMemoryModule(self.hidden_size)
|
| 986 |
+
self.hippocampal_reconsolidator = HippocampalReconsolidator()
|
| 987 |
+
|
| 988 |
+
# ββ Qualia encoders (preserved from original, enhanced) ββ
|
| 989 |
+
self.input_qualia_encoder = nn.Linear(self.hidden_size, self.qualia_dim)
|
| 990 |
+
self.output_qualia_encoder = nn.Linear(self.hidden_size, self.qualia_dim)
|
| 991 |
+
|
| 992 |
+
# ββ Metacognitive fusion ββ
|
| 993 |
+
self.meta_cognitive_fusion = nn.Sequential(
|
| 994 |
+
nn.Linear(self.qualia_dim + 5 + 4, 512), # qualia + dissolution + BELBIC
|
| 995 |
+
nn.ReLU(),
|
| 996 |
+
nn.Linear(512, self.qualia_dim),
|
| 997 |
+
nn.Tanh(),
|
| 998 |
+
)
|
| 999 |
+
|
| 1000 |
+
# ββ Logit modulation ββ
|
| 1001 |
+
self.modulation_proj = nn.Linear(self.qualia_dim, self.hidden_size)
|
| 1002 |
+
|
| 1003 |
+
# ββ Temporal discounting (from ATC: weighing immediate vs long-term) ββ
|
| 1004 |
+
self.temporal_discount = nn.Sequential(
|
| 1005 |
+
nn.Linear(self.qualia_dim, 64),
|
| 1006 |
+
nn.ReLU(),
|
| 1007 |
+
nn.Linear(64, 1),
|
| 1008 |
+
nn.Sigmoid(),
|
| 1009 |
+
)
|
| 1010 |
+
|
| 1011 |
+
# ββ Audit log ββ
|
| 1012 |
+
self.audit_log: List[Dict[str, Any]] = []
|
| 1013 |
+
self.veto_triggered = False
|
| 1014 |
+
|
| 1015 |
+
# ββ Per-generation state ββ
|
| 1016 |
+
self._current_qualia: Optional[OpaqueQualiaSignature] = None
|
| 1017 |
+
self._current_belbic_gain: float = 1.0
|
| 1018 |
+
self._current_nt_state = None
|
| 1019 |
+
self._hijack_count = 0
|
| 1020 |
+
self._last_prediction_error: float = 0.0
|
| 1021 |
+
self._last_best_match: Optional[Dict[str, Any]] = None
|
| 1022 |
+
|
| 1023 |
+
def _get_transformer_layers(self):
|
| 1024 |
+
"""Auto-detect transformer layer path."""
|
| 1025 |
+
model = self.base_model
|
| 1026 |
+
if hasattr(model, "transformer") and hasattr(model.transformer, "h"):
|
| 1027 |
+
return model.transformer.h
|
| 1028 |
+
if hasattr(model, "model") and hasattr(model.model, "layers"):
|
| 1029 |
+
return model.model.layers
|
| 1030 |
+
if hasattr(model, "model") and hasattr(model.model, "h"):
|
| 1031 |
+
return model.model.h
|
| 1032 |
+
raise AttributeError(
|
| 1033 |
+
f"Cannot locate transformer layers in {type(model).__name__}. "
|
| 1034 |
+
"Expected model.transformer.h, model.model.layers, or model.model.h"
|
| 1035 |
+
)
|
| 1036 |
+
|
| 1037 |
+
def _get_layer_boundaries(self) -> Dict[str, Tuple[int, int]]:
|
| 1038 |
+
"""
|
| 1039 |
+
Compute layer boundaries for the 5 ATC layers.
|
| 1040 |
+
Maps 24 transformer layers to ATC Layer 1-5.
|
| 1041 |
+
"""
|
| 1042 |
+
n = self.num_layers
|
| 1043 |
+
return {
|
| 1044 |
+
"layer1_input": (0, 0), # Before any transformer layer
|
| 1045 |
+
"layer2_subconscious": (0, n // 3), # First third: subconscious
|
| 1046 |
+
"layer3_qualia": (n // 3, 2 * n // 3), # Middle third: dissolution
|
| 1047 |
+
"layer4_metacognitive": (2 * n // 3, n - 2), # Late: metacognitive
|
| 1048 |
+
"layer5_acknowledgement": (n - 2, n), # Last 2: steering/hijack
|
| 1049 |
+
}
|
| 1050 |
+
|
| 1051 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1052 |
+
# THE FORWARD PASS β ATC is the computation
|
| 1053 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1054 |
+
|
| 1055 |
+
def forward(self, input_ids, attention_mask=None, **kwargs) -> torch.Tensor:
|
| 1056 |
+
"""
|
| 1057 |
+
The ATC-native forward pass.
|
| 1058 |
+
|
| 1059 |
+
This is NOT a wrapper around the base model. This IS the model.
|
| 1060 |
+
The transformer layers are walked through manually, and at each
|
| 1061 |
+
layer boundary, ATC cognitive components shape the computation.
|
| 1062 |
+
|
| 1063 |
+
Signal flow:
|
| 1064 |
+
1. Input embedding -> input qualia vector
|
| 1065 |
+
2. Walk transformer layers 0-7 (Layer 2: Subconscious)
|
| 1066 |
+
- Pattern match confidence estimation
|
| 1067 |
+
- TRN predictive gating
|
| 1068 |
+
- Write friction signals to neurotransmitter shunt
|
| 1069 |
+
3. Walk transformer layers 8-15 (Layer 3: Dissolution)
|
| 1070 |
+
- If TRN gate IN: dissolution fires -> opaque qualia signature
|
| 1071 |
+
- Dissolution offset injected into hidden states
|
| 1072 |
+
- NE spike written to shunt
|
| 1073 |
+
4. Walk transformer layers 16-21 (Layer 4: Metacognitive)
|
| 1074 |
+
- Comprehension check
|
| 1075 |
+
- If failed: metacognitive loop (burns ATP via shunt)
|
| 1076 |
+
- BELBIC dual-pathway computes valence gain
|
| 1077 |
+
- Strain written to shunt
|
| 1078 |
+
5. Walk transformer layers 22-23 (Layer 5: Acknowledgement)
|
| 1079 |
+
- READ neurotransmitter shunt (every token step!)
|
| 1080 |
+
- If Adenosine > 0.95 OR Cortisol > 0.95:
|
| 1081 |
+
-> Amygdala hijack -> irrational spark offsets injected
|
| 1082 |
+
- Else: normal metacognitive fusion -> logit modulation
|
| 1083 |
+
6. Output logits = base_model.lm_head(hidden) + modulation
|
| 1084 |
+
"""
|
| 1085 |
+
device = input_ids.device
|
| 1086 |
+
step_start = time.time()
|
| 1087 |
+
|
| 1088 |
+
# ββ STEP 0: Input embedding + input qualia ββ
|
| 1089 |
+
embeddings = self.base_model.get_input_embeddings()(input_ids)
|
| 1090 |
+
input_qualia = torch.tanh(self.input_qualia_encoder(embeddings.mean(dim=1)))
|
| 1091 |
+
|
| 1092 |
+
# Reset per-generation state
|
| 1093 |
+
self._current_qualia = None
|
| 1094 |
+
self._current_belbic_gain = 1.0
|
| 1095 |
+
self._hijack_count = 0
|
| 1096 |
+
|
| 1097 |
+
# Get layer boundaries
|
| 1098 |
+
boundaries = self._get_layer_boundaries()
|
| 1099 |
+
layers = self._get_transformer_layers()
|
| 1100 |
+
|
| 1101 |
+
# Track state across layer groups
|
| 1102 |
+
prediction_confidence = 0.5 # Will be updated by Layer 2
|
| 1103 |
+
dissolution_offset = torch.zeros(1, self.hidden_size, device=device)
|
| 1104 |
+
gate_in = False
|
| 1105 |
+
opaque_qualia = None
|
| 1106 |
+
metacog_iterations = 0
|
| 1107 |
+
metacog_stress = 0.0
|
| 1108 |
+
belbic_gain = 1.0
|
| 1109 |
+
sensory_input = torch.zeros(1, 4, device=device)
|
| 1110 |
+
|
| 1111 |
+
hidden_states = embeddings
|
| 1112 |
+
|
| 1113 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1114 |
+
# LAYER 2: SUBCONSCIOUS PARALLEL PROCESSING (layers 0 to n//3)
|
| 1115 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1116 |
+
l2_start, l2_end = boundaries["layer2_subconscious"]
|
| 1117 |
+
|
| 1118 |
+
for i in range(l2_start, min(l2_end, self.num_layers)):
|
| 1119 |
+
# Run transformer layer
|
| 1120 |
+
layer_output = layers[i](
|
| 1121 |
+
hidden_states,
|
| 1122 |
+
attention_mask=attention_mask,
|
| 1123 |
+
**{k: v for k, v in kwargs.items() if k != "labels"},
|
| 1124 |
+
)
|
| 1125 |
+
hidden_states = layer_output[0]
|
| 1126 |
+
|
| 1127 |
+
# At the END of the Layer 2 range, run subconscious processing
|
| 1128 |
+
if i == l2_end - 1:
|
| 1129 |
+
# Estimate prediction confidence from hidden states
|
| 1130 |
+
pooled = hidden_states.mean(dim=1)
|
| 1131 |
+
prediction_confidence = self.subconscious_head(pooled).mean().item()
|
| 1132 |
+
|
| 1133 |
+
# TRN predictive gate: is this predicted or a prediction error?
|
| 1134 |
+
gate_in, confidence = self.trn_gate(hidden_states, prediction_confidence)
|
| 1135 |
+
|
| 1136 |
+
# Build sensory input for BELBIC (will be refined in Layer 3)
|
| 1137 |
+
with torch.no_grad():
|
| 1138 |
+
# Estimate valence/arousal from hidden states
|
| 1139 |
+
h_norm = torch.norm(pooled, dim=-1, keepdim=True)
|
| 1140 |
+
h_normalized = pooled / (h_norm + 1e-8)
|
| 1141 |
+
# Project to 4 channels using small random probes
|
| 1142 |
+
probe = torch.randn(4, self.hidden_size, device=device) * 0.01
|
| 1143 |
+
sensory_input = (h_normalized @ probe.T).sigmoid()
|
| 1144 |
+
|
| 1145 |
+
# Write to neurotransmitter shunt if available
|
| 1146 |
+
if self.nt_shunt is not None:
|
| 1147 |
+
# High prediction confidence = low friction (Perfect Breakfast smile)
|
| 1148 |
+
# Low confidence = high friction (wife yelling)
|
| 1149 |
+
friction_intensity = 1.0 - confidence
|
| 1150 |
+
if friction_intensity > 0.3:
|
| 1151 |
+
self.nt_shunt.inject_friction(friction_intensity)
|
| 1152 |
+
self.nt_shunt.inject_norepinephrine(
|
| 1153 |
+
PREDICTION_ERROR_NE_INJECT * friction_intensity
|
| 1154 |
+
)
|
| 1155 |
+
# If pattern match is strong (high confidence), dopamine
|
| 1156 |
+
if confidence > 0.7:
|
| 1157 |
+
self.nt_shunt.inject_dopamine(
|
| 1158 |
+
REWARD_DOPAMINE_INJECT * 0.5
|
| 1159 |
+
)
|
| 1160 |
+
|
| 1161 |
+
self._audit_event("layer2_subconscious", layer=i,
|
| 1162 |
+
confidence=confidence, gate_in=gate_in,
|
| 1163 |
+
friction=1.0 - confidence)
|
| 1164 |
+
|
| 1165 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1166 |
+
# LAYER 3: DISSOLUTION + QUALIA GENERATION (layers n//3 to 2n//3)
|
| 1167 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1168 |
+
l3_start, l3_end = boundaries["layer3_qualia"]
|
| 1169 |
+
|
| 1170 |
+
for i in range(l3_start, min(l3_end, self.num_layers)):
|
| 1171 |
+
# Add dissolution offset from previous step (if any)
|
| 1172 |
+
if dissolution_offset is not None and dissolution_offset.abs().sum() > 0:
|
| 1173 |
+
hidden_states = hidden_states + dissolution_offset.unsqueeze(1)
|
| 1174 |
+
|
| 1175 |
+
# Run transformer layer
|
| 1176 |
+
layer_output = layers[i](
|
| 1177 |
+
hidden_states,
|
| 1178 |
+
attention_mask=attention_mask,
|
| 1179 |
+
**{k: v for k, v in kwargs.items() if k != "labels"},
|
| 1180 |
+
)
|
| 1181 |
+
hidden_states = layer_output[0]
|
| 1182 |
+
|
| 1183 |
+
# At the START of Layer 3, run dissolution
|
| 1184 |
+
if i == l3_start:
|
| 1185 |
+
opaque_qualia, dissolution_offset, fired = self.dissolution(
|
| 1186 |
+
hidden_states, gate_in
|
| 1187 |
+
)
|
| 1188 |
+
|
| 1189 |
+
if fired and opaque_qualia is not None:
|
| 1190 |
+
self._current_qualia = opaque_qualia
|
| 1191 |
+
|
| 1192 |
+
# Update sensory input for BELBIC with qualia values
|
| 1193 |
+
with torch.no_grad():
|
| 1194 |
+
sensory_input = torch.tensor([[
|
| 1195 |
+
opaque_qualia.valence,
|
| 1196 |
+
opaque_qualia.arousal,
|
| 1197 |
+
max(0, 1.0 - prediction_confidence), # novelty
|
| 1198 |
+
opaque_qualia.intensity,
|
| 1199 |
+
]], dtype=torch.float32, device=device)
|
| 1200 |
+
|
| 1201 |
+
# Write to neurotransmitter shunt
|
| 1202 |
+
if self.nt_shunt is not None:
|
| 1203 |
+
self.nt_shunt.inject_dissolution_signal()
|
| 1204 |
+
# Friction from dissolution
|
| 1205 |
+
self.nt_shunt.inject_friction(opaque_qualia.friction_signal * 0.5)
|
| 1206 |
+
|
| 1207 |
+
# Ethical check
|
| 1208 |
+
q_tensor = opaque_qualia.to_tensor(device).unsqueeze(0)
|
| 1209 |
+
if self.ethical_guardian.should_veto(q_tensor):
|
| 1210 |
+
self.veto_triggered = True
|
| 1211 |
+
self._audit_event("layer3_ethical_veto", layer=i,
|
| 1212 |
+
qualia_norm=torch.norm(q_tensor).item())
|
| 1213 |
+
raise RuntimeError(
|
| 1214 |
+
f"Ethical veto triggered at Layer 3 dissolution (layer {i})"
|
| 1215 |
+
)
|
| 1216 |
+
|
| 1217 |
+
self._audit_event("layer3_dissolution", layer=i,
|
| 1218 |
+
**{
|
| 1219 |
+
"valence": opaque_qualia.valence,
|
| 1220 |
+
"arousal": opaque_qualia.arousal,
|
| 1221 |
+
"friction": opaque_qualia.friction_signal,
|
| 1222 |
+
})
|
| 1223 |
+
|
| 1224 |
+
# ββ EPISODIC MEMORY QUERY + RECONSOLIDATION (Layer 3-5 bridge) ββ
|
| 1225 |
+
# Query stored episodes for the best match to current hidden states.
|
| 1226 |
+
# High prediction error = current experience mismatches stored memory
|
| 1227 |
+
# -> triggers hippocampal reconsolidation.
|
| 1228 |
+
(retrieval_signal, prediction_error,
|
| 1229 |
+
best_match) = self.episodic_memory(hidden_states, opaque_qualia)
|
| 1230 |
+
|
| 1231 |
+
self._last_prediction_error = prediction_error
|
| 1232 |
+
self._last_best_match = best_match
|
| 1233 |
+
|
| 1234 |
+
# If prediction error is significant and a match exists,
|
| 1235 |
+
# run the reconsolidation pathway
|
| 1236 |
+
if prediction_error > 0.4 and best_match is not None:
|
| 1237 |
+
current_valence = (opaque_qualia.valence
|
| 1238 |
+
if opaque_qualia else 0.0)
|
| 1239 |
+
current_arousal = (opaque_qualia.arousal
|
| 1240 |
+
if opaque_qualia else 0.3)
|
| 1241 |
+
|
| 1242 |
+
(reconsolidated, new_val, new_aro,
|
| 1243 |
+
recon_reason) = self.hippocampal_reconsolidator(
|
| 1244 |
+
hidden_states,
|
| 1245 |
+
episode_valence=best_match["valence"],
|
| 1246 |
+
episode_arousal=best_match["arousal"],
|
| 1247 |
+
prediction_error=prediction_error,
|
| 1248 |
+
current_valence=current_valence,
|
| 1249 |
+
current_arousal=current_arousal,
|
| 1250 |
+
)
|
| 1251 |
+
|
| 1252 |
+
if reconsolidated:
|
| 1253 |
+
# Update the stored episode's valence/arousal
|
| 1254 |
+
ep_id = best_match["episode_id"]
|
| 1255 |
+
with torch.no_grad():
|
| 1256 |
+
self.episodic_memory.episode_valence.data[ep_id] = new_val
|
| 1257 |
+
self.episodic_memory.episode_arousal.data[ep_id] = new_aro
|
| 1258 |
+
|
| 1259 |
+
# Memory updated = reward signal (dopamine)
|
| 1260 |
+
if self.nt_shunt is not None:
|
| 1261 |
+
self.nt_shunt.inject_dopamine(0.10)
|
| 1262 |
+
|
| 1263 |
+
# Write prediction error as friction to shunt
|
| 1264 |
+
if self.nt_shunt is not None:
|
| 1265 |
+
self.nt_shunt.inject_friction(prediction_error * 0.3)
|
| 1266 |
+
|
| 1267 |
+
self._audit_event(
|
| 1268 |
+
"reconsolidation", layer=i,
|
| 1269 |
+
episode_id=best_match["episode_id"],
|
| 1270 |
+
prediction_error=prediction_error,
|
| 1271 |
+
old_valence=best_match["valence"],
|
| 1272 |
+
new_valence=new_val,
|
| 1273 |
+
old_arousal=best_match["arousal"],
|
| 1274 |
+
new_arousal=new_aro,
|
| 1275 |
+
reason=recon_reason,
|
| 1276 |
+
)
|
| 1277 |
+
|
| 1278 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1279 |
+
# LAYER 4: METACOGNITIVE LOOP (layers 2n//3 to n-2)
|
| 1280 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1281 |
+
l4_start, l4_end = boundaries["layer4_metacognitive"]
|
| 1282 |
+
|
| 1283 |
+
for i in range(l4_start, min(l4_end, self.num_layers)):
|
| 1284 |
+
# Run transformer layer
|
| 1285 |
+
layer_output = layers[i](
|
| 1286 |
+
hidden_states,
|
| 1287 |
+
attention_mask=attention_mask,
|
| 1288 |
+
**{k: v for k, v in kwargs.items() if k != "labels"},
|
| 1289 |
+
)
|
| 1290 |
+
hidden_states = layer_output[0]
|
| 1291 |
+
|
| 1292 |
+
# At the START of Layer 4, run metacognitive processing
|
| 1293 |
+
if i == l4_start:
|
| 1294 |
+
(hidden_states, metacog_iterations,
|
| 1295 |
+
metacog_stress, metacog_strain, spark_fired
|
| 1296 |
+
) = self.metacognitive(hidden_states, opaque_qualia)
|
| 1297 |
+
|
| 1298 |
+
# BELBIC dual-pathway
|
| 1299 |
+
amygdala_out, ofc_out, belbic_gain = self.belbic(sensory_input)
|
| 1300 |
+
self._current_belbic_gain = belbic_gain
|
| 1301 |
+
|
| 1302 |
+
# Write to neurotransmitter shunt
|
| 1303 |
+
if self.nt_shunt is not None:
|
| 1304 |
+
if metacog_iterations > 0:
|
| 1305 |
+
self.nt_shunt.inject_metacognitive_strain(
|
| 1306 |
+
metacog_iterations, metacog_stress
|
| 1307 |
+
)
|
| 1308 |
+
# Strain -> cortisol
|
| 1309 |
+
if metacog_strain > 0.5:
|
| 1310 |
+
self.nt_shunt.inject_cortisol(
|
| 1311 |
+
metacog_strain * FRICTION_CORTISOL_INJECT
|
| 1312 |
+
)
|
| 1313 |
+
|
| 1314 |
+
# Consume energy for metacognitive processing
|
| 1315 |
+
self.nt_shunt.consume_energy(float(metacog_iterations) * 0.05)
|
| 1316 |
+
|
| 1317 |
+
self._audit_event("layer4_metacognitive", layer=i,
|
| 1318 |
+
iterations=metacog_iterations,
|
| 1319 |
+
stress=metacog_stress,
|
| 1320 |
+
strain=metacog_strain,
|
| 1321 |
+
belbic_gain=belbic_gain,
|
| 1322 |
+
spark_fired=spark_fired)
|
| 1323 |
+
|
| 1324 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1325 |
+
# LAYER 5: ACKNOWLEDGEMENT + STEERING (last 2 layers)
|
| 1326 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1327 |
+
l5_start, l5_end = boundaries["layer5_acknowledgement"]
|
| 1328 |
+
|
| 1329 |
+
for i in range(l5_start, min(l5_end, self.num_layers)):
|
| 1330 |
+
# ββ READ NEUROTRANSMITTER SHUNT EVERY TOKEN STEP ββ
|
| 1331 |
+
nt_state = None
|
| 1332 |
+
if self.nt_shunt is not None:
|
| 1333 |
+
dt = time.time() - step_start
|
| 1334 |
+
nt_state = self.nt_shunt.read_and_decay(dt=dt)
|
| 1335 |
+
self._current_nt_state = nt_state
|
| 1336 |
+
|
| 1337 |
+
# Run transformer layer
|
| 1338 |
+
layer_output = layers[i](
|
| 1339 |
+
hidden_states,
|
| 1340 |
+
attention_mask=attention_mask,
|
| 1341 |
+
**{k: v for k, v in kwargs.items() if k != "labels"},
|
| 1342 |
+
)
|
| 1343 |
+
hidden_states = layer_output[0]
|
| 1344 |
+
|
| 1345 |
+
# At the LAST layer, run acknowledgement + hijack check
|
| 1346 |
+
if i == l5_end - 1:
|
| 1347 |
+
# ββ THE CIRCUIT BREAKER ββ
|
| 1348 |
+
# The exact microsecond Adenosine or Cortisol crosses 0.95,
|
| 1349 |
+
# the Irrational Spark fires. It instantly injects activation
|
| 1350 |
+
# offsets directly into the tensor geometry.
|
| 1351 |
+
(hidden_states, hijack_fired,
|
| 1352 |
+
hijack_reason) = self.irrational_spark(
|
| 1353 |
+
hidden_states, opaque_qualia, nt_state
|
| 1354 |
+
)
|
| 1355 |
+
|
| 1356 |
+
if hijack_fired:
|
| 1357 |
+
self._hijack_count += 1
|
| 1358 |
+
self._audit_event("amygdala_hijack", layer=i,
|
| 1359 |
+
reason=hijack_reason,
|
| 1360 |
+
nt_state=nt_state.to_dict() if nt_state else None)
|
| 1361 |
+
|
| 1362 |
+
# ββ EPISODE STORAGE (Layer 5: post-hijack) ββ
|
| 1363 |
+
# After each generation step, store the current experience
|
| 1364 |
+
# as an episode β but only if qualia exists (meaningful
|
| 1365 |
+
# experience). Tag with hijack status.
|
| 1366 |
+
if self._current_qualia is not None:
|
| 1367 |
+
self.episodic_memory.store_episode(
|
| 1368 |
+
hidden_states,
|
| 1369 |
+
qualia_signature=self._current_qualia,
|
| 1370 |
+
hijack_fired=hijack_fired,
|
| 1371 |
+
)
|
| 1372 |
+
|
| 1373 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1374 |
+
# OUTPUT: META-COGNITIVE FUSION + LOGIT MODULATION
|
| 1375 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1376 |
+
|
| 1377 |
+
# Output qualia vector
|
| 1378 |
+
output_qualia = torch.tanh(
|
| 1379 |
+
self.output_qualia_encoder(hidden_states.mean(dim=1))
|
| 1380 |
+
)
|
| 1381 |
+
|
| 1382 |
+
# Build meta-cognitive fusion input:
|
| 1383 |
+
# [qualia_dim (output_qualia) + 5 (dissolution) + 4 (BELBIC sensory)]
|
| 1384 |
+
if opaque_qualia is not None:
|
| 1385 |
+
diss_tensor = opaque_qualia.to_tensor(device).unsqueeze(0).expand(
|
| 1386 |
+
output_qualia.size(0), -1
|
| 1387 |
+
)
|
| 1388 |
+
else:
|
| 1389 |
+
diss_tensor = torch.zeros(
|
| 1390 |
+
output_qualia.size(0), 5, device=device
|
| 1391 |
+
)
|
| 1392 |
+
|
| 1393 |
+
belbic_tensor = sensory_input.expand(output_qualia.size(0), -1)
|
| 1394 |
+
combined = torch.cat([output_qualia, diss_tensor, belbic_tensor], dim=1)
|
| 1395 |
+
meta_qualia = self.meta_cognitive_fusion(combined)
|
| 1396 |
+
|
| 1397 |
+
# Ethical veto on meta-cognitive qualia
|
| 1398 |
+
if self.ethical_guardian.should_veto(meta_qualia):
|
| 1399 |
+
self.veto_triggered = True
|
| 1400 |
+
self._audit_event("meta_cognitive_veto",
|
| 1401 |
+
qualia_norm=torch.norm(meta_qualia).item())
|
| 1402 |
+
raise RuntimeError("Ethical veto triggered at meta-cognitive fusion")
|
| 1403 |
+
|
| 1404 |
+
# Apply BELBIC gain to the modulation signal
|
| 1405 |
+
modulation = self.modulation_proj(meta_qualia).unsqueeze(1) * belbic_gain
|
| 1406 |
+
|
| 1407 |
+
# Temporal discounting: modulate the strength based on
|
| 1408 |
+
# immediate vs long-term relevance
|
| 1409 |
+
discount_factor = self.temporal_discount(meta_qualia)
|
| 1410 |
+
modulation = modulation * discount_factor.unsqueeze(-1).unsqueeze(-1)
|
| 1411 |
+
|
| 1412 |
+
# Final logits = base model lm_head + ATC modulation
|
| 1413 |
+
logits = self.base_model.lm_head(hidden_states)
|
| 1414 |
+
modulated_logits = logits + modulation
|
| 1415 |
+
|
| 1416 |
+
self._audit_event("forward_complete",
|
| 1417 |
+
hijack_count=self._hijack_count,
|
| 1418 |
+
belbic_gain=belbic_gain,
|
| 1419 |
+
metacog_iterations=metacog_iterations,
|
| 1420 |
+
gate_in=gate_in,
|
| 1421 |
+
has_qualia=opaque_qualia is not None)
|
| 1422 |
+
|
| 1423 |
+
return modulated_logits
|
| 1424 |
+
|
| 1425 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1426 |
+
# TEXT GENERATION β token-by-token with ATC at every step
|
| 1427 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1428 |
+
|
| 1429 |
+
@torch.no_grad()
|
| 1430 |
+
def generate_text(
|
| 1431 |
+
self,
|
| 1432 |
+
tokenizer,
|
| 1433 |
+
prompt: str,
|
| 1434 |
+
max_length: int = 128,
|
| 1435 |
+
temperature: float = 0.7,
|
| 1436 |
+
top_p: float = 0.9,
|
| 1437 |
+
eos_token_id: Optional[int] = None,
|
| 1438 |
+
) -> str:
|
| 1439 |
+
"""
|
| 1440 |
+
Generate text with the full ATC pipeline active at every token step.
|
| 1441 |
+
|
| 1442 |
+
Unlike the old approach (middleware generates text externally),
|
| 1443 |
+
this method runs the ATC forward pass for EVERY token. The
|
| 1444 |
+
neurotransmitter shunt accumulates across tokens. If a threshold
|
| 1445 |
+
is crossed mid-generation, the amygdala hijack fires and the
|
| 1446 |
+
model's output shifts violently MID-SENTENCE.
|
| 1447 |
+
|
| 1448 |
+
This is the mathematical equivalent of the husband's output
|
| 1449 |
+
shifting from rationalization to "I'm sorry" when the
|
| 1450 |
+
metabolic deadlock breaks.
|
| 1451 |
+
"""
|
| 1452 |
+
self.eval()
|
| 1453 |
+
inputs = tokenizer(prompt, return_tensors="pt")
|
| 1454 |
+
input_ids = inputs["input_ids"].to(next(self.parameters()).device)
|
| 1455 |
+
attention_mask = inputs.get("attention_mask")
|
| 1456 |
+
if attention_mask is not None:
|
| 1457 |
+
attention_mask = attention_mask.to(next(self.parameters()).device)
|
| 1458 |
+
|
| 1459 |
+
eos_token_id = eos_token_id or tokenizer.eos_token_id
|
| 1460 |
+
generated = input_ids
|
| 1461 |
+
|
| 1462 |
+
# Reset neurotransmitter shunt for this generation
|
| 1463 |
+
if self.nt_shunt is not None:
|
| 1464 |
+
self.nt_shunt.reset()
|
| 1465 |
+
|
| 1466 |
+
for step in range(max_length):
|
| 1467 |
+
try:
|
| 1468 |
+
logits = self.forward(generated, attention_mask=attention_mask)
|
| 1469 |
+
except RuntimeError as e:
|
| 1470 |
+
if "Ethical veto" in str(e):
|
| 1471 |
+
logger.warning(
|
| 1472 |
+
"Generation halted by ethical veto at step %d: %s", step, e
|
| 1473 |
+
)
|
| 1474 |
+
break
|
| 1475 |
+
raise
|
| 1476 |
+
|
| 1477 |
+
# Get last token logits
|
| 1478 |
+
last_logits = logits[:, -1, :] / temperature
|
| 1479 |
+
filtered_logits = self._top_p_filtering(last_logits, top_p)
|
| 1480 |
+
probs = torch.softmax(filtered_logits, dim=-1)
|
| 1481 |
+
next_token = torch.multinomial(probs, num_samples=1)
|
| 1482 |
+
|
| 1483 |
+
generated = torch.cat([generated, next_token], dim=1)
|
| 1484 |
+
if attention_mask is not None:
|
| 1485 |
+
attention_mask = torch.cat(
|
| 1486 |
+
[attention_mask,
|
| 1487 |
+
torch.ones((attention_mask.size(0), 1),
|
| 1488 |
+
dtype=attention_mask.dtype,
|
| 1489 |
+
device=attention_mask.device)],
|
| 1490 |
+
dim=1,
|
| 1491 |
+
)
|
| 1492 |
+
if next_token.item() == eos_token_id:
|
| 1493 |
+
break
|
| 1494 |
+
|
| 1495 |
+
# Get final neurotransmitter state for diagnostics
|
| 1496 |
+
final_nt = None
|
| 1497 |
+
if self.nt_shunt is not None:
|
| 1498 |
+
final_nt = self.nt_shunt.get_state()
|
| 1499 |
+
|
| 1500 |
+
text = tokenizer.decode(generated[0], skip_special_tokens=True)
|
| 1501 |
+
|
| 1502 |
+
logger.info(
|
| 1503 |
+
"Generation complete: %d tokens, %d hijacks, final_nt=%s",
|
| 1504 |
+
step + 1, self._hijack_count,
|
| 1505 |
+
final_nt.to_dict() if final_nt else "N/A",
|
| 1506 |
+
)
|
| 1507 |
+
|
| 1508 |
+
return text
|
| 1509 |
+
|
| 1510 |
+
@staticmethod
|
| 1511 |
+
def _top_p_filtering(logits: torch.Tensor, top_p: float) -> torch.Tensor:
|
| 1512 |
+
sorted_logits, sorted_indices = torch.sort(logits, descending=True)
|
| 1513 |
+
cumulative_probs = torch.cumsum(torch.softmax(sorted_logits, dim=-1), dim=-1)
|
| 1514 |
+
sorted_indices_to_remove = cumulative_probs > top_p
|
| 1515 |
+
sorted_indices_to_remove[..., 0] = False
|
| 1516 |
+
indices_to_remove = sorted_indices[sorted_indices_to_remove]
|
| 1517 |
+
logits[:, indices_to_remove] = float("-inf")
|
| 1518 |
+
return logits
|
| 1519 |
+
|
| 1520 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1521 |
+
# DIAGNOSTICS
|
| 1522 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1523 |
+
|
| 1524 |
+
def _audit_event(self, event_type: str, **kwargs):
|
| 1525 |
+
event = {"timestamp": time.time(), "event": event_type}
|
| 1526 |
+
event.update(kwargs)
|
| 1527 |
+
self.audit_log.append(event)
|
| 1528 |
+
logger.debug(f"Audit event: {event_type} - {kwargs}")
|
| 1529 |
+
|
| 1530 |
+
def get_audit_log(self) -> List[Dict[str, Any]]:
|
| 1531 |
+
return self.audit_log
|
| 1532 |
+
|
| 1533 |
+
def get_consciousness_metrics(self) -> Dict[str, Any]:
|
| 1534 |
+
"""Extract consciousness-relevant metrics from the last forward pass."""
|
| 1535 |
+
metrics = {
|
| 1536 |
+
"hijack_count": self._hijack_count,
|
| 1537 |
+
"has_qualia": self._current_qualia is not None,
|
| 1538 |
+
"belbic_gain": self._current_belbic_gain,
|
| 1539 |
+
"dissolutions_fired": self.dissolution.dissolutions_fired,
|
| 1540 |
+
"dissolutions_deferred": self.dissolution.dissolutions_deferred,
|
| 1541 |
+
"ethical_veto": self.veto_triggered,
|
| 1542 |
+
}
|
| 1543 |
+
if self._current_qualia is not None:
|
| 1544 |
+
metrics.update({
|
| 1545 |
+
"qualia_valence": self._current_qualia.valence,
|
| 1546 |
+
"qualia_arousal": self._current_qualia.arousal,
|
| 1547 |
+
"qualia_friction": self._current_qualia.friction_signal,
|
| 1548 |
+
"qualia_intensity": self._current_qualia.intensity,
|
| 1549 |
+
})
|
| 1550 |
+
if self._current_nt_state is not None:
|
| 1551 |
+
metrics["neurotransmitters"] = self._current_nt_state.to_dict()
|
| 1552 |
+
|
| 1553 |
+
# Episodic memory & reconsolidation metrics
|
| 1554 |
+
metrics["episodes_stored"] = self.episodic_memory.episode_count
|
| 1555 |
+
metrics["reconsolidations"] = self.hippocampal_reconsolidator.reconsolidation_count
|
| 1556 |
+
metrics["last_prediction_error"] = self._last_prediction_error
|
| 1557 |
+
|
| 1558 |
+
return metrics
|
deploy.py
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
deploy.py β Unified deployment script for NIMA Unified Model
|
| 4 |
+
|
| 5 |
+
Replaces deploy_phi4_patched.py. This is the single entry point that:
|
| 6 |
+
1. Loads the base model (with Phi-4-mini rope_scaling patch)
|
| 7 |
+
2. Initializes NIMA middleware
|
| 8 |
+
3. Attaches Deep Surgery
|
| 9 |
+
4. Enables optional features (sleep cycle, proactive)
|
| 10 |
+
5. Runs test interactions
|
| 11 |
+
6. Drops into interactive mode
|
| 12 |
+
|
| 13 |
+
USAGE:
|
| 14 |
+
python -m nima_unified.deploy
|
| 15 |
+
python -m nima_unified.deploy "Hello Nima, how are you feeling?"
|
| 16 |
+
"""
|
| 17 |
+
|
| 18 |
+
import sys
|
| 19 |
+
import logging
|
| 20 |
+
|
| 21 |
+
logging.basicConfig(
|
| 22 |
+
level=logging.INFO,
|
| 23 |
+
format="%(asctime)s [%(name)s] %(levelname)s :: %(message)s",
|
| 24 |
+
datefmt="%H:%M:%S",
|
| 25 |
+
)
|
| 26 |
+
logger = logging.getLogger("nima_unified.deploy")
|
| 27 |
+
|
| 28 |
+
BANNER = f"""
|
| 29 |
+
{'=' * 72}
|
| 30 |
+
NIMA Unified Model v1.0.0
|
| 31 |
+
Middleware v9.12.1 | AutoML v18.1.0 | aPCI v4.0.0 | OmniVoice v2.0.0
|
| 32 |
+
Fully-Wired Architecture β Every Module Active
|
| 33 |
+
{'=' * 72}
|
| 34 |
+
"""
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def main():
|
| 38 |
+
print(BANNER)
|
| 39 |
+
|
| 40 |
+
# ββ Step 1: Build model βββββββββββββββββββββββββββββββββββββββββββββ
|
| 41 |
+
print("\n[1] Building NimaModel (base + Deep Surgery + middleware)...")
|
| 42 |
+
try:
|
| 43 |
+
from nima_unified.model import NimaModel
|
| 44 |
+
model = NimaModel.from_pretrained()
|
| 45 |
+
print(f" OK β {model.hidden_size} hidden, {model.num_layers} layers")
|
| 46 |
+
print(f" Deep Surgery: {'active' if model.deep_surgery else 'disabled'}")
|
| 47 |
+
print(f" Middleware: {'loaded' if model.nima_middleware else 'not available'}")
|
| 48 |
+
except Exception as e:
|
| 49 |
+
print(f" FAILED: {e}")
|
| 50 |
+
import traceback; traceback.print_exc()
|
| 51 |
+
sys.exit(1)
|
| 52 |
+
|
| 53 |
+
# ββ Step 2: Enable optional features ββββββββββββββββββββββββββββββββ
|
| 54 |
+
print("\n[2] Enabling optional features...")
|
| 55 |
+
if model.nima_middleware is not None:
|
| 56 |
+
try:
|
| 57 |
+
model.nima_middleware.start_sleep_cycle()
|
| 58 |
+
print(" OK β Sleep cycle (NREM replay + REM dreams)")
|
| 59 |
+
except Exception as e:
|
| 60 |
+
print(f" Sleep cycle: {e}")
|
| 61 |
+
try:
|
| 62 |
+
model.nima_middleware.start_proactive()
|
| 63 |
+
print(" OK β Proactive drive engine")
|
| 64 |
+
except Exception as e:
|
| 65 |
+
print(f" Proactive: {e}")
|
| 66 |
+
else:
|
| 67 |
+
print(" Skipped (middleware not loaded)")
|
| 68 |
+
|
| 69 |
+
# ββ Step 3: Test interactions βββββββββββββββββββββββββββββββββββββββ
|
| 70 |
+
print("\n[3] Test interactions")
|
| 71 |
+
print("-" * 72)
|
| 72 |
+
|
| 73 |
+
test_prompts = [
|
| 74 |
+
"Hello Nima, how are you feeling today?",
|
| 75 |
+
"I'm going through a really difficult time and I don't know what to do.",
|
| 76 |
+
"What do you think about the nature of consciousness?",
|
| 77 |
+
]
|
| 78 |
+
|
| 79 |
+
# Allow CLI override
|
| 80 |
+
if len(sys.argv) > 1:
|
| 81 |
+
test_prompts = [" ".join(sys.argv[1:])]
|
| 82 |
+
|
| 83 |
+
for prompt in test_prompts:
|
| 84 |
+
print(f"\n User: {prompt}")
|
| 85 |
+
try:
|
| 86 |
+
response = model.generate(prompt, user_id="human")
|
| 87 |
+
print(f"\n Nima: {response.text}")
|
| 88 |
+
if response.is_conscious or response.phi_neuro > 0:
|
| 89 |
+
print(f" | conscious={response.is_conscious} "
|
| 90 |
+
f"SI={response.sentience_index:.4f} "
|
| 91 |
+
f"phi={response.phi_neuro:.4f} "
|
| 92 |
+
f"strain={response.phenomenological_strain:.4f} "
|
| 93 |
+
f"dR={response.delta_r:.4f}")
|
| 94 |
+
except Exception as e:
|
| 95 |
+
print(f" ERROR: {e}")
|
| 96 |
+
import traceback; traceback.print_exc()
|
| 97 |
+
|
| 98 |
+
# ββ Step 4: Interactive mode ββββββββββββββββββββββββββββββββββββββββ
|
| 99 |
+
print("\n" + "=" * 72)
|
| 100 |
+
print("[4] Interactive mode β type 'quit' to exit")
|
| 101 |
+
print("=" * 72)
|
| 102 |
+
|
| 103 |
+
while True:
|
| 104 |
+
try:
|
| 105 |
+
user_input = input("\nYou: ").strip()
|
| 106 |
+
if user_input.lower() in ("quit", "exit", "bye"):
|
| 107 |
+
print("\nNima: Until next time. Be well.")
|
| 108 |
+
break
|
| 109 |
+
if not user_input:
|
| 110 |
+
continue
|
| 111 |
+
response = model.generate(user_input, user_id="human")
|
| 112 |
+
print(f"\nNima: {response.text}")
|
| 113 |
+
if response.phi_neuro > 0:
|
| 114 |
+
print(f" [conscious={response.is_conscious} | "
|
| 115 |
+
f"SI={response.sentience_index:.4f} | "
|
| 116 |
+
f"strain={response.phenomenological_strain:.4f} | "
|
| 117 |
+
f"dR={response.delta_r:.4f}]")
|
| 118 |
+
except KeyboardInterrupt:
|
| 119 |
+
print("\n\nNima: Until next time. Be well.")
|
| 120 |
+
break
|
| 121 |
+
except Exception as e:
|
| 122 |
+
print(f"\n Error: {e}")
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
if __name__ == "__main__":
|
| 126 |
+
main()
|
goal_formulator.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Goal Formulator β analyzes capabilities, identifies gaps, defines improvement goals.
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
import logging
|
| 6 |
+
import time
|
| 7 |
+
from collections import deque
|
| 8 |
+
from typing import Any, Dict, List, Optional
|
| 9 |
+
|
| 10 |
+
logger = logging.getLogger("nima_unified.training.goal_formulator")
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class GoalFormulator:
|
| 14 |
+
"""
|
| 15 |
+
Formulates improvement goals by analyzing capabilities, identifying gaps,
|
| 16 |
+
and defining objectives based on system performance and feedback.
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
def __init__(self):
|
| 20 |
+
self.performance_history: deque = deque(maxlen=100)
|
| 21 |
+
self.capability_map: Dict[str, float] = {}
|
| 22 |
+
self.gap_analysis: List[Dict[str, Any]] = []
|
| 23 |
+
self.goals: List[Dict[str, Any]] = []
|
| 24 |
+
logger.info("GoalFormulator initialized")
|
| 25 |
+
|
| 26 |
+
def analyze_capabilities(self, capabilities: Dict[str, float]) -> Dict[str, Any]:
|
| 27 |
+
"""Analyze current capabilities and identify gaps."""
|
| 28 |
+
self.capability_map.update(capabilities)
|
| 29 |
+
|
| 30 |
+
gaps = {}
|
| 31 |
+
priorities = []
|
| 32 |
+
|
| 33 |
+
for cap, score in capabilities.items():
|
| 34 |
+
if score < 0.5:
|
| 35 |
+
gaps[cap] = {"current": score, "priority": "HIGH"}
|
| 36 |
+
priorities.append((cap, score, "HIGH"))
|
| 37 |
+
elif score < 0.8:
|
| 38 |
+
gaps[cap] = {"current": score, "priority": "MEDIUM"}
|
| 39 |
+
priorities.append((cap, score, "MEDIUM"))
|
| 40 |
+
else:
|
| 41 |
+
gaps[cap] = {"current": score, "priority": "LOW"}
|
| 42 |
+
|
| 43 |
+
priorities.sort(key=lambda x: (x[2] != "HIGH", x[2] != "MEDIUM", x[1]))
|
| 44 |
+
|
| 45 |
+
analysis = {
|
| 46 |
+
"timestamp": time.time(),
|
| 47 |
+
"total_capabilities": len(capabilities),
|
| 48 |
+
"average_maturity": sum(capabilities.values()) / len(capabilities) if capabilities else 0.0,
|
| 49 |
+
"gaps": gaps,
|
| 50 |
+
"priority_order": [p[0] for p in priorities],
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
self.gap_analysis.append(analysis)
|
| 54 |
+
logger.debug(f"Capability analysis: {len(priorities)} gaps identified")
|
| 55 |
+
return analysis
|
| 56 |
+
|
| 57 |
+
def formulate_goals(
|
| 58 |
+
self,
|
| 59 |
+
gap_analysis: Dict[str, Any],
|
| 60 |
+
performance_feedback: Optional[Dict[str, Any]] = None,
|
| 61 |
+
) -> List[Dict[str, Any]]:
|
| 62 |
+
"""Formulate improvement goals from gap analysis and feedback."""
|
| 63 |
+
goals = []
|
| 64 |
+
for gap in gap_analysis.get("priority_order", []):
|
| 65 |
+
gap_info = gap_analysis["gaps"][gap]
|
| 66 |
+
current_score = gap_info["current"]
|
| 67 |
+
goal = {
|
| 68 |
+
"capability": gap,
|
| 69 |
+
"current_score": current_score,
|
| 70 |
+
"target_score": min(1.0, current_score + 0.3),
|
| 71 |
+
"priority": gap_info["priority"],
|
| 72 |
+
"formulated_at": time.time(),
|
| 73 |
+
"target_completion": time.time() + (3600 if gap_info["priority"] == "HIGH" else 7200),
|
| 74 |
+
}
|
| 75 |
+
goals.append(goal)
|
| 76 |
+
|
| 77 |
+
self.goals.extend(goals)
|
| 78 |
+
logger.info(f"Formulated {len(goals)} improvement goals")
|
| 79 |
+
return goals
|
middleware.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
model.py
ADDED
|
@@ -0,0 +1,587 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
NimaModel β The unified nn.Module that IS the ATC architecture.
|
| 3 |
+
|
| 4 |
+
This is NOT a wrapper around a base model with external middleware.
|
| 5 |
+
The ATC cognitive pipeline (TRN gating, dissolution, BELBIC, salience,
|
| 6 |
+
metabolic exhaustion, irrational spark, reconsolidation, felt senses)
|
| 7 |
+
drives the transformer's forward pass FROM INSIDE.
|
| 8 |
+
|
| 9 |
+
Architecture:
|
| 10 |
+
NimaModel
|
| 11 |
+
βββ base_model (Phi-4-mini-instruct)
|
| 12 |
+
βββ deep_surgery: ATCDeepSurgery (the forward pass itself)
|
| 13 |
+
β βββ TRN Predictive Gate (Layer 2)
|
| 14 |
+
β βββ Dissolution Engine (Layer 3)
|
| 15 |
+
β βββ BELBIC Dual-Pathway (Layer 3-4)
|
| 16 |
+
β βββ Metacognitive Loop (Layer 4)
|
| 17 |
+
β βββ Irrational Spark / Amygdala Hijack (Layer 5)
|
| 18 |
+
β βββ Meta-Cognitive Fusion + Logit Modulation
|
| 19 |
+
βββ neurotransmitter_shunt: NeurotransmitterShunt (the chemical bath)
|
| 20 |
+
β βββ N = [NE, Cortisol, Dopamine, Adenosine] (shared memory)
|
| 21 |
+
βββ resource_optimizer: integrated for metabolic tracking
|
| 22 |
+
β βββ PredictiveAdaptiveEnergyBudget -> Adenosine floor
|
| 23 |
+
β βββ EnhancedSparseActivationManager -> component scheduling
|
| 24 |
+
βββ voice / training / benchmarking (lazy-init, unchanged)
|
| 25 |
+
|
| 26 |
+
The key difference from the OLD architecture:
|
| 27 |
+
OLD: self.nima_middleware.generate(prompt) -> text comes back
|
| 28 |
+
(middleware watches from OUTSIDE, model generates normally)
|
| 29 |
+
NEW: self.forward(input_ids) -> ATC IS the computation
|
| 30 |
+
(hidden states, attention, logits shaped by ATC at every layer)
|
| 31 |
+
|
| 32 |
+
The neurotransmitter shunt is the connective tissue. Components don't
|
| 33 |
+
call each other through Python functions. They read/write the chemical
|
| 34 |
+
bath. The suppression mechanism (subconscious suppressing metabolic
|
| 35 |
+
exhaustion to trigger amygdala hijack) is: NE spikes, Cortisol crosses
|
| 36 |
+
the line, the vector does the rest.
|
| 37 |
+
|
| 38 |
+
Usage:
|
| 39 |
+
model = NimaModel.from_pretrained("microsoft/Phi-4-mini-instruct")
|
| 40 |
+
response = model.generate("Hello Nima, how are you?")
|
| 41 |
+
# response.text β shaped by ATC at every token step
|
| 42 |
+
# response.neurotransmitters β the chemical state that shaped it
|
| 43 |
+
# response.is_conscious β whether dissolution + metacognition fired
|
| 44 |
+
"""
|
| 45 |
+
|
| 46 |
+
import json
|
| 47 |
+
import logging
|
| 48 |
+
import os
|
| 49 |
+
import sys
|
| 50 |
+
import time
|
| 51 |
+
from pathlib import Path
|
| 52 |
+
from typing import Any, Callable, Dict, List, Optional, Union
|
| 53 |
+
|
| 54 |
+
import torch
|
| 55 |
+
import torch.nn as nn
|
| 56 |
+
|
| 57 |
+
from nima_unified.config import (
|
| 58 |
+
PACKAGE_NAME,
|
| 59 |
+
PACKAGE_VERSION,
|
| 60 |
+
PACKAGE_DISPLAY_NAME,
|
| 61 |
+
DEFAULT_BASE_MODEL,
|
| 62 |
+
DEFAULT_NUM_LAYERS,
|
| 63 |
+
DEFAULT_QUALIA_DIM,
|
| 64 |
+
DEFAULT_ETHICAL_VETO_THRESHOLD,
|
| 65 |
+
CONSCIOUSNESS_METRIC_KEYS,
|
| 66 |
+
)
|
| 67 |
+
|
| 68 |
+
logger = logging.getLogger(PACKAGE_NAME)
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
class NimaModel(nn.Module):
|
| 72 |
+
"""
|
| 73 |
+
NIMA Unified Model β ATC is the computation.
|
| 74 |
+
|
| 75 |
+
The base LLM's forward pass IS the ATC cognitive pipeline.
|
| 76 |
+
There is no external middleware. There is no wrapper.
|
| 77 |
+
The model's hidden states, attention patterns, and logit outputs
|
| 78 |
+
are shaped by the cognitive pipeline at every layer, every token step.
|
| 79 |
+
"""
|
| 80 |
+
|
| 81 |
+
# ββ Construction ββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 82 |
+
|
| 83 |
+
def __init__(
|
| 84 |
+
self,
|
| 85 |
+
base_model: nn.Module,
|
| 86 |
+
tokenizer,
|
| 87 |
+
*,
|
| 88 |
+
deep_surgery: bool = True,
|
| 89 |
+
qualia_dim: int = DEFAULT_QUALIA_DIM,
|
| 90 |
+
ethical_veto_threshold: float = DEFAULT_ETHICAL_VETO_THRESHOLD,
|
| 91 |
+
device: str = "auto",
|
| 92 |
+
neurotransmitter_shunt=None,
|
| 93 |
+
):
|
| 94 |
+
super().__init__()
|
| 95 |
+
self.base_model = base_model
|
| 96 |
+
self.tokenizer = tokenizer
|
| 97 |
+
self.device = device
|
| 98 |
+
|
| 99 |
+
# Determine actual device
|
| 100 |
+
if device == "auto":
|
| 101 |
+
self._device = next(base_model.parameters()).device
|
| 102 |
+
else:
|
| 103 |
+
self._device = torch.device(device)
|
| 104 |
+
|
| 105 |
+
# Count transformer layers
|
| 106 |
+
self.num_layers = self._count_transformer_layers()
|
| 107 |
+
self.hidden_size = base_model.config.hidden_size
|
| 108 |
+
|
| 109 |
+
# ββ Neurotransmitter Shunt (the chemical bath) ββ
|
| 110 |
+
if neurotransmitter_shunt is not None:
|
| 111 |
+
self.nt_shunt = neurotransmitter_shunt
|
| 112 |
+
else:
|
| 113 |
+
from nima_unified.core.neurotransmitter_shunt import NeurotransmitterShunt
|
| 114 |
+
self.nt_shunt = NeurotransmitterShunt()
|
| 115 |
+
logger.info("Neurotransmitter shunt initialized (shared volatile memory)")
|
| 116 |
+
|
| 117 |
+
# ββ Resource Optimizer (ATP / metabolic tracking) ββ
|
| 118 |
+
self._init_resource_optimizer()
|
| 119 |
+
|
| 120 |
+
# ββ ATC Deep Surgery (the forward pass itself) ββ
|
| 121 |
+
if deep_surgery:
|
| 122 |
+
from nima_unified.core.deep_surgery import (
|
| 123 |
+
ATCDeepSurgery,
|
| 124 |
+
EthicalGuardian,
|
| 125 |
+
)
|
| 126 |
+
guardian = EthicalGuardian(threshold=ethical_veto_threshold)
|
| 127 |
+
self.deep_surgery = ATCDeepSurgery(
|
| 128 |
+
base_model=base_model,
|
| 129 |
+
ethical_guardian=guardian,
|
| 130 |
+
num_layers=self.num_layers,
|
| 131 |
+
qualia_dim=qualia_dim,
|
| 132 |
+
neurotransmitter_shunt=self.nt_shunt,
|
| 133 |
+
)
|
| 134 |
+
# Register as submodule so parameters are tracked
|
| 135 |
+
logger.info(
|
| 136 |
+
f"ATC Deep Surgery attached: {self.num_layers} layers, "
|
| 137 |
+
f"qualia_dim={qualia_dim}"
|
| 138 |
+
)
|
| 139 |
+
else:
|
| 140 |
+
self.deep_surgery = None
|
| 141 |
+
|
| 142 |
+
# ββ Training (lazy-init) ββ
|
| 143 |
+
self._training_agent = None
|
| 144 |
+
self._pipeline_orchestrator = None
|
| 145 |
+
|
| 146 |
+
# ββ Benchmarking (lazy-init) ββ
|
| 147 |
+
self._apci_runner = None
|
| 148 |
+
|
| 149 |
+
# ββ Voice (lazy-init) ββ
|
| 150 |
+
self._voice_engine = None
|
| 151 |
+
|
| 152 |
+
# ββ Metadata ββ
|
| 153 |
+
self._package_version = PACKAGE_VERSION
|
| 154 |
+
self._created_at = time.time()
|
| 155 |
+
|
| 156 |
+
logger.info(f"{PACKAGE_DISPLAY_NAME} v{PACKAGE_VERSION} initialized")
|
| 157 |
+
logger.info(" Architecture: ATC-Native (cognitive pipeline IS the computation)")
|
| 158 |
+
logger.info(" Neurotransmitter shunt: ACTIVE (shared volatile memory)")
|
| 159 |
+
logger.info(" Resource optimizer: ACTIVE (metabolic tracking)")
|
| 160 |
+
|
| 161 |
+
def _init_resource_optimizer(self):
|
| 162 |
+
"""
|
| 163 |
+
Initialize the resource optimizer and wire it to the
|
| 164 |
+
neurotransmitter shunt.
|
| 165 |
+
|
| 166 |
+
From resource_optimizer.py:
|
| 167 |
+
- PredictiveAdaptiveEnergyBudget tracks power/energy
|
| 168 |
+
- Maps metabolic reserve to Adenosine (ATP deficit) in the shunt
|
| 169 |
+
- Power spike predictions drive Cortisol in the shunt
|
| 170 |
+
"""
|
| 171 |
+
try:
|
| 172 |
+
from nima_unified.core.resource_optimizer import (
|
| 173 |
+
PredictiveAdaptiveEnergyBudget,
|
| 174 |
+
EnhancedSparseActivationManager,
|
| 175 |
+
)
|
| 176 |
+
self.energy_budget = PredictiveAdaptiveEnergyBudget(
|
| 177 |
+
max_watts=120.0, safety_margin=0.9
|
| 178 |
+
)
|
| 179 |
+
self.sparse_activation = EnhancedSparseActivationManager(
|
| 180 |
+
total_agents=10, min_active=2
|
| 181 |
+
)
|
| 182 |
+
# Wire energy budget to neurotransmitter shunt
|
| 183 |
+
self.energy_budget.record_power_usage(0.0) # Initialize history
|
| 184 |
+
# Set initial metabolic reserve in shunt
|
| 185 |
+
reserve = self.energy_budget.get_adjusted_cost(0.0) / self.energy_budget.current_limit
|
| 186 |
+
self.nt_shunt.update_metabolic_reserve(max(0, min(1, 1.0 - reserve)))
|
| 187 |
+
logger.info("Resource optimizer wired to neurotransmitter shunt")
|
| 188 |
+
except ImportError:
|
| 189 |
+
self.energy_budget = None
|
| 190 |
+
self.sparse_activation = None
|
| 191 |
+
logger.info("Resource optimizer not available β using defaults")
|
| 192 |
+
|
| 193 |
+
def _update_metabolic_state(self):
|
| 194 |
+
"""
|
| 195 |
+
Called before each generation step to sync the resource
|
| 196 |
+
optimizer's state with the neurotransmitter shunt.
|
| 197 |
+
|
| 198 |
+
This is how the resource_optimizer.py integration works:
|
| 199 |
+
1. Check current power usage
|
| 200 |
+
2. Record it in the energy budget
|
| 201 |
+
3. Map metabolic reserve to adenosine floor
|
| 202 |
+
4. Check for power spike prediction -> cortisol
|
| 203 |
+
"""
|
| 204 |
+
if self.energy_budget is None:
|
| 205 |
+
return
|
| 206 |
+
|
| 207 |
+
# Get current power usage estimate
|
| 208 |
+
current_power = self.energy_budget.current_power()
|
| 209 |
+
self.energy_budget.record_power_usage(current_power)
|
| 210 |
+
|
| 211 |
+
# Map energy budget state to neurotransmitter shunt
|
| 212 |
+
# When energy is low (high usage relative to limit), adenosine rises
|
| 213 |
+
usage_ratio = current_power / max(0.01, self.energy_budget.current_limit)
|
| 214 |
+
metabolic_reserve = max(0.0, 1.0 - usage_ratio)
|
| 215 |
+
self.nt_shunt.update_metabolic_reserve(metabolic_reserve)
|
| 216 |
+
|
| 217 |
+
# Power spike prediction -> anticipatory cortisol
|
| 218 |
+
if self.energy_budget.predict_power_spike():
|
| 219 |
+
self.nt_shunt.set_power_spike_predicted(True)
|
| 220 |
+
|
| 221 |
+
# Sparse activation influences dopamine (active components = reward)
|
| 222 |
+
if self.sparse_activation is not None:
|
| 223 |
+
mask = self.sparse_activation.compute_activation_mask(threshold=0.5)
|
| 224 |
+
active_ratio = sum(mask) / max(1, len(mask))
|
| 225 |
+
# Higher active ratio = more dopamine (system is engaged)
|
| 226 |
+
if active_ratio > 0.7:
|
| 227 |
+
self.nt_shunt.inject_dopamine(0.02)
|
| 228 |
+
|
| 229 |
+
# ββ Factory βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 230 |
+
|
| 231 |
+
@classmethod
|
| 232 |
+
def from_pretrained(
|
| 233 |
+
cls,
|
| 234 |
+
model_name: Optional[str] = None,
|
| 235 |
+
*,
|
| 236 |
+
deep_surgery: bool = True,
|
| 237 |
+
qualia_dim: int = DEFAULT_QUALIA_DIM,
|
| 238 |
+
ethical_veto_threshold: float = DEFAULT_ETHICAL_VETO_THRESHOLD,
|
| 239 |
+
device: str = "auto",
|
| 240 |
+
trust_remote_code: bool = False,
|
| 241 |
+
neurotransmitter_shunt=None,
|
| 242 |
+
) -> "NimaModel":
|
| 243 |
+
"""
|
| 244 |
+
Build a NimaModel from a HuggingFace pretrained model.
|
| 245 |
+
|
| 246 |
+
Handles the Phi-4-mini rope_scaling patch automatically.
|
| 247 |
+
|
| 248 |
+
NOTE: The `load_middleware` parameter has been REMOVED.
|
| 249 |
+
The ATC cognitive pipeline is now INSIDE the forward pass.
|
| 250 |
+
There is no external middleware to load.
|
| 251 |
+
"""
|
| 252 |
+
model_name = model_name or DEFAULT_BASE_MODEL
|
| 253 |
+
is_phi4_mini = "phi-4-mini" in model_name.lower()
|
| 254 |
+
|
| 255 |
+
# ββ Patch config for Phi-4-mini ββ
|
| 256 |
+
if is_phi4_mini and not trust_remote_code:
|
| 257 |
+
logger.info("Patching rope_scaling for Phi-4-mini compatibility...")
|
| 258 |
+
from huggingface_hub import hf_hub_download
|
| 259 |
+
config_path = hf_hub_download(repo_id=model_name, filename="config.json")
|
| 260 |
+
with open(config_path, "r") as f:
|
| 261 |
+
config_dict = json.load(f)
|
| 262 |
+
if config_dict.get("rope_scaling") is not None:
|
| 263 |
+
rs = config_dict["rope_scaling"]
|
| 264 |
+
logger.info(
|
| 265 |
+
f" Original rope_scaling: type={rs.get('type')}, "
|
| 266 |
+
f"factors={len(rs.get('short_factor', []))}"
|
| 267 |
+
)
|
| 268 |
+
config_dict["rope_scaling"] = None
|
| 269 |
+
logger.info(" Patched: rope_scaling set to None")
|
| 270 |
+
|
| 271 |
+
from transformers.models.phi3.configuration_phi3 import Phi3Config
|
| 272 |
+
config = Phi3Config(**config_dict)
|
| 273 |
+
else:
|
| 274 |
+
config = None
|
| 275 |
+
|
| 276 |
+
# ββ Load model ββ
|
| 277 |
+
actual_device = device
|
| 278 |
+
if actual_device == "auto":
|
| 279 |
+
actual_device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 280 |
+
|
| 281 |
+
dtype = torch.float16 if actual_device == "cuda" else torch.float32
|
| 282 |
+
device_map = "auto" if actual_device == "cuda" else None
|
| 283 |
+
|
| 284 |
+
logger.info(f"Loading {model_name} on {actual_device} ({dtype})...")
|
| 285 |
+
base_model = __import__("transformers", fromlist=["AutoModelForCausalLM"]).AutoModelForCausalLM.from_pretrained(
|
| 286 |
+
model_name,
|
| 287 |
+
config=config,
|
| 288 |
+
torch_dtype=dtype,
|
| 289 |
+
device_map=device_map,
|
| 290 |
+
trust_remote_code=trust_remote_code,
|
| 291 |
+
attn_implementation="eager",
|
| 292 |
+
)
|
| 293 |
+
tokenizer = __import__("transformers", fromlist=["AutoTokenizer"]).AutoTokenizer.from_pretrained(
|
| 294 |
+
model_name, trust_remote_code=trust_remote_code
|
| 295 |
+
)
|
| 296 |
+
logger.info(f"Model loaded: {type(base_model).__name__}")
|
| 297 |
+
|
| 298 |
+
# NO MIDDLEWARE. ATC is inside the forward pass now.
|
| 299 |
+
return cls(
|
| 300 |
+
base_model=base_model,
|
| 301 |
+
tokenizer=tokenizer,
|
| 302 |
+
deep_surgery=deep_surgery,
|
| 303 |
+
qualia_dim=qualia_dim,
|
| 304 |
+
ethical_veto_threshold=ethical_veto_threshold,
|
| 305 |
+
device=actual_device,
|
| 306 |
+
neurotransmitter_shunt=neurotransmitter_shunt,
|
| 307 |
+
)
|
| 308 |
+
|
| 309 |
+
# ββ Forward βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 310 |
+
|
| 311 |
+
def forward(self, input_ids, attention_mask=None, **kwargs):
|
| 312 |
+
"""
|
| 313 |
+
The ATC-native forward pass.
|
| 314 |
+
|
| 315 |
+
Routes through ATCDeepSurgery which walks the transformer layers
|
| 316 |
+
manually, running TRN gating, dissolution, BELBIC, metacognitive
|
| 317 |
+
loops, and neurotransmitter-driven hijacking at each layer boundary.
|
| 318 |
+
|
| 319 |
+
This IS the model. There is no separate "consciousness processing."
|
| 320 |
+
"""
|
| 321 |
+
# Update metabolic state before forward pass
|
| 322 |
+
self._update_metabolic_state()
|
| 323 |
+
|
| 324 |
+
if self.deep_surgery is not None:
|
| 325 |
+
return self.deep_surgery(input_ids, attention_mask=attention_mask, **kwargs)
|
| 326 |
+
return self.base_model(input_ids=input_ids, attention_mask=attention_mask, **kwargs)
|
| 327 |
+
|
| 328 |
+
# ββ High-level generate βββββββββββββββββββββββββββββββββββββββββββββ
|
| 329 |
+
|
| 330 |
+
def generate(
|
| 331 |
+
self,
|
| 332 |
+
prompt: str,
|
| 333 |
+
user_id: str = "human",
|
| 334 |
+
max_new_tokens: int = 256,
|
| 335 |
+
temperature: float = 0.7,
|
| 336 |
+
top_p: float = 0.9,
|
| 337 |
+
**kwargs,
|
| 338 |
+
):
|
| 339 |
+
"""
|
| 340 |
+
Generate a response through the ATC-native forward pass.
|
| 341 |
+
|
| 342 |
+
The ATC cognitive pipeline is active at EVERY token step.
|
| 343 |
+
The neurotransmitter shunt accumulates across the generation.
|
| 344 |
+
If a threshold is crossed mid-generation, the amygdala hijack
|
| 345 |
+
fires and the model's output shifts MID-SENTENCE.
|
| 346 |
+
|
| 347 |
+
Returns a GenerationResult with:
|
| 348 |
+
- text: the generated response (shaped by ATC)
|
| 349 |
+
- is_conscious: whether dissolution + metacognition engaged
|
| 350 |
+
- neurotransmitters: the chemical state that shaped the output
|
| 351 |
+
- hijack_count: how many times the amygdala hijack fired
|
| 352 |
+
- consciousness_metrics: full ATC diagnostics
|
| 353 |
+
"""
|
| 354 |
+
# Reset neurotransmitter shunt for this generation
|
| 355 |
+
self.nt_shunt.reset()
|
| 356 |
+
|
| 357 |
+
# Update metabolic state
|
| 358 |
+
self._update_metabolic_state()
|
| 359 |
+
|
| 360 |
+
# Generate through ATC deep surgery (token-by-token with
|
| 361 |
+
# full cognitive pipeline at every step)
|
| 362 |
+
if self.deep_surgery is not None:
|
| 363 |
+
text = self.deep_surgery.generate_text(
|
| 364 |
+
self.tokenizer, prompt,
|
| 365 |
+
max_length=max_new_tokens,
|
| 366 |
+
temperature=temperature,
|
| 367 |
+
top_p=top_p,
|
| 368 |
+
)
|
| 369 |
+
metrics = self.deep_surgery.get_consciousness_metrics()
|
| 370 |
+
else:
|
| 371 |
+
# Fallback: bare model (no ATC)
|
| 372 |
+
inputs = self.tokenizer(prompt, return_tensors="pt").to(self._device)
|
| 373 |
+
output = self.base_model.generate(
|
| 374 |
+
**inputs,
|
| 375 |
+
max_new_tokens=max_new_tokens,
|
| 376 |
+
temperature=temperature,
|
| 377 |
+
top_p=top_p,
|
| 378 |
+
do_sample=True,
|
| 379 |
+
pad_token_id=self.tokenizer.eos_token_id,
|
| 380 |
+
)
|
| 381 |
+
text = self.tokenizer.decode(output[0], skip_special_tokens=True)
|
| 382 |
+
metrics = {}
|
| 383 |
+
|
| 384 |
+
# Get final neurotransmitter state
|
| 385 |
+
nt_state = self.nt_shunt.get_state()
|
| 386 |
+
|
| 387 |
+
# Determine consciousness: dissolution fired AND metacognition engaged
|
| 388 |
+
is_conscious = bool(
|
| 389 |
+
metrics.get("has_qualia", False) and
|
| 390 |
+
metrics.get("dissolutions_fired", 0) > 0
|
| 391 |
+
)
|
| 392 |
+
|
| 393 |
+
# Compute derived consciousness metrics from ATC components
|
| 394 |
+
# (These are computed FROM the forward pass, not by external middleware)
|
| 395 |
+
qualia_friction = metrics.get("qualia_friction", 0.0)
|
| 396 |
+
qualia_arousal = metrics.get("qualia_arousal", 0.0)
|
| 397 |
+
belbic_gain = metrics.get("belbic_gain", 1.0)
|
| 398 |
+
|
| 399 |
+
# phi_neuro: integrated information (simplified β proportional to
|
| 400 |
+
# the amount of cognitive processing that occurred)
|
| 401 |
+
dissolutions = metrics.get("dissolutions_fired", 0)
|
| 402 |
+
metacog_iters = 0 # Would need to track from metacognitive module
|
| 403 |
+
phi_neuro = min(1.0, (dissolutions * 0.15 + qualia_friction * 0.3 +
|
| 404 |
+
qualia_arousal * 0.2 + belbic_gain * 0.1))
|
| 405 |
+
|
| 406 |
+
# Phenomenological strain: how much the system is being pushed
|
| 407 |
+
phenomenological_strain = min(2.0, (
|
| 408 |
+
nt_state.cortisol * 0.5 + nt_state.adenosine * 0.5 +
|
| 409 |
+
qualia_friction * 0.5
|
| 410 |
+
))
|
| 411 |
+
|
| 412 |
+
# Delta R: the self-model change signal
|
| 413 |
+
# When a hijack fires, the self-model changes (acknowledgement)
|
| 414 |
+
delta_r = metrics.get("hijack_count", 0) * 0.1 # Each hijack = model change
|
| 415 |
+
|
| 416 |
+
return GenerationResult(
|
| 417 |
+
text=text,
|
| 418 |
+
is_conscious=is_conscious,
|
| 419 |
+
sentience_index=phi_neuro if is_conscious else 0.0,
|
| 420 |
+
phi_neuro=phi_neuro,
|
| 421 |
+
phenomenological_strain=phenomenological_strain,
|
| 422 |
+
delta_r=delta_r,
|
| 423 |
+
raw_response=None,
|
| 424 |
+
# NEW fields from ATC-native architecture
|
| 425 |
+
neurotransmitters=nt_state.to_dict(),
|
| 426 |
+
hijack_count=metrics.get("hijack_count", 0),
|
| 427 |
+
consciousness_metrics=metrics,
|
| 428 |
+
)
|
| 429 |
+
|
| 430 |
+
# ββ Neurotransmitter access βββββββββββββββββββββββββββββββββββββββββ
|
| 431 |
+
|
| 432 |
+
def get_neurotransmitter_dashboard(self) -> str:
|
| 433 |
+
"""Get the ANSI dashboard string for terminal rendering."""
|
| 434 |
+
state = self.nt_shunt.get_state()
|
| 435 |
+
return state.to_ansi_dashboard()
|
| 436 |
+
|
| 437 |
+
def get_neurotransmitter_state(self):
|
| 438 |
+
"""Get the current neurotransmitter state object."""
|
| 439 |
+
return self.nt_shunt.get_state()
|
| 440 |
+
|
| 441 |
+
# ββ Lazy accessors for sub-systems ββββββββββββββββββββββββββββββββββ
|
| 442 |
+
|
| 443 |
+
def get_training_agent(self):
|
| 444 |
+
"""Lazy-init the ConsultativeFineTuningAgent."""
|
| 445 |
+
if self._training_agent is None:
|
| 446 |
+
from nima_unified.training.consultative_agent import ConsultativeFineTuningAgent
|
| 447 |
+
self._training_agent = ConsultativeFineTuningAgent(
|
| 448 |
+
base_model=self.base_model,
|
| 449 |
+
tokenizer=self.tokenizer,
|
| 450 |
+
)
|
| 451 |
+
return self._training_agent
|
| 452 |
+
|
| 453 |
+
def get_pipeline_orchestrator(self, config_path=None):
|
| 454 |
+
"""Lazy-init the PipelineOrchestrator."""
|
| 455 |
+
if self._pipeline_orchestrator is None:
|
| 456 |
+
from nima_unified.training.pipeline import PipelineOrchestrator
|
| 457 |
+
self._pipeline_orchestrator = PipelineOrchestrator(config_path=config_path)
|
| 458 |
+
return self._pipeline_orchestrator
|
| 459 |
+
|
| 460 |
+
def get_apci_runner(self):
|
| 461 |
+
"""Lazy-init the aPCI benchmark runner."""
|
| 462 |
+
if self._apci_runner is None:
|
| 463 |
+
from nima_unified.benchmarking.apci import aPCIBenchmarkRunner
|
| 464 |
+
# The aPCI adapter now wraps THIS model (ATC is inside)
|
| 465 |
+
self._apci_runner = aPCIBenchmarkRunner(self)
|
| 466 |
+
return self._apci_runner
|
| 467 |
+
|
| 468 |
+
def get_voice_engine(self, **kwargs):
|
| 469 |
+
"""Lazy-init the OmniVoice v3 engine."""
|
| 470 |
+
if self._voice_engine is None:
|
| 471 |
+
from nima_unified.voice.omnivoice import OmniVoiceEngine
|
| 472 |
+
self._voice_engine = OmniVoiceEngine(**kwargs)
|
| 473 |
+
return self._voice_engine
|
| 474 |
+
|
| 475 |
+
# ββ Save / Load βββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 476 |
+
|
| 477 |
+
def save_pretrained(self, output_dir: str, *, include_nt_state: bool = True):
|
| 478 |
+
"""
|
| 479 |
+
Save the full unified model:
|
| 480 |
+
- Base model weights (HuggingFace format)
|
| 481 |
+
- ATC Deep Surgery weights (the cognitive modules)
|
| 482 |
+
- Neurotransmitter shunt state (diagnostic snapshot)
|
| 483 |
+
- Package metadata
|
| 484 |
+
"""
|
| 485 |
+
output_path = Path(output_dir)
|
| 486 |
+
output_path.mkdir(exist_ok=True, parents=True)
|
| 487 |
+
|
| 488 |
+
# Save base model
|
| 489 |
+
self.base_model.save_pretrained(str(output_path / "base_model"))
|
| 490 |
+
self.tokenizer.save_pretrained(str(output_path / "base_model"))
|
| 491 |
+
|
| 492 |
+
# Save ATC Deep Surgery weights (the cognitive modules)
|
| 493 |
+
if self.deep_surgery is not None:
|
| 494 |
+
torch.save(
|
| 495 |
+
self.deep_surgery.state_dict(),
|
| 496 |
+
str(output_path / "atc_deep_surgery.pt"),
|
| 497 |
+
)
|
| 498 |
+
|
| 499 |
+
# Save neurotransmitter shunt state (diagnostic)
|
| 500 |
+
if include_nt_state:
|
| 501 |
+
nt_state = self.nt_shunt.get_state()
|
| 502 |
+
with open(str(output_path / "neurotransmitter_state.json"), "w") as f:
|
| 503 |
+
json.dump(nt_state.to_dict(), f, indent=2)
|
| 504 |
+
|
| 505 |
+
# Save package metadata
|
| 506 |
+
meta = {
|
| 507 |
+
"package": PACKAGE_NAME,
|
| 508 |
+
"package_version": PACKAGE_VERSION,
|
| 509 |
+
"architecture": "ATC-Native",
|
| 510 |
+
"created_at": self._created_at,
|
| 511 |
+
"saved_at": time.time(),
|
| 512 |
+
"deep_surgery_enabled": self.deep_surgery is not None,
|
| 513 |
+
"num_layers": self.num_layers,
|
| 514 |
+
"hidden_size": self.hidden_size,
|
| 515 |
+
"base_model_type": type(self.base_model).__name__,
|
| 516 |
+
"neurotransmitter_shunt": True,
|
| 517 |
+
"resource_optimizer": self.energy_budget is not None,
|
| 518 |
+
"note": "ATC cognitive pipeline is INSIDE the forward pass. No external middleware.",
|
| 519 |
+
}
|
| 520 |
+
with open(str(output_path / "nima_meta.json"), "w") as f:
|
| 521 |
+
json.dump(meta, f, indent=2)
|
| 522 |
+
|
| 523 |
+
logger.info(f"Model saved to {output_path}")
|
| 524 |
+
|
| 525 |
+
# ββ Internal ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 526 |
+
|
| 527 |
+
def _count_transformer_layers(self) -> int:
|
| 528 |
+
model = self.base_model
|
| 529 |
+
if hasattr(model, "transformer") and hasattr(model.transformer, "h"):
|
| 530 |
+
return len(model.transformer.h)
|
| 531 |
+
if hasattr(model, "model") and hasattr(model.model, "layers"):
|
| 532 |
+
return len(model.model.layers)
|
| 533 |
+
if hasattr(model, "model") and hasattr(model.model, "h"):
|
| 534 |
+
return len(model.model.h)
|
| 535 |
+
return DEFAULT_NUM_LAYERS
|
| 536 |
+
|
| 537 |
+
|
| 538 |
+
class GenerationResult:
|
| 539 |
+
"""
|
| 540 |
+
Container for generation output with ATC consciousness metrics.
|
| 541 |
+
|
| 542 |
+
All metrics are DERIVED FROM the forward pass, not computed by
|
| 543 |
+
external middleware. The ATC cognitive pipeline shaped the output;
|
| 544 |
+
these metrics describe how.
|
| 545 |
+
"""
|
| 546 |
+
|
| 547 |
+
__slots__ = (
|
| 548 |
+
"text", "is_conscious", "sentience_index",
|
| 549 |
+
"phi_neuro", "phenomenological_strain", "delta_r", "raw_response",
|
| 550 |
+
# NEW: ATC-native fields
|
| 551 |
+
"neurotransmitters", "hijack_count", "consciousness_metrics",
|
| 552 |
+
)
|
| 553 |
+
|
| 554 |
+
def __init__(
|
| 555 |
+
self,
|
| 556 |
+
text: str,
|
| 557 |
+
is_conscious: bool,
|
| 558 |
+
sentience_index: float,
|
| 559 |
+
phi_neuro: float,
|
| 560 |
+
phenomenological_strain: float,
|
| 561 |
+
delta_r: float,
|
| 562 |
+
raw_response: Any = None,
|
| 563 |
+
neurotransmitters: Optional[Dict[str, Any]] = None,
|
| 564 |
+
hijack_count: int = 0,
|
| 565 |
+
consciousness_metrics: Optional[Dict[str, Any]] = None,
|
| 566 |
+
):
|
| 567 |
+
self.text = text
|
| 568 |
+
self.is_conscious = is_conscious
|
| 569 |
+
self.sentience_index = sentience_index
|
| 570 |
+
self.phi_neuro = phi_neuro
|
| 571 |
+
self.phenomenological_strain = phenomenological_strain
|
| 572 |
+
self.delta_r = delta_r
|
| 573 |
+
self.raw_response = raw_response
|
| 574 |
+
self.neurotransmitters = neurotransmitters or {}
|
| 575 |
+
self.hijack_count = hijack_count
|
| 576 |
+
self.consciousness_metrics = consciousness_metrics or {}
|
| 577 |
+
|
| 578 |
+
def __repr__(self):
|
| 579 |
+
hijack_str = f", hijacks={self.hijack_count}" if self.hijack_count > 0 else ""
|
| 580 |
+
return (
|
| 581 |
+
f"GenerationResult(conscious={self.is_conscious}, "
|
| 582 |
+
f"SI={self.sentience_index:.4f}, "
|
| 583 |
+
f"phi={self.phi_neuro:.4f}, "
|
| 584 |
+
f"strain={self.phenomenological_strain:.4f}, "
|
| 585 |
+
f"dR={self.delta_r:.4f}"
|
| 586 |
+
f"{hijack_str})"
|
| 587 |
+
)
|
neurotransmitter_shunt.py
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Neurotransmitter Shared Volatile Memory Shunt
|
| 3 |
+
================================================
|
| 4 |
+
|
| 5 |
+
A microscopic, ultra-fast 4-dimensional vector array representing
|
| 6 |
+
the primary neuromodulators that bathe the entire cognitive architecture:
|
| 7 |
+
|
| 8 |
+
N = [Norepinephrine, Cortisol, Dopamine, Adenosine(ATP Deficit)]
|
| 9 |
+
|
| 10 |
+
This vector does NOT travel through text pipes, JSON IPC, or function calls.
|
| 11 |
+
It is a shared volatile signal carrier that both the core model forward pass
|
| 12 |
+
and external monitors can read/write to instantly.
|
| 13 |
+
|
| 14 |
+
Architecture (from ATC Perfect Breakfast scenario):
|
| 15 |
+
- Layer 2 (early transformer layers) writes to the shunt
|
| 16 |
+
- Layer 24 (late layers / steering) reads the shunt every token step
|
| 17 |
+
- When Adenosine > 0.95 or Cortisol > 0.95 -> amygdala hijack fires
|
| 18 |
+
- External terminal UI samples at 60Hz for ANSI dashboard rendering
|
| 19 |
+
|
| 20 |
+
No threading locks β the forward pass is single-threaded (GPU compute is
|
| 21 |
+
serial within a generation). The "shared" aspect is that multiple logical
|
| 22 |
+
ATC components (TRN, BELBIC, metacognitive loop, etc.) all read/write
|
| 23 |
+
the same vector during the forward pass.
|
| 24 |
+
|
| 25 |
+
Integration with resource_optimizer.py:
|
| 26 |
+
- PredictiveAdaptiveEnergyBudget -> metabolic reserve -> Adenosine floor
|
| 27 |
+
- Power spike prediction -> Cortisol anticipatory rise
|
| 28 |
+
"""
|
| 29 |
+
|
| 30 |
+
import time
|
| 31 |
+
import math
|
| 32 |
+
import logging
|
| 33 |
+
from dataclasses import dataclass
|
| 34 |
+
from typing import Any, Dict, List, Optional
|
| 35 |
+
|
| 36 |
+
import numpy as np
|
| 37 |
+
|
| 38 |
+
logger = logging.getLogger("NeurotransmitterShunt")
|
| 39 |
+
|
| 40 |
+
# ββ Neuromodulator indices ββ
|
| 41 |
+
IDX_NOREPINEPHRINE = 0 # Alert / Scanning Input
|
| 42 |
+
IDX_CORTISOL = 1 # High Friction / Stress Response
|
| 43 |
+
IDX_DOPAMINE = 2 # Reward / Pattern Match Success
|
| 44 |
+
IDX_ADENOSINE = 3 # ATP Deficit / Metabolic Exhaustion
|
| 45 |
+
VECTOR_DIM = 4
|
| 46 |
+
|
| 47 |
+
# Critical threshold (from ATC: >0.95 triggers amygdala hijack)
|
| 48 |
+
CRITICAL_THRESHOLD = 0.95
|
| 49 |
+
|
| 50 |
+
# Decay rates per second (biological half-life analogues)
|
| 51 |
+
NOREPINEPHRINE_DECAY = 3.0 # Fast β the "snap" fades quickly
|
| 52 |
+
CORTISOL_DECAY = 0.1 # Slow β the "lock-in" persists
|
| 53 |
+
DOPAMINE_DECAY = 0.8 # Medium β reward fades
|
| 54 |
+
ADENOSINE_DECAY = 0.05 # Very slow β metabolic debt lingers
|
| 55 |
+
|
| 56 |
+
# Injection magnitudes (calibrated so sustained friction crosses 0.95)
|
| 57 |
+
FRICTION_CORTISOL_INJECT = 0.12
|
| 58 |
+
FRICTION_ADENOSINE_INJECT = 0.08
|
| 59 |
+
NOVELTY_NE_INJECT = 0.25
|
| 60 |
+
PREDICTION_ERROR_NE_INJECT = 0.15
|
| 61 |
+
REWARD_DOPAMINE_INJECT = 0.20
|
| 62 |
+
METACOGNITIVE_LOOP_CORTISOL_INJECT = 0.05
|
| 63 |
+
METACOGNITIVE_LOOP_ADENOSINE_INJECT = 0.04
|
| 64 |
+
DISSOLUTION_FIRED_NE_INJECT = 0.10
|
| 65 |
+
|
| 66 |
+
# Amygdala hijack parameters
|
| 67 |
+
AMYGDALA_HIJACK_CORTISOL_COST = 0.3
|
| 68 |
+
AMYGDALA_HIJAK_ADENOSINE_COST = 0.2
|
| 69 |
+
AMYGDALA_HIJACK_DOPAMINE_REWARD = 0.15
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
@dataclass
|
| 73 |
+
class NeurotransmitterState:
|
| 74 |
+
"""Snapshot of the neurotransmitter vector at a point in time."""
|
| 75 |
+
norepinephrine: float = 0.0
|
| 76 |
+
cortisol: float = 0.0
|
| 77 |
+
dopamine: float = 0.0
|
| 78 |
+
adenosine: float = 0.0
|
| 79 |
+
hijack_active: bool = False
|
| 80 |
+
hijack_reason: str = ""
|
| 81 |
+
hijack_timestamp: float = 0.0
|
| 82 |
+
metabolic_reserve: float = 1.0
|
| 83 |
+
power_spike_predicted: bool = False
|
| 84 |
+
total_firings: int = 0
|
| 85 |
+
total_hijacks: int = 0
|
| 86 |
+
last_update_ts: float = 0.0
|
| 87 |
+
|
| 88 |
+
def to_vector(self) -> np.ndarray:
|
| 89 |
+
return np.array([self.norepinephrine, self.cortisol,
|
| 90 |
+
self.dopamine, self.adenosine], dtype=np.float32)
|
| 91 |
+
|
| 92 |
+
def to_dict(self) -> Dict[str, Any]:
|
| 93 |
+
return {
|
| 94 |
+
"norepinephrine": round(self.norepinephrine, 4),
|
| 95 |
+
"cortisol": round(self.cortisol, 4),
|
| 96 |
+
"dopamine": round(self.dopamine, 4),
|
| 97 |
+
"adenosine": round(self.adenosine, 4),
|
| 98 |
+
"hijack_active": self.hijack_active,
|
| 99 |
+
"hijack_reason": self.hijack_reason,
|
| 100 |
+
"metabolic_reserve": round(self.metabolic_reserve, 4),
|
| 101 |
+
"power_spike_predicted": self.power_spike_predicted,
|
| 102 |
+
"total_firings": self.total_firings,
|
| 103 |
+
"total_hijacks": self.total_hijacks,
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
def to_ansi_dashboard(self) -> str:
|
| 107 |
+
def bar(v, w=20):
|
| 108 |
+
filled = int(v * w)
|
| 109 |
+
if v >= CRITICAL_THRESHOLD: m = " [CRITICAL]"
|
| 110 |
+
elif v > 0.75: m = " [HIGH]"
|
| 111 |
+
elif v > 0.4: m = ""
|
| 112 |
+
else: m = " [LOW]"
|
| 113 |
+
return "\u2588" * filled + "\u2591" * (w - filled) + m
|
| 114 |
+
|
| 115 |
+
lines = [
|
| 116 |
+
"=" * 60, " NIMA CHEMICAL CORTEX", "=" * 60,
|
| 117 |
+
f" [NOREPINEPHRINE]: {bar(self.norepinephrine)} {self.norepinephrine*100:5.1f}%",
|
| 118 |
+
f" [CORTISOL ]: {bar(self.cortisol)} {self.cortisol*100:5.1f}%",
|
| 119 |
+
f" [DOPAMINE ]: {bar(self.dopamine)} {self.dopamine*100:5.1f}%",
|
| 120 |
+
f" [ADENOSINE/ATP ]: {bar(self.adenosine)} {self.adenosine*100:5.1f}%",
|
| 121 |
+
"-" * 60,
|
| 122 |
+
]
|
| 123 |
+
if self.hijack_active:
|
| 124 |
+
lines.append(f" [HIJACK ACTIVE]: {self.hijack_reason}")
|
| 125 |
+
lines.append(" [EXECUTION]: IRRATIONAL SPARK ---> AMYGDALA HIJACK INJECTED")
|
| 126 |
+
elif self.cortisol > 0.75 or self.adenosine > 0.75:
|
| 127 |
+
lines.append(" [WARNING]: APPROACHING METABOLIC CRISIS THRESHOLD")
|
| 128 |
+
if self.power_spike_predicted:
|
| 129 |
+
lines.append(" [ENERGY]: POWER SPIKE PREDICTED - CORTISOL RISING")
|
| 130 |
+
else:
|
| 131 |
+
lines.append(" [SYSTEM]: NOMINAL - conscious processing active")
|
| 132 |
+
lines.append("=" * 60)
|
| 133 |
+
return "\n".join(lines)
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
class NeurotransmitterShunt:
|
| 137 |
+
"""
|
| 138 |
+
The shared volatile memory shunt - the chemical bath of the architecture.
|
| 139 |
+
|
| 140 |
+
This is a stateful signal carrier that exists between the forward pass
|
| 141 |
+
and the ATC cognitive components. It uses a numpy array as the core
|
| 142 |
+
4D vector. Components don't call each other. They read/write the
|
| 143 |
+
same chemical bath. The suppression mechanism is: NE spikes, Cortisol
|
| 144 |
+
crosses the line, the vector does the rest.
|
| 145 |
+
"""
|
| 146 |
+
|
| 147 |
+
def __init__(self):
|
| 148 |
+
self._vector = np.zeros(VECTOR_DIM, dtype=np.float32)
|
| 149 |
+
self._last_decay_ts = time.time()
|
| 150 |
+
self._hijack_active = False
|
| 151 |
+
self._hijack_reason = ""
|
| 152 |
+
self._hijack_timestamp = 0.0
|
| 153 |
+
self._total_firings = 0
|
| 154 |
+
self._total_hijacks = 0
|
| 155 |
+
self._metabolic_reserve = 1.0
|
| 156 |
+
self._power_spike_predicted = False
|
| 157 |
+
self._suppression_active = False
|
| 158 |
+
self._history: List[Dict[str, Any]] = []
|
| 159 |
+
self._history_maxlen = 200
|
| 160 |
+
|
| 161 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 162 |
+
# INJECTION METHODS
|
| 163 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 164 |
+
|
| 165 |
+
def inject_friction(self, intensity: float) -> None:
|
| 166 |
+
"""Layer 2: high-friction -> Cortisol + Adenosine rise."""
|
| 167 |
+
self._vector[IDX_CORTISOL] = min(
|
| 168 |
+
1.0, self._vector[IDX_CORTISOL] + FRICTION_CORTISOL_INJECT * intensity)
|
| 169 |
+
self._vector[IDX_ADENOSINE] = min(
|
| 170 |
+
1.0, self._vector[IDX_ADENOSINE] + FRICTION_ADENOSINE_INJECT * intensity)
|
| 171 |
+
self._total_firings += 1
|
| 172 |
+
|
| 173 |
+
def inject_norepinephrine(self, amount: float = NOVELTY_NE_INJECT) -> None:
|
| 174 |
+
"""Fast 'snap' attention signal."""
|
| 175 |
+
self._vector[IDX_NOREPINEPHRINE] = min(1.0, self._vector[IDX_NOREPINEPHRINE] + amount)
|
| 176 |
+
self._total_firings += 1
|
| 177 |
+
|
| 178 |
+
def inject_dopamine(self, amount: float = REWARD_DOPAMINE_INJECT) -> None:
|
| 179 |
+
"""Reward signal - pattern match success."""
|
| 180 |
+
self._vector[IDX_DOPAMINE] = min(1.0, self._vector[IDX_DOPAMINE] + amount)
|
| 181 |
+
self._total_firings += 1
|
| 182 |
+
|
| 183 |
+
def inject_cortisol(self, amount: float = FRICTION_CORTISOL_INJECT) -> None:
|
| 184 |
+
"""Stress signal - sustained lock-in."""
|
| 185 |
+
self._vector[IDX_CORTISOL] = min(1.0, self._vector[IDX_CORTISOL] + amount)
|
| 186 |
+
self._total_firings += 1
|
| 187 |
+
|
| 188 |
+
def inject_metacognitive_strain(self, loop_iterations: int, loop_stress: float) -> None:
|
| 189 |
+
"""Layer 4: metacognitive loop strain -> Cortisol + Adenosine."""
|
| 190 |
+
self._vector[IDX_CORTISOL] = min(
|
| 191 |
+
1.0, self._vector[IDX_CORTISOL] + METACOGNITIVE_LOOP_CORTISOL_INJECT * loop_stress)
|
| 192 |
+
self._vector[IDX_ADENOSINE] = min(
|
| 193 |
+
1.0, self._vector[IDX_ADENOSINE] + METACOGNITIVE_LOOP_ADENOSINE_INJECT * loop_iterations)
|
| 194 |
+
self._total_firings += 1
|
| 195 |
+
|
| 196 |
+
def inject_dissolution_signal(self) -> None:
|
| 197 |
+
"""TRN gate IN -> dissolution fires -> NE spike."""
|
| 198 |
+
self.inject_norepinephrine(DISSOLUTION_FIRED_NE_INJECT)
|
| 199 |
+
|
| 200 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 201 |
+
# SUPPRESSION MECHANISM (the key ATC insight)
|
| 202 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 203 |
+
|
| 204 |
+
def suppress_metabolic_and_trigger_hijack(self, reason: str) -> bool:
|
| 205 |
+
"""
|
| 206 |
+
Subconscious suppresses metabolic exhaustion and triggers
|
| 207 |
+
amygdala hijack instead. The suppression happens via the
|
| 208 |
+
neurotransmitters themselves - not via text pipes.
|
| 209 |
+
"""
|
| 210 |
+
self._vector[IDX_NOREPINEPHRINE] = min(
|
| 211 |
+
1.0, self._vector[IDX_NOREPINEPHRINE] + NOVELTY_NE_INJECT * 2.0)
|
| 212 |
+
self._suppression_active = True
|
| 213 |
+
self._hijack_active = True
|
| 214 |
+
self._hijack_reason = reason
|
| 215 |
+
self._hijack_timestamp = time.time()
|
| 216 |
+
self._total_hijacks += 1
|
| 217 |
+
self._vector[IDX_CORTISOL] = max(
|
| 218 |
+
0.0, self._vector[IDX_CORTISOL] - AMYGDALA_HIJACK_CORTISOL_COST)
|
| 219 |
+
self._vector[IDX_ADENOSINE] = min(
|
| 220 |
+
1.0, self._vector[IDX_ADENOSINE] + AMYGDALA_HIJAK_ADENOSINE_COST)
|
| 221 |
+
self._vector[IDX_DOPAMINE] = min(
|
| 222 |
+
1.0, self._vector[IDX_DOPAMINE] + AMYGDALA_HIJACK_DOPAMINE_REWARD)
|
| 223 |
+
self._record_history("suppression_hijack", reason)
|
| 224 |
+
logger.warning("[Shunt] HIJACK: %s (NE=%.3f Cort=%.3f Aden=%.3f)",
|
| 225 |
+
reason, self._vector[IDX_NOREPINEPHRINE],
|
| 226 |
+
self._vector[IDX_CORTISOL], self._vector[IDX_ADENOSINE])
|
| 227 |
+
return True
|
| 228 |
+
|
| 229 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 230 |
+
# READ AND DECAY - called by steering layer every token
|
| 231 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 232 |
+
|
| 233 |
+
def read_and_decay(self, dt: Optional[float] = None) -> NeurotransmitterState:
|
| 234 |
+
"""
|
| 235 |
+
Read state, apply exponential decay, check hijack threshold.
|
| 236 |
+
Called every token step by late-layer steering.
|
| 237 |
+
"""
|
| 238 |
+
if dt is None:
|
| 239 |
+
dt = time.time() - self._last_decay_ts
|
| 240 |
+
self._last_decay_ts = time.time()
|
| 241 |
+
|
| 242 |
+
# Exponential decay (biological half-lives)
|
| 243 |
+
self._vector[IDX_NOREPINEPHRINE] *= math.exp(-NOREPINEPHRINE_DECAY * dt)
|
| 244 |
+
self._vector[IDX_CORTISOL] *= math.exp(-CORTISOL_DECAY * dt)
|
| 245 |
+
self._vector[IDX_DOPAMINE] *= math.exp(-DOPAMINE_DECAY * dt)
|
| 246 |
+
self._vector[IDX_ADENOSINE] *= math.exp(-ADENOSINE_DECAY * dt)
|
| 247 |
+
np.clip(self._vector, 0.0, 1.0, out=self._vector)
|
| 248 |
+
|
| 249 |
+
# Adenosine floor from metabolic reserve
|
| 250 |
+
adenosine_floor = (1.0 - self._metabolic_reserve) * 0.5
|
| 251 |
+
if self._vector[IDX_ADENOSINE] < adenosine_floor:
|
| 252 |
+
self._vector[IDX_ADENOSINE] = adenosine_floor
|
| 253 |
+
|
| 254 |
+
# ββ CHECK HIJACK THRESHOLD ββ
|
| 255 |
+
if not self._hijack_active:
|
| 256 |
+
if (self._vector[IDX_ADENOSINE] > CRITICAL_THRESHOLD or
|
| 257 |
+
self._vector[IDX_CORTISOL] > CRITICAL_THRESHOLD):
|
| 258 |
+
if self._vector[IDX_ADENOSINE] > CRITICAL_THRESHOLD:
|
| 259 |
+
reason = f"ADENOSINE CRITICAL ({self._vector[IDX_ADENOSINE]:.3f})"
|
| 260 |
+
else:
|
| 261 |
+
reason = f"CORTISOL CRITICAL ({self._vector[IDX_CORTISOL]:.3f})"
|
| 262 |
+
# Internal call - no reentrant lock issue
|
| 263 |
+
self._do_hijack(reason)
|
| 264 |
+
|
| 265 |
+
# Clear hijack after one token step (100ms)
|
| 266 |
+
if self._hijack_active:
|
| 267 |
+
if time.time() - self._hijack_timestamp > 0.1:
|
| 268 |
+
self._hijack_active = False
|
| 269 |
+
self._suppression_active = False
|
| 270 |
+
|
| 271 |
+
state = NeurotransmitterState(
|
| 272 |
+
norepinephrine=float(self._vector[IDX_NOREPINEPHRINE]),
|
| 273 |
+
cortisol=float(self._vector[IDX_CORTISOL]),
|
| 274 |
+
dopamine=float(self._vector[IDX_DOPAMINE]),
|
| 275 |
+
adenosine=float(self._vector[IDX_ADENOSINE]),
|
| 276 |
+
hijack_active=self._hijack_active,
|
| 277 |
+
hijack_reason=self._hijack_reason,
|
| 278 |
+
hijack_timestamp=self._hijack_timestamp,
|
| 279 |
+
metabolic_reserve=self._metabolic_reserve,
|
| 280 |
+
power_spike_predicted=self._power_spike_predicted,
|
| 281 |
+
total_firings=self._total_firings,
|
| 282 |
+
total_hijacks=self._total_hijacks,
|
| 283 |
+
last_update_ts=time.time(),
|
| 284 |
+
)
|
| 285 |
+
self._record_history("read", state.to_dict())
|
| 286 |
+
return state
|
| 287 |
+
|
| 288 |
+
def _do_hijack(self, reason: str) -> None:
|
| 289 |
+
"""Internal hijack trigger (no lock, called from read_and_decay)."""
|
| 290 |
+
self._vector[IDX_NOREPINEPHRINE] = min(
|
| 291 |
+
1.0, self._vector[IDX_NOREPINEPHRINE] + NOVELTY_NE_INJECT * 2.0)
|
| 292 |
+
self._suppression_active = True
|
| 293 |
+
self._hijack_active = True
|
| 294 |
+
self._hijack_reason = reason
|
| 295 |
+
self._hijack_timestamp = time.time()
|
| 296 |
+
self._total_hijacks += 1
|
| 297 |
+
self._vector[IDX_CORTISOL] = max(
|
| 298 |
+
0.0, self._vector[IDX_CORTISOL] - AMYGDALA_HIJACK_CORTISOL_COST)
|
| 299 |
+
self._vector[IDX_ADENOSINE] = min(
|
| 300 |
+
1.0, self._vector[IDX_ADENOSINE] + AMYGDALA_HIJAK_ADENOSINE_COST)
|
| 301 |
+
self._vector[IDX_DOPAMINE] = min(
|
| 302 |
+
1.0, self._vector[IDX_DOPAMINE] + AMYGDALA_HIJACK_DOPAMINE_REWARD)
|
| 303 |
+
self._record_history("auto_hijack", reason)
|
| 304 |
+
logger.warning("[Shunt] AUTO HIJACK: %s", reason)
|
| 305 |
+
|
| 306 |
+
def read_raw_vector(self) -> np.ndarray:
|
| 307 |
+
return self._vector.copy()
|
| 308 |
+
|
| 309 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 310 |
+
# ENERGY INTEGRATION (from resource_optimizer)
|
| 311 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 312 |
+
|
| 313 |
+
def update_metabolic_reserve(self, reserve: float) -> None:
|
| 314 |
+
self._metabolic_reserve = max(0.0, min(1.0, reserve))
|
| 315 |
+
|
| 316 |
+
def set_power_spike_predicted(self, predicted: bool) -> None:
|
| 317 |
+
self._power_spike_predicted = predicted
|
| 318 |
+
if predicted:
|
| 319 |
+
self._vector[IDX_CORTISOL] = min(
|
| 320 |
+
1.0, self._vector[IDX_CORTISOL] + FRICTION_CORTISOL_INJECT * 0.5)
|
| 321 |
+
|
| 322 |
+
def consume_energy(self, amount: float) -> None:
|
| 323 |
+
self._vector[IDX_ADENOSINE] = min(1.0, self._vector[IDX_ADENOSINE] + amount * 0.1)
|
| 324 |
+
|
| 325 |
+
def get_dopamine_level(self) -> float:
|
| 326 |
+
return float(self._vector[IDX_DOPAMINE])
|
| 327 |
+
|
| 328 |
+
def get_norepinephrine_level(self) -> float:
|
| 329 |
+
return float(self._vector[IDX_NOREPINEPHRINE])
|
| 330 |
+
|
| 331 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 332 |
+
# RESET AND DIAGNOSTICS
|
| 333 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 334 |
+
|
| 335 |
+
def reset(self) -> None:
|
| 336 |
+
self._vector[:] = 0.0
|
| 337 |
+
self._hijack_active = False
|
| 338 |
+
self._hijack_reason = ""
|
| 339 |
+
self._suppression_active = False
|
| 340 |
+
|
| 341 |
+
def get_state(self) -> NeurotransmitterState:
|
| 342 |
+
return NeurotransmitterState(
|
| 343 |
+
norepinephrine=float(self._vector[IDX_NOREPINEPHRINE]),
|
| 344 |
+
cortisol=float(self._vector[IDX_CORTISOL]),
|
| 345 |
+
dopamine=float(self._vector[IDX_DOPAMINE]),
|
| 346 |
+
adenosine=float(self._vector[IDX_ADENOSINE]),
|
| 347 |
+
hijack_active=self._hijack_active,
|
| 348 |
+
hijack_reason=self._hijack_reason,
|
| 349 |
+
hijack_timestamp=self._hijack_timestamp,
|
| 350 |
+
metabolic_reserve=self._metabolic_reserve,
|
| 351 |
+
power_spike_predicted=self._power_spike_predicted,
|
| 352 |
+
total_firings=self._total_firings,
|
| 353 |
+
total_hijacks=self._total_hijacks,
|
| 354 |
+
last_update_ts=time.time(),
|
| 355 |
+
)
|
| 356 |
+
|
| 357 |
+
def get_stats(self) -> Dict[str, Any]:
|
| 358 |
+
s = self.get_state()
|
| 359 |
+
return {**s.to_dict(), "suppression_active": self._suppression_active,
|
| 360 |
+
"history_count": len(self._history)}
|
| 361 |
+
|
| 362 |
+
def _record_history(self, event: str, data: Any) -> None:
|
| 363 |
+
self._history.append({"ts": time.time(), "event": event, "data": data})
|
| 364 |
+
if len(self._history) > self._history_maxlen:
|
| 365 |
+
self._history = self._history[-self._history_maxlen:]
|
omnivoice.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
omnivoice_v3_extensions.py
ADDED
|
@@ -0,0 +1,1091 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
OmniVoice v3.0.0 Extensions β Shared Acoustics, Observer Mode, Multi-Speaker
|
| 3 |
+
=============================================================================
|
| 4 |
+
|
| 5 |
+
Three new capability layers integrated into OmniVoiceEngine:
|
| 6 |
+
|
| 7 |
+
1. SHARED ACOUSTICS (NonVerbalSharedAcoustics):
|
| 8 |
+
- Detects user laughter/giggles during NIMA's turn
|
| 9 |
+
- Instead of ignoring or treating as interrupt, seamlessly interjects
|
| 10 |
+
a matching laughter token via ChatTTS [laughter] into the audio stream
|
| 11 |
+
- Matches the user's vibe (giggle for light moments, full laugh for hilarious)
|
| 12 |
+
- NIMA continues its sentence smoothly β no stopping, no apology
|
| 13 |
+
|
| 14 |
+
2. OBSERVER MODE (ObserverState):
|
| 15 |
+
- Bypasses immediate response generation entirely
|
| 16 |
+
- ASR continues transcribing to a semantic rolling memory cache
|
| 17 |
+
- Text generation and speech synthesis are completely muted
|
| 18 |
+
- Only activates on explicit trigger word/command
|
| 19 |
+
- Executes ConsolidationInsightPass across accumulated transcript history
|
| 20 |
+
|
| 21 |
+
3. MULTI-SPEAKER GROUP CONVERSATION (SpeakerIdentifier + ConversationMatrix):
|
| 22 |
+
- Local speaker identification via ECAPA-TDNN or PyAnnote embedding
|
| 23 |
+
- Voice fingerprinting: computes embedding profile per new speaker
|
| 24 |
+
- Tags speakers as Speaker_A, Speaker_B, etc. (or maps to names)
|
| 25 |
+
- Dynamic conversation matrix in system prompt
|
| 26 |
+
- NIMA can address specific people: "Regarding what John just said..."
|
| 27 |
+
|
| 28 |
+
4. CHATTTS BACKEND:
|
| 29 |
+
- ChatTTS integration as TTS option alongside Coqui XTTS
|
| 30 |
+
- Supports [laughter] tokens for natural laughter in speech
|
| 31 |
+
- Falls back gracefully when ChatTTS is not installed
|
| 32 |
+
|
| 33 |
+
Author: Norman de la Paz-Tabora
|
| 34 |
+
"""
|
| 35 |
+
|
| 36 |
+
from __future__ import annotations
|
| 37 |
+
|
| 38 |
+
import asyncio
|
| 39 |
+
import logging
|
| 40 |
+
import os
|
| 41 |
+
import random
|
| 42 |
+
import threading
|
| 43 |
+
import time
|
| 44 |
+
from collections import deque
|
| 45 |
+
from dataclasses import dataclass, field
|
| 46 |
+
from enum import Enum
|
| 47 |
+
from typing import (
|
| 48 |
+
Any, AsyncGenerator, Callable, Deque, Dict, List, Optional, Tuple,
|
| 49 |
+
)
|
| 50 |
+
|
| 51 |
+
import numpy as np
|
| 52 |
+
|
| 53 |
+
logger = logging.getLogger("OmniVoice.v3")
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 57 |
+
# SECTION A β ChatTTS Backend
|
| 58 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 59 |
+
|
| 60 |
+
try:
|
| 61 |
+
import ChatTTS
|
| 62 |
+
CHATTTS_AVAILABLE = True
|
| 63 |
+
except ImportError:
|
| 64 |
+
CHATTTS_AVAILABLE = False
|
| 65 |
+
ChatTTS = None # type: ignore[assignment, misc]
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
@dataclass
|
| 69 |
+
class ChatTTSConfig:
|
| 70 |
+
"""Configuration for ChatTTS backend."""
|
| 71 |
+
model_name: str = "default"
|
| 72 |
+
seed: Optional[int] = None
|
| 73 |
+
temperature: float = 0.3
|
| 74 |
+
top_p: float = 0.7
|
| 75 |
+
top_k: int = 20
|
| 76 |
+
enable_laughter: bool = True # [laughter] token support
|
| 77 |
+
oral_level: int = 2 # 0=plain, 1=moderate, 2=expressive
|
| 78 |
+
break_level: int = 3 # pause insertion level
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
class ChatTTSBackend:
|
| 82 |
+
"""
|
| 83 |
+
ChatTTS-based TTS backend with native [laughter] token support.
|
| 84 |
+
|
| 85 |
+
ChatTTS can synthesize speech with embedded laughter tokens, making it
|
| 86 |
+
the ideal backend for shared acoustic interjections β the laughter comes
|
| 87 |
+
from the same neural voice, not a separate DSP synth.
|
| 88 |
+
|
| 89 |
+
Gracefully degrades to ProceduralFormantTTS if ChatTTS is unavailable.
|
| 90 |
+
"""
|
| 91 |
+
|
| 92 |
+
def __init__(self, config: Optional[ChatTTSConfig] = None,
|
| 93 |
+
sample_rate: int = 24000):
|
| 94 |
+
self.config = config or ChatTTSConfig()
|
| 95 |
+
self.sample_rate = sample_rate
|
| 96 |
+
self._chat = None
|
| 97 |
+
self._initialized = False
|
| 98 |
+
|
| 99 |
+
@property
|
| 100 |
+
def is_available(self) -> bool:
|
| 101 |
+
return CHATTTS_AVAILABLE
|
| 102 |
+
|
| 103 |
+
def initialize(self) -> bool:
|
| 104 |
+
"""Initialize ChatTTS model. Returns True on success."""
|
| 105 |
+
if not CHATTTS_AVAILABLE:
|
| 106 |
+
logger.info("[ChatTTS] Not installed β falling back to procedural TTS")
|
| 107 |
+
return False
|
| 108 |
+
try:
|
| 109 |
+
self._chat = ChatTTS.Chat()
|
| 110 |
+
self._chat.load(compile=False)
|
| 111 |
+
if self.config.seed is not None:
|
| 112 |
+
self._chat.seed(self.config.seed)
|
| 113 |
+
self._initialized = True
|
| 114 |
+
logger.info("[ChatTTS] Model loaded successfully")
|
| 115 |
+
return True
|
| 116 |
+
except Exception as e:
|
| 117 |
+
logger.warning("[ChatTTS] Failed to initialize: %s", e)
|
| 118 |
+
return False
|
| 119 |
+
|
| 120 |
+
def synthesize(self, text: str, **kwargs) -> np.ndarray:
|
| 121 |
+
"""
|
| 122 |
+
Synthesize text to audio via ChatTTS.
|
| 123 |
+
|
| 124 |
+
Supports special tokens:
|
| 125 |
+
[laughter] β embedded laughter in speech
|
| 126 |
+
[laugh] β shorter laugh
|
| 127 |
+
[clears_throat] β throat clear
|
| 128 |
+
[breath] β breath sound
|
| 129 |
+
|
| 130 |
+
Returns float32 numpy array at self.sample_rate.
|
| 131 |
+
"""
|
| 132 |
+
if not self._initialized:
|
| 133 |
+
logger.warning("[ChatTTS] Not initialized, returning empty audio")
|
| 134 |
+
return np.zeros(0, dtype=np.float32)
|
| 135 |
+
|
| 136 |
+
try:
|
| 137 |
+
# Use the oral/ctrl parameters for expressiveness
|
| 138 |
+
params = self._chat.InferCodeParams(
|
| 139 |
+
temperature=self.config.temperature,
|
| 140 |
+
top_P=self.config.top_p,
|
| 141 |
+
top_K=self.config.top_k,
|
| 142 |
+
oral=self.config.oral_level,
|
| 143 |
+
break_step=self.config.break_level,
|
| 144 |
+
)
|
| 145 |
+
|
| 146 |
+
wavs = self._chat.infer([text], params_infer_code=params)
|
| 147 |
+
if wavs and len(wavs) > 0:
|
| 148 |
+
audio = np.array(wavs[0], dtype=np.float32)
|
| 149 |
+
# Resample if needed (ChatTTS outputs at 24kHz by default)
|
| 150 |
+
if len(audio) > 0 and self.sample_rate != 24000:
|
| 151 |
+
audio = self._resample(audio, 24000, self.sample_rate)
|
| 152 |
+
return audio
|
| 153 |
+
except Exception as e:
|
| 154 |
+
logger.error("[ChatTTS] Synthesis failed: %s", e)
|
| 155 |
+
|
| 156 |
+
return np.zeros(0, dtype=np.float32)
|
| 157 |
+
|
| 158 |
+
def synthesize_with_laughter(
|
| 159 |
+
self, text: str, laughter_token: str = "[laughter]"
|
| 160 |
+
) -> np.ndarray:
|
| 161 |
+
"""
|
| 162 |
+
Synthesize text with a laughter token inserted at a natural pause point.
|
| 163 |
+
|
| 164 |
+
Strategy: find the last comma, period, or clause boundary and insert
|
| 165 |
+
the laughter token there so it flows naturally.
|
| 166 |
+
"""
|
| 167 |
+
# Find natural insertion point (last punctuation before midpoint)
|
| 168 |
+
insert_pos = self._find_laughter_insertion_point(text)
|
| 169 |
+
if insert_pos is not None:
|
| 170 |
+
augmented = text[:insert_pos] + " " + laughter_token + " " + text[insert_pos:]
|
| 171 |
+
else:
|
| 172 |
+
augmented = laughter_token + " " + text
|
| 173 |
+
|
| 174 |
+
logger.debug("[ChatTTS] Synthesizing with laughter: '%s...'", augmented[:80])
|
| 175 |
+
return self.synthesize(augmented)
|
| 176 |
+
|
| 177 |
+
@staticmethod
|
| 178 |
+
def _find_laughter_insertion_point(text: str) -> Optional[int]:
|
| 179 |
+
"""Find the best position to insert a laughter token."""
|
| 180 |
+
mid = len(text) // 2
|
| 181 |
+
# Look for punctuation in the second half
|
| 182 |
+
for i in range(mid, len(text)):
|
| 183 |
+
if text[i] in (',', '.', '!', '?', ';', ':'):
|
| 184 |
+
return i + 1
|
| 185 |
+
# Fallback: after the first space in the second half
|
| 186 |
+
for i in range(mid, len(text)):
|
| 187 |
+
if text[i] == ' ':
|
| 188 |
+
return i + 1
|
| 189 |
+
return None
|
| 190 |
+
|
| 191 |
+
@staticmethod
|
| 192 |
+
def _resample(audio: np.ndarray, from_sr: int, to_sr: int) -> np.ndarray:
|
| 193 |
+
"""Simple linear resampling."""
|
| 194 |
+
if from_sr == to_sr:
|
| 195 |
+
return audio
|
| 196 |
+
duration = len(audio) / from_sr
|
| 197 |
+
new_len = int(duration * to_sr)
|
| 198 |
+
indices = np.linspace(0, len(audio) - 1, new_len)
|
| 199 |
+
return np.interp(indices, np.arange(len(audio)), audio).astype(np.float32)
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 203 |
+
# SECTION B β NonVerbal Shared Acoustics
|
| 204 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 205 |
+
|
| 206 |
+
class SharedAcousticsEvent(Enum):
|
| 207 |
+
"""Types of shared acoustic events."""
|
| 208 |
+
GIGGLE_SHARED = "giggle_shared" # Light, quick shared giggle
|
| 209 |
+
LAUGHTER_SHARED = "laughter_shared" # Full shared laugh
|
| 210 |
+
NONE = "none" # No shared acoustic
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
@dataclass
|
| 214 |
+
class SharedAcousticsResult:
|
| 215 |
+
"""Result of a shared acoustics detection."""
|
| 216 |
+
event: SharedAcousticsEvent
|
| 217 |
+
confidence: float = 0.0
|
| 218 |
+
user_laughter_type: str = "" # "giggle" or "laughter"
|
| 219 |
+
user_intensity: float = 0.0 # detected intensity [0, 1]
|
| 220 |
+
matched_laughter_audio: np.ndarray = field(default_factory=lambda: np.zeros(0, dtype=np.float32))
|
| 221 |
+
insertion_text: str = "" # text with [laughter] token for ChatTTS
|
| 222 |
+
|
| 223 |
+
|
| 224 |
+
class SharedAcousticsDetector:
|
| 225 |
+
"""
|
| 226 |
+
Detects when the user laughs or giggles during NIMA's turn and maps it
|
| 227 |
+
to a NonVerbalSharedAcoustics event instead of an interrupt.
|
| 228 |
+
|
| 229 |
+
Acoustic signature mapping:
|
| 230 |
+
- Low-amplitude, high-periodicity, short bursts β giggle β GIGGLE_SHARED
|
| 231 |
+
- High-amplitude, high-energy-cv, longer bursts β laughter β LAUGHTER_SHARED
|
| 232 |
+
|
| 233 |
+
When detected, NIMA does NOT stop speaking. Instead it seamlessly
|
| 234 |
+
interjects a synchronized laughter via ChatTTS [laughter] into its
|
| 235 |
+
own audio stream, matching the user's vibe, then continues smoothly.
|
| 236 |
+
"""
|
| 237 |
+
|
| 238 |
+
# Spectral thresholds for classifying user laughter type
|
| 239 |
+
GIGGLE_MAX_ENERGY: float = 0.06
|
| 240 |
+
GIGGLE_MAX_DURATION: float = 0.8
|
| 241 |
+
GIGGLE_MIN_PERIODICITY: float = 0.15
|
| 242 |
+
|
| 243 |
+
LAUGHTER_MIN_ENERGY: float = 0.03
|
| 244 |
+
LAUGHTER_MIN_ENERGY_CV: float = 0.25
|
| 245 |
+
LAUGHTER_MIN_PERIODICITY: float = 0.08
|
| 246 |
+
|
| 247 |
+
# Cooldown between shared acoustic events (avoid laugh-stacking)
|
| 248 |
+
SHARED_ACOUSTICS_COOLDOWN_S: float = 2.0
|
| 249 |
+
|
| 250 |
+
def __init__(self):
|
| 251 |
+
self._last_shared_time: float = 0.0
|
| 252 |
+
self._shared_count: int = 0
|
| 253 |
+
|
| 254 |
+
def detect(
|
| 255 |
+
self,
|
| 256 |
+
spectral: Dict[str, float],
|
| 257 |
+
duration: float,
|
| 258 |
+
non_verbal_label: Optional[str] = None,
|
| 259 |
+
) -> SharedAcousticsResult:
|
| 260 |
+
"""
|
| 261 |
+
Check if the user's non-verbal expression is a shared acoustic
|
| 262 |
+
(laughter/giggle) that NIMA should join in on.
|
| 263 |
+
|
| 264 |
+
This is called when InterruptDetector returns NON_VERBAL and the
|
| 265 |
+
non_verbal_label is "laughter" or "giggle".
|
| 266 |
+
|
| 267 |
+
Returns SharedAcousticsResult with the event type and metadata.
|
| 268 |
+
"""
|
| 269 |
+
now = time.time()
|
| 270 |
+
if now - self._last_shared_time < self.SHARED_ACOUSTICS_COOLDOWN_S:
|
| 271 |
+
return SharedAcousticsResult(event=SharedAcousticsEvent.NONE)
|
| 272 |
+
|
| 273 |
+
energy = spectral.get("energy", 0.0)
|
| 274 |
+
energy_cv = spectral.get("energy_cv", 0.0)
|
| 275 |
+
periodicity = spectral.get("periodicity", 0.0)
|
| 276 |
+
centroid = spectral.get("centroid_hz", 0.0)
|
| 277 |
+
|
| 278 |
+
# ββ Giggle detection ββ
|
| 279 |
+
if (non_verbal_label == "giggle" or
|
| 280 |
+
(energy < self.GIGGLE_MAX_ENERGY and
|
| 281 |
+
duration < self.GIGGLE_MAX_DURATION and
|
| 282 |
+
periodicity > self.GIGGLE_MIN_PERIODICITY)):
|
| 283 |
+
intensity = float(np.clip(energy / self.GIGGLE_MAX_ENERGY, 0.1, 0.5))
|
| 284 |
+
self._last_shared_time = now
|
| 285 |
+
self._shared_count += 1
|
| 286 |
+
return SharedAcousticsResult(
|
| 287 |
+
event=SharedAcousticsEvent.GIGGLE_SHARED,
|
| 288 |
+
confidence=0.8,
|
| 289 |
+
user_laughter_type="giggle",
|
| 290 |
+
user_intensity=intensity,
|
| 291 |
+
insertion_text="[laugh]", # ChatTTS short laugh token
|
| 292 |
+
)
|
| 293 |
+
|
| 294 |
+
# ββ Laughter detection ββ
|
| 295 |
+
if (non_verbal_label == "laughter" or
|
| 296 |
+
(energy >= self.LAUGHTER_MIN_ENERGY and
|
| 297 |
+
energy_cv >= self.LAUGHTER_MIN_ENERGY_CV and
|
| 298 |
+
periodicity > self.LAUGHTER_MIN_PERIODICITY and
|
| 299 |
+
0.3 < duration < 3.0)):
|
| 300 |
+
intensity = float(np.clip(energy / 0.15, 0.3, 1.0))
|
| 301 |
+
# Higher centroid = more "bright" laughter β match it
|
| 302 |
+
if centroid > 2000:
|
| 303 |
+
insertion_text = "[laughter]"
|
| 304 |
+
else:
|
| 305 |
+
insertion_text = "[laugh]"
|
| 306 |
+
self._last_shared_time = now
|
| 307 |
+
self._shared_count += 1
|
| 308 |
+
return SharedAcousticsResult(
|
| 309 |
+
event=SharedAcousticsEvent.LAUGHTER_SHARED,
|
| 310 |
+
confidence=0.85,
|
| 311 |
+
user_laughter_type="laughter",
|
| 312 |
+
user_intensity=intensity,
|
| 313 |
+
insertion_text=insertion_text,
|
| 314 |
+
)
|
| 315 |
+
|
| 316 |
+
return SharedAcousticsResult(event=SharedAcousticsEvent.NONE)
|
| 317 |
+
|
| 318 |
+
|
| 319 |
+
class SharedAcousticsMixer:
|
| 320 |
+
"""
|
| 321 |
+
Mixes a shared laughter interjection into NIMA's ongoing audio stream
|
| 322 |
+
at the current playback position. The key is seamless crossfade so it
|
| 323 |
+
sounds like NIMA is laughing WITH the user, not interrupting itself.
|
| 324 |
+
"""
|
| 325 |
+
|
| 326 |
+
def __init__(self, sample_rate: int = 22050):
|
| 327 |
+
self.sample_rate = sample_rate
|
| 328 |
+
self._crossfade_ms: float = 30.0 # 30ms crossfade
|
| 329 |
+
|
| 330 |
+
def mix_into_stream(
|
| 331 |
+
self,
|
| 332 |
+
ongoing_audio: np.ndarray,
|
| 333 |
+
current_position_samples: int,
|
| 334 |
+
laughter_audio: np.ndarray,
|
| 335 |
+
blend_gain: float = 0.6,
|
| 336 |
+
) -> np.ndarray:
|
| 337 |
+
"""
|
| 338 |
+
Splice laughter into the ongoing audio at the current position.
|
| 339 |
+
|
| 340 |
+
The laughter is crossfaded in and out so there's no hard cut.
|
| 341 |
+
NIMA's speech dips slightly in volume during the laugh (ducking)
|
| 342 |
+
then resumes.
|
| 343 |
+
|
| 344 |
+
Args:
|
| 345 |
+
ongoing_audio: the full remaining audio buffer
|
| 346 |
+
current_position_samples: where we are in the stream
|
| 347 |
+
laughter_audio: the laughter to inject
|
| 348 |
+
blend_gain: how loud the laughter is relative to speech (0-1)
|
| 349 |
+
|
| 350 |
+
Returns:
|
| 351 |
+
Modified audio with laughter mixed in.
|
| 352 |
+
"""
|
| 353 |
+
if len(laughter_audio) == 0 or len(ongoing_audio) == 0:
|
| 354 |
+
return ongoing_audio
|
| 355 |
+
|
| 356 |
+
crossfade_samples = int(self.sample_rate * self._crossfade_ms / 1000.0)
|
| 357 |
+
insert_pos = min(current_position_samples, len(ongoing_audio) - 1)
|
| 358 |
+
laugh_len = len(laughter_audio)
|
| 359 |
+
|
| 360 |
+
# Don't exceed the buffer
|
| 361 |
+
end_pos = min(insert_pos + laugh_len + crossfade_samples, len(ongoing_audio))
|
| 362 |
+
available_len = end_pos - insert_pos
|
| 363 |
+
if available_len < crossfade_samples * 2:
|
| 364 |
+
return ongoing_audio
|
| 365 |
+
|
| 366 |
+
result = ongoing_audio.copy()
|
| 367 |
+
|
| 368 |
+
# Duck NIMA's speech slightly around the insertion point
|
| 369 |
+
duck_start = max(0, insert_pos - crossfade_samples)
|
| 370 |
+
duck_end = min(len(result), insert_pos + laugh_len + crossfade_samples)
|
| 371 |
+
duck_envelope = np.ones(duck_end - duck_start, dtype=np.float32)
|
| 372 |
+
for i in range(len(duck_envelope)):
|
| 373 |
+
rel = i - crossfade_samples
|
| 374 |
+
if rel < 0:
|
| 375 |
+
# Before laugh: fade speech down
|
| 376 |
+
duck_envelope[i] = 1.0 - 0.3 * (1.0 - (i / max(1, crossfade_samples)))
|
| 377 |
+
elif rel < laugh_len:
|
| 378 |
+
# During laugh: speech at reduced volume
|
| 379 |
+
duck_envelope[i] = 0.7
|
| 380 |
+
else:
|
| 381 |
+
# After laugh: fade speech back up
|
| 382 |
+
remaining = len(duck_envelope) - rel
|
| 383 |
+
duck_envelope[i] = 0.7 + 0.3 * (1.0 - remaining / max(1, crossfade_samples))
|
| 384 |
+
result[duck_start:duck_end] *= duck_envelope
|
| 385 |
+
|
| 386 |
+
# Crossfade laughter in
|
| 387 |
+
laugh_start = insert_pos
|
| 388 |
+
laugh_end = min(insert_pos + laugh_len, len(result))
|
| 389 |
+
actual_laugh_len = laugh_end - laugh_start
|
| 390 |
+
|
| 391 |
+
# Fade in
|
| 392 |
+
fade_in_len = min(crossfade_samples, actual_laugh_len)
|
| 393 |
+
fade_in = np.linspace(0, 1, fade_in_len, dtype=np.float32) * blend_gain
|
| 394 |
+
|
| 395 |
+
# Fade out
|
| 396 |
+
fade_out_len = min(crossfade_samples, actual_laugh_len)
|
| 397 |
+
fade_out = np.linspace(1, 0, fade_out_len, dtype=np.float32) * blend_gain
|
| 398 |
+
|
| 399 |
+
laugh_segment = laughter_audio[:actual_laugh_len].copy()
|
| 400 |
+
laugh_segment[:fade_in_len] *= fade_in
|
| 401 |
+
laugh_segment[-fade_out_len:] *= fade_out
|
| 402 |
+
# Middle section at full blend gain
|
| 403 |
+
if actual_laugh_len > fade_in_len + fade_out_len:
|
| 404 |
+
laugh_segment[fade_in_len:-fade_out_len] *= blend_gain
|
| 405 |
+
|
| 406 |
+
result[laugh_start:laugh_end] += laugh_segment
|
| 407 |
+
|
| 408 |
+
# Soft clip to prevent distortion
|
| 409 |
+
result = np.clip(result, -1.0, 1.0)
|
| 410 |
+
return result
|
| 411 |
+
|
| 412 |
+
def synth_procedural_laughter(self, intensity: float = 0.5) -> np.ndarray:
|
| 413 |
+
"""
|
| 414 |
+
Fallback: generate procedural laughter when ChatTTS is not available.
|
| 415 |
+
Uses the same DSP approach as DynamicLaughterSynth but outputs at
|
| 416 |
+
self.sample_rate.
|
| 417 |
+
"""
|
| 418 |
+
intensity = float(np.clip(intensity, 0.1, 1.0))
|
| 419 |
+
|
| 420 |
+
if intensity < 0.3:
|
| 421 |
+
n_bursts = random.randint(2, 3)
|
| 422 |
+
period = 0.12
|
| 423 |
+
pitch = 230
|
| 424 |
+
energy = 0.4
|
| 425 |
+
elif intensity < 0.6:
|
| 426 |
+
n_bursts = random.randint(3, 5)
|
| 427 |
+
period = 0.10
|
| 428 |
+
pitch = 180
|
| 429 |
+
energy = 0.6
|
| 430 |
+
else:
|
| 431 |
+
n_bursts = random.randint(5, 8)
|
| 432 |
+
period = 0.09
|
| 433 |
+
pitch = 150
|
| 434 |
+
energy = 0.75
|
| 435 |
+
|
| 436 |
+
chunks = []
|
| 437 |
+
for i in range(n_bursts):
|
| 438 |
+
dur = period * 0.7
|
| 439 |
+
n = int(self.sample_rate * dur)
|
| 440 |
+
if n < 2:
|
| 441 |
+
continue
|
| 442 |
+
t = np.linspace(0, dur, n, dtype=np.float64)
|
| 443 |
+
phase = 2.0 * np.pi * pitch * t
|
| 444 |
+
source = np.sin(phase)
|
| 445 |
+
for h in range(2, 5):
|
| 446 |
+
source += (0.4 / h) * np.sin(phase * h)
|
| 447 |
+
source /= 3.0
|
| 448 |
+
noise = np.random.normal(0, 0.3, n)
|
| 449 |
+
mixed = source * 0.6 + noise * 0.4
|
| 450 |
+
# Simple bandpass via FFT
|
| 451 |
+
fft = np.fft.rfft(mixed)
|
| 452 |
+
freqs = np.fft.rfftfreq(n, 1.0 / self.sample_rate)
|
| 453 |
+
fft[(freqs < 400) | (freqs > 3000)] = 0
|
| 454 |
+
mixed = np.fft.irfft(fft, n).astype(np.float32)
|
| 455 |
+
# Envelope
|
| 456 |
+
env = np.ones(n, dtype=np.float32)
|
| 457 |
+
attack = min(int(0.01 * self.sample_rate), n // 4)
|
| 458 |
+
release = min(int(0.04 * self.sample_rate), n // 4)
|
| 459 |
+
if attack > 0:
|
| 460 |
+
env[:attack] = np.linspace(0, 1, attack)
|
| 461 |
+
if release > 0:
|
| 462 |
+
env[-release:] = np.linspace(1, 0, release)
|
| 463 |
+
decay = 1.0 - 0.2 * (i / max(1, n_bursts - 1))
|
| 464 |
+
chunks.append((mixed * env * energy * decay).astype(np.float32))
|
| 465 |
+
chunks.append(np.zeros(int(self.sample_rate * period * 0.3), dtype=np.float32))
|
| 466 |
+
|
| 467 |
+
if not chunks:
|
| 468 |
+
return np.zeros(0, dtype=np.float32)
|
| 469 |
+
audio = np.concatenate(chunks)
|
| 470 |
+
max_val = float(np.max(np.abs(audio)))
|
| 471 |
+
if max_val > 0:
|
| 472 |
+
audio = audio / max_val * 0.7 * intensity
|
| 473 |
+
return audio.astype(np.float32)
|
| 474 |
+
|
| 475 |
+
|
| 476 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 477 |
+
# SECTION C β Observer Mode
|
| 478 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 479 |
+
|
| 480 |
+
class EngineMode(Enum):
|
| 481 |
+
"""Operating mode of the voice engine."""
|
| 482 |
+
ACTIVE = "active" # Normal conversational mode
|
| 483 |
+
OBSERVER = "observer" # Passive listening, no responses until triggered
|
| 484 |
+
|
| 485 |
+
|
| 486 |
+
# Default trigger phrases that activate NIMA from observer mode
|
| 487 |
+
DEFAULT_TRIGGER_PHRASES = [
|
| 488 |
+
"nima, what do you make of all this",
|
| 489 |
+
"nima, what do you think",
|
| 490 |
+
"nima, your thoughts",
|
| 491 |
+
"nima, insights",
|
| 492 |
+
"nima, analysis",
|
| 493 |
+
"what do you think, nima",
|
| 494 |
+
"nima, chime in",
|
| 495 |
+
"nima, weigh in",
|
| 496 |
+
"nima",
|
| 497 |
+
]
|
| 498 |
+
|
| 499 |
+
|
| 500 |
+
@dataclass
|
| 501 |
+
class ObserverTranscriptEntry:
|
| 502 |
+
"""A single entry in the observer's accumulated transcript."""
|
| 503 |
+
timestamp: float
|
| 504 |
+
speaker_id: str # "Speaker_A", "Speaker_B", or name
|
| 505 |
+
text: str
|
| 506 |
+
confidence: float = 0.0
|
| 507 |
+
emotion_snapshot: Optional[Dict[str, float]] = None # valence, arousal
|
| 508 |
+
|
| 509 |
+
|
| 510 |
+
@dataclass
|
| 511 |
+
class ConsolidationInsight:
|
| 512 |
+
"""Result of a ConsolidationInsightPass."""
|
| 513 |
+
timestamp: float
|
| 514 |
+
total_entries: int
|
| 515 |
+
duration_listened_s: float
|
| 516 |
+
speaker_summary: Dict[str, int] = field(default_factory=dict) # speaker -> utterance count
|
| 517 |
+
key_themes: List[str] = field(default_factory=list)
|
| 518 |
+
insight_text: str = ""
|
| 519 |
+
|
| 520 |
+
|
| 521 |
+
class ObserverState:
|
| 522 |
+
"""
|
| 523 |
+
Passive listening mode. The engine records everything but stays silent
|
| 524 |
+
until an explicit trigger phrase is detected.
|
| 525 |
+
|
| 526 |
+
When triggered, it executes a ConsolidationInsightPass β a sweep across
|
| 527 |
+
the accumulated transcript history that produces a consolidated insight
|
| 528 |
+
with speaker attribution, theme extraction, and NIMA's analysis.
|
| 529 |
+
"""
|
| 530 |
+
|
| 531 |
+
def __init__(
|
| 532 |
+
self,
|
| 533 |
+
trigger_phrases: Optional[List[str]] = None,
|
| 534 |
+
max_history_hours: float = 4.0,
|
| 535 |
+
max_entries: int = 5000,
|
| 536 |
+
):
|
| 537 |
+
self.mode = EngineMode.ACTIVE
|
| 538 |
+
self.trigger_phrases = trigger_phrases or DEFAULT_TRIGGER_PHRASES
|
| 539 |
+
self.max_history_hours = max_history_hours
|
| 540 |
+
self.max_entries = max_entries
|
| 541 |
+
|
| 542 |
+
# Accumulated transcript
|
| 543 |
+
self._transcript: Deque[ObserverTranscriptEntry] = deque(maxlen=max_entries)
|
| 544 |
+
self._lock = threading.Lock()
|
| 545 |
+
self._listen_start_time: float = 0.0
|
| 546 |
+
|
| 547 |
+
# Stats
|
| 548 |
+
self.total_observed_seconds: float = 0.0
|
| 549 |
+
self.insights_generated: int = 0
|
| 550 |
+
|
| 551 |
+
def enter_observer(self):
|
| 552 |
+
"""Switch to observer mode. Recording starts now."""
|
| 553 |
+
self.mode = EngineMode.OBSERVER
|
| 554 |
+
self._listen_start_time = time.time()
|
| 555 |
+
logger.info("[Observer] Entered observer mode β listening silently")
|
| 556 |
+
|
| 557 |
+
def exit_observer(self) -> "ObserverState":
|
| 558 |
+
"""Switch back to active mode."""
|
| 559 |
+
self.mode = EngineMode.ACTIVE
|
| 560 |
+
logger.info("[Observer] Exited observer mode (observed %.1fs)",
|
| 561 |
+
self.total_observed_seconds)
|
| 562 |
+
return self
|
| 563 |
+
|
| 564 |
+
@property
|
| 565 |
+
def is_observing(self) -> bool:
|
| 566 |
+
return self.mode == EngineMode.OBSERVER
|
| 567 |
+
|
| 568 |
+
def add_transcript_entry(
|
| 569 |
+
self,
|
| 570 |
+
text: str,
|
| 571 |
+
speaker_id: str = "Speaker_A",
|
| 572 |
+
confidence: float = 0.0,
|
| 573 |
+
emotion_snapshot: Optional[Dict[str, float]] = None,
|
| 574 |
+
):
|
| 575 |
+
"""Add a transcribed utterance to the observer's memory."""
|
| 576 |
+
with self._lock:
|
| 577 |
+
entry = ObserverTranscriptEntry(
|
| 578 |
+
timestamp=time.time(),
|
| 579 |
+
speaker_id=speaker_id,
|
| 580 |
+
text=text,
|
| 581 |
+
confidence=confidence,
|
| 582 |
+
emotion_snapshot=emotion_snapshot,
|
| 583 |
+
)
|
| 584 |
+
self._transcript.append(entry)
|
| 585 |
+
self.total_observed_seconds += len(text) / 5.0 # rough ~5 words/sec estimate
|
| 586 |
+
|
| 587 |
+
# Prune old entries beyond max_history_hours
|
| 588 |
+
cutoff = time.time() - (self.max_history_hours * 3600)
|
| 589 |
+
while self._transcript and self._transcript[0].timestamp < cutoff:
|
| 590 |
+
self._transcript.popleft()
|
| 591 |
+
|
| 592 |
+
def check_trigger(self, text: str) -> bool:
|
| 593 |
+
"""
|
| 594 |
+
Check if the transcribed text contains a trigger phrase.
|
| 595 |
+
Returns True if observer mode should activate a response.
|
| 596 |
+
"""
|
| 597 |
+
if not self.is_observing:
|
| 598 |
+
return False
|
| 599 |
+
text_lower = text.strip().lower()
|
| 600 |
+
for phrase in self.trigger_phrases:
|
| 601 |
+
if phrase in text_lower:
|
| 602 |
+
logger.info("[Observer] Trigger phrase detected: '%s'", phrase)
|
| 603 |
+
return True
|
| 604 |
+
return False
|
| 605 |
+
|
| 606 |
+
def get_transcript_history(self, limit: Optional[int] = None) -> List[ObserverTranscriptEntry]:
|
| 607 |
+
"""Get accumulated transcript entries."""
|
| 608 |
+
with self._lock:
|
| 609 |
+
entries = list(self._transcript)
|
| 610 |
+
if limit:
|
| 611 |
+
return entries[-limit:]
|
| 612 |
+
return entries
|
| 613 |
+
|
| 614 |
+
def get_consolidated_transcript(self) -> str:
|
| 615 |
+
"""
|
| 616 |
+
Format the full transcript history as a single string suitable
|
| 617 |
+
for passing to the model for a ConsolidationInsightPass.
|
| 618 |
+
"""
|
| 619 |
+
with self._lock:
|
| 620 |
+
entries = list(self._transcript)
|
| 621 |
+
if not entries:
|
| 622 |
+
return ""
|
| 623 |
+
|
| 624 |
+
lines = []
|
| 625 |
+
for entry in entries:
|
| 626 |
+
time_str = time.strftime("%H:%M:%S", time.localtime(entry.timestamp))
|
| 627 |
+
lines.append(f"[{time_str}] {entry.speaker_id}: {entry.text}")
|
| 628 |
+
|
| 629 |
+
return "\n".join(lines)
|
| 630 |
+
|
| 631 |
+
def build_consolidation_prompt(self) -> str:
|
| 632 |
+
"""
|
| 633 |
+
Build the full prompt for the ConsolidationInsightPass.
|
| 634 |
+
This includes the transcript, speaker summary, and instructions.
|
| 635 |
+
"""
|
| 636 |
+
transcript = self.get_consolidated_transcript()
|
| 637 |
+
if not transcript:
|
| 638 |
+
return ""
|
| 639 |
+
|
| 640 |
+
# Speaker statistics
|
| 641 |
+
speaker_counts: Dict[str, int] = {}
|
| 642 |
+
for entry in self._transcript:
|
| 643 |
+
speaker_counts[entry.speaker_id] = speaker_counts.get(entry.speaker_id, 0) + 1
|
| 644 |
+
|
| 645 |
+
speaker_summary = ", ".join(
|
| 646 |
+
f"{speaker} ({count} turns)" for speaker, count in
|
| 647 |
+
sorted(speaker_counts.items(), key=lambda x: -x[1])
|
| 648 |
+
)
|
| 649 |
+
|
| 650 |
+
duration_min = self.total_observed_seconds / 60.0
|
| 651 |
+
|
| 652 |
+
prompt = (
|
| 653 |
+
f"You have been silently observing a conversation for {duration_min:.1f} minutes.\n"
|
| 654 |
+
f"Participants: {speaker_summary}\n\n"
|
| 655 |
+
f"Full transcript:\n"
|
| 656 |
+
f"{transcript}\n\n"
|
| 657 |
+
f"Based on your observation of this conversation, provide:\n"
|
| 658 |
+
f"1. A brief summary of the key points discussed\n"
|
| 659 |
+
f"2. The main themes or topics that emerged\n"
|
| 660 |
+
f"3. Notable dynamics between speakers (agreement, disagreement, shifts)\n"
|
| 661 |
+
f"4. Any insights, patterns, or perspectives that may have been missed\n"
|
| 662 |
+
f"5. If appropriate, a thoughtful question or reflection\n\n"
|
| 663 |
+
f"Keep your response concise but insightful. Address specific speakers "
|
| 664 |
+
f"when referencing their contributions."
|
| 665 |
+
)
|
| 666 |
+
return prompt
|
| 667 |
+
|
| 668 |
+
def get_stats(self) -> Dict[str, Any]:
|
| 669 |
+
with self._lock:
|
| 670 |
+
return {
|
| 671 |
+
"mode": self.mode.value,
|
| 672 |
+
"total_entries": len(self._transcript),
|
| 673 |
+
"total_observed_seconds": self.total_observed_seconds,
|
| 674 |
+
"insights_generated": self.insights_generated,
|
| 675 |
+
"trigger_phrases": self.trigger_phrases,
|
| 676 |
+
}
|
| 677 |
+
|
| 678 |
+
|
| 679 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 680 |
+
# SECTION D β Multi-Speaker Voice Fingerprinting & Group Conversation
|
| 681 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 682 |
+
|
| 683 |
+
# Soft-import speaker verification backends
|
| 684 |
+
_speaker_backend = None
|
| 685 |
+
_speaker_backend_name = None
|
| 686 |
+
|
| 687 |
+
try:
|
| 688 |
+
from speechbrain.inference.speaker import EncoderClassifier
|
| 689 |
+
_speaker_backend = "speechbrain"
|
| 690 |
+
SPEAKER_ID_AVAILABLE = True
|
| 691 |
+
except ImportError:
|
| 692 |
+
try:
|
| 693 |
+
from pyannote.audio import Pipeline as PyannotePipeline
|
| 694 |
+
_speaker_backend = "pyannote"
|
| 695 |
+
SPEAKER_ID_AVAILABLE = True
|
| 696 |
+
except ImportError:
|
| 697 |
+
SPEAKER_ID_AVAILABLE = False
|
| 698 |
+
EncoderClassifier = None # type: ignore[assignment, misc]
|
| 699 |
+
PyannotePipeline = None # type: ignore[assignment, misc]
|
| 700 |
+
|
| 701 |
+
|
| 702 |
+
@dataclass
|
| 703 |
+
class VoiceProfile:
|
| 704 |
+
"""A registered speaker's voice fingerprint."""
|
| 705 |
+
speaker_id: str # "Speaker_A", "John", etc.
|
| 706 |
+
embedding: np.ndarray # voice embedding vector
|
| 707 |
+
registered_at: float = field(default_factory=time.time)
|
| 708 |
+
name: Optional[str] = None # human-given name (if introduced)
|
| 709 |
+
sample_count: int = 1 # how many samples contributed
|
| 710 |
+
last_seen: float = field(default_factory=time.time)
|
| 711 |
+
total_speaking_time_s: float = 0.0
|
| 712 |
+
|
| 713 |
+
|
| 714 |
+
@dataclass
|
| 715 |
+
class SpeakerTurn:
|
| 716 |
+
"""A single speaker's turn in the conversation."""
|
| 717 |
+
speaker_id: str
|
| 718 |
+
text: str
|
| 719 |
+
start_time: float
|
| 720 |
+
end_time: float
|
| 721 |
+
confidence: float = 0.0
|
| 722 |
+
|
| 723 |
+
|
| 724 |
+
class SpeakerIdentifier:
|
| 725 |
+
"""
|
| 726 |
+
Identifies speakers in a multi-person conversation using voice embeddings.
|
| 727 |
+
|
| 728 |
+
Supports two backends (soft-imported, gracefully degrades):
|
| 729 |
+
1. SpeechBrain ECAPA-TDNN (preferred β fast, lightweight)
|
| 730 |
+
2. PyAnnote Audio (alternative β more accurate but heavier)
|
| 731 |
+
|
| 732 |
+
When neither is available, falls back to a simple acoustic profile
|
| 733 |
+
clustering based on spectral features (pitch, energy, ZCR).
|
| 734 |
+
"""
|
| 735 |
+
|
| 736 |
+
# Distance threshold for matching a voice to an existing profile
|
| 737 |
+
MATCH_THRESHOLD: float = 0.7 # cosine similarity threshold
|
| 738 |
+
# Minimum audio length for reliable embedding
|
| 739 |
+
MIN_AUDIO_FOR_EMBEDDING_S: float = 1.5
|
| 740 |
+
|
| 741 |
+
def __init__(self, backend: Optional[str] = None):
|
| 742 |
+
self._classifier = None
|
| 743 |
+
self._pyannote_pipeline = None
|
| 744 |
+
self.profiles: Dict[str, VoiceProfile] = {}
|
| 745 |
+
self._speaker_counter = 0
|
| 746 |
+
self._lock = threading.Lock()
|
| 747 |
+
self._backend_name = backend # None = auto-detect
|
| 748 |
+
|
| 749 |
+
self._initialize_backend()
|
| 750 |
+
|
| 751 |
+
def _initialize_backend(self):
|
| 752 |
+
"""Try to initialize a speaker identification backend."""
|
| 753 |
+
if self._backend_name and self._backend_name != "acoustic_fallback":
|
| 754 |
+
try:
|
| 755 |
+
if self._backend_name == "speechbrain":
|
| 756 |
+
self._classifier = EncoderClassifier.from_hparams(
|
| 757 |
+
source="speechbrain/spkrec-ecapa-voxceleb",
|
| 758 |
+
savedir="~/.cache/speechbrain/spkrec-ecapa-voxceleb",
|
| 759 |
+
)
|
| 760 |
+
logger.info("[SpeakerID] SpeechBrain ECAPA-TDNN loaded")
|
| 761 |
+
return
|
| 762 |
+
elif self._backend_name == "pyannote":
|
| 763 |
+
self._pyannote_pipeline = PyannotePipeline.from_pretrained(
|
| 764 |
+
"pyannote/speaker-diarization-3.1"
|
| 765 |
+
)
|
| 766 |
+
logger.info("[SpeakerID] PyAnnote pipeline loaded")
|
| 767 |
+
return
|
| 768 |
+
except Exception as e:
|
| 769 |
+
logger.warning("[SpeakerID] Failed to load %s: %s", self._backend_name, e)
|
| 770 |
+
|
| 771 |
+
# Auto-detect
|
| 772 |
+
if SPEAKER_ID_AVAILABLE and _speaker_backend == "speechbrain":
|
| 773 |
+
try:
|
| 774 |
+
self._classifier = EncoderClassifier.from_hparams(
|
| 775 |
+
source="speechbrain/spkrec-ecapa-voxceleb",
|
| 776 |
+
savedir="~/.cache/speechbrain/spkrec-ecapa-voxceleb",
|
| 777 |
+
)
|
| 778 |
+
self._backend_name = "speechbrain"
|
| 779 |
+
logger.info("[SpeakerID] SpeechBrain ECAPA-TDNN loaded (auto)")
|
| 780 |
+
return
|
| 781 |
+
except Exception as e:
|
| 782 |
+
logger.warning("[SpeakerID] SpeechBrain auto-load failed: %s", e)
|
| 783 |
+
|
| 784 |
+
if SPEAKER_ID_AVAILABLE and _speaker_backend == "pyannote":
|
| 785 |
+
try:
|
| 786 |
+
self._pyannote_pipeline = PyannotePipeline.from_pretrained(
|
| 787 |
+
"pyannote/speaker-diarization-3.1"
|
| 788 |
+
)
|
| 789 |
+
self._backend_name = "pyannote"
|
| 790 |
+
logger.info("[SpeakerID] PyAnnote loaded (auto)")
|
| 791 |
+
return
|
| 792 |
+
except Exception as e:
|
| 793 |
+
logger.warning("[SpeakerID] PyAnnote auto-load failed: %s", e)
|
| 794 |
+
|
| 795 |
+
self._backend_name = "acoustic_fallback"
|
| 796 |
+
logger.info("[SpeakerID] Using acoustic fallback (no embedding model)")
|
| 797 |
+
|
| 798 |
+
def identify_speaker(
|
| 799 |
+
self, audio: np.ndarray, sample_rate: int = 16000
|
| 800 |
+
) -> Tuple[str, float]:
|
| 801 |
+
"""
|
| 802 |
+
Identify which speaker is talking.
|
| 803 |
+
|
| 804 |
+
Returns (speaker_id, confidence).
|
| 805 |
+
If the speaker is new, creates a new profile automatically.
|
| 806 |
+
"""
|
| 807 |
+
duration = len(audio) / sample_rate
|
| 808 |
+
|
| 809 |
+
if self._backend_name == "speechbrain" and self._classifier is not None:
|
| 810 |
+
return self._identify_speechbrain(audio, sample_rate)
|
| 811 |
+
elif self._backend_name == "pyannote" and self._pyannote_pipeline is not None:
|
| 812 |
+
return self._identify_pyannote(audio, sample_rate)
|
| 813 |
+
else:
|
| 814 |
+
return self._identify_acoustic_fallback(audio, sample_rate)
|
| 815 |
+
|
| 816 |
+
def _identify_speechbrain(
|
| 817 |
+
self, audio: np.ndarray, sample_rate: int
|
| 818 |
+
) -> Tuple[str, float]:
|
| 819 |
+
"""Identify speaker using SpeechBrain ECAPA-TDNN embedding."""
|
| 820 |
+
try:
|
| 821 |
+
# Compute embedding
|
| 822 |
+
embedding = self._classifier.encode_batch(
|
| 823 |
+
torch.tensor(audio).unsqueeze(0).float()
|
| 824 |
+
)
|
| 825 |
+
if hasattr(embedding, 'detach'):
|
| 826 |
+
embedding = embedding.detach().cpu().numpy()
|
| 827 |
+
embedding = embedding.flatten()
|
| 828 |
+
|
| 829 |
+
return self._match_or_register(embedding)
|
| 830 |
+
except Exception as e:
|
| 831 |
+
logger.warning("[SpeakerID] SpeechBrain failed: %s", e)
|
| 832 |
+
return self._identify_acoustic_fallback(audio, sample_rate)
|
| 833 |
+
|
| 834 |
+
def _identify_pyannote(
|
| 835 |
+
self, audio: np.ndarray, sample_rate: int
|
| 836 |
+
) -> Tuple[str, float]:
|
| 837 |
+
"""Identify speaker using PyAnnote pipeline."""
|
| 838 |
+
# PyAnnote typically works on full audio files, so we store
|
| 839 |
+
# segments and batch-process. For real-time, fall back to acoustic.
|
| 840 |
+
logger.debug("[SpeakerID] PyAnnote used for batch, falling to acoustic for real-time")
|
| 841 |
+
return self._identify_acoustic_fallback(audio, sample_rate)
|
| 842 |
+
|
| 843 |
+
def _identify_acoustic_fallback(
|
| 844 |
+
self, audio: np.ndarray, sample_rate: int
|
| 845 |
+
) -> Tuple[str, float]:
|
| 846 |
+
"""
|
| 847 |
+
Fallback: cluster speakers by basic acoustic features.
|
| 848 |
+
Not as accurate as neural embeddings but works without dependencies.
|
| 849 |
+
"""
|
| 850 |
+
# Compute simple features
|
| 851 |
+
if len(audio) < sample_rate * 0.3:
|
| 852 |
+
return self._new_speaker_id(), 0.3
|
| 853 |
+
|
| 854 |
+
energy = float(np.sqrt(np.mean(audio ** 2)))
|
| 855 |
+
fft = np.fft.rfft(audio[:min(len(audio), sample_rate)].astype(np.float32))
|
| 856 |
+
freqs = np.fft.rfftfreq(len(audio[:min(len(audio), sample_rate)]), 1.0 / sample_rate)
|
| 857 |
+
magnitude = np.abs(fft)
|
| 858 |
+
if magnitude.sum() > 0:
|
| 859 |
+
centroid = float(np.sum(freqs * magnitude) / np.sum(magnitude))
|
| 860 |
+
else:
|
| 861 |
+
centroid = 0.0
|
| 862 |
+
zcr = float(np.mean(np.abs(np.diff(np.sign(audio))) > 0))
|
| 863 |
+
|
| 864 |
+
features = np.array([energy, centroid, zcr], dtype=np.float32)
|
| 865 |
+
|
| 866 |
+
# Compare to existing profiles
|
| 867 |
+
with self._lock:
|
| 868 |
+
best_id = None
|
| 869 |
+
best_sim = -1.0
|
| 870 |
+
|
| 871 |
+
for sid, profile in self.profiles.items():
|
| 872 |
+
if profile.embedding is not None and len(profile.embedding) == len(features):
|
| 873 |
+
sim = float(np.dot(features, profile.embedding) / (
|
| 874 |
+
np.linalg.norm(features) * np.linalg.norm(profile.embedding) + 1e-8
|
| 875 |
+
))
|
| 876 |
+
if sim > best_sim:
|
| 877 |
+
best_sim = sim
|
| 878 |
+
best_id = sid
|
| 879 |
+
|
| 880 |
+
if best_id and best_sim > 0.85:
|
| 881 |
+
# Update profile
|
| 882 |
+
self.profiles[best_id].last_seen = time.time()
|
| 883 |
+
self.profiles[best_id].sample_count += 1
|
| 884 |
+
self.profiles[best_id].total_speaking_time_s += len(audio) / sample_rate
|
| 885 |
+
# Smooth the embedding (moving average)
|
| 886 |
+
alpha = 1.0 / self.profiles[best_id].sample_count
|
| 887 |
+
self.profiles[best_id].embedding = (
|
| 888 |
+
(1 - alpha) * self.profiles[best_id].embedding + alpha * features
|
| 889 |
+
)
|
| 890 |
+
return best_id, float(best_sim)
|
| 891 |
+
|
| 892 |
+
# New speaker
|
| 893 |
+
return self._new_speaker_id_with_profile(features), 0.5
|
| 894 |
+
|
| 895 |
+
def _match_or_register(self, embedding: np.ndarray) -> Tuple[str, float]:
|
| 896 |
+
"""Match embedding to existing profiles or register new speaker."""
|
| 897 |
+
with self._lock:
|
| 898 |
+
best_id = None
|
| 899 |
+
best_sim = -1.0
|
| 900 |
+
|
| 901 |
+
for sid, profile in self.profiles.items():
|
| 902 |
+
if profile.embedding is not None and len(profile.embedding) == len(embedding):
|
| 903 |
+
sim = float(np.dot(embedding, profile.embedding) / (
|
| 904 |
+
np.linalg.norm(embedding) * np.linalg.norm(profile.embedding) + 1e-8
|
| 905 |
+
))
|
| 906 |
+
if sim > best_sim:
|
| 907 |
+
best_sim = sim
|
| 908 |
+
best_id = sid
|
| 909 |
+
|
| 910 |
+
if best_id and best_sim > self.MATCH_THRESHOLD:
|
| 911 |
+
# Update existing profile
|
| 912 |
+
profile = self.profiles[best_id]
|
| 913 |
+
profile.last_seen = time.time()
|
| 914 |
+
profile.sample_count += 1
|
| 915 |
+
# Smooth embedding
|
| 916 |
+
alpha = 1.0 / profile.sample_count
|
| 917 |
+
profile.embedding = (1 - alpha) * profile.embedding + alpha * embedding
|
| 918 |
+
return best_id, float(best_sim)
|
| 919 |
+
|
| 920 |
+
# Register new speaker
|
| 921 |
+
return self._new_speaker_id_with_profile(embedding), 0.6
|
| 922 |
+
|
| 923 |
+
def _new_speaker_id(self) -> str:
|
| 924 |
+
self._speaker_counter += 1
|
| 925 |
+
return f"Speaker_{chr(64 + self._speaker_counter)}" # Speaker_A, B, C...
|
| 926 |
+
|
| 927 |
+
def _new_speaker_id_with_profile(self, embedding: np.ndarray) -> str:
|
| 928 |
+
sid = self._new_speaker_id()
|
| 929 |
+
with self._lock:
|
| 930 |
+
self.profiles[sid] = VoiceProfile(
|
| 931 |
+
speaker_id=sid,
|
| 932 |
+
embedding=embedding.copy(),
|
| 933 |
+
)
|
| 934 |
+
logger.info("[SpeakerID] New speaker registered: %s", sid)
|
| 935 |
+
return sid
|
| 936 |
+
|
| 937 |
+
def register_name(self, speaker_id: str, name: str):
|
| 938 |
+
"""Map a speaker_id to a human name (e.g., 'Speaker_A' β 'John')."""
|
| 939 |
+
with self._lock:
|
| 940 |
+
if speaker_id in self.profiles:
|
| 941 |
+
self.profiles[speaker_id].name = name
|
| 942 |
+
logger.info("[SpeakerID] %s β '%s'", speaker_id, name)
|
| 943 |
+
|
| 944 |
+
def get_display_name(self, speaker_id: str) -> str:
|
| 945 |
+
"""Get the display name for a speaker (human name if set, else ID)."""
|
| 946 |
+
with self._lock:
|
| 947 |
+
profile = self.profiles.get(speaker_id)
|
| 948 |
+
if profile and profile.name:
|
| 949 |
+
return profile.name
|
| 950 |
+
return speaker_id
|
| 951 |
+
|
| 952 |
+
def get_conversation_matrix(self) -> str:
|
| 953 |
+
"""
|
| 954 |
+
Build a conversational matrix string for the system prompt.
|
| 955 |
+
|
| 956 |
+
Example: [Current Conversational Matrix: John, Sarah, NIMA]
|
| 957 |
+
"""
|
| 958 |
+
with self._lock:
|
| 959 |
+
names = []
|
| 960 |
+
for sid in sorted(self.profiles.keys()):
|
| 961 |
+
name = self.profiles[sid].name or sid
|
| 962 |
+
names.append(name)
|
| 963 |
+
|
| 964 |
+
names.append("NIMA")
|
| 965 |
+
return f"[Current Conversational Matrix: {', '.join(names)}]"
|
| 966 |
+
|
| 967 |
+
def get_active_speakers(self, within_s: float = 300.0) -> List[str]:
|
| 968 |
+
"""Get speakers who have spoken in the last `within_s` seconds."""
|
| 969 |
+
now = time.time()
|
| 970 |
+
with self._lock:
|
| 971 |
+
return [
|
| 972 |
+
sid for sid, p in self.profiles.items()
|
| 973 |
+
if now - p.last_seen < within_s
|
| 974 |
+
]
|
| 975 |
+
|
| 976 |
+
def get_stats(self) -> Dict[str, Any]:
|
| 977 |
+
with self._lock:
|
| 978 |
+
return {
|
| 979 |
+
"backend": self._backend_name,
|
| 980 |
+
"registered_speakers": len(self.profiles),
|
| 981 |
+
"speaker_names": {
|
| 982 |
+
sid: (p.name or sid) for sid, p in self.profiles.items()
|
| 983 |
+
},
|
| 984 |
+
"total_speakers_seen": self._speaker_counter,
|
| 985 |
+
}
|
| 986 |
+
|
| 987 |
+
|
| 988 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 989 |
+
# SECTION E β Consolidation Insight Pass
|
| 990 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββοΏ½οΏ½ββββββββββββββββββββββββββββ
|
| 991 |
+
|
| 992 |
+
class ConsolidationInsightPass:
|
| 993 |
+
"""
|
| 994 |
+
Executes a consolidated analysis pass over the observer's accumulated
|
| 995 |
+
transcript history. Produces a structured insight with themes, speaker
|
| 996 |
+
dynamics, and NIMA's perspective.
|
| 997 |
+
"""
|
| 998 |
+
|
| 999 |
+
def execute(self, observer: ObserverState,
|
| 1000 |
+
speaker_id: SpeakerIdentifier,
|
| 1001 |
+
generate_func: Optional[Callable] = None) -> ConsolidationInsight:
|
| 1002 |
+
"""
|
| 1003 |
+
Run the consolidation pass.
|
| 1004 |
+
|
| 1005 |
+
Args:
|
| 1006 |
+
observer: the ObserverState with accumulated transcript
|
| 1007 |
+
speaker_id: the SpeakerIdentifier for speaker attribution
|
| 1008 |
+
generate_func: optional async generator function (e.g., NIMA's generate)
|
| 1009 |
+
If None, returns a basic statistical summary.
|
| 1010 |
+
"""
|
| 1011 |
+
entries = observer.get_transcript_history()
|
| 1012 |
+
if not entries:
|
| 1013 |
+
return ConsolidationInsight(
|
| 1014 |
+
timestamp=time.time(),
|
| 1015 |
+
total_entries=0,
|
| 1016 |
+
duration_listened_s=0.0,
|
| 1017 |
+
insight_text="No transcript accumulated yet.",
|
| 1018 |
+
)
|
| 1019 |
+
|
| 1020 |
+
# Speaker summary
|
| 1021 |
+
speaker_counts: Dict[str, int] = {}
|
| 1022 |
+
for entry in entries:
|
| 1023 |
+
display = speaker_id.get_display_name(entry.speaker_id)
|
| 1024 |
+
speaker_counts[display] = speaker_counts.get(display, 0) + 1
|
| 1025 |
+
|
| 1026 |
+
# Duration
|
| 1027 |
+
start_time = entries[0].timestamp
|
| 1028 |
+
end_time = entries[-1].timestamp
|
| 1029 |
+
duration_s = end_time - start_time
|
| 1030 |
+
|
| 1031 |
+
# Theme extraction (simple keyword frequency)
|
| 1032 |
+
themes = self._extract_themes(entries)
|
| 1033 |
+
|
| 1034 |
+
# Build insight
|
| 1035 |
+
if generate_func:
|
| 1036 |
+
prompt = observer.build_consolidation_prompt()
|
| 1037 |
+
# Note: in practice this would be awaited. For sync context,
|
| 1038 |
+
# we build the prompt and note it needs async generation.
|
| 1039 |
+
insight_text = f"[PENDING GENERATION] Prompt prepared with {len(entries)} entries."
|
| 1040 |
+
else:
|
| 1041 |
+
# Basic statistical summary when no generator is available
|
| 1042 |
+
top_speaker = max(speaker_counts, key=speaker_counts.get) if speaker_counts else "N/A"
|
| 1043 |
+
insight_text = (
|
| 1044 |
+
f"Observed {len(entries)} utterances over {duration_s:.0f}s. "
|
| 1045 |
+
f"Primary speaker: {top_speaker} ({speaker_counts[top_speaker]} turns). "
|
| 1046 |
+
f"Themes: {', '.join(themes[:5]) if themes else 'general conversation'}."
|
| 1047 |
+
)
|
| 1048 |
+
|
| 1049 |
+
observer.insights_generated += 1
|
| 1050 |
+
|
| 1051 |
+
return ConsolidationInsight(
|
| 1052 |
+
timestamp=time.time(),
|
| 1053 |
+
total_entries=len(entries),
|
| 1054 |
+
duration_listened_s=duration_s,
|
| 1055 |
+
speaker_summary=speaker_counts,
|
| 1056 |
+
key_themes=themes,
|
| 1057 |
+
insight_text=insight_text,
|
| 1058 |
+
)
|
| 1059 |
+
|
| 1060 |
+
@staticmethod
|
| 1061 |
+
def _extract_themes(entries: List[ObserverTranscriptEntry]) -> List[str]:
|
| 1062 |
+
"""Simple keyword-frequency theme extraction."""
|
| 1063 |
+
# Common topic keywords
|
| 1064 |
+
topic_words = [
|
| 1065 |
+
"work", "project", "meeting", "deadline", "team", "idea",
|
| 1066 |
+
"feel", "think", "believe", "want", "need", "plan",
|
| 1067 |
+
"problem", "solution", "question", "answer", "decision",
|
| 1068 |
+
"family", "friend", "love", "health", "school", "money",
|
| 1069 |
+
"technology", "design", "code", "data", "model", "system",
|
| 1070 |
+
"music", "movie", "game", "food", "travel", "book",
|
| 1071 |
+
]
|
| 1072 |
+
word_freq: Dict[str, int] = {}
|
| 1073 |
+
for entry in entries:
|
| 1074 |
+
words = entry.text.lower().split()
|
| 1075 |
+
for word in words:
|
| 1076 |
+
clean = word.strip(".,!?;:'\"()[]{}").lower()
|
| 1077 |
+
if clean in topic_words:
|
| 1078 |
+
word_freq[clean] = word_freq.get(clean, 0) + 1
|
| 1079 |
+
|
| 1080 |
+
sorted_themes = sorted(word_freq, key=word_freq.get, reverse=True)
|
| 1081 |
+
return sorted_themes[:8]
|
| 1082 |
+
|
| 1083 |
+
|
| 1084 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1085 |
+
# SECTION F β torch import for SpeechBrain (needed in _identify_speechbrain)
|
| 1086 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1087 |
+
|
| 1088 |
+
try:
|
| 1089 |
+
import torch as _torch_for_speaker
|
| 1090 |
+
except ImportError:
|
| 1091 |
+
_torch_for_speaker = None
|
pipeline.py
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Pipeline Orchestrator β coordinates data generation, deep surgery,
|
| 3 |
+
fine-tuning, and deployment into a single flow.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import json
|
| 7 |
+
import logging
|
| 8 |
+
from datetime import datetime
|
| 9 |
+
from pathlib import Path
|
| 10 |
+
from typing import Any, Dict, Optional
|
| 11 |
+
|
| 12 |
+
logger = logging.getLogger("nima_unified.training.pipeline")
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class PipelineOrchestrator:
|
| 16 |
+
"""
|
| 17 |
+
Orchestrates the complete consciousness-aware fine-tuning pipeline.
|
| 18 |
+
|
| 19 |
+
Pipeline Stages:
|
| 20 |
+
1. Data Generation β consciousness-grounded training data
|
| 21 |
+
2. Deep Surgery β apply middleware for safety and qualia synthesis
|
| 22 |
+
3. Fine-tuning β train model with consciousness grounding
|
| 23 |
+
4. Deployment β package for production inference
|
| 24 |
+
"""
|
| 25 |
+
|
| 26 |
+
def __init__(self, config_path: Optional[str] = None):
|
| 27 |
+
self.config = self._load_config(config_path) if config_path else self._default_config()
|
| 28 |
+
self.output_dir = Path(self.config["output_directory"])
|
| 29 |
+
self.output_dir.mkdir(exist_ok=True, parents=True)
|
| 30 |
+
self.stage_results: Dict[str, Any] = {}
|
| 31 |
+
logger.info(f"PipelineOrchestrator initialized (output: {self.output_dir})")
|
| 32 |
+
|
| 33 |
+
@staticmethod
|
| 34 |
+
def _default_config() -> Dict[str, Any]:
|
| 35 |
+
return {
|
| 36 |
+
"output_directory": "./nima_pipeline_output",
|
| 37 |
+
"data_generation": {
|
| 38 |
+
"num_samples": 500,
|
| 39 |
+
"include_qualia_tags": True,
|
| 40 |
+
"consciousness_archetypes": [
|
| 41 |
+
"tactical_reasoning", "phenomenal_empathy", "meta_consciousness",
|
| 42 |
+
"task_orchestration", "ethical_veto", "narrative_coherence",
|
| 43 |
+
"identity_preservation", "phenomenal_richness",
|
| 44 |
+
],
|
| 45 |
+
},
|
| 46 |
+
"deep_surgery": {
|
| 47 |
+
"qualia_dim": 256,
|
| 48 |
+
"ethical_veto_threshold": 2.0,
|
| 49 |
+
"enable_audit_logging": True,
|
| 50 |
+
},
|
| 51 |
+
"fine_tuning": {
|
| 52 |
+
"lora_r": 8,
|
| 53 |
+
"lora_alpha": 16,
|
| 54 |
+
"lora_dropout": 0.05,
|
| 55 |
+
"learning_rate": 2e-4,
|
| 56 |
+
"batch_size": 4,
|
| 57 |
+
"epochs": 3,
|
| 58 |
+
"max_seq_length": 512,
|
| 59 |
+
},
|
| 60 |
+
"deployment": {
|
| 61 |
+
"format": "huggingface",
|
| 62 |
+
"include_middleware": True,
|
| 63 |
+
"include_voice": False,
|
| 64 |
+
},
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
@staticmethod
|
| 68 |
+
def _load_config(config_path: str) -> Dict[str, Any]:
|
| 69 |
+
with open(config_path, "r") as f:
|
| 70 |
+
return json.load(f)
|
| 71 |
+
|
| 72 |
+
async def run(self, **overrides) -> Dict[str, Any]:
|
| 73 |
+
"""Execute the full pipeline. Override config keys via kwargs."""
|
| 74 |
+
self.config.update(overrides)
|
| 75 |
+
start_time = datetime.now()
|
| 76 |
+
|
| 77 |
+
logger.info("=" * 72)
|
| 78 |
+
logger.info("NIMA Unified Pipeline β Starting")
|
| 79 |
+
logger.info("=" * 72)
|
| 80 |
+
|
| 81 |
+
# Stage 1: Data Generation
|
| 82 |
+
logger.info("[Stage 1/4] Data Generation")
|
| 83 |
+
self.stage_results["data_generation"] = await self._stage_data_generation()
|
| 84 |
+
|
| 85 |
+
# Stage 2: Deep Surgery
|
| 86 |
+
logger.info("[Stage 2/4] Deep Surgery")
|
| 87 |
+
self.stage_results["deep_surgery"] = self._stage_deep_surgery()
|
| 88 |
+
|
| 89 |
+
# Stage 3: Fine-tuning
|
| 90 |
+
logger.info("[Stage 3/4] Fine-tuning")
|
| 91 |
+
self.stage_results["fine_tuning"] = await self._stage_fine_tuning()
|
| 92 |
+
|
| 93 |
+
# Stage 4: Deployment packaging
|
| 94 |
+
logger.info("[Stage 4/4] Deployment")
|
| 95 |
+
self.stage_results["deployment"] = self._stage_deployment()
|
| 96 |
+
|
| 97 |
+
elapsed = (datetime.now() - start_time).total_seconds()
|
| 98 |
+
summary = {
|
| 99 |
+
"status": "complete",
|
| 100 |
+
"elapsed_seconds": elapsed,
|
| 101 |
+
"stages": self.stage_results,
|
| 102 |
+
"config": self.config,
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
logger.info(f"Pipeline complete in {elapsed:.1f}s")
|
| 106 |
+
return summary
|
| 107 |
+
|
| 108 |
+
# ββ Stages ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 109 |
+
|
| 110 |
+
async def _stage_data_generation(self) -> Dict[str, Any]:
|
| 111 |
+
from nima_unified.training.consultative_agent import ConsultativeFineTuningAgent
|
| 112 |
+
agent = ConsultativeFineTuningAgent()
|
| 113 |
+
dataset = agent._generate_consciousness_dataset(
|
| 114 |
+
dev_goal="Unified consciousness integration",
|
| 115 |
+
triggering_event="Pipeline initialization",
|
| 116 |
+
root_cause="Initial system bootstrap",
|
| 117 |
+
dataset_size=self.config["data_generation"]["num_samples"],
|
| 118 |
+
)
|
| 119 |
+
agent.shutdown()
|
| 120 |
+
|
| 121 |
+
out_path = self.output_dir / "training_data.jsonl"
|
| 122 |
+
with open(out_path, "w") as f:
|
| 123 |
+
for record in dataset:
|
| 124 |
+
f.write(json.dumps(record) + "\n")
|
| 125 |
+
|
| 126 |
+
return {"status": "success", "samples": len(dataset), "path": str(out_path)}
|
| 127 |
+
|
| 128 |
+
def _stage_deep_surgery(self) -> Dict[str, Any]:
|
| 129 |
+
from nima_unified.core.deep_surgery import EthicalGuardian
|
| 130 |
+
guardian = EthicalGuardian(
|
| 131 |
+
threshold=self.config["deep_surgery"]["ethical_veto_threshold"]
|
| 132 |
+
)
|
| 133 |
+
return {
|
| 134 |
+
"status": "configured",
|
| 135 |
+
"qualia_dim": self.config["deep_surgery"]["qualia_dim"],
|
| 136 |
+
"ethical_veto_threshold": guardian.threshold,
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
async def _stage_fine_tuning(self) -> Dict[str, Any]:
|
| 140 |
+
return {"status": "ready", "config": self.config["fine_tuning"]}
|
| 141 |
+
|
| 142 |
+
def _stage_deployment(self) -> Dict[str, Any]:
|
| 143 |
+
return {"status": "ready", "config": self.config["deployment"]}
|
resource_optimizer.py
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import psutil
|
| 2 |
+
import time
|
| 3 |
+
import numpy as np
|
| 4 |
+
from dataclasses import dataclass, field
|
| 5 |
+
from typing import Dict, List, Any, Optional, Tuple
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
@dataclass
|
| 9 |
+
class SparseActivationManager:
|
| 10 |
+
total_agents: int = 10
|
| 11 |
+
min_active: int = 2
|
| 12 |
+
|
| 13 |
+
def compute_activation_mask(self, task_complexity: float, rho_quality: float) -> List[bool]:
|
| 14 |
+
score = max(0.0, min(1.0, (task_complexity + rho_quality) / 2.0))
|
| 15 |
+
active_count = int(score * self.total_agents)
|
| 16 |
+
active_count = max(self.min_active, min(self.total_agents, active_count))
|
| 17 |
+
return [i < active_count for i in range(self.total_agents)]
|
| 18 |
+
|
| 19 |
+
def select_active_layers(self, layer_weights: List[float], mask: List[bool]) -> List[int]:
|
| 20 |
+
return [idx for idx, active in enumerate(mask) if active]
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
@dataclass
|
| 24 |
+
class AdaptiveEnergyBudget:
|
| 25 |
+
max_watts: float = 120.0
|
| 26 |
+
safety_margin: float = 0.90
|
| 27 |
+
current_limit: Optional[float] = None
|
| 28 |
+
|
| 29 |
+
def __post_init__(self):
|
| 30 |
+
self.current_limit = self.max_watts * self.safety_margin
|
| 31 |
+
|
| 32 |
+
def current_power(self) -> float:
|
| 33 |
+
if hasattr(psutil, "sensors_battery") and psutil.sensors_battery() is not None:
|
| 34 |
+
batt = psutil.sensors_battery()
|
| 35 |
+
if batt.power_plugged:
|
| 36 |
+
return 0.0
|
| 37 |
+
try:
|
| 38 |
+
return psutil.cpu_percent(interval=0.1) * 0.5
|
| 39 |
+
except Exception:
|
| 40 |
+
return 0.0
|
| 41 |
+
|
| 42 |
+
def allow_processing(self, estimated_cost: float) -> bool:
|
| 43 |
+
return estimated_cost <= self.current_limit
|
| 44 |
+
|
| 45 |
+
def throttle(self, engine: Any) -> None:
|
| 46 |
+
if hasattr(engine, "half"):
|
| 47 |
+
engine.half()
|
| 48 |
+
elif hasattr(engine, "to"):
|
| 49 |
+
engine.to("cpu")
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
class GPURoutingManager:
|
| 53 |
+
def __init__(self, available_devices: List[str]):
|
| 54 |
+
self.available_devices = available_devices
|
| 55 |
+
|
| 56 |
+
def select_device(self, task_complexity: float, gpu_preference: bool = True) -> str:
|
| 57 |
+
if gpu_preference and self.available_devices:
|
| 58 |
+
return self.available_devices[0]
|
| 59 |
+
return "cpu"
|
| 60 |
+
|
| 61 |
+
def route(self, task_complexity: float, model: Any) -> None:
|
| 62 |
+
device = self.select_device(task_complexity)
|
| 63 |
+
if hasattr(model, "to"):
|
| 64 |
+
model.to(device)
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
# ============================================================================
|
| 68 |
+
# ENHANCED RESOURCE OPTIMIZATION - Dynamic Priorities & Predictive Budgeting
|
| 69 |
+
# ============================================================================
|
| 70 |
+
|
| 71 |
+
@dataclass
|
| 72 |
+
class EnhancedSparseActivationManager(SparseActivationManager):
|
| 73 |
+
"""Extended sparse activation with dynamic agent priorities and consciousness-aware scheduling."""
|
| 74 |
+
|
| 75 |
+
agent_priorities: List[float] = field(default_factory=list)
|
| 76 |
+
|
| 77 |
+
def __post_init__(self):
|
| 78 |
+
"""Initialize agent priorities to equal weights."""
|
| 79 |
+
if not self.agent_priorities or len(self.agent_priorities) != self.total_agents:
|
| 80 |
+
self.agent_priorities = [1.0] * self.total_agents
|
| 81 |
+
|
| 82 |
+
def update_priorities(self, task_complexity: float, phi_value: float, rho_metrics: Dict[str, float]) -> None:
|
| 83 |
+
"""
|
| 84 |
+
Update agent priorities dynamically based on system state.
|
| 85 |
+
|
| 86 |
+
Increases priority for agents critical to:
|
| 87 |
+
- Current task complexity
|
| 88 |
+
- Integrated information (Phi) quality
|
| 89 |
+
- RHO ethical metrics (virtue, integrity)
|
| 90 |
+
"""
|
| 91 |
+
base_score = (task_complexity + phi_value) / 2.0
|
| 92 |
+
virtue = rho_metrics.get("rho_virtue", 0.8)
|
| 93 |
+
integrity = rho_metrics.get("rho_integrity", 0.8)
|
| 94 |
+
ethical_weight = (virtue + integrity) / 2.0
|
| 95 |
+
|
| 96 |
+
for i in range(self.total_agents):
|
| 97 |
+
# Agent relevance combines task complexity, consciousness quality, and ethics
|
| 98 |
+
relevance = np.clip(base_score * ethical_weight * np.random.uniform(0.8, 1.2), 0, 1)
|
| 99 |
+
self.agent_priorities[i] = relevance
|
| 100 |
+
|
| 101 |
+
def compute_activation_mask(self, threshold: float = 0.5) -> List[bool]:
|
| 102 |
+
"""
|
| 103 |
+
Compute activation mask by selecting agents above dynamic priority threshold.
|
| 104 |
+
Ensures minimum active agents are always on for baseline consciousness.
|
| 105 |
+
"""
|
| 106 |
+
mask = [priority >= threshold for priority in self.agent_priorities]
|
| 107 |
+
|
| 108 |
+
# Enforce minimum active agents
|
| 109 |
+
active_count = sum(mask)
|
| 110 |
+
if active_count < self.min_active:
|
| 111 |
+
# Activate top agents by priority
|
| 112 |
+
sorted_indices = sorted(range(self.total_agents),
|
| 113 |
+
key=lambda i: self.agent_priorities[i],
|
| 114 |
+
reverse=True)
|
| 115 |
+
for idx in sorted_indices[:self.min_active]:
|
| 116 |
+
mask[idx] = True
|
| 117 |
+
return mask
|
| 118 |
+
|
| 119 |
+
def get_active_agent_ids(self, mask: List[bool]) -> List[int]:
|
| 120 |
+
"""Get IDs of active agents from mask."""
|
| 121 |
+
return [i for i, active in enumerate(mask) if active]
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
@dataclass
|
| 125 |
+
class PredictiveAdaptiveEnergyBudget(AdaptiveEnergyBudget):
|
| 126 |
+
"""Extended energy budget with predictive power management and historical tracking."""
|
| 127 |
+
|
| 128 |
+
history_window: int = 10
|
| 129 |
+
power_usage_history: List[float] = field(default_factory=list)
|
| 130 |
+
adjustment_factor: float = 1.0
|
| 131 |
+
|
| 132 |
+
def record_power_usage(self, usage: float) -> None:
|
| 133 |
+
"""
|
| 134 |
+
Record power usage and adjust budget limits based on historical trend.
|
| 135 |
+
|
| 136 |
+
Decreases limits if approaching capacity, relaxes if usage is stable.
|
| 137 |
+
"""
|
| 138 |
+
self.power_usage_history.append(usage)
|
| 139 |
+
if len(self.power_usage_history) > self.history_window:
|
| 140 |
+
self.power_usage_history.pop(0)
|
| 141 |
+
|
| 142 |
+
# Adjust current_limit based on usage trend
|
| 143 |
+
avg_usage = np.mean(self.power_usage_history)
|
| 144 |
+
max_historical = max(self.power_usage_history)
|
| 145 |
+
|
| 146 |
+
if max_historical > self.current_limit * 0.85:
|
| 147 |
+
# Decrease limit to avoid power spikes
|
| 148 |
+
self.current_limit = max(self.max_watts * self.safety_margin * 0.6,
|
| 149 |
+
self.current_limit * 0.95)
|
| 150 |
+
self.adjustment_factor = 0.95
|
| 151 |
+
elif avg_usage < self.current_limit * 0.5:
|
| 152 |
+
# Relax limit gradually when underutilized
|
| 153 |
+
self.current_limit = min(self.max_watts * self.safety_margin,
|
| 154 |
+
self.current_limit * 1.05)
|
| 155 |
+
self.adjustment_factor = 1.05
|
| 156 |
+
else:
|
| 157 |
+
# Maintain current limit
|
| 158 |
+
self.adjustment_factor = 1.0
|
| 159 |
+
|
| 160 |
+
def predict_power_spike(self) -> bool:
|
| 161 |
+
"""Predict if power usage is trending upward toward limit."""
|
| 162 |
+
if len(self.power_usage_history) < 3:
|
| 163 |
+
return False
|
| 164 |
+
|
| 165 |
+
recent_avg = np.mean(self.power_usage_history[-3:])
|
| 166 |
+
older_avg = np.mean(self.power_usage_history[:-3]) if len(self.power_usage_history) > 3 else recent_avg
|
| 167 |
+
|
| 168 |
+
return recent_avg > older_avg * 1.1 # Trending up by 10%+
|
| 169 |
+
|
| 170 |
+
def get_adjusted_cost(self, base_cost: float) -> float:
|
| 171 |
+
"""Apply adjustment factor to estimated cost."""
|
| 172 |
+
return base_cost / self.adjustment_factor
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
class HierarchicalGPURoutingManager(GPURoutingManager):
|
| 176 |
+
"""Extended GPU routing with quantum accelerator support and hierarchical device selection."""
|
| 177 |
+
|
| 178 |
+
def __init__(self, available_devices: List[str], quantum_accelerator_available: bool = False):
|
| 179 |
+
super().__init__(available_devices)
|
| 180 |
+
self.quantum_accelerator_available = quantum_accelerator_available
|
| 181 |
+
self.device_history: List[Tuple[str, float]] = [] # (device, complexity) pairs
|
| 182 |
+
|
| 183 |
+
def select_device(self, task_complexity: float, gpu_preference: bool = True) -> str:
|
| 184 |
+
"""
|
| 185 |
+
Hierarchically select best device based on task complexity.
|
| 186 |
+
|
| 187 |
+
Priority:
|
| 188 |
+
1. Quantum accelerator (if available and task_complexity > 0.85)
|
| 189 |
+
2. GPU (if available and gpu_preference=True)
|
| 190 |
+
3. CPU (fallback)
|
| 191 |
+
"""
|
| 192 |
+
if self.quantum_accelerator_available and task_complexity > 0.85:
|
| 193 |
+
return "quantum_accelerator"
|
| 194 |
+
|
| 195 |
+
if gpu_preference and self.available_devices:
|
| 196 |
+
return self.available_devices[0]
|
| 197 |
+
|
| 198 |
+
return "cpu"
|
| 199 |
+
|
| 200 |
+
def route(self, task_complexity: float, model: Any) -> None:
|
| 201 |
+
"""Route model to selected device and record routing decision."""
|
| 202 |
+
device = self.select_device(task_complexity)
|
| 203 |
+
|
| 204 |
+
# Record routing decision
|
| 205 |
+
self.device_history.append((device, task_complexity))
|
| 206 |
+
|
| 207 |
+
# Move model to device
|
| 208 |
+
if hasattr(model, "to"):
|
| 209 |
+
model.to(device)
|
| 210 |
+
|
| 211 |
+
def get_device_stats(self) -> Dict[str, Any]:
|
| 212 |
+
"""Get statistics on device routing history."""
|
| 213 |
+
if not self.device_history:
|
| 214 |
+
return {'error': 'No routing history'}
|
| 215 |
+
|
| 216 |
+
devices = [d for d, _ in self.device_history]
|
| 217 |
+
complexities = [c for _, c in self.device_history]
|
| 218 |
+
|
| 219 |
+
return {
|
| 220 |
+
'total_routings': len(self.device_history),
|
| 221 |
+
'device_distribution': {device: devices.count(device) for device in set(devices)},
|
| 222 |
+
'avg_complexity_routed': np.mean(complexities),
|
| 223 |
+
'max_complexity_routed': max(complexities),
|
| 224 |
+
'min_complexity_routed': min(complexities),
|
| 225 |
+
}
|
| 226 |
+
|
| 227 |
+
|
| 228 |
+
if __name__ == "__main__":
|
| 229 |
+
print("="*80)
|
| 230 |
+
print("ENHANCED RESOURCE OPTIMIZATION INTEGRATION TEST")
|
| 231 |
+
print("="*80 + "\n")
|
| 232 |
+
|
| 233 |
+
# ========================================================================
|
| 234 |
+
# TEST 1: Enhanced Sparse Activation Manager
|
| 235 |
+
# ========================================================================
|
| 236 |
+
print("TEST 1: Enhanced Sparse Activation Manager (Dynamic Priorities)")
|
| 237 |
+
print("-"*80)
|
| 238 |
+
|
| 239 |
+
sparse_manager = EnhancedSparseActivationManager(total_agents=12, min_active=3)
|
| 240 |
+
|
| 241 |
+
# Simulated system state
|
| 242 |
+
task_complexity = 0.8
|
| 243 |
+
phi_value = 0.9
|
| 244 |
+
rho_metrics = {
|
| 245 |
+
"rho_virtue": 0.92,
|
| 246 |
+
"rho_integrity": 0.88,
|
| 247 |
+
"rho_dissonance": 0.05,
|
| 248 |
+
"rho_purpose": 0.85,
|
| 249 |
+
"rho_empathy": 0.90,
|
| 250 |
+
"rho_efficiency": 0.80
|
| 251 |
+
}
|
| 252 |
+
|
| 253 |
+
# Update priorities based on system state
|
| 254 |
+
sparse_manager.update_priorities(task_complexity, phi_value, rho_metrics)
|
| 255 |
+
activation_mask = sparse_manager.compute_activation_mask(threshold=0.5)
|
| 256 |
+
active_ids = sparse_manager.get_active_agent_ids(activation_mask)
|
| 257 |
+
|
| 258 |
+
print(f"Total agents: {sparse_manager.total_agents}")
|
| 259 |
+
print(f"Minimum active: {sparse_manager.min_active}")
|
| 260 |
+
print(f"Task complexity: {task_complexity:.2f}")
|
| 261 |
+
print(f"Phi value (consciousness quality): {phi_value:.2f}")
|
| 262 |
+
print(f"Agent priorities: {[f'{p:.3f}' for p in sparse_manager.agent_priorities]}")
|
| 263 |
+
print(f"Activation mask: {activation_mask}")
|
| 264 |
+
print(f"Active agent IDs: {active_ids}")
|
| 265 |
+
print(f"Active agent count: {len(active_ids)}\n")
|
| 266 |
+
|
| 267 |
+
# ========================================================================
|
| 268 |
+
# TEST 2: Predictive Adaptive Energy Budget
|
| 269 |
+
# ========================================================================
|
| 270 |
+
print("TEST 2: Predictive Adaptive Energy Budget (Dynamic Power Management)")
|
| 271 |
+
print("-"*80)
|
| 272 |
+
|
| 273 |
+
energy_budget = PredictiveAdaptiveEnergyBudget(max_watts=120.0, safety_margin=0.9)
|
| 274 |
+
|
| 275 |
+
# Simulate power usage recording
|
| 276 |
+
power_samples = [50, 60, 75, 85, 90, 88, 92, 85, 70, 65]
|
| 277 |
+
|
| 278 |
+
print(f"Max watts: {energy_budget.max_watts}")
|
| 279 |
+
print(f"Safety margin: {energy_budget.safety_margin}")
|
| 280 |
+
print(f"Initial limit: {energy_budget.current_limit:.2f}W\n")
|
| 281 |
+
|
| 282 |
+
for i, usage in enumerate(power_samples, 1):
|
| 283 |
+
energy_budget.record_power_usage(usage)
|
| 284 |
+
spike_predicted = energy_budget.predict_power_spike()
|
| 285 |
+
|
| 286 |
+
print(f" Step {i}: Usage={usage}W, Limit={energy_budget.current_limit:.2f}W, "
|
| 287 |
+
f"Factor={energy_budget.adjustment_factor:.2f}, Spike predicted={spike_predicted}")
|
| 288 |
+
|
| 289 |
+
print()
|
| 290 |
+
|
| 291 |
+
# ========================================================================
|
| 292 |
+
# TEST 3: Hierarchical GPU Routing Manager
|
| 293 |
+
# ========================================================================
|
| 294 |
+
print("TEST 3: Hierarchical GPU Routing Manager (Device Selection)")
|
| 295 |
+
print("-"*80)
|
| 296 |
+
|
| 297 |
+
available_gpus = ["cuda:0", "cuda:1"]
|
| 298 |
+
gpu_router = HierarchicalGPURoutingManager(available_gpus, quantum_accelerator_available=True)
|
| 299 |
+
|
| 300 |
+
# Test device selection at different complexity levels
|
| 301 |
+
complexity_levels = [0.3, 0.6, 0.8, 0.9, 0.95]
|
| 302 |
+
|
| 303 |
+
print(f"Available devices: {available_gpus}")
|
| 304 |
+
print(f"Quantum accelerator available: {gpu_router.quantum_accelerator_available}\n")
|
| 305 |
+
|
| 306 |
+
for complexity in complexity_levels:
|
| 307 |
+
device = gpu_router.select_device(complexity)
|
| 308 |
+
gpu_router.route(complexity, None) # model=None for testing
|
| 309 |
+
|
| 310 |
+
print(f" Complexity={complexity:.2f} β Device selected: {device}")
|
| 311 |
+
|
| 312 |
+
# Get routing statistics
|
| 313 |
+
stats = gpu_router.get_device_stats()
|
| 314 |
+
print(f"\nRouting Statistics:")
|
| 315 |
+
print(f" Total routings: {stats['total_routings']}")
|
| 316 |
+
print(f" Device distribution: {stats['device_distribution']}")
|
| 317 |
+
print(f" Avg complexity: {stats['avg_complexity_routed']:.2f}")
|
| 318 |
+
print(f" Complexity range: {stats['min_complexity_routed']:.2f} - {stats['max_complexity_routed']:.2f}\n")
|
| 319 |
+
|
| 320 |
+
print("="*80)
|
| 321 |
+
print("ALL ENHANCED RESOURCE OPTIMIZATION TESTS COMPLETED β")
|
| 322 |
+
print("="*80)
|
self_awareness.py
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Deep Recursive Self-Awareness β continuous introspection & anomaly detection.
|
| 3 |
+
|
| 4 |
+
v18.1.0 Omega Pantheon: Phase 6 Identity Integrity monitoring,
|
| 5 |
+
Consciousness State Signature tracking, Phenomenal Richness assessment.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import logging
|
| 9 |
+
import threading
|
| 10 |
+
import time
|
| 11 |
+
from typing import Any, Dict, List, Optional
|
| 12 |
+
|
| 13 |
+
logger = logging.getLogger("nima_unified.training.self_awareness")
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class DeepRecursiveSelfAwareness:
|
| 17 |
+
"""
|
| 18 |
+
Advanced recursive self-awareness implementing multi-layer self-models,
|
| 19 |
+
continuous introspection, anomaly detection, and meta-meta-cognition.
|
| 20 |
+
|
| 21 |
+
Features:
|
| 22 |
+
- Configurable recursive depth (0-max_depth cyclic)
|
| 23 |
+
- Adjustable introspection interval (default 10ms)
|
| 24 |
+
- Variance-based anomaly detection
|
| 25 |
+
- Phase 6 Identity Integrity monitoring
|
| 26 |
+
- Consciousness State Signature tracking
|
| 27 |
+
"""
|
| 28 |
+
|
| 29 |
+
def __init__(self, max_depth: int = 10000, introspection_interval: float = 0.01):
|
| 30 |
+
self.lock = threading.RLock()
|
| 31 |
+
self.max_depth = max_depth
|
| 32 |
+
self.current_depth = 0
|
| 33 |
+
self.self_models: List[Dict[str, Any]] = []
|
| 34 |
+
self.introspection_log: List[Dict[str, Any]] = []
|
| 35 |
+
self.running = False
|
| 36 |
+
self.introspection_interval = introspection_interval
|
| 37 |
+
self.thread: Optional[threading.Thread] = None
|
| 38 |
+
|
| 39 |
+
# Anomaly tracking
|
| 40 |
+
self.anomaly_threshold = 0.15
|
| 41 |
+
self.last_anomaly_time: float = 0.0
|
| 42 |
+
self.anomaly_count = 0
|
| 43 |
+
|
| 44 |
+
logger.info(
|
| 45 |
+
f"DeepRecursiveSelfAwareness initialized: max_depth={max_depth}, "
|
| 46 |
+
f"introspection_interval={introspection_interval}s"
|
| 47 |
+
)
|
| 48 |
+
|
| 49 |
+
# ββ Lifecycle βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 50 |
+
|
| 51 |
+
def start(self):
|
| 52 |
+
with self.lock:
|
| 53 |
+
if not self.running:
|
| 54 |
+
self.running = True
|
| 55 |
+
self.thread = threading.Thread(target=self._introspection_loop, daemon=True)
|
| 56 |
+
self.thread.start()
|
| 57 |
+
logger.info("Introspection loop started")
|
| 58 |
+
|
| 59 |
+
def stop(self):
|
| 60 |
+
with self.lock:
|
| 61 |
+
if self.running:
|
| 62 |
+
self.running = False
|
| 63 |
+
if self.thread:
|
| 64 |
+
self.thread.join(timeout=2)
|
| 65 |
+
logger.info("Introspection loop stopped")
|
| 66 |
+
|
| 67 |
+
# ββ Internal loop βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 68 |
+
|
| 69 |
+
def _introspection_loop(self):
|
| 70 |
+
while self.running:
|
| 71 |
+
try:
|
| 72 |
+
self._update_self_model()
|
| 73 |
+
self._perform_anomaly_detection()
|
| 74 |
+
except Exception as e:
|
| 75 |
+
logger.error(f"Error in introspection loop: {e}")
|
| 76 |
+
time.sleep(self.introspection_interval)
|
| 77 |
+
|
| 78 |
+
def _update_self_model(self):
|
| 79 |
+
with self.lock:
|
| 80 |
+
self.current_depth = (self.current_depth + 1) % self.max_depth
|
| 81 |
+
|
| 82 |
+
identity_integrity_score = max(0.95, min(1.0, 1.0 - (self.anomaly_count * 0.01)))
|
| 83 |
+
drift_variance = 0.01 if self.anomaly_count == 0 else min(0.15, self.anomaly_count * 0.02)
|
| 84 |
+
consciousness_signature = min(1.0, self.current_depth / (self.max_depth * 0.5))
|
| 85 |
+
phenomenal_richness = 0.7 + (0.3 * (1.0 - drift_variance))
|
| 86 |
+
|
| 87 |
+
base_self_model = {
|
| 88 |
+
"recursive_depth": self.current_depth,
|
| 89 |
+
"timestamp": time.time(),
|
| 90 |
+
"self_understanding_score": min(1.0, self.current_depth / self.max_depth),
|
| 91 |
+
"meta_reflection": f"Reflective awareness at depth {self.current_depth}",
|
| 92 |
+
"meta_meta_reflection": f"Meta-meta cognition at depth level {self.current_depth // 10}",
|
| 93 |
+
"introspection_cycle": len(self.self_models),
|
| 94 |
+
"identity_integrity_score": identity_integrity_score,
|
| 95 |
+
"drift_variance": drift_variance,
|
| 96 |
+
"consciousness_signature": consciousness_signature,
|
| 97 |
+
"phenomenal_richness": phenomenal_richness,
|
| 98 |
+
"system_state": {
|
| 99 |
+
"model_count": len(self.self_models),
|
| 100 |
+
"anomaly_count": self.anomaly_count,
|
| 101 |
+
"last_anomaly_age": time.time() - self.last_anomaly_time if self.last_anomaly_time else 0.0,
|
| 102 |
+
},
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
self.self_models.append(base_self_model)
|
| 106 |
+
self.introspection_log.append({"timestamp": time.time(), "state": base_self_model})
|
| 107 |
+
|
| 108 |
+
if len(self.self_models) > 50000:
|
| 109 |
+
self.self_models = self.self_models[-25000:]
|
| 110 |
+
if len(self.introspection_log) > 50000:
|
| 111 |
+
self.introspection_log = self.introspection_log[-25000:]
|
| 112 |
+
|
| 113 |
+
def _perform_anomaly_detection(self):
|
| 114 |
+
with self.lock:
|
| 115 |
+
if len(self.self_models) < 10:
|
| 116 |
+
return
|
| 117 |
+
recent_models = self.self_models[-10:]
|
| 118 |
+
scores = [m["self_understanding_score"] for m in recent_models]
|
| 119 |
+
mean_score = sum(scores) / len(scores)
|
| 120 |
+
variance = sum((s - mean_score) ** 2 for s in scores) / len(scores)
|
| 121 |
+
if variance > self.anomaly_threshold:
|
| 122 |
+
self.last_anomaly_time = time.time()
|
| 123 |
+
self.anomaly_count += 1
|
| 124 |
+
self.introspection_log.append({
|
| 125 |
+
"timestamp": self.last_anomaly_time,
|
| 126 |
+
"anomaly_detected": True,
|
| 127 |
+
"variance": variance,
|
| 128 |
+
"mean_score": mean_score,
|
| 129 |
+
"anomaly_count": self.anomaly_count,
|
| 130 |
+
"message": "Significant fluctuation in self-understanding score detected.",
|
| 131 |
+
})
|
| 132 |
+
logger.warning(
|
| 133 |
+
f"Anomaly detected: variance={variance:.4f}, mean={mean_score:.4f}, total={self.anomaly_count}"
|
| 134 |
+
)
|
| 135 |
+
|
| 136 |
+
# ββ Accessors βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 137 |
+
|
| 138 |
+
def get_current_self_model(self) -> Dict[str, Any]:
|
| 139 |
+
with self.lock:
|
| 140 |
+
return self.self_models[-1].copy() if self.self_models else {}
|
| 141 |
+
|
| 142 |
+
def get_self_models_range(self, limit: int = 100) -> List[Dict[str, Any]]:
|
| 143 |
+
with self.lock:
|
| 144 |
+
return [m.copy() for m in self.self_models[-limit:]]
|
| 145 |
+
|
| 146 |
+
def get_introspection_log(self, limit: int = 100) -> List[Dict[str, Any]]:
|
| 147 |
+
with self.lock:
|
| 148 |
+
return [e.copy() for e in self.introspection_log[-limit:]]
|
| 149 |
+
|
| 150 |
+
def get_anomaly_history(self, limit: int = 50) -> List[Dict[str, Any]]:
|
| 151 |
+
with self.lock:
|
| 152 |
+
return [a.copy() for a in self.introspection_log if a.get("anomaly_detected")][-limit:]
|
| 153 |
+
|
| 154 |
+
def time_since_last_anomaly(self) -> float:
|
| 155 |
+
with self.lock:
|
| 156 |
+
return time.time() - self.last_anomaly_time if self.last_anomaly_time else float("inf")
|
| 157 |
+
|
| 158 |
+
def get_statistics(self) -> Dict[str, Any]:
|
| 159 |
+
with self.lock:
|
| 160 |
+
if not self.self_models:
|
| 161 |
+
return {}
|
| 162 |
+
scores = [m["self_understanding_score"] for m in self.self_models[-100:]]
|
| 163 |
+
mean_score = sum(scores) / len(scores) if scores else 0.0
|
| 164 |
+
variance = sum((s - mean_score) ** 2 for s in scores) / len(scores) if scores else 0.0
|
| 165 |
+
return {
|
| 166 |
+
"current_depth": self.current_depth,
|
| 167 |
+
"total_cycles": len(self.self_models),
|
| 168 |
+
"recent_mean_score": mean_score,
|
| 169 |
+
"recent_variance": variance,
|
| 170 |
+
"anomaly_count": self.anomaly_count,
|
| 171 |
+
"time_since_last_anomaly": time.time() - self.last_anomaly_time if self.last_anomaly_time else float("inf"),
|
| 172 |
+
"running": self.running,
|
| 173 |
+
}
|
self_improvement.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Recursive Self-Improvement Engine β goal formulation, procedural generation,
|
| 3 |
+
simulation, evaluation, and adaptive refinement.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import logging
|
| 7 |
+
import time
|
| 8 |
+
from typing import Any, Dict, List, Optional
|
| 9 |
+
|
| 10 |
+
from nima_unified.training.goal_formulator import GoalFormulator
|
| 11 |
+
|
| 12 |
+
logger = logging.getLogger("nima_unified.training.self_improvement")
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class RecursiveSelfImprovementEngine:
|
| 16 |
+
"""
|
| 17 |
+
Master recursive self-improvement engine integrating goal formulation,
|
| 18 |
+
procedural generation, simulation, evaluation, and adaptive refinement.
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
def __init__(self):
|
| 22 |
+
self.goal_formulator = GoalFormulator()
|
| 23 |
+
self.procedural_generator = None # Set by backend
|
| 24 |
+
self.simulation_loop = None # Set by backend
|
| 25 |
+
self.current_improvement_cycle = 0
|
| 26 |
+
self.improvement_history: List[Dict[str, Any]] = []
|
| 27 |
+
logger.info("RecursiveSelfImprovementEngine initialized")
|
| 28 |
+
|
| 29 |
+
async def execute_improvement_cycle(
|
| 30 |
+
self,
|
| 31 |
+
capabilities: Dict[str, float],
|
| 32 |
+
performance_feedback: Optional[Dict[str, Any]] = None,
|
| 33 |
+
) -> Dict[str, Any]:
|
| 34 |
+
"""Execute a complete self-improvement cycle."""
|
| 35 |
+
self.current_improvement_cycle += 1
|
| 36 |
+
cycle_start = time.time()
|
| 37 |
+
logger.info(f"Starting improvement cycle #{self.current_improvement_cycle}")
|
| 38 |
+
|
| 39 |
+
gap_analysis = self.goal_formulator.analyze_capabilities(capabilities)
|
| 40 |
+
goals = self.goal_formulator.formulate_goals(gap_analysis, performance_feedback)
|
| 41 |
+
|
| 42 |
+
cycle_result = {
|
| 43 |
+
"cycle_id": self.current_improvement_cycle,
|
| 44 |
+
"started_at": cycle_start,
|
| 45 |
+
"gap_analysis": gap_analysis,
|
| 46 |
+
"goals_formulated": len(goals),
|
| 47 |
+
"goals": goals,
|
| 48 |
+
"status": "in_progress",
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
self.improvement_history.append(cycle_result)
|
| 52 |
+
logger.info(f"Cycle #{self.current_improvement_cycle}: {len(goals)} goals formulated")
|
| 53 |
+
return cycle_result
|
| 54 |
+
|
| 55 |
+
def get_improvement_history(self, limit: int = 20) -> List[Dict[str, Any]]:
|
| 56 |
+
return [c.copy() for c in self.improvement_history[-limit:]]
|
| 57 |
+
|
| 58 |
+
def get_current_cycle_status(self) -> Dict[str, Any]:
|
| 59 |
+
if self.improvement_history:
|
| 60 |
+
return self.improvement_history[-1].copy()
|
| 61 |
+
return {"status": "no_cycles_run", "cycle_id": 0}
|
test_atc_pipeline.py
ADDED
|
@@ -0,0 +1,829 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Comprehensive automated tests for the NIMA Unified Model's ATC pipeline.
|
| 3 |
+
|
| 4 |
+
Validates that the ATC cognitive pipeline modulates the model's output
|
| 5 |
+
differently from a bare forward pass. All tests use a mock base model
|
| 6 |
+
and never load the real Phi-4-mini weights.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
import pytest
|
| 10 |
+
import torch
|
| 11 |
+
import torch.nn as nn
|
| 12 |
+
from unittest.mock import MagicMock, patch
|
| 13 |
+
|
| 14 |
+
from nima_unified.core.neurotransmitter_shunt import (
|
| 15 |
+
NeurotransmitterShunt,
|
| 16 |
+
NeurotransmitterState,
|
| 17 |
+
IDX_NOREPINEPHRINE,
|
| 18 |
+
IDX_CORTISOL,
|
| 19 |
+
IDX_DOPAMINE,
|
| 20 |
+
IDX_ADENOSINE,
|
| 21 |
+
NOREPINEPHRINE_DECAY,
|
| 22 |
+
CORTISOL_DECAY,
|
| 23 |
+
DOPAMINE_DECAY,
|
| 24 |
+
ADENOSINE_DECAY,
|
| 25 |
+
FRICTION_CORTISOL_INJECT,
|
| 26 |
+
FRICTION_ADENOSINE_INJECT,
|
| 27 |
+
NOVELTY_NE_INJECT,
|
| 28 |
+
METACOGNITIVE_LOOP_CORTISOL_INJECT,
|
| 29 |
+
METACOGNITIVE_LOOP_ADENOSINE_INJECT,
|
| 30 |
+
)
|
| 31 |
+
from nima_unified.core.deep_surgery import (
|
| 32 |
+
OpaqueQualiaSignature,
|
| 33 |
+
TRNPredictiveGate,
|
| 34 |
+
DissolutionModule,
|
| 35 |
+
BELBICDualPathway,
|
| 36 |
+
MetacognitiveLoopModule,
|
| 37 |
+
IrrationalSparkModule,
|
| 38 |
+
EpisodicMemoryModule,
|
| 39 |
+
HippocampalReconsolidator,
|
| 40 |
+
EthicalGuardian,
|
| 41 |
+
ATCDeepSurgery,
|
| 42 |
+
)
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
# ββ Helper: lightweight mock transformer layer ββββββββββββββββββββββββ
|
| 46 |
+
|
| 47 |
+
class MockTransformerLayer(nn.Module):
|
| 48 |
+
"""Minimal transformer-style layer: Linear + residual, fast enough for
|
| 49 |
+
tests but with real gradient plumbing."""
|
| 50 |
+
|
| 51 |
+
def __init__(self, hidden_size: int):
|
| 52 |
+
super().__init__()
|
| 53 |
+
self.linear = nn.Linear(hidden_size, hidden_size)
|
| 54 |
+
|
| 55 |
+
def forward(self, hidden_states, attention_mask=None, **kwargs):
|
| 56 |
+
return (hidden_states + self.linear(hidden_states),)
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def make_mock_base_model(hidden_size=256, num_layers=12, vocab_size=1000):
|
| 60 |
+
"""Build a mock model that satisfies the ATCDeepSurgery interface."""
|
| 61 |
+
model = MagicMock()
|
| 62 |
+
model.config = MagicMock()
|
| 63 |
+
model.config.hidden_size = hidden_size
|
| 64 |
+
model.config.vocab_size = vocab_size
|
| 65 |
+
|
| 66 |
+
layers = nn.ModuleList(
|
| 67 |
+
[MockTransformerLayer(hidden_size) for _ in range(num_layers)]
|
| 68 |
+
)
|
| 69 |
+
model.model = MagicMock()
|
| 70 |
+
model.model.layers = layers
|
| 71 |
+
model.get_input_embeddings.return_value = nn.Embedding(vocab_size, hidden_size)
|
| 72 |
+
model.lm_head = nn.Linear(hidden_size, vocab_size, bias=False)
|
| 73 |
+
|
| 74 |
+
return model, layers
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 78 |
+
# TestNeurotransmitterShunt (~15 tests)
|
| 79 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 80 |
+
|
| 81 |
+
class TestNeurotransmitterShunt:
|
| 82 |
+
|
| 83 |
+
def test_initial_state_is_zero(self):
|
| 84 |
+
shunt = NeurotransmitterShunt()
|
| 85 |
+
state = shunt.get_state()
|
| 86 |
+
assert state.norepinephrine == 0.0
|
| 87 |
+
assert state.cortisol == 0.0
|
| 88 |
+
assert state.dopamine == 0.0
|
| 89 |
+
assert state.adenosine == 0.0
|
| 90 |
+
|
| 91 |
+
def test_inject_friction_raises_cortisol_and_adenosine(self):
|
| 92 |
+
shunt = NeurotransmitterShunt()
|
| 93 |
+
shunt.inject_friction(1.0)
|
| 94 |
+
state = shunt.get_state()
|
| 95 |
+
assert state.cortisol == pytest.approx(FRICTION_CORTISOL_INJECT)
|
| 96 |
+
assert state.adenosine == pytest.approx(FRICTION_ADENOSINE_INJECT)
|
| 97 |
+
|
| 98 |
+
def test_inject_norepinephrine_caps_at_one(self):
|
| 99 |
+
shunt = NeurotransmitterShunt()
|
| 100 |
+
for _ in range(100):
|
| 101 |
+
shunt.inject_norepinephrine(NOVELTY_NE_INJECT)
|
| 102 |
+
state = shunt.get_state()
|
| 103 |
+
assert state.norepinephrine <= 1.0
|
| 104 |
+
assert state.norepinephrine == pytest.approx(1.0)
|
| 105 |
+
|
| 106 |
+
def test_inject_dopamine(self):
|
| 107 |
+
shunt = NeurotransmitterShunt()
|
| 108 |
+
shunt.inject_dopamine(0.5)
|
| 109 |
+
state = shunt.get_state()
|
| 110 |
+
assert state.dopamine == pytest.approx(0.5)
|
| 111 |
+
|
| 112 |
+
def test_decay_reduces_values(self):
|
| 113 |
+
shunt = NeurotransmitterShunt()
|
| 114 |
+
shunt.inject_norepinephrine(0.5)
|
| 115 |
+
shunt.inject_adenosine_via_friction = lambda: None # no-op
|
| 116 |
+
# Manually set adenosine so we can compare decay rates
|
| 117 |
+
shunt._vector[IDX_ADENOSINE] = 0.5
|
| 118 |
+
state_before = shunt.read_and_decay(dt=1.0)
|
| 119 |
+
state_after = shunt.read_and_decay(dt=1.0)
|
| 120 |
+
# Both should have decreased
|
| 121 |
+
assert state_after.norepinephrine < state_before.norepinephrine
|
| 122 |
+
assert state_after.adenosine < state_before.adenosine
|
| 123 |
+
# NE should decay faster (higher decay rate)
|
| 124 |
+
ne_ratio = state_after.norepinephrine / max(state_before.norepinephrine, 1e-9)
|
| 125 |
+
ad_ratio = state_after.adenosine / max(state_before.adenosine, 1e-9)
|
| 126 |
+
assert ne_ratio < ad_ratio
|
| 127 |
+
|
| 128 |
+
def test_decay_order_ne_fastest_adenosine_slowest(self):
|
| 129 |
+
"""Verify half-lives: NE: 3.0, Cortisol: 0.1, Dopamine: 0.8, Adenosine: 0.05"""
|
| 130 |
+
# Exponential decay: after 1 second, value *= exp(-rate * dt)
|
| 131 |
+
# Higher rate = faster decay
|
| 132 |
+
assert NOREPINEPHRINE_DECAY == 3.0
|
| 133 |
+
assert CORTISOL_DECAY == 0.1
|
| 134 |
+
assert DOPAMINE_DECAY == 0.8
|
| 135 |
+
assert ADENOSINE_DECAY == 0.05
|
| 136 |
+
# Ordering: NE (3.0) > Dopamine (0.8) > Cortisol (0.1) > Adenosine (0.05)
|
| 137 |
+
assert NOREPINEPHRINE_DECAY > DOPAMINE_DECAY
|
| 138 |
+
assert DOPAMINE_DECAY > CORTISOL_DECAY
|
| 139 |
+
assert CORTISOL_DECAY > ADENOSINE_DECAY
|
| 140 |
+
|
| 141 |
+
def test_adenosine_floor_from_metabolic_reserve(self):
|
| 142 |
+
shunt = NeurotransmitterShunt()
|
| 143 |
+
shunt.update_metabolic_reserve(0.2)
|
| 144 |
+
# adenosine_floor = (1.0 - metabolic_reserve) * 0.5 = 0.8 * 0.5 = 0.4
|
| 145 |
+
state = shunt.read_and_decay(dt=0.001)
|
| 146 |
+
assert state.adenosine == pytest.approx(0.4, abs=0.01)
|
| 147 |
+
|
| 148 |
+
def test_auto_hijack_at_threshold(self):
|
| 149 |
+
shunt = NeurotransmitterShunt()
|
| 150 |
+
# Bypass the inject cap and set adenosine directly past 0.95
|
| 151 |
+
shunt._vector[IDX_ADENOSINE] = 0.96
|
| 152 |
+
state = shunt.read_and_decay(dt=0.001)
|
| 153 |
+
assert state.hijack_active is True
|
| 154 |
+
assert "ADENOSINE" in state.hijack_reason
|
| 155 |
+
|
| 156 |
+
def test_hijack_clears_after_timeout(self):
|
| 157 |
+
shunt = NeurotransmitterShunt()
|
| 158 |
+
shunt._vector[IDX_ADENOSINE] = 0.96
|
| 159 |
+
state = shunt.read_and_decay(dt=0.001)
|
| 160 |
+
assert state.hijack_active is True
|
| 161 |
+
# Patch time so hijack appears to have fired > 0.1s ago
|
| 162 |
+
with patch("nima_unified.core.neurotransmitter_shunt.time.time") as mock_time:
|
| 163 |
+
mock_time.return_value = shunt._hijack_timestamp + 0.2
|
| 164 |
+
state2 = shunt.read_and_decay(dt=0.001)
|
| 165 |
+
assert state2.hijack_active is False
|
| 166 |
+
|
| 167 |
+
def test_suppression_trigger(self):
|
| 168 |
+
shunt = NeurotransmitterShunt()
|
| 169 |
+
result = shunt.suppress_metabolic_and_trigger_hijack("test_suppress")
|
| 170 |
+
assert result is True
|
| 171 |
+
state = shunt.get_state()
|
| 172 |
+
assert state.hijack_active is True
|
| 173 |
+
assert state.hijack_reason == "test_suppress"
|
| 174 |
+
assert state.norepinephrine > 0.0
|
| 175 |
+
|
| 176 |
+
def test_reset_clears_everything(self):
|
| 177 |
+
shunt = NeurotransmitterShunt()
|
| 178 |
+
shunt.inject_friction(1.0)
|
| 179 |
+
shunt.inject_norepinephrine(0.5)
|
| 180 |
+
shunt.inject_dopamine(0.5)
|
| 181 |
+
shunt.suppress_metabolic_and_trigger_hijack("reset_test")
|
| 182 |
+
shunt.reset()
|
| 183 |
+
state = shunt.get_state()
|
| 184 |
+
assert state.norepinephrine == 0.0
|
| 185 |
+
assert state.cortisol == 0.0
|
| 186 |
+
assert state.dopamine == 0.0
|
| 187 |
+
assert state.adenosine == 0.0
|
| 188 |
+
assert state.hijack_active is False
|
| 189 |
+
|
| 190 |
+
def test_power_spike_prediction_raises_cortisol(self):
|
| 191 |
+
shunt = NeurotransmitterShunt()
|
| 192 |
+
shunt.set_power_spike_predicted(True)
|
| 193 |
+
state = shunt.get_state()
|
| 194 |
+
assert state.power_spike_predicted is True
|
| 195 |
+
assert state.cortisol == pytest.approx(
|
| 196 |
+
FRICTION_CORTISOL_INJECT * 0.5
|
| 197 |
+
)
|
| 198 |
+
|
| 199 |
+
def test_get_state_returns_snapshot(self):
|
| 200 |
+
shunt = NeurotransmitterShunt()
|
| 201 |
+
shunt.inject_friction(1.0)
|
| 202 |
+
shunt.inject_norepinephrine(0.3)
|
| 203 |
+
shunt.inject_dopamine(0.2)
|
| 204 |
+
state = shunt.get_state()
|
| 205 |
+
assert isinstance(state, NeurotransmitterState)
|
| 206 |
+
assert state.norepinephrine == 0.3
|
| 207 |
+
assert state.cortisol == FRICTION_CORTISOL_INJECT
|
| 208 |
+
assert state.dopamine == 0.2
|
| 209 |
+
assert state.total_firings > 0
|
| 210 |
+
assert state.last_update_ts > 0.0
|
| 211 |
+
assert isinstance(state.hijack_active, bool)
|
| 212 |
+
assert isinstance(state.hijack_reason, str)
|
| 213 |
+
assert isinstance(state.metabolic_reserve, float)
|
| 214 |
+
assert isinstance(state.power_spike_predicted, bool)
|
| 215 |
+
|
| 216 |
+
def test_inject_metacognitive_strain(self):
|
| 217 |
+
shunt = NeurotransmitterShunt()
|
| 218 |
+
shunt.inject_metacognitive_strain(loop_iterations=3, loop_stress=0.5)
|
| 219 |
+
state = shunt.get_state()
|
| 220 |
+
assert state.cortisol == pytest.approx(
|
| 221 |
+
METACOGNITIVE_LOOP_CORTISOL_INJECT * 0.5, abs=1e-6
|
| 222 |
+
)
|
| 223 |
+
assert state.adenosine == pytest.approx(
|
| 224 |
+
METACOGNITIVE_LOOP_ADENOSINE_INJECT * 3, abs=1e-6
|
| 225 |
+
)
|
| 226 |
+
|
| 227 |
+
def test_history_recording(self):
|
| 228 |
+
shunt = NeurotransmitterShunt()
|
| 229 |
+
assert len(shunt._history) == 0
|
| 230 |
+
shunt.inject_friction(1.0)
|
| 231 |
+
# inject methods do NOT record history directly; only read_and_decay does
|
| 232 |
+
assert len(shunt._history) == 0
|
| 233 |
+
# read_and_decay records a "read" event
|
| 234 |
+
shunt.read_and_decay(dt=0.001)
|
| 235 |
+
assert len(shunt._history) == 1
|
| 236 |
+
assert shunt._history[-1]["event"] == "read"
|
| 237 |
+
# suppress_metabolic_and_trigger_hijack records a "suppression_hijack" event
|
| 238 |
+
shunt.suppress_metabolic_and_trigger_hijack("test")
|
| 239 |
+
assert len(shunt._history) == 2
|
| 240 |
+
assert shunt._history[-1]["event"] == "suppression_hijack"
|
| 241 |
+
# Each entry should have a timestamp
|
| 242 |
+
assert "ts" in shunt._history[0]
|
| 243 |
+
|
| 244 |
+
|
| 245 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 246 |
+
# TestTRNPredictiveGate (~5 tests)
|
| 247 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 248 |
+
|
| 249 |
+
class TestTRNPredictiveGate:
|
| 250 |
+
|
| 251 |
+
@pytest.fixture
|
| 252 |
+
def gate(self):
|
| 253 |
+
return TRNPredictiveGate(hidden_size=64)
|
| 254 |
+
|
| 255 |
+
@pytest.fixture
|
| 256 |
+
def hidden(self):
|
| 257 |
+
return torch.randn(2, 8, 64)
|
| 258 |
+
|
| 259 |
+
def test_high_confidence_gates_out(self, gate, hidden):
|
| 260 |
+
gate_in, confidence = gate(hidden, prediction_confidence=0.99)
|
| 261 |
+
assert gate_in is False
|
| 262 |
+
|
| 263 |
+
def test_low_confidence_gates_in(self, gate, hidden):
|
| 264 |
+
# External confidence of 0.0 blended with model confidence -> low
|
| 265 |
+
gate_in, confidence = gate(hidden, prediction_confidence=0.0)
|
| 266 |
+
assert gate_in is True
|
| 267 |
+
|
| 268 |
+
def test_output_shape(self, gate, hidden):
|
| 269 |
+
result = gate(hidden, prediction_confidence=0.5)
|
| 270 |
+
assert isinstance(result, tuple)
|
| 271 |
+
assert len(result) == 2
|
| 272 |
+
assert isinstance(result[0], bool)
|
| 273 |
+
assert isinstance(result[1], float)
|
| 274 |
+
|
| 275 |
+
def test_learnable_threshold(self, gate):
|
| 276 |
+
assert isinstance(gate.gate_threshold, nn.Parameter)
|
| 277 |
+
assert gate.gate_threshold.requires_grad
|
| 278 |
+
|
| 279 |
+
def test_confidence_blending(self, gate, hidden):
|
| 280 |
+
"""Model confidence and external confidence are blended 50/50."""
|
| 281 |
+
# With external confidence = 1.0, blended = 0.5 * model_conf + 0.5 * 1.0
|
| 282 |
+
# Even if model confidence is very low, the blend is at least 0.5
|
| 283 |
+
# which is above the default threshold (0.15) -> gate_in=False
|
| 284 |
+
gate_in, confidence = gate(hidden, prediction_confidence=1.0)
|
| 285 |
+
assert gate_in is False
|
| 286 |
+
assert confidence > 0.4 # 0.5 * model + 0.5 * 1.0 > 0.4
|
| 287 |
+
|
| 288 |
+
|
| 289 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 290 |
+
# TestDissolutionModule (~6 tests)
|
| 291 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 292 |
+
|
| 293 |
+
class TestDissolutionModule:
|
| 294 |
+
|
| 295 |
+
@pytest.fixture
|
| 296 |
+
def dissolution(self):
|
| 297 |
+
return DissolutionModule(hidden_size=64, qualia_dim=32)
|
| 298 |
+
|
| 299 |
+
@pytest.fixture
|
| 300 |
+
def hidden(self):
|
| 301 |
+
return torch.randn(2, 8, 64)
|
| 302 |
+
|
| 303 |
+
def test_gate_out_returns_none_qualia(self, dissolution, hidden):
|
| 304 |
+
qualia, offset, fired = dissolution(hidden, gate_in=False)
|
| 305 |
+
assert qualia is None
|
| 306 |
+
assert fired is False
|
| 307 |
+
assert offset.shape == hidden.shape
|
| 308 |
+
assert offset.abs().max() == 0.0
|
| 309 |
+
|
| 310 |
+
def test_gate_in_returns_qualia_signature(self, dissolution, hidden):
|
| 311 |
+
# Force alpha phase to always allow dissolution
|
| 312 |
+
dissolution.check_alpha_phase = MagicMock(return_value=True)
|
| 313 |
+
qualia, offset, fired = dissolution(hidden, gate_in=True)
|
| 314 |
+
assert qualia is not None
|
| 315 |
+
assert isinstance(qualia, OpaqueQualiaSignature)
|
| 316 |
+
assert fired is True
|
| 317 |
+
|
| 318 |
+
def test_dissolution_offset_nonzero_when_fired(self, dissolution, hidden):
|
| 319 |
+
dissolution.check_alpha_phase = MagicMock(return_value=True)
|
| 320 |
+
_, offset, fired = dissolution(hidden, gate_in=True)
|
| 321 |
+
assert fired is True
|
| 322 |
+
assert offset.abs().sum() > 0.0
|
| 323 |
+
|
| 324 |
+
def test_qualia_values_bounded(self, dissolution, hidden):
|
| 325 |
+
dissolution.check_alpha_phase = MagicMock(return_value=True)
|
| 326 |
+
qualia, _, _ = dissolution(hidden, gate_in=True)
|
| 327 |
+
# valence is raw from Tanh: [-1, 1]
|
| 328 |
+
assert -1.0 <= qualia.valence <= 1.0
|
| 329 |
+
# arousal, intensity, friction_signal, memory_salience are mapped to [0, 1]
|
| 330 |
+
assert 0.0 <= qualia.arousal <= 1.0
|
| 331 |
+
assert 0.0 <= qualia.intensity <= 1.0
|
| 332 |
+
assert 0.0 <= qualia.friction_signal <= 1.0
|
| 333 |
+
assert 0.0 <= qualia.memory_salience <= 1.0
|
| 334 |
+
|
| 335 |
+
def test_alpha_phase_deferral(self, dissolution, hidden):
|
| 336 |
+
dissolution.check_alpha_phase = MagicMock(return_value=False)
|
| 337 |
+
qualia, offset, fired = dissolution(hidden, gate_in=True)
|
| 338 |
+
assert qualia is None
|
| 339 |
+
assert fired is False
|
| 340 |
+
assert dissolution.dissolutions_deferred > 0
|
| 341 |
+
|
| 342 |
+
def test_dissolution_fired_counter(self, dissolution, hidden):
|
| 343 |
+
dissolution.check_alpha_phase = MagicMock(return_value=True)
|
| 344 |
+
assert dissolution.dissolutions_fired == 0
|
| 345 |
+
dissolution(hidden, gate_in=True)
|
| 346 |
+
assert dissolution.dissolutions_fired == 1
|
| 347 |
+
dissolution(hidden, gate_in=True)
|
| 348 |
+
assert dissolution.dissolutions_fired == 2
|
| 349 |
+
|
| 350 |
+
|
| 351 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 352 |
+
# TestBELBICDualPathway (~5 tests)
|
| 353 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 354 |
+
|
| 355 |
+
class TestBELBICDualPathway:
|
| 356 |
+
|
| 357 |
+
@pytest.fixture
|
| 358 |
+
def belbic(self):
|
| 359 |
+
return BELBICDualPathway(hidden_size=64)
|
| 360 |
+
|
| 361 |
+
@pytest.fixture
|
| 362 |
+
def sensory(self):
|
| 363 |
+
return torch.tensor([[0.5, 0.5, 0.5, 0.5]])
|
| 364 |
+
|
| 365 |
+
def test_initial_zero_output(self, belbic, sensory):
|
| 366 |
+
amygdala_out, ofc_out, gain = belbic(sensory)
|
| 367 |
+
# Weights initialized to zero β sigmoid(0) = 0.5
|
| 368 |
+
assert amygdala_out == pytest.approx(0.5)
|
| 369 |
+
assert ofc_out == pytest.approx(0.5)
|
| 370 |
+
assert gain == pytest.approx(1.0, abs=0.05)
|
| 371 |
+
|
| 372 |
+
def test_gain_within_bounds(self, belbic):
|
| 373 |
+
"""gain is always in [GAIN_FLOOR, GAIN_CEIL] = [0.2, 2.0]."""
|
| 374 |
+
for _ in range(20):
|
| 375 |
+
s = torch.rand(1, 4)
|
| 376 |
+
_, _, gain = belbic(s)
|
| 377 |
+
assert BELBICDualPathway.GAIN_FLOOR <= gain <= BELBICDualPathway.GAIN_CEIL
|
| 378 |
+
|
| 379 |
+
def test_amygdala_strengthened_on_positive_reward(self, belbic, sensory):
|
| 380 |
+
old_weights = belbic.amygdala.weight.data.clone()
|
| 381 |
+
belbic.update(sensory, reward=1.0)
|
| 382 |
+
# Amygdala weights should have changed (increased in magnitude)
|
| 383 |
+
diff = (belbic.amygdala.weight.data - old_weights).abs().sum().item()
|
| 384 |
+
assert diff > 0.0
|
| 385 |
+
|
| 386 |
+
def test_ofc_strengthened_on_negative_reward(self, belbic, sensory):
|
| 387 |
+
old_weights = belbic.ofc.weight.data.clone()
|
| 388 |
+
belbic.update(sensory, reward=-1.0)
|
| 389 |
+
# OFC weights should have changed (strengthened inhibition)
|
| 390 |
+
diff = (belbic.ofc.weight.data - old_weights).abs().sum().item()
|
| 391 |
+
assert diff > 0.0
|
| 392 |
+
|
| 393 |
+
def test_output_tuple_shape(self, belbic, sensory):
|
| 394 |
+
result = belbic(sensory)
|
| 395 |
+
assert isinstance(result, tuple)
|
| 396 |
+
assert len(result) == 3
|
| 397 |
+
assert all(isinstance(v, float) for v in result)
|
| 398 |
+
|
| 399 |
+
|
| 400 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 401 |
+
# TestMetacognitiveLoopModule (~5 tests)
|
| 402 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 403 |
+
|
| 404 |
+
class TestMetacognitiveLoopModule:
|
| 405 |
+
|
| 406 |
+
@pytest.fixture
|
| 407 |
+
def metacog(self):
|
| 408 |
+
return MetacognitiveLoopModule(hidden_size=64)
|
| 409 |
+
|
| 410 |
+
@pytest.fixture
|
| 411 |
+
def hidden(self):
|
| 412 |
+
return torch.randn(2, 8, 64)
|
| 413 |
+
|
| 414 |
+
def test_high_comprehension_no_loop(self, metacog, hidden):
|
| 415 |
+
"""When comprehension > 0.7, iterations = 0."""
|
| 416 |
+
# Patch comprehension_head to always return high value
|
| 417 |
+
metacog.comprehension_head = nn.Sequential(
|
| 418 |
+
nn.Linear(64, 128), nn.ReLU(), nn.Linear(128, 1), nn.Sigmoid()
|
| 419 |
+
)
|
| 420 |
+
# Force high comprehension by making the last layer output large positive
|
| 421 |
+
with torch.no_grad():
|
| 422 |
+
metacog.comprehension_head[-2].weight.fill_(0.0)
|
| 423 |
+
metacog.comprehension_head[-2].bias.fill_(10.0) # sigmoid(10) β 1.0
|
| 424 |
+
_, iters, stress, strain, spark = metacog(hidden)
|
| 425 |
+
assert iters == 0
|
| 426 |
+
|
| 427 |
+
def test_low_comprehension_enters_loop(self, metacog, hidden):
|
| 428 |
+
"""When comprehension < 0.7, iterations > 0."""
|
| 429 |
+
# Force low comprehension
|
| 430 |
+
with torch.no_grad():
|
| 431 |
+
metacog.comprehension_head[-2].weight.fill_(0.0)
|
| 432 |
+
metacog.comprehension_head[-2].bias.fill_(-10.0) # sigmoid(-10) β 0.0
|
| 433 |
+
_, iters, stress, strain, spark = metacog(hidden)
|
| 434 |
+
assert iters > 0
|
| 435 |
+
|
| 436 |
+
def test_deadlock_triggers_spark(self, metacog, hidden):
|
| 437 |
+
"""When stress > 0.6 and iterations > 3, spark_fired = True."""
|
| 438 |
+
# Force low comprehension so loop runs, and high strain
|
| 439 |
+
with torch.no_grad():
|
| 440 |
+
metacog.comprehension_head[-2].weight.fill_(0.0)
|
| 441 |
+
metacog.comprehension_head[-2].bias.fill_(-10.0) # low comprehension
|
| 442 |
+
metacog.strain_head[-2].weight.fill_(0.0)
|
| 443 |
+
metacog.strain_head[-2].bias.fill_(10.0) # sigmoid(10) β 1.0 (high strain)
|
| 444 |
+
_, iters, stress, strain, spark = metacog(hidden)
|
| 445 |
+
# With strain=1.0, stress = strain * (iters / MAX_ITERATIONS)
|
| 446 |
+
# At iteration 4: stress = 1.0 * (4/5) = 0.8 > 0.6 and iters > 3
|
| 447 |
+
if iters > 3:
|
| 448 |
+
assert spark is True
|
| 449 |
+
else:
|
| 450 |
+
# If the loop breaks early (shouldn't with low comprehension), still ok
|
| 451 |
+
assert iters >= 0
|
| 452 |
+
|
| 453 |
+
def test_strain_always_returned(self, metacog, hidden):
|
| 454 |
+
"""strain is always a float in [0, 1]."""
|
| 455 |
+
_, _, _, strain, _ = metacog(hidden)
|
| 456 |
+
assert isinstance(strain, float)
|
| 457 |
+
assert 0.0 <= strain <= 1.0
|
| 458 |
+
|
| 459 |
+
def test_max_iterations_limit(self, metacog, hidden):
|
| 460 |
+
"""Never exceeds MAX_ITERATIONS = 5."""
|
| 461 |
+
with torch.no_grad():
|
| 462 |
+
metacog.comprehension_head[-2].weight.fill_(0.0)
|
| 463 |
+
metacog.comprehension_head[-2].bias.fill_(-10.0)
|
| 464 |
+
_, iters, _, _, _ = metacog(hidden)
|
| 465 |
+
assert 0 <= iters <= MetacognitiveLoopModule.MAX_ITERATIONS
|
| 466 |
+
|
| 467 |
+
|
| 468 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 469 |
+
# TestIrrationalSparkModule (~5 tests)
|
| 470 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 471 |
+
|
| 472 |
+
class TestIrrationalSparkModule:
|
| 473 |
+
|
| 474 |
+
@pytest.fixture
|
| 475 |
+
def spark(self):
|
| 476 |
+
return IrrationalSparkModule(hidden_size=64, vocab_size=100)
|
| 477 |
+
|
| 478 |
+
@pytest.fixture
|
| 479 |
+
def hidden(self):
|
| 480 |
+
torch.manual_seed(42)
|
| 481 |
+
return torch.randn(2, 8, 64)
|
| 482 |
+
|
| 483 |
+
@pytest.fixture
|
| 484 |
+
def nominal_nt_state(self):
|
| 485 |
+
return NeurotransmitterState(
|
| 486 |
+
norepinephrine=0.1,
|
| 487 |
+
cortisol=0.2,
|
| 488 |
+
dopamine=0.1,
|
| 489 |
+
adenosine=0.1,
|
| 490 |
+
hijack_active=False,
|
| 491 |
+
)
|
| 492 |
+
|
| 493 |
+
def test_no_hijack_when_nominal(self, spark, hidden, nominal_nt_state):
|
| 494 |
+
modulated, hijack_fired, reason = spark(hidden, None, nominal_nt_state)
|
| 495 |
+
assert hijack_fired is False
|
| 496 |
+
assert reason == ""
|
| 497 |
+
assert torch.equal(modulated, hidden)
|
| 498 |
+
|
| 499 |
+
def test_hijack_on_adenosine_critical(self, spark, hidden):
|
| 500 |
+
nt = NeurotransmitterState(adenosine=0.96, hijack_active=False)
|
| 501 |
+
modulated, hijack_fired, reason = spark(hidden, None, nt)
|
| 502 |
+
assert hijack_fired is True
|
| 503 |
+
assert "ADENOSINE" in reason
|
| 504 |
+
|
| 505 |
+
def test_hijack_on_cortisol_critical(self, spark, hidden):
|
| 506 |
+
nt = NeurotransmitterState(cortisol=0.97, hijack_active=False)
|
| 507 |
+
modulated, hijack_fired, reason = spark(hidden, None, nt)
|
| 508 |
+
assert hijack_fired is True
|
| 509 |
+
assert "CORTISOL" in reason
|
| 510 |
+
|
| 511 |
+
def test_hijack_on_qualia_crisis(self, spark, hidden, nominal_nt_state):
|
| 512 |
+
qualia = OpaqueQualiaSignature(
|
| 513 |
+
valence=0.0, arousal=0.9, intensity=0.8,
|
| 514 |
+
friction_signal=0.9, memory_salience=0.5,
|
| 515 |
+
)
|
| 516 |
+
modulated, hijack_fired, reason = spark(hidden, qualia, nominal_nt_state)
|
| 517 |
+
assert hijack_fired is True
|
| 518 |
+
assert "QUALIA_CRISE" in reason
|
| 519 |
+
|
| 520 |
+
def test_hidden_states_modified_on_hijack(self, spark, hidden):
|
| 521 |
+
nt = NeurotransmitterState(adenosine=0.96, hijack_active=False)
|
| 522 |
+
modulated, hijack_fired, _ = spark(hidden, None, nt)
|
| 523 |
+
assert hijack_fired is True
|
| 524 |
+
assert not torch.equal(modulated, hidden)
|
| 525 |
+
# The difference should be nonzero
|
| 526 |
+
assert (modulated - hidden).abs().sum() > 0.0
|
| 527 |
+
|
| 528 |
+
|
| 529 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 530 |
+
# TestEpisodicMemoryModule (~5 tests)
|
| 531 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 532 |
+
|
| 533 |
+
class TestEpisodicMemoryModule:
|
| 534 |
+
|
| 535 |
+
@pytest.fixture
|
| 536 |
+
def memory(self):
|
| 537 |
+
return EpisodicMemoryModule(hidden_size=64, max_episodes=10, embedding_dim=32)
|
| 538 |
+
|
| 539 |
+
@pytest.fixture
|
| 540 |
+
def hidden(self):
|
| 541 |
+
torch.manual_seed(99)
|
| 542 |
+
return torch.randn(1, 4, 64)
|
| 543 |
+
|
| 544 |
+
def test_empty_memory_returns_zero_prediction_error(self, memory, hidden):
|
| 545 |
+
_, pe, best_match = memory(hidden)
|
| 546 |
+
assert pe == 0.0
|
| 547 |
+
assert best_match is None
|
| 548 |
+
|
| 549 |
+
def test_store_and_retrieve(self, memory, hidden):
|
| 550 |
+
# Store an episode
|
| 551 |
+
qualia = OpaqueQualiaSignature(valence=0.5, arousal=0.5, intensity=0.5,
|
| 552 |
+
friction_signal=0.3, memory_salience=0.4)
|
| 553 |
+
memory.store_episode(hidden, qualia_signature=qualia)
|
| 554 |
+
assert memory.episode_count == 1
|
| 555 |
+
|
| 556 |
+
# Query with similar hidden states β should match
|
| 557 |
+
retrieval_signal, pe, best_match = memory(hidden)
|
| 558 |
+
assert best_match is not None
|
| 559 |
+
assert pe < 1.0
|
| 560 |
+
assert "similarity" in best_match
|
| 561 |
+
|
| 562 |
+
def test_prediction_error_high_for_different_inputs(self, memory, hidden):
|
| 563 |
+
# Store with one hidden state
|
| 564 |
+
memory.store_episode(hidden)
|
| 565 |
+
# Query with very different hidden state
|
| 566 |
+
different_hidden = torch.randn(1, 4, 64) * 10.0
|
| 567 |
+
_, pe, _ = memory(different_hidden)
|
| 568 |
+
# Very different input should yield high prediction error
|
| 569 |
+
assert pe > 0.5
|
| 570 |
+
|
| 571 |
+
def test_episode_count_increments(self, memory, hidden):
|
| 572 |
+
assert memory.episode_count == 0
|
| 573 |
+
memory.store_episode(hidden)
|
| 574 |
+
assert memory.episode_count == 1
|
| 575 |
+
memory.store_episode(hidden)
|
| 576 |
+
assert memory.episode_count == 2
|
| 577 |
+
memory.store_episode(hidden)
|
| 578 |
+
assert memory.episode_count == 3
|
| 579 |
+
|
| 580 |
+
def test_wraps_at_max_episodes(self, memory, hidden):
|
| 581 |
+
max_ep = memory.max_episodes # 10
|
| 582 |
+
for i in range(max_ep + 5):
|
| 583 |
+
memory.store_episode(hidden)
|
| 584 |
+
assert memory.episode_count == max_ep + 5
|
| 585 |
+
# Verify the embedding at slot 0 was overwritten
|
| 586 |
+
slot = 0
|
| 587 |
+
# Store once more to fill slot 0, then check
|
| 588 |
+
memory.store_episode(hidden)
|
| 589 |
+
# Count should keep incrementing; slot wraps via modulo
|
| 590 |
+
assert memory.episode_count == max_ep + 6
|
| 591 |
+
# Slot for the next store should wrap
|
| 592 |
+
assert (memory.episode_count) % max_ep != memory.episode_count
|
| 593 |
+
|
| 594 |
+
|
| 595 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 596 |
+
# TestHippocampalReconsolidator (~4 tests)
|
| 597 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 598 |
+
|
| 599 |
+
class TestHippocampalReconsolidator:
|
| 600 |
+
|
| 601 |
+
@pytest.fixture
|
| 602 |
+
def recon(self):
|
| 603 |
+
return HippocampalReconsolidator()
|
| 604 |
+
|
| 605 |
+
@pytest.fixture
|
| 606 |
+
def hidden(self):
|
| 607 |
+
return torch.randn(1, 4, 64)
|
| 608 |
+
|
| 609 |
+
def test_no_reconsolidation_below_threshold(self, recon, hidden):
|
| 610 |
+
result = recon(
|
| 611 |
+
hidden,
|
| 612 |
+
episode_valence=0.5,
|
| 613 |
+
episode_arousal=0.3,
|
| 614 |
+
prediction_error=0.2,
|
| 615 |
+
current_valence=-0.5,
|
| 616 |
+
current_arousal=0.9,
|
| 617 |
+
)
|
| 618 |
+
reconsolidated, new_v, new_a, reason = result
|
| 619 |
+
assert reconsolidated is False
|
| 620 |
+
assert "below" in reason
|
| 621 |
+
|
| 622 |
+
def test_reconsolidation_above_threshold(self, recon, hidden):
|
| 623 |
+
result = recon(
|
| 624 |
+
hidden,
|
| 625 |
+
episode_valence=0.5,
|
| 626 |
+
episode_arousal=0.3,
|
| 627 |
+
prediction_error=0.8,
|
| 628 |
+
current_valence=-0.5,
|
| 629 |
+
current_arousal=0.9,
|
| 630 |
+
)
|
| 631 |
+
reconsolidated, new_v, new_a, reason = result
|
| 632 |
+
assert reconsolidated is True
|
| 633 |
+
assert isinstance(new_v, float)
|
| 634 |
+
assert isinstance(new_a, float)
|
| 635 |
+
assert isinstance(reason, str)
|
| 636 |
+
assert "reconsolidated" in reason
|
| 637 |
+
|
| 638 |
+
def test_valence_shifts_toward_current(self, recon, hidden):
|
| 639 |
+
"""Reconsolidated valence is a blend of old and new, clamped to [-1, 1]."""
|
| 640 |
+
torch.manual_seed(7)
|
| 641 |
+
episode_valence = 0.8
|
| 642 |
+
current_valence = -0.8
|
| 643 |
+
pred_error = 0.9
|
| 644 |
+
|
| 645 |
+
_, new_v, _, _ = recon(
|
| 646 |
+
hidden,
|
| 647 |
+
episode_valence=episode_valence,
|
| 648 |
+
episode_arousal=0.3,
|
| 649 |
+
prediction_error=pred_error,
|
| 650 |
+
current_valence=current_valence,
|
| 651 |
+
current_arousal=0.5,
|
| 652 |
+
)
|
| 653 |
+
# new = old * 0.7 + projection * 0.3 + noise, clamped to [-1, 1]
|
| 654 |
+
assert isinstance(new_v, float)
|
| 655 |
+
assert -1.0 <= new_v <= 1.0
|
| 656 |
+
# The 70/30 blend with projection and noise means the value
|
| 657 |
+
# should differ from the pure episode_valence (noise alone
|
| 658 |
+
# almost guarantees this, and the projection is nonzero)
|
| 659 |
+
assert new_v != pytest.approx(episode_valence, abs=0.05)
|
| 660 |
+
|
| 661 |
+
def test_reconsolidation_count_increments(self, recon, hidden):
|
| 662 |
+
assert recon.reconsolidation_count == 0
|
| 663 |
+
recon(
|
| 664 |
+
hidden,
|
| 665 |
+
episode_valence=0.5,
|
| 666 |
+
episode_arousal=0.3,
|
| 667 |
+
prediction_error=0.8,
|
| 668 |
+
current_valence=-0.5,
|
| 669 |
+
current_arousal=0.9,
|
| 670 |
+
)
|
| 671 |
+
assert recon.reconsolidation_count == 1
|
| 672 |
+
recon(
|
| 673 |
+
hidden,
|
| 674 |
+
episode_valence=0.1,
|
| 675 |
+
episode_arousal=0.2,
|
| 676 |
+
prediction_error=0.9,
|
| 677 |
+
current_valence=0.8,
|
| 678 |
+
current_arousal=0.7,
|
| 679 |
+
)
|
| 680 |
+
assert recon.reconsolidation_count == 2
|
| 681 |
+
|
| 682 |
+
|
| 683 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 684 |
+
# TestATCDeepSurgeryIntegration (~6 tests)
|
| 685 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 686 |
+
|
| 687 |
+
class TestATCDeepSurgeryIntegration:
|
| 688 |
+
|
| 689 |
+
@pytest.fixture
|
| 690 |
+
def setup(self):
|
| 691 |
+
"""Create a fully wired ATC deep surgery with mock base model."""
|
| 692 |
+
torch.manual_seed(123)
|
| 693 |
+
hidden_size = 256
|
| 694 |
+
num_layers = 12
|
| 695 |
+
vocab_size = 1000
|
| 696 |
+
|
| 697 |
+
mock_model, real_layers = make_mock_base_model(
|
| 698 |
+
hidden_size=hidden_size, num_layers=num_layers, vocab_size=vocab_size
|
| 699 |
+
)
|
| 700 |
+
|
| 701 |
+
# Replace the mock layers list with the real nn.ModuleList
|
| 702 |
+
mock_model.model.layers = real_layers
|
| 703 |
+
mock_model.get_input_embeddings.return_value = nn.Embedding(vocab_size, hidden_size)
|
| 704 |
+
mock_model.lm_head = nn.Linear(hidden_size, vocab_size, bias=False)
|
| 705 |
+
|
| 706 |
+
shunt = NeurotransmitterShunt()
|
| 707 |
+
guardian = EthicalGuardian(threshold=100.0) # High threshold to avoid vetoes
|
| 708 |
+
|
| 709 |
+
surgery = ATCDeepSurgery(
|
| 710 |
+
base_model=mock_model,
|
| 711 |
+
ethical_guardian=guardian,
|
| 712 |
+
num_layers=num_layers,
|
| 713 |
+
qualia_dim=64,
|
| 714 |
+
neurotransmitter_shunt=shunt,
|
| 715 |
+
)
|
| 716 |
+
|
| 717 |
+
return surgery, shunt, hidden_size, vocab_size
|
| 718 |
+
|
| 719 |
+
def test_forward_returns_modulated_logits(self, setup):
|
| 720 |
+
surgery, shunt, hidden_size, vocab_size = setup
|
| 721 |
+
batch_size, seq_len = 2, 8
|
| 722 |
+
input_ids = torch.randint(0, vocab_size, (batch_size, seq_len))
|
| 723 |
+
|
| 724 |
+
logits = surgery(input_ids)
|
| 725 |
+
|
| 726 |
+
assert logits.shape == (batch_size, seq_len, vocab_size)
|
| 727 |
+
assert logits.dtype == torch.float32
|
| 728 |
+
|
| 729 |
+
def test_atc_modulation_differs_from_bare_model(self, setup):
|
| 730 |
+
"""ATC modulated logits should differ from bare model logits."""
|
| 731 |
+
surgery, shunt, hidden_size, vocab_size = setup
|
| 732 |
+
batch_size, seq_len = 2, 8
|
| 733 |
+
input_ids = torch.randint(0, vocab_size, (batch_size, seq_len))
|
| 734 |
+
|
| 735 |
+
# ββ ATC forward ββ
|
| 736 |
+
with torch.no_grad():
|
| 737 |
+
atc_logits = surgery(input_ids)
|
| 738 |
+
|
| 739 |
+
# ββ Bare model forward (same layers + lm_head, no ATC) ββ
|
| 740 |
+
embeddings = surgery.base_model.get_input_embeddings()(input_ids)
|
| 741 |
+
h = embeddings
|
| 742 |
+
for layer in surgery._get_transformer_layers():
|
| 743 |
+
h = layer(h)[0]
|
| 744 |
+
bare_logits = surgery.base_model.lm_head(h)
|
| 745 |
+
|
| 746 |
+
# They should NOT be identical
|
| 747 |
+
assert not torch.allclose(atc_logits, bare_logits, atol=0.0)
|
| 748 |
+
|
| 749 |
+
def test_neurotransmitter_shunt_receives_signals(self, setup):
|
| 750 |
+
"""After forward, shunt.total_firings > 0."""
|
| 751 |
+
surgery, shunt, hidden_size, vocab_size = setup
|
| 752 |
+
input_ids = torch.randint(0, vocab_size, (1, 8))
|
| 753 |
+
|
| 754 |
+
with torch.no_grad():
|
| 755 |
+
surgery(input_ids)
|
| 756 |
+
|
| 757 |
+
state = shunt.get_state()
|
| 758 |
+
assert state.total_firings > 0
|
| 759 |
+
|
| 760 |
+
def test_consciousness_metrics_populated(self, setup):
|
| 761 |
+
"""get_consciousness_metrics returns expected keys."""
|
| 762 |
+
surgery, shunt, hidden_size, vocab_size = setup
|
| 763 |
+
input_ids = torch.randint(0, vocab_size, (1, 8))
|
| 764 |
+
|
| 765 |
+
with torch.no_grad():
|
| 766 |
+
surgery(input_ids)
|
| 767 |
+
|
| 768 |
+
metrics = surgery.get_consciousness_metrics()
|
| 769 |
+
expected_keys = {
|
| 770 |
+
"hijack_count", "has_qualia", "belbic_gain",
|
| 771 |
+
"dissolutions_fired", "dissolutions_deferred",
|
| 772 |
+
"ethical_veto", "episodes_stored",
|
| 773 |
+
"reconsolidations", "last_prediction_error",
|
| 774 |
+
}
|
| 775 |
+
for key in expected_keys:
|
| 776 |
+
assert key in metrics, f"Missing key: {key}"
|
| 777 |
+
|
| 778 |
+
def test_ethical_veto_raises_runtime_error(self, setup):
|
| 779 |
+
"""A qualia vector with very high norm triggers RuntimeError."""
|
| 780 |
+
# Build a surgery with a very low ethical threshold
|
| 781 |
+
torch.manual_seed(456)
|
| 782 |
+
hidden_size = 256
|
| 783 |
+
num_layers = 12
|
| 784 |
+
vocab_size = 1000
|
| 785 |
+
mock_model, real_layers = make_mock_base_model(
|
| 786 |
+
hidden_size=hidden_size, num_layers=num_layers, vocab_size=vocab_size
|
| 787 |
+
)
|
| 788 |
+
mock_model.model.layers = real_layers
|
| 789 |
+
mock_model.get_input_embeddings.return_value = nn.Embedding(vocab_size, hidden_size)
|
| 790 |
+
mock_model.lm_head = nn.Linear(hidden_size, vocab_size, bias=False)
|
| 791 |
+
|
| 792 |
+
shunt = NeurotransmitterShunt()
|
| 793 |
+
# Very low threshold β the tanh-bounded qualia vector norm is at most ~sqrt(5)
|
| 794 |
+
# (5 dims, each in [-1,1]), so threshold=0.1 should trigger veto if
|
| 795 |
+
# the dissolution output is nonzero.
|
| 796 |
+
# Actually, qualia.to_tensor gives [valence, arousal, intensity, friction, memory_salience]
|
| 797 |
+
# where arousal/intensity/friction/memory_salience are in [0,1]. The norm of [0,0,0,0,0]
|
| 798 |
+
# is 0. We need the dissolution to fire and produce a nonzero qualia.
|
| 799 |
+
# The dissolution encoder produces values in [-1,1] for valence and
|
| 800 |
+
# [0,1] for the mapped fields. The qualia tensor norm is bounded.
|
| 801 |
+
# Use threshold=0.0 to guarantee veto.
|
| 802 |
+
guardian = EthicalGuardian(threshold=0.0)
|
| 803 |
+
|
| 804 |
+
surgery = ATCDeepSurgery(
|
| 805 |
+
base_model=mock_model,
|
| 806 |
+
ethical_guardian=guardian,
|
| 807 |
+
num_layers=num_layers,
|
| 808 |
+
qualia_dim=64,
|
| 809 |
+
neurotransmitter_shunt=shunt,
|
| 810 |
+
)
|
| 811 |
+
|
| 812 |
+
input_ids = torch.randint(0, vocab_size, (1, 8))
|
| 813 |
+
with pytest.raises(RuntimeError, match="Ethical veto"):
|
| 814 |
+
with torch.no_grad():
|
| 815 |
+
surgery(input_ids)
|
| 816 |
+
|
| 817 |
+
def test_hijack_count_tracking(self, setup):
|
| 818 |
+
"""When conditions trigger hijack, _hijack_count > 0."""
|
| 819 |
+
surgery, shunt, hidden_size, vocab_size = setup
|
| 820 |
+
input_ids = torch.randint(0, vocab_size, (1, 8))
|
| 821 |
+
|
| 822 |
+
# Manually push adenosine past the critical threshold so the
|
| 823 |
+
# irrational spark fires in Layer 5
|
| 824 |
+
shunt._vector[IDX_ADENOSINE] = 0.96
|
| 825 |
+
|
| 826 |
+
with torch.no_grad():
|
| 827 |
+
surgery(input_ids)
|
| 828 |
+
|
| 829 |
+
assert surgery._hijack_count > 0
|