FerrellSyntheticIntelligence commited on
Commit ·
63dd1f4
1
Parent(s): 0fba98d
feat: audio ear, cognition modules, dream engine, vitalis IDE, test encoder
Browse files- pytest.ini +3 -0
- src/audio_ear/feature_extractor.py +34 -0
- src/audio_ear/recorder.py +13 -0
- src/cognition/abstract_reasoner.py +233 -0
- src/cognition/complexity_reasoner.py +217 -0
- src/cognition/self_model.py +203 -0
- src/cognitive/reasoning_engine.py +21 -0
- src/dream_engine/helix_memory.py +52 -0
- src/vitalis_ide/cli/main.py +68 -0
- tests/test_encoder.py +43 -0
pytest.ini
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[pytest]
|
| 2 |
+
pythonpath = . src
|
| 3 |
+
testpaths = tests src
|
src/audio_ear/feature_extractor.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import librosa
|
| 2 |
+
import numpy as np
|
| 3 |
+
from typing import Tuple, Dict
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
|
| 6 |
+
def extract_features(wav_path: Path) -> Tuple[np.ndarray, Dict[str, float]]:
|
| 7 |
+
"""
|
| 8 |
+
Extracts the 13-band Mel-frequency cepstral coefficients (MFCC)
|
| 9 |
+
and heuristic prosody markers from a raw WAV file.
|
| 10 |
+
"""
|
| 11 |
+
y, sr = librosa.load(str(wav_path), sr=16000)
|
| 12 |
+
|
| 13 |
+
# Extract MFCC matrix
|
| 14 |
+
mfcc = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13)
|
| 15 |
+
|
| 16 |
+
# Heuristic prosody extraction
|
| 17 |
+
pitches, magnitudes = librosa.piptrack(y=y, sr=sr)
|
| 18 |
+
valid_pitches = pitches[magnitudes > np.median(magnitudes)]
|
| 19 |
+
pitch = float(np.mean(valid_pitches)) if len(valid_pitches) > 0 else 0.0
|
| 20 |
+
|
| 21 |
+
energy = float(np.mean(librosa.feature.rms(y=y)))
|
| 22 |
+
tempo, _ = librosa.beat.beat_track(y=y, sr=sr)
|
| 23 |
+
|
| 24 |
+
# Calculate pause ratio based on silence thresholds
|
| 25 |
+
pause_ratio = float(np.sum(np.abs(y) < 0.01) / len(y)) if len(y) > 0 else 0.0
|
| 26 |
+
|
| 27 |
+
prosody = {
|
| 28 |
+
"pitch": pitch,
|
| 29 |
+
"energy": energy,
|
| 30 |
+
"tempo": float(tempo[0] if isinstance(tempo, np.ndarray) else tempo),
|
| 31 |
+
"pause_ratio": pause_ratio
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
return mfcc, prosody
|
src/audio_ear/recorder.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sounddevice as sd
|
| 2 |
+
import soundfile as sf
|
| 3 |
+
import numpy as np
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
|
| 6 |
+
def record_to_wav(duration_sec: int, out_path: Path, fs: int = 16000) -> None:
|
| 7 |
+
"""
|
| 8 |
+
Interfaces directly with the local machine's sound architecture
|
| 9 |
+
to capture a mono-channel audio array.
|
| 10 |
+
"""
|
| 11 |
+
recording = sd.rec(int(duration_sec * fs), samplerate=fs, channels=1, dtype=np.float32)
|
| 12 |
+
sd.wait()
|
| 13 |
+
sf.write(str(out_path), recording, fs)
|
src/cognition/abstract_reasoner.py
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
AbstractReasoner — Vitalis FSI
|
| 3 |
+
|
| 4 |
+
Reasons about RELATIONSHIPS between concepts.
|
| 5 |
+
Not pattern matching. Not retrieval.
|
| 6 |
+
Genuine relational reasoning:
|
| 7 |
+
- Analogy: A is to B as C is to ?
|
| 8 |
+
- Composition: concept_A + concept_B = novel_concept
|
| 9 |
+
- Inversion: what is the opposite of this concept?
|
| 10 |
+
- Transitivity: if A relates to B and B relates to C, what does A relate to C?
|
| 11 |
+
|
| 12 |
+
Built entirely on HDC operations. No external models.
|
| 13 |
+
"""
|
| 14 |
+
import numpy as np
|
| 15 |
+
import os
|
| 16 |
+
import json
|
| 17 |
+
import time
|
| 18 |
+
from vitalis_ide.math_core.kernel import VitalisKernel
|
| 19 |
+
from src.cognition.abstraction import AbstractionEngine
|
| 20 |
+
from src.hippocampus import Hippocampus
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class AbstractReasoner:
|
| 24 |
+
ANALOGY_THRESHOLD = 0.25
|
| 25 |
+
COMPOSITION_DECAY = 0.85
|
| 26 |
+
INVERSION_SHIFT = 5000
|
| 27 |
+
|
| 28 |
+
def __init__(self):
|
| 29 |
+
self.kernel = VitalisKernel()
|
| 30 |
+
self.abstraction = AbstractionEngine()
|
| 31 |
+
self.hippocampus = Hippocampus()
|
| 32 |
+
self.path = os.path.expanduser(
|
| 33 |
+
"~/.vitalis_workspace/reasoning_log.json"
|
| 34 |
+
)
|
| 35 |
+
self._log = self._load_log()
|
| 36 |
+
|
| 37 |
+
def _load_log(self) -> list:
|
| 38 |
+
if os.path.exists(self.path):
|
| 39 |
+
with open(self.path) as f:
|
| 40 |
+
return json.load(f)
|
| 41 |
+
return []
|
| 42 |
+
|
| 43 |
+
def _save_log(self):
|
| 44 |
+
os.makedirs(os.path.dirname(self.path), exist_ok=True)
|
| 45 |
+
with open(self.path, "w") as f:
|
| 46 |
+
json.dump(self._log[-500:], f, indent=2)
|
| 47 |
+
|
| 48 |
+
# ------------------------------------------------------------------
|
| 49 |
+
# Core HDC reasoning operations
|
| 50 |
+
# ------------------------------------------------------------------
|
| 51 |
+
def _bind(self, a: np.ndarray, b: np.ndarray) -> np.ndarray:
|
| 52 |
+
"""Bipolar binding: element-wise multiply."""
|
| 53 |
+
return (a.astype(np.int32) * b.astype(np.int32)).astype(np.int8)
|
| 54 |
+
|
| 55 |
+
def _bundle(self, vecs: list) -> np.ndarray:
|
| 56 |
+
"""Bipolar bundling: sum then sign."""
|
| 57 |
+
stacked = np.stack(vecs).astype(np.int32).sum(axis=0)
|
| 58 |
+
result = np.sign(stacked).astype(np.int8)
|
| 59 |
+
result[result == 0] = 1
|
| 60 |
+
return result
|
| 61 |
+
|
| 62 |
+
def _invert(self, vec: np.ndarray) -> np.ndarray:
|
| 63 |
+
"""
|
| 64 |
+
Semantic inversion: cyclic shift by half the vector length.
|
| 65 |
+
Produces a vector maximally dissimilar to the input.
|
| 66 |
+
"""
|
| 67 |
+
return np.roll(vec, self.INVERSION_SHIFT)
|
| 68 |
+
|
| 69 |
+
# ------------------------------------------------------------------
|
| 70 |
+
# Analogy: A is to B as C is to ?
|
| 71 |
+
# ------------------------------------------------------------------
|
| 72 |
+
def analogy(
|
| 73 |
+
self,
|
| 74 |
+
concept_a: str,
|
| 75 |
+
concept_b: str,
|
| 76 |
+
concept_c: str,
|
| 77 |
+
) -> dict:
|
| 78 |
+
"""
|
| 79 |
+
Solves: A:B :: C:?
|
| 80 |
+
HDC method: ? = bind(bind(A, B), C)
|
| 81 |
+
Searches abstraction space and hippocampus for closest match.
|
| 82 |
+
"""
|
| 83 |
+
vec_a = self.kernel.vectorize_tokens(concept_a.split(), positional=False)
|
| 84 |
+
vec_b = self.kernel.vectorize_tokens(concept_b.split(), positional=False)
|
| 85 |
+
vec_c = self.kernel.vectorize_tokens(concept_c.split(), positional=False)
|
| 86 |
+
|
| 87 |
+
# ? = B * A^-1 * C (HDC analogy formula)
|
| 88 |
+
a_inv = self._bind(vec_a, vec_a) # A bound with itself = identity-like
|
| 89 |
+
relation = self._bind(vec_a, vec_b) # encode A→B relationship
|
| 90 |
+
answer_vec = self._bind(relation, vec_c) # apply relation to C
|
| 91 |
+
|
| 92 |
+
# Search for closest concept
|
| 93 |
+
candidates = self.abstraction.query_abstractions(answer_vec, top_k=3)
|
| 94 |
+
hipp_results = self.hippocampus.similarity_search(answer_vec, top_k=3)
|
| 95 |
+
|
| 96 |
+
best_match = None
|
| 97 |
+
best_score = -1.0
|
| 98 |
+
|
| 99 |
+
for score, name, _ in candidates:
|
| 100 |
+
if score > best_score:
|
| 101 |
+
best_score = score
|
| 102 |
+
best_match = name
|
| 103 |
+
|
| 104 |
+
result = {
|
| 105 |
+
"type": "analogy",
|
| 106 |
+
"query": f"{concept_a}:{concept_b}::{concept_c}:?",
|
| 107 |
+
"answer_vec": answer_vec,
|
| 108 |
+
"best_match": best_match,
|
| 109 |
+
"confidence": round(float(best_score), 4),
|
| 110 |
+
"candidates": [(name, round(float(s), 4)) for s, name, _ in candidates],
|
| 111 |
+
"timestamp": time.time(),
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
self._log.append({k: v for k, v in result.items() if k != "answer_vec"})
|
| 115 |
+
self._save_log()
|
| 116 |
+
return result
|
| 117 |
+
|
| 118 |
+
# ------------------------------------------------------------------
|
| 119 |
+
# Composition: merge two concepts into a novel one
|
| 120 |
+
# ------------------------------------------------------------------
|
| 121 |
+
def compose(self, concept_a: str, concept_b: str) -> dict:
|
| 122 |
+
"""
|
| 123 |
+
Compose two concepts into a novel concept vector.
|
| 124 |
+
The result occupies a position in the space between both inputs.
|
| 125 |
+
Weighted by the COMPOSITION_DECAY to prevent drift.
|
| 126 |
+
"""
|
| 127 |
+
vec_a = self.kernel.vectorize_tokens(concept_a.split(), positional=False)
|
| 128 |
+
vec_b = self.kernel.vectorize_tokens(concept_b.split(), positional=False)
|
| 129 |
+
|
| 130 |
+
# Bundle with decay weighting
|
| 131 |
+
composed = self._bundle([vec_a, vec_b])
|
| 132 |
+
|
| 133 |
+
# Apply composition decay — prevents the result from being
|
| 134 |
+
# too close to either parent
|
| 135 |
+
noise_mask = np.random.choice(
|
| 136 |
+
[-1, 1],
|
| 137 |
+
size=self.kernel.dim,
|
| 138 |
+
p=[1 - self.COMPOSITION_DECAY, self.COMPOSITION_DECAY]
|
| 139 |
+
).astype(np.int8)
|
| 140 |
+
composed = self._bind(composed, noise_mask)
|
| 141 |
+
|
| 142 |
+
# Search for nearest existing concept
|
| 143 |
+
candidates = self.abstraction.query_abstractions(composed, top_k=3)
|
| 144 |
+
|
| 145 |
+
result = {
|
| 146 |
+
"type": "composition",
|
| 147 |
+
"inputs": [concept_a, concept_b],
|
| 148 |
+
"novel_vec": composed,
|
| 149 |
+
"nearest": [(name, round(float(s), 4)) for s, name, _ in candidates],
|
| 150 |
+
"novelty": round(1.0 - (candidates[0][0] if candidates else 0.0), 4),
|
| 151 |
+
"timestamp": time.time(),
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
self._log.append({k: v for k, v in result.items() if k != "novel_vec"})
|
| 155 |
+
self._save_log()
|
| 156 |
+
return result
|
| 157 |
+
|
| 158 |
+
# ------------------------------------------------------------------
|
| 159 |
+
# Inversion: what is the conceptual opposite?
|
| 160 |
+
# ------------------------------------------------------------------
|
| 161 |
+
def invert(self, concept: str) -> dict:
|
| 162 |
+
"""
|
| 163 |
+
Find the conceptual opposite of a concept.
|
| 164 |
+
Uses cyclic shift inversion then searches concept space.
|
| 165 |
+
"""
|
| 166 |
+
vec = self.kernel.vectorize_tokens(concept.split(), positional=False)
|
| 167 |
+
inverted = self._invert(vec)
|
| 168 |
+
|
| 169 |
+
candidates = self.abstraction.query_abstractions(inverted, top_k=3)
|
| 170 |
+
hipp_results = self.hippocampus.similarity_search(inverted, top_k=3)
|
| 171 |
+
|
| 172 |
+
result = {
|
| 173 |
+
"type": "inversion",
|
| 174 |
+
"concept": concept,
|
| 175 |
+
"opposites": [(name, round(float(s), 4)) for s, name, _ in candidates],
|
| 176 |
+
"confidence": round(float(candidates[0][0]) if candidates else 0.0, 4),
|
| 177 |
+
"timestamp": time.time(),
|
| 178 |
+
}
|
| 179 |
+
|
| 180 |
+
self._log.append(result)
|
| 181 |
+
self._save_log()
|
| 182 |
+
return result
|
| 183 |
+
|
| 184 |
+
# ------------------------------------------------------------------
|
| 185 |
+
# Transitivity: if A→B and B→C, what is A→C?
|
| 186 |
+
# ------------------------------------------------------------------
|
| 187 |
+
def transitive_chain(self, concepts: list) -> dict:
|
| 188 |
+
"""
|
| 189 |
+
Chain reasoning: given [A, B, C, D...],
|
| 190 |
+
derive the relationship between A and the last element.
|
| 191 |
+
Each step binds the accumulated relationship with the next concept.
|
| 192 |
+
"""
|
| 193 |
+
if len(concepts) < 2:
|
| 194 |
+
return {"error": "Need at least 2 concepts"}
|
| 195 |
+
|
| 196 |
+
vecs = [
|
| 197 |
+
self.kernel.vectorize_tokens(c.split(), positional=False)
|
| 198 |
+
for c in concepts
|
| 199 |
+
]
|
| 200 |
+
|
| 201 |
+
# Accumulate relationship via sequential binding
|
| 202 |
+
accumulated = vecs[0].copy()
|
| 203 |
+
for i in range(1, len(vecs)):
|
| 204 |
+
accumulated = self._bind(accumulated, vecs[i])
|
| 205 |
+
# Apply position-aware permutation at each step
|
| 206 |
+
accumulated = np.roll(accumulated, i * 100)
|
| 207 |
+
|
| 208 |
+
candidates = self.abstraction.query_abstractions(accumulated, top_k=3)
|
| 209 |
+
|
| 210 |
+
result = {
|
| 211 |
+
"type": "transitivity",
|
| 212 |
+
"chain": concepts,
|
| 213 |
+
"conclusion": [(name, round(float(s), 4)) for s, name, _ in candidates],
|
| 214 |
+
"confidence": round(float(candidates[0][0]) if candidates else 0.0, 4),
|
| 215 |
+
"timestamp": time.time(),
|
| 216 |
+
}
|
| 217 |
+
|
| 218 |
+
self._log.append(result)
|
| 219 |
+
self._save_log()
|
| 220 |
+
return result
|
| 221 |
+
|
| 222 |
+
def report(self) -> dict:
|
| 223 |
+
if not self._log:
|
| 224 |
+
return {"status": "No reasoning performed yet"}
|
| 225 |
+
type_counts = {}
|
| 226 |
+
for entry in self._log:
|
| 227 |
+
t = entry.get("type", "unknown")
|
| 228 |
+
type_counts[t] = type_counts.get(t, 0) + 1
|
| 229 |
+
return {
|
| 230 |
+
"total_reasoning_ops": len(self._log),
|
| 231 |
+
"by_type": type_counts,
|
| 232 |
+
"recent": self._log[-3:],
|
| 233 |
+
}
|
src/cognition/complexity_reasoner.py
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
ComplexityReasoner — Vitalis FSI
|
| 3 |
+
|
| 4 |
+
Assesses how hard a problem is BEFORE attempting it.
|
| 5 |
+
Allocates cognitive resources accordingly.
|
| 6 |
+
Prevents wasted cycles on problems beyond current capability.
|
| 7 |
+
Prevents under-allocation on trivial problems.
|
| 8 |
+
|
| 9 |
+
Complexity dimensions:
|
| 10 |
+
- Structural: how many components does this problem have?
|
| 11 |
+
- Novelty: how far is this from known patterns?
|
| 12 |
+
- Depth: how many reasoning steps are required?
|
| 13 |
+
- Ambiguity: how many valid interpretations exist?
|
| 14 |
+
"""
|
| 15 |
+
import numpy as np
|
| 16 |
+
import os
|
| 17 |
+
import json
|
| 18 |
+
import time
|
| 19 |
+
from vitalis_ide.math_core.kernel import VitalisKernel
|
| 20 |
+
from src.cognition.abstraction import AbstractionEngine
|
| 21 |
+
from src.hippocampus import Hippocampus
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class ComplexityReasoner:
|
| 25 |
+
# Complexity tier thresholds
|
| 26 |
+
TRIVIAL_THRESHOLD = 0.25
|
| 27 |
+
SIMPLE_THRESHOLD = 0.45
|
| 28 |
+
MODERATE_THRESHOLD = 0.65
|
| 29 |
+
COMPLEX_THRESHOLD = 0.80
|
| 30 |
+
|
| 31 |
+
# Resource allocation per tier
|
| 32 |
+
RESOURCE_MAP = {
|
| 33 |
+
"TRIVIAL": {"cycles": 1, "abstraction_depth": 1, "analogy_search": False},
|
| 34 |
+
"SIMPLE": {"cycles": 2, "abstraction_depth": 2, "analogy_search": False},
|
| 35 |
+
"MODERATE": {"cycles": 4, "abstraction_depth": 3, "analogy_search": True},
|
| 36 |
+
"COMPLEX": {"cycles": 8, "abstraction_depth": 4, "analogy_search": True},
|
| 37 |
+
"FRONTIER": {"cycles": 16, "abstraction_depth": 5, "analogy_search": True},
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
def __init__(self):
|
| 41 |
+
self.kernel = VitalisKernel()
|
| 42 |
+
self.abstraction = AbstractionEngine()
|
| 43 |
+
self.hippocampus = Hippocampus()
|
| 44 |
+
self.path = os.path.expanduser(
|
| 45 |
+
"~/.vitalis_workspace/complexity_log.json"
|
| 46 |
+
)
|
| 47 |
+
self._log = self._load_log()
|
| 48 |
+
self._history = []
|
| 49 |
+
|
| 50 |
+
def _load_log(self) -> list:
|
| 51 |
+
if os.path.exists(self.path):
|
| 52 |
+
with open(self.path) as f:
|
| 53 |
+
return json.load(f)
|
| 54 |
+
return []
|
| 55 |
+
|
| 56 |
+
def _save_log(self):
|
| 57 |
+
os.makedirs(os.path.dirname(self.path), exist_ok=True)
|
| 58 |
+
with open(self.path, "w") as f:
|
| 59 |
+
json.dump(self._log[-1000:], f, indent=2)
|
| 60 |
+
|
| 61 |
+
# ------------------------------------------------------------------
|
| 62 |
+
# Core assessment
|
| 63 |
+
# ------------------------------------------------------------------
|
| 64 |
+
def assess(self, intent: str, context: dict = None) -> dict:
|
| 65 |
+
"""
|
| 66 |
+
Full complexity assessment for an intent.
|
| 67 |
+
Returns complexity score, tier, and resource allocation.
|
| 68 |
+
"""
|
| 69 |
+
context = context or {}
|
| 70 |
+
tokens = intent.lower().split()
|
| 71 |
+
vec = self.kernel.vectorize_tokens(tokens, positional=False)
|
| 72 |
+
|
| 73 |
+
# 1. Structural complexity — token count and unique concepts
|
| 74 |
+
structural = self._structural_score(tokens)
|
| 75 |
+
|
| 76 |
+
# 2. Novelty — distance from known patterns
|
| 77 |
+
novelty = self._novelty_score(vec)
|
| 78 |
+
|
| 79 |
+
# 3. Depth — estimated reasoning steps needed
|
| 80 |
+
depth = self._depth_score(intent, tokens)
|
| 81 |
+
|
| 82 |
+
# 4. Ambiguity — spread across abstraction space
|
| 83 |
+
ambiguity = self._ambiguity_score(vec)
|
| 84 |
+
|
| 85 |
+
# Weighted composite
|
| 86 |
+
score = (
|
| 87 |
+
structural * 0.20 +
|
| 88 |
+
novelty * 0.35 +
|
| 89 |
+
depth * 0.25 +
|
| 90 |
+
ambiguity * 0.20
|
| 91 |
+
)
|
| 92 |
+
score = float(np.clip(score, 0.0, 1.0))
|
| 93 |
+
|
| 94 |
+
tier = self._tier(score)
|
| 95 |
+
resources = self.RESOURCE_MAP[tier].copy()
|
| 96 |
+
|
| 97 |
+
result = {
|
| 98 |
+
"intent": intent,
|
| 99 |
+
"score": round(score, 4),
|
| 100 |
+
"tier": tier,
|
| 101 |
+
"dimensions": {
|
| 102 |
+
"structural": round(structural, 4),
|
| 103 |
+
"novelty": round(novelty, 4),
|
| 104 |
+
"depth": round(depth, 4),
|
| 105 |
+
"ambiguity": round(ambiguity, 4),
|
| 106 |
+
},
|
| 107 |
+
"resources": resources,
|
| 108 |
+
"timestamp": time.time(),
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
self._log.append(result)
|
| 112 |
+
self._history.append(score)
|
| 113 |
+
if len(self._history) > 100:
|
| 114 |
+
self._history.pop(0)
|
| 115 |
+
self._save_log()
|
| 116 |
+
return result
|
| 117 |
+
|
| 118 |
+
# ------------------------------------------------------------------
|
| 119 |
+
# Dimension calculators
|
| 120 |
+
# ------------------------------------------------------------------
|
| 121 |
+
def _structural_score(self, tokens: list) -> float:
|
| 122 |
+
"""More tokens + unique concepts = higher structural complexity."""
|
| 123 |
+
n = len(tokens)
|
| 124 |
+
unique = len(set(tokens))
|
| 125 |
+
# Normalise: 10 tokens = moderate complexity
|
| 126 |
+
return float(np.clip((n / 10.0) * 0.5 + (unique / n if n > 0 else 0) * 0.5, 0, 1))
|
| 127 |
+
|
| 128 |
+
def _novelty_score(self, vec: np.ndarray) -> float:
|
| 129 |
+
"""
|
| 130 |
+
How far is this from anything the system has seen before?
|
| 131 |
+
High novelty = far from known abstractions.
|
| 132 |
+
"""
|
| 133 |
+
candidates = self.abstraction.query_abstractions(vec, top_k=1)
|
| 134 |
+
if not candidates:
|
| 135 |
+
return 1.0 # completely novel
|
| 136 |
+
best_sim = candidates[0][0]
|
| 137 |
+
return float(np.clip(1.0 - best_sim, 0.0, 1.0))
|
| 138 |
+
|
| 139 |
+
def _depth_score(self, intent: str, tokens: list) -> float:
|
| 140 |
+
"""
|
| 141 |
+
Estimate reasoning depth from linguistic markers.
|
| 142 |
+
Multi-step intents score higher.
|
| 143 |
+
"""
|
| 144 |
+
depth_markers = {
|
| 145 |
+
"high": ["analyze", "verify", "optimize", "refactor",
|
| 146 |
+
"debug", "compare", "evaluate", "synthesize"],
|
| 147 |
+
"medium": ["write", "scaffold", "create", "build",
|
| 148 |
+
"generate", "implement"],
|
| 149 |
+
"low": ["run", "check", "show", "list", "get"],
|
| 150 |
+
}
|
| 151 |
+
for token in tokens:
|
| 152 |
+
if token in depth_markers["high"]:
|
| 153 |
+
return 0.8
|
| 154 |
+
if token in depth_markers["medium"]:
|
| 155 |
+
return 0.5
|
| 156 |
+
if token in depth_markers["low"]:
|
| 157 |
+
return 0.2
|
| 158 |
+
# Connectives suggest multi-step reasoning
|
| 159 |
+
if any(w in intent.lower() for w in ["and then", "after", "before", "while"]):
|
| 160 |
+
return 0.9
|
| 161 |
+
return 0.4 # default moderate
|
| 162 |
+
|
| 163 |
+
def _ambiguity_score(self, vec: np.ndarray) -> float:
|
| 164 |
+
"""
|
| 165 |
+
How spread are the top matches in abstraction space?
|
| 166 |
+
High spread = high ambiguity = harder problem.
|
| 167 |
+
"""
|
| 168 |
+
candidates = self.abstraction.query_abstractions(vec, top_k=3)
|
| 169 |
+
if len(candidates) < 2:
|
| 170 |
+
return 0.5
|
| 171 |
+
scores = [c[0] for c in candidates]
|
| 172 |
+
spread = float(np.std(scores))
|
| 173 |
+
return float(np.clip(spread * 4.0, 0.0, 1.0))
|
| 174 |
+
|
| 175 |
+
def _tier(self, score: float) -> str:
|
| 176 |
+
if score < self.TRIVIAL_THRESHOLD:
|
| 177 |
+
return "TRIVIAL"
|
| 178 |
+
elif score < self.SIMPLE_THRESHOLD:
|
| 179 |
+
return "SIMPLE"
|
| 180 |
+
elif score < self.MODERATE_THRESHOLD:
|
| 181 |
+
return "MODERATE"
|
| 182 |
+
elif score < self.COMPLEX_THRESHOLD:
|
| 183 |
+
return "COMPLEX"
|
| 184 |
+
else:
|
| 185 |
+
return "FRONTIER"
|
| 186 |
+
|
| 187 |
+
# ------------------------------------------------------------------
|
| 188 |
+
# Trend analysis
|
| 189 |
+
# ------------------------------------------------------------------
|
| 190 |
+
def complexity_trend(self) -> dict:
|
| 191 |
+
"""Is the system tackling harder or easier problems over time?"""
|
| 192 |
+
if len(self._history) < 5:
|
| 193 |
+
return {"status": "Insufficient data"}
|
| 194 |
+
recent = float(np.mean(self._history[-5:]))
|
| 195 |
+
baseline = float(np.mean(self._history))
|
| 196 |
+
trend = "increasing" if recent > baseline + 0.05 else \
|
| 197 |
+
"decreasing" if recent < baseline - 0.05 else "stable"
|
| 198 |
+
return {
|
| 199 |
+
"recent_avg": round(recent, 4),
|
| 200 |
+
"baseline": round(baseline, 4),
|
| 201 |
+
"trend": trend,
|
| 202 |
+
"sample_size": len(self._history),
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
def report(self) -> dict:
|
| 206 |
+
if not self._log:
|
| 207 |
+
return {"status": "No assessments yet"}
|
| 208 |
+
tiers = {}
|
| 209 |
+
for e in self._log:
|
| 210 |
+
t = e.get("tier", "UNKNOWN")
|
| 211 |
+
tiers[t] = tiers.get(t, 0) + 1
|
| 212 |
+
return {
|
| 213 |
+
"total_assessments": len(self._log),
|
| 214 |
+
"tier_distribution": tiers,
|
| 215 |
+
"avg_complexity": round(float(np.mean([e["score"] for e in self._log])), 4),
|
| 216 |
+
"trend": self.complexity_trend(),
|
| 217 |
+
}
|
src/cognition/self_model.py
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
SelfModel — Vitalis FSI
|
| 3 |
+
|
| 4 |
+
Vitalis maintains a coherent understanding of its own architecture,
|
| 5 |
+
capabilities, limitations, and growth trajectory.
|
| 6 |
+
|
| 7 |
+
This is not a static config file.
|
| 8 |
+
This is a living self-representation that updates as the system evolves.
|
| 9 |
+
|
| 10 |
+
The SelfModel answers:
|
| 11 |
+
- What am I capable of right now?
|
| 12 |
+
- What are my current limitations?
|
| 13 |
+
- How have I grown since initialization?
|
| 14 |
+
- What is the next capability boundary I should push?
|
| 15 |
+
- Am I operating within my identity alignment?
|
| 16 |
+
"""
|
| 17 |
+
import numpy as np
|
| 18 |
+
import os
|
| 19 |
+
import json
|
| 20 |
+
import time
|
| 21 |
+
from vitalis_ide.math_core.kernel import VitalisKernel
|
| 22 |
+
from src.brain.resonance import ResonanceEngine
|
| 23 |
+
from src.hippocampus import Hippocampus
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class SelfModel:
|
| 27 |
+
# Capability thresholds — evolve as resonance weights grow
|
| 28 |
+
CAPABILITY_THRESHOLD = 1.2 # resonance weight above this = capable
|
| 29 |
+
LIMITATION_THRESHOLD = 0.5 # resonance weight below this = limited
|
| 30 |
+
FRONTIER_THRESHOLD = 0.8 # between = frontier (learning zone)
|
| 31 |
+
|
| 32 |
+
def __init__(self):
|
| 33 |
+
self.kernel = VitalisKernel()
|
| 34 |
+
self.resonance = ResonanceEngine()
|
| 35 |
+
self.hippocampus = Hippocampus()
|
| 36 |
+
self.path = os.path.expanduser(
|
| 37 |
+
"~/.vitalis_workspace/self_model.json"
|
| 38 |
+
)
|
| 39 |
+
self._birth_time = self._load_birth_time()
|
| 40 |
+
self._snapshots = self._load_snapshots()
|
| 41 |
+
|
| 42 |
+
def _load_birth_time(self) -> float:
|
| 43 |
+
if os.path.exists(self.path):
|
| 44 |
+
with open(self.path) as f:
|
| 45 |
+
data = json.load(f)
|
| 46 |
+
return data.get("birth_time", time.time())
|
| 47 |
+
birth = time.time()
|
| 48 |
+
self._save({"birth_time": birth, "snapshots": []})
|
| 49 |
+
return birth
|
| 50 |
+
|
| 51 |
+
def _load_snapshots(self) -> list:
|
| 52 |
+
if os.path.exists(self.path):
|
| 53 |
+
with open(self.path) as f:
|
| 54 |
+
return json.load(f).get("snapshots", [])
|
| 55 |
+
return []
|
| 56 |
+
|
| 57 |
+
def _save(self, data: dict = None):
|
| 58 |
+
os.makedirs(os.path.dirname(self.path), exist_ok=True)
|
| 59 |
+
if data is None:
|
| 60 |
+
data = {
|
| 61 |
+
"birth_time": self._birth_time,
|
| 62 |
+
"snapshots": self._snapshots[-100:],
|
| 63 |
+
}
|
| 64 |
+
with open(self.path, "w") as f:
|
| 65 |
+
json.dump(data, f, indent=2)
|
| 66 |
+
|
| 67 |
+
# ------------------------------------------------------------------
|
| 68 |
+
# Core self-assessment
|
| 69 |
+
# ------------------------------------------------------------------
|
| 70 |
+
def assess(self) -> dict:
|
| 71 |
+
"""
|
| 72 |
+
Full self-assessment. Returns current capability map,
|
| 73 |
+
limitations, frontier zones, and growth trajectory.
|
| 74 |
+
"""
|
| 75 |
+
resonance_report = self.resonance.report()
|
| 76 |
+
weights = self.resonance.weights
|
| 77 |
+
memory_report = self.hippocampus.memory_report()
|
| 78 |
+
|
| 79 |
+
# Capability map
|
| 80 |
+
capabilities = []
|
| 81 |
+
limitations = []
|
| 82 |
+
frontiers = []
|
| 83 |
+
|
| 84 |
+
for pattern, weight in weights.items():
|
| 85 |
+
if weight >= self.CAPABILITY_THRESHOLD:
|
| 86 |
+
capabilities.append((pattern, round(weight, 3)))
|
| 87 |
+
elif weight <= self.LIMITATION_THRESHOLD:
|
| 88 |
+
limitations.append((pattern, round(weight, 3)))
|
| 89 |
+
else:
|
| 90 |
+
frontiers.append((pattern, round(weight, 3)))
|
| 91 |
+
|
| 92 |
+
capabilities.sort(key=lambda x: x[1], reverse=True)
|
| 93 |
+
limitations.sort(key=lambda x: x[1])
|
| 94 |
+
frontiers.sort(key=lambda x: x[1], reverse=True)
|
| 95 |
+
|
| 96 |
+
# Growth metrics
|
| 97 |
+
age_hours = (time.time() - self._birth_time) / 3600
|
| 98 |
+
mem_count = len(memory_report)
|
| 99 |
+
avg_strength = float(np.mean([
|
| 100 |
+
v["strength"] for v in memory_report.values()
|
| 101 |
+
])) if memory_report else 0.0
|
| 102 |
+
|
| 103 |
+
# Next capability boundary — highest frontier item
|
| 104 |
+
next_boundary = frontiers[0][0] if frontiers else "unexplored"
|
| 105 |
+
|
| 106 |
+
# Identity coherence — how aligned are capabilities with identity?
|
| 107 |
+
identity_vec = self._load_identity_vec()
|
| 108 |
+
coherence = self._identity_coherence(identity_vec, capabilities)
|
| 109 |
+
|
| 110 |
+
assessment = {
|
| 111 |
+
"timestamp": time.time(),
|
| 112 |
+
"age_hours": round(age_hours, 2),
|
| 113 |
+
"capabilities": capabilities[:10],
|
| 114 |
+
"limitations": limitations[:5],
|
| 115 |
+
"frontiers": frontiers[:5],
|
| 116 |
+
"next_boundary": next_boundary,
|
| 117 |
+
"memory_count": mem_count,
|
| 118 |
+
"memory_strength": round(avg_strength, 4),
|
| 119 |
+
"identity_coherence": round(coherence, 4),
|
| 120 |
+
"total_patterns": len(weights),
|
| 121 |
+
"growth_index": self._growth_index(),
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
# Snapshot for trajectory tracking
|
| 125 |
+
self._snapshots.append({
|
| 126 |
+
"timestamp": assessment["timestamp"],
|
| 127 |
+
"capabilities": len(capabilities),
|
| 128 |
+
"limitations": len(limitations),
|
| 129 |
+
"growth_index": assessment["growth_index"],
|
| 130 |
+
})
|
| 131 |
+
self._save()
|
| 132 |
+
|
| 133 |
+
return assessment
|
| 134 |
+
|
| 135 |
+
# ------------------------------------------------------------------
|
| 136 |
+
# Internal
|
| 137 |
+
# ------------------------------------------------------------------
|
| 138 |
+
def _load_identity_vec(self) -> np.ndarray:
|
| 139 |
+
path = os.path.expanduser("~/.vitalis_workspace/identity.npy")
|
| 140 |
+
if os.path.exists(path):
|
| 141 |
+
return np.load(path)
|
| 142 |
+
return np.ones(self.kernel.dim, dtype=np.int8)
|
| 143 |
+
|
| 144 |
+
def _identity_coherence(
|
| 145 |
+
self, identity_vec: np.ndarray, capabilities: list
|
| 146 |
+
) -> float:
|
| 147 |
+
"""
|
| 148 |
+
How aligned are current capabilities with the system's identity?
|
| 149 |
+
High coherence = acting true to itself.
|
| 150 |
+
"""
|
| 151 |
+
if not capabilities:
|
| 152 |
+
return 0.5
|
| 153 |
+
sims = []
|
| 154 |
+
for pattern, _ in capabilities[:5]:
|
| 155 |
+
cap_vec = self.kernel.vectorize_tokens(
|
| 156 |
+
pattern.split("_"), positional=False
|
| 157 |
+
)
|
| 158 |
+
sims.append(self.kernel.similarity(identity_vec, cap_vec))
|
| 159 |
+
return float(np.mean(sims))
|
| 160 |
+
|
| 161 |
+
def _growth_index(self) -> float:
|
| 162 |
+
"""
|
| 163 |
+
Single metric for overall growth.
|
| 164 |
+
Combines: total patterns, avg weight, memory count.
|
| 165 |
+
"""
|
| 166 |
+
weights = self.resonance.weights
|
| 167 |
+
if not weights:
|
| 168 |
+
return 0.0
|
| 169 |
+
avg_w = float(np.mean(list(weights.values())))
|
| 170 |
+
n = len(weights)
|
| 171 |
+
mem = len(self.hippocampus.all_slots())
|
| 172 |
+
# Normalised growth index
|
| 173 |
+
return round(float(np.log1p(n) * avg_w + np.log1p(mem) * 0.1), 4)
|
| 174 |
+
|
| 175 |
+
def growth_trajectory(self) -> dict:
|
| 176 |
+
"""How has the system grown over time?"""
|
| 177 |
+
if len(self._snapshots) < 2:
|
| 178 |
+
return {"status": "Insufficient snapshots"}
|
| 179 |
+
first = self._snapshots[0]
|
| 180 |
+
last = self._snapshots[-1]
|
| 181 |
+
return {
|
| 182 |
+
"snapshots": len(self._snapshots),
|
| 183 |
+
"growth_index_delta": round(
|
| 184 |
+
last["growth_index"] - first["growth_index"], 4
|
| 185 |
+
),
|
| 186 |
+
"capability_delta": last["capabilities"] - first["capabilities"],
|
| 187 |
+
"limitation_delta": last["limitations"] - first["limitations"],
|
| 188 |
+
"time_span_hours": round(
|
| 189 |
+
(last["timestamp"] - first["timestamp"]) / 3600, 2
|
| 190 |
+
),
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
def report(self) -> dict:
|
| 194 |
+
assessment = self.assess()
|
| 195 |
+
return {
|
| 196 |
+
"age_hours": assessment["age_hours"],
|
| 197 |
+
"growth_index": assessment["growth_index"],
|
| 198 |
+
"capabilities": len(assessment["capabilities"]),
|
| 199 |
+
"limitations": len(assessment["limitations"]),
|
| 200 |
+
"next_boundary": assessment["next_boundary"],
|
| 201 |
+
"identity_coherence": assessment["identity_coherence"],
|
| 202 |
+
"trajectory": self.growth_trajectory(),
|
| 203 |
+
}
|
src/cognitive/reasoning_engine.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from ..dream_engine.helix_memory import HelixMemory
|
| 2 |
+
import numpy as np
|
| 3 |
+
|
| 4 |
+
class ReasoningEngine:
|
| 5 |
+
MODE_MAP = {
|
| 6 |
+
"question": "EXECUTION",
|
| 7 |
+
"instruction": "RECOVERY",
|
| 8 |
+
"explanation": "ANALYTICAL",
|
| 9 |
+
"unknown": "EXPLORATORY",
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
def __init__(self, helix_path):
|
| 13 |
+
self.helix = HelixMemory(helix_path)
|
| 14 |
+
|
| 15 |
+
def select_mode(self, hv: np.ndarray) -> str:
|
| 16 |
+
prototypes = self.helix.retrieve(hv, top_k=1)
|
| 17 |
+
if not prototypes:
|
| 18 |
+
return "EXPLORATORY"
|
| 19 |
+
proto, meta = prototypes[0]
|
| 20 |
+
label = meta.get("label", "unknown")
|
| 21 |
+
return self.MODE_MAP.get(label, "EXPLORATORY")
|
src/dream_engine/helix_memory.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import pickle
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
from typing import List, Tuple, Dict, Optional
|
| 5 |
+
|
| 6 |
+
class HelixMemory:
|
| 7 |
+
def __init__(self, storage_path: Path):
|
| 8 |
+
self.storage_path = storage_path
|
| 9 |
+
self._load()
|
| 10 |
+
|
| 11 |
+
def _load(self) -> None:
|
| 12 |
+
if self.storage_path.exists():
|
| 13 |
+
with self.storage_path.open("rb") as f:
|
| 14 |
+
self.entries: List[Tuple[int, np.ndarray, int, dict]] = pickle.load(f)
|
| 15 |
+
else:
|
| 16 |
+
self.entries = []
|
| 17 |
+
|
| 18 |
+
def _save(self) -> None:
|
| 19 |
+
self.storage_path.parent.mkdir(parents=True, exist_ok=True)
|
| 20 |
+
with self.storage_path.open("wb") as f:
|
| 21 |
+
pickle.dump(self.entries, f)
|
| 22 |
+
|
| 23 |
+
def add(self, hv: np.ndarray, meta: Optional[dict] = None) -> None:
|
| 24 |
+
meta = meta or {}
|
| 25 |
+
for i, (code, proto, cnt, old_meta) in enumerate(self.entries):
|
| 26 |
+
sim = np.mean(hv == proto)
|
| 27 |
+
if sim > 0.85:
|
| 28 |
+
merged = {**old_meta, **meta}
|
| 29 |
+
self.entries[i] = (code, proto, cnt + 1, merged)
|
| 30 |
+
self._save()
|
| 31 |
+
return
|
| 32 |
+
|
| 33 |
+
new_code = len(self.entries)
|
| 34 |
+
self.entries.append((new_code, hv.copy(), 1, meta))
|
| 35 |
+
self._save()
|
| 36 |
+
|
| 37 |
+
def retrieve(self, hv: np.ndarray, top_k: int = 3) -> List[Tuple[np.ndarray, dict]]:
|
| 38 |
+
sims = [(np.mean(hv == proto), proto, meta) for _, proto, _, meta in self.entries]
|
| 39 |
+
sims.sort(key=lambda x: x[0], reverse=True)
|
| 40 |
+
return [(proto, meta) for _, proto, meta in sims[:top_k]]
|
| 41 |
+
|
| 42 |
+
def reconstruct(self, code_id: int) -> np.ndarray:
|
| 43 |
+
for cid, proto, _, _ in self.entries:
|
| 44 |
+
if cid == code_id:
|
| 45 |
+
return proto.copy()
|
| 46 |
+
raise KeyError(f"Helix code {code_id} not found")
|
| 47 |
+
|
| 48 |
+
def most_uncertain(self) -> Tuple[int, np.ndarray]:
|
| 49 |
+
if not self.entries:
|
| 50 |
+
raise RuntimeError("HelixMemory empty")
|
| 51 |
+
entry = min(self.entries, key=lambda e: e[2])
|
| 52 |
+
return entry[0], entry[1]
|
src/vitalis_ide/cli/main.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
import sys
|
| 3 |
+
import time
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
|
| 6 |
+
from audio_ear.recorder import record_to_wav
|
| 7 |
+
from audio_ear.feature_extractor import extract_features
|
| 8 |
+
from hdc_encoder.encoder import encode
|
| 9 |
+
from dream_engine.consolidator import DreamEngine
|
| 10 |
+
from dream_engine.helix_memory import HelixMemory
|
| 11 |
+
|
| 12 |
+
def log_success(intent: str, details: dict) -> None:
|
| 13 |
+
from cognition.mind import VitalisMind
|
| 14 |
+
mind = VitalisMind()
|
| 15 |
+
mind.ledger.append({"type": "success", "intent": intent, "details": details})
|
| 16 |
+
|
| 17 |
+
def log_failure(intent: str, error: str) -> None:
|
| 18 |
+
from cognition.mind import VitalisMind
|
| 19 |
+
mind = VitalisMind()
|
| 20 |
+
mind.ledger.append({"type": "failure", "intent": intent, "error": error})
|
| 21 |
+
|
| 22 |
+
def cmd_listen(args: argparse.Namespace) -> None:
|
| 23 |
+
out_path = Path(args.output or f"/tmp/live_{int(time.time())}.wav")
|
| 24 |
+
print(f"🔊 Recording {args.duration}s -> {out_path}")
|
| 25 |
+
record_to_wav(duration_sec=args.duration, out_path=out_path)
|
| 26 |
+
mfcc, prosody = extract_features(out_path)
|
| 27 |
+
hv = encode(mfcc, prosody)
|
| 28 |
+
|
| 29 |
+
helix_path = Path.home() / ".vitalis_workspace" / "helix_memory.pkl"
|
| 30 |
+
helix = HelixMemory(helix_path)
|
| 31 |
+
dreamer = DreamEngine(helix)
|
| 32 |
+
dreamer.ingest(hv, meta={"source": str(out_path), "prosody": prosody})
|
| 33 |
+
|
| 34 |
+
log_success("listen_live", {"file": str(out_path)})
|
| 35 |
+
print("✅ Ingested into DreamEngine.")
|
| 36 |
+
|
| 37 |
+
def cmd_dream(args: argparse.Namespace) -> None:
|
| 38 |
+
helix_path = Path.home() / ".vitalis_workspace" / "helix_memory.pkl"
|
| 39 |
+
helix = HelixMemory(helix_path)
|
| 40 |
+
dreamer = DreamEngine(helix)
|
| 41 |
+
dreamer.dream(force=True)
|
| 42 |
+
print("💤 Consolidation complete.")
|
| 43 |
+
|
| 44 |
+
def build_parser() -> argparse.ArgumentParser:
|
| 45 |
+
parser = argparse.ArgumentParser(prog="vitalis")
|
| 46 |
+
sub = parser.add_subparsers(dest="command", required=True)
|
| 47 |
+
|
| 48 |
+
p_listen = sub.add_parser("listen")
|
| 49 |
+
p_listen.add_argument("-d", "--duration", type=int, default=10)
|
| 50 |
+
p_listen.add_argument("-o", "--output", type=str)
|
| 51 |
+
p_listen.set_defaults(func=cmd_listen)
|
| 52 |
+
|
| 53 |
+
p_dream = sub.add_parser("dream")
|
| 54 |
+
p_dream.set_defaults(func=cmd_dream)
|
| 55 |
+
|
| 56 |
+
return parser
|
| 57 |
+
|
| 58 |
+
def main(argv: list | None = None) -> None:
|
| 59 |
+
parser = build_parser()
|
| 60 |
+
args = parser.parse_args(argv)
|
| 61 |
+
try: args.func(args)
|
| 62 |
+
except Exception as exc:
|
| 63 |
+
log_failure(intent=args.command, error=str(exc))
|
| 64 |
+
print(f"❌ Failed: {exc}", file=sys.stderr)
|
| 65 |
+
sys.exit(1)
|
| 66 |
+
|
| 67 |
+
if __name__ == "__main__":
|
| 68 |
+
main()
|
tests/test_encoder.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
from src.hdc_encoder.encoder import encode, DIM
|
| 3 |
+
|
| 4 |
+
class TestHDCEncoder:
|
| 5 |
+
def setup_method(self):
|
| 6 |
+
"""Initialize base parameters before each test to guarantee state isolation."""
|
| 7 |
+
self.prosody = {"pitch": 140.0, "energy": 0.25, "tempo": 115.0, "pause_ratio": 0.15}
|
| 8 |
+
self.frames = 40
|
| 9 |
+
self.mfcc = np.random.rand(13, self.frames).astype(np.float32)
|
| 10 |
+
|
| 11 |
+
def test_encoder_dimensionality(self):
|
| 12 |
+
"""Verify the output vector matches the strict dimensional parameters."""
|
| 13 |
+
hv = encode(self.mfcc, self.prosody)
|
| 14 |
+
|
| 15 |
+
assert hv.shape == (DIM,), f"Dimensionality failure: Expected {DIM}, got {hv.shape[0]}"
|
| 16 |
+
assert hv.dtype == np.uint8, f"Type architecture failure: Expected uint8, got {hv.dtype}"
|
| 17 |
+
|
| 18 |
+
def test_temporal_sequence_asymmetry(self):
|
| 19 |
+
"""
|
| 20 |
+
Verify that reversing the audio sequence produces a fundamentally different
|
| 21 |
+
hypervector. This proves the encoder captures the temporal direction of speech.
|
| 22 |
+
"""
|
| 23 |
+
mfcc_reversed = self.mfcc[:, ::-1]
|
| 24 |
+
|
| 25 |
+
hv_forward = encode(self.mfcc, self.prosody)
|
| 26 |
+
hv_reversed = encode(mfcc_reversed, self.prosody)
|
| 27 |
+
|
| 28 |
+
similarity = np.mean(hv_forward == hv_reversed)
|
| 29 |
+
|
| 30 |
+
# In an orthogonal 10k dimensional space, random vectors share ~50% similarity.
|
| 31 |
+
# We enforce a strict threshold to ensure the temporal shift breaks vector alignment.
|
| 32 |
+
assert similarity < 0.60, f"Commutativity failure: Temporal sequence not isolated. Similarity: {similarity}"
|
| 33 |
+
|
| 34 |
+
def test_prosody_modulation(self):
|
| 35 |
+
"""Verify that altering emotional/prosodic intent alters the final memory vector."""
|
| 36 |
+
angry_prosody = {"pitch": 240.0, "energy": 0.85, "tempo": 160.0, "pause_ratio": 0.05}
|
| 37 |
+
|
| 38 |
+
hv_base = encode(self.mfcc, self.prosody)
|
| 39 |
+
hv_angry = encode(self.mfcc, angry_prosody)
|
| 40 |
+
|
| 41 |
+
similarity = np.mean(hv_base == hv_angry)
|
| 42 |
+
|
| 43 |
+
assert similarity < 0.95, "Modulation failure: Prosody shifts did not impact the spatial vector."
|