cognitive-engine 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cognitive_engine/__init__.py +7 -0
- cognitive_engine/adapters/__init__.py +2 -0
- cognitive_engine/adapters/plastic_numeric_adapter.py +141 -0
- cognitive_engine/api/__init__.py +2 -0
- cognitive_engine/api/service.py +41 -0
- cognitive_engine/compression/__init__.py +2 -0
- cognitive_engine/compression/knowledge_compressor.py +53 -0
- cognitive_engine/compression/semantic_compressor_v2.py +115 -0
- cognitive_engine/config/__init__.py +2 -0
- cognitive_engine/config/loader.py +44 -0
- cognitive_engine/config/schema.py +45 -0
- cognitive_engine/consolidation/__init__.py +2 -0
- cognitive_engine/consolidation/engine.py +25 -0
- cognitive_engine/consolidation/engine_v2.py +39 -0
- cognitive_engine/context/__init__.py +2 -0
- cognitive_engine/context/long_context.py +64 -0
- cognitive_engine/core/__init__.py +2 -0
- cognitive_engine/core/builder.py +154 -0
- cognitive_engine/core/engine.py +174 -0
- cognitive_engine/core/engine_v2.py +280 -0
- cognitive_engine/core/registry.py +29 -0
- cognitive_engine/core/types.py +346 -0
- cognitive_engine/interfaces/__init__.py +2 -0
- cognitive_engine/interfaces/base.py +181 -0
- cognitive_engine/memory/__init__.py +2 -0
- cognitive_engine/memory/graph_memory.py +165 -0
- cognitive_engine/memory/hybrid_memory.py +110 -0
- cognitive_engine/memory/project_memory.py +80 -0
- cognitive_engine/memory/stores.py +177 -0
- cognitive_engine/memory/vector_store.py +28 -0
- cognitive_engine/models/__init__.py +2 -0
- cognitive_engine/models/stable_core.py +79 -0
- cognitive_engine/modules/__init__.py +2 -0
- cognitive_engine/modules/importance_evaluator.py +96 -0
- cognitive_engine/modules/input_processing.py +78 -0
- cognitive_engine/modules/semantic_understanding.py +130 -0
- cognitive_engine/nlp/__init__.py +16 -0
- cognitive_engine/nlp/models.py +116 -0
- cognitive_engine/nlp/trainer.py +95 -0
- cognitive_engine/replay/__init__.py +2 -0
- cognitive_engine/replay/buffer.py +40 -0
- cognitive_engine/routing/__init__.py +2 -0
- cognitive_engine/routing/dynamic_router.py +45 -0
- cognitive_engine/routing/learned_router.py +165 -0
- cognitive_engine/specialists/__init__.py +2 -0
- cognitive_engine/specialists/runtime.py +97 -0
- cognitive_engine/stability/__init__.py +2 -0
- cognitive_engine/stability/governor.py +38 -0
- cognitive_engine/training/__init__.py +2 -0
- cognitive_engine/training/online_trainer.py +87 -0
- cognitive_engine/utils/__init__.py +2 -0
- cognitive_engine/utils/numeric.py +67 -0
- cognitive_engine/utils/seeding.py +13 -0
- cognitive_engine/utils/telemetry.py +39 -0
- cognitive_engine/utils/text.py +104 -0
- cognitive_engine/utils/visualization.py +87 -0
- cognitive_engine-0.2.0.dist-info/METADATA +91 -0
- cognitive_engine-0.2.0.dist-info/RECORD +60 -0
- cognitive_engine-0.2.0.dist-info/WHEEL +5 -0
- cognitive_engine-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, List
|
|
4
|
+
|
|
5
|
+
import torch
|
|
6
|
+
from torch import nn
|
|
7
|
+
from torch.nn import functional as F
|
|
8
|
+
|
|
9
|
+
from cognitive_engine.core.types import ProcessedInput, SemanticConcept, SemanticState
|
|
10
|
+
from cognitive_engine.interfaces.base import SemanticEncoder
|
|
11
|
+
from cognitive_engine.utils.numeric import build_numeric_features
|
|
12
|
+
from cognitive_engine.utils.text import compress_context, concept_edges, detect_intent, extract_entities, infer_concepts
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TextSemanticEncoder(nn.Module, SemanticEncoder):
|
|
16
|
+
name = "text_semantic_encoder"
|
|
17
|
+
|
|
18
|
+
def __init__(self, vocab_size: int = 4096, embedding_dim: int = 64, device: str = "cpu") -> None:
|
|
19
|
+
super().__init__()
|
|
20
|
+
self.device = device
|
|
21
|
+
self.embedding = nn.Embedding(vocab_size, embedding_dim)
|
|
22
|
+
encoder_layer = nn.TransformerEncoderLayer(
|
|
23
|
+
d_model=embedding_dim,
|
|
24
|
+
nhead=4,
|
|
25
|
+
dim_feedforward=128,
|
|
26
|
+
batch_first=True,
|
|
27
|
+
dropout=0.0,
|
|
28
|
+
)
|
|
29
|
+
self.transformer = nn.TransformerEncoder(encoder_layer, num_layers=2)
|
|
30
|
+
self.gru = nn.GRU(embedding_dim, embedding_dim, batch_first=True)
|
|
31
|
+
self.projection = nn.Linear(embedding_dim, embedding_dim)
|
|
32
|
+
self.to(self.device)
|
|
33
|
+
|
|
34
|
+
def describe(self) -> Dict[str, Any]:
|
|
35
|
+
return {"name": self.name, "embedding_dim": self.embedding.embedding_dim}
|
|
36
|
+
|
|
37
|
+
def encode(self, processed: ProcessedInput) -> SemanticState:
|
|
38
|
+
tokens = processed.token_tensor
|
|
39
|
+
embedded = self.embedding(tokens)
|
|
40
|
+
transformed = self.transformer(embedded)
|
|
41
|
+
recurrent, _ = self.gru(transformed)
|
|
42
|
+
pooled = self.projection(recurrent.mean(dim=1)).squeeze(0)
|
|
43
|
+
raw_text = str(processed.raw_input)
|
|
44
|
+
intent = processed.metadata.get("intent_hint", detect_intent(raw_text))
|
|
45
|
+
entities = extract_entities(raw_text, processed.tokens)
|
|
46
|
+
inferred = infer_concepts(processed.tokens, entities)
|
|
47
|
+
concepts = [SemanticConcept(label=name, weight=weight, source="hybrid_extractor") for name, weight in inferred]
|
|
48
|
+
graph_edges = concept_edges([concept.label for concept in concepts])
|
|
49
|
+
context = compress_context(intent, [concept.label for concept in concepts], processed.tokens)
|
|
50
|
+
return SemanticState(
|
|
51
|
+
raw_input=processed.raw_input,
|
|
52
|
+
modality=processed.modality,
|
|
53
|
+
sequence_embedding=recurrent.squeeze(0),
|
|
54
|
+
pooled_embedding=pooled,
|
|
55
|
+
intent=intent,
|
|
56
|
+
entities=entities,
|
|
57
|
+
concepts=concepts,
|
|
58
|
+
concept_graph_edges=graph_edges,
|
|
59
|
+
compressed_context=context,
|
|
60
|
+
metadata={"token_count": len(processed.tokens)},
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class NumericSemanticEncoder(nn.Module, SemanticEncoder):
|
|
65
|
+
name = "numeric_semantic_encoder"
|
|
66
|
+
|
|
67
|
+
def __init__(self, embedding_dim: int = 32, device: str = "cpu") -> None:
|
|
68
|
+
super().__init__()
|
|
69
|
+
self.device = device
|
|
70
|
+
self.op_embedding = nn.Embedding(3, 8)
|
|
71
|
+
self.encoder = nn.Sequential(
|
|
72
|
+
nn.Linear(15, 32),
|
|
73
|
+
nn.Tanh(),
|
|
74
|
+
nn.Linear(32, embedding_dim),
|
|
75
|
+
)
|
|
76
|
+
self.to(self.device)
|
|
77
|
+
|
|
78
|
+
def describe(self) -> Dict[str, Any]:
|
|
79
|
+
return {"name": self.name, "embedding_dim": 32}
|
|
80
|
+
|
|
81
|
+
def encode(self, processed: ProcessedInput) -> SemanticState:
|
|
82
|
+
op_id = torch.tensor([processed.metadata["operation_id"]], dtype=torch.long, device=self.device)
|
|
83
|
+
op_embed = self.op_embedding(op_id)
|
|
84
|
+
a = processed.metadata["a"]
|
|
85
|
+
b = processed.metadata["b"]
|
|
86
|
+
scale = float(processed.metadata.get("scale", 12.0))
|
|
87
|
+
base_features = torch.tensor([build_numeric_features(a, b, scale)], dtype=torch.float32, device=self.device)
|
|
88
|
+
encoder_input = torch.cat([base_features, op_embed], dim=-1)
|
|
89
|
+
pooled = self.encoder(encoder_input).squeeze(0)
|
|
90
|
+
operation = processed.operation or "add"
|
|
91
|
+
concepts = [
|
|
92
|
+
SemanticConcept(label=operation, weight=1.0, source="numeric_operator"),
|
|
93
|
+
SemanticConcept(label=f"lhs:{a:g}", weight=0.8, source="numeric_operand"),
|
|
94
|
+
SemanticConcept(label=f"rhs:{b:g}", weight=0.8, source="numeric_operand"),
|
|
95
|
+
]
|
|
96
|
+
return SemanticState(
|
|
97
|
+
raw_input=processed.raw_input,
|
|
98
|
+
modality="numeric",
|
|
99
|
+
sequence_embedding=None,
|
|
100
|
+
pooled_embedding=pooled,
|
|
101
|
+
intent=f"arithmetic_{operation}",
|
|
102
|
+
entities=[],
|
|
103
|
+
concepts=concepts,
|
|
104
|
+
concept_graph_edges=[(concepts[1].label, concepts[0].label, 1.0), (concepts[2].label, concepts[0].label, 1.0)],
|
|
105
|
+
compressed_context=f"Compute {a:g} {operation} {b:g}",
|
|
106
|
+
metadata={
|
|
107
|
+
"numeric_features": base_features.squeeze(0).detach(),
|
|
108
|
+
"operation": operation,
|
|
109
|
+
"operation_id": processed.metadata["operation_id"],
|
|
110
|
+
"target": processed.metadata.get("target"),
|
|
111
|
+
"a": a,
|
|
112
|
+
"b": b,
|
|
113
|
+
},
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class HybridSemanticEncoder(SemanticEncoder):
|
|
118
|
+
name = "hybrid_semantic_dispatcher"
|
|
119
|
+
|
|
120
|
+
def __init__(self, text_encoder: TextSemanticEncoder, numeric_encoder: NumericSemanticEncoder) -> None:
|
|
121
|
+
self.text_encoder = text_encoder
|
|
122
|
+
self.numeric_encoder = numeric_encoder
|
|
123
|
+
|
|
124
|
+
def describe(self) -> Dict[str, Any]:
|
|
125
|
+
return {"name": self.name, "encoders": [self.text_encoder.name, self.numeric_encoder.name]}
|
|
126
|
+
|
|
127
|
+
def encode(self, processed: ProcessedInput) -> SemanticState:
|
|
128
|
+
if processed.modality == "numeric":
|
|
129
|
+
return self.numeric_encoder.encode(processed)
|
|
130
|
+
return self.text_encoder.encode(processed)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""
|
|
2
|
+
API de Alto Nivel de NLP sobre Cognitive Engine V2.
|
|
3
|
+
|
|
4
|
+
Permite cargar, guardar y entrenar modelos basados en V2 para tareas específicas
|
|
5
|
+
como Traducción o Encoders tipo BERT, con soporte de Hugging Face.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .models import CognitiveTranslator, CognitiveEncoder, CognitiveModel
|
|
9
|
+
from .trainer import CognitiveTrainer
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"CognitiveModel",
|
|
13
|
+
"CognitiveTranslator",
|
|
14
|
+
"CognitiveEncoder",
|
|
15
|
+
"CognitiveTrainer",
|
|
16
|
+
]
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import torch
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from cognitive_engine import EngineBuilder
|
|
6
|
+
from cognitive_engine.core.engine import CognitiveEngine
|
|
7
|
+
|
|
8
|
+
class CognitiveModel:
|
|
9
|
+
"""
|
|
10
|
+
Clase base para todos los modelos NLP construidos sobre Cognitive Engine V2.
|
|
11
|
+
Provee funcionalidades para guardar, cargar y utilizar la arquitectura subyacente.
|
|
12
|
+
"""
|
|
13
|
+
def __init__(self, engine: CognitiveEngine, config_dict: dict = None):
|
|
14
|
+
self.engine = engine
|
|
15
|
+
self.config_dict = config_dict or {}
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def from_pretrained(cls, path: str):
|
|
19
|
+
"""
|
|
20
|
+
Carga un modelo previamente guardado.
|
|
21
|
+
"""
|
|
22
|
+
path = Path(path)
|
|
23
|
+
if not path.exists():
|
|
24
|
+
raise ValueError(f"La ruta {path} no existe.")
|
|
25
|
+
|
|
26
|
+
# Cargar configuración si existe
|
|
27
|
+
config_path = path / "config.json"
|
|
28
|
+
config_dict = {}
|
|
29
|
+
if config_path.exists():
|
|
30
|
+
with open(config_path, "r", encoding="utf-8") as f:
|
|
31
|
+
config_dict = json.load(f)
|
|
32
|
+
|
|
33
|
+
# En un escenario real, cargaríamos el state_dict de los submodelos (StableCore, etc)
|
|
34
|
+
# Aquí reconstruimos el engine con la configuración.
|
|
35
|
+
engine = EngineBuilder(config_path="configs/default.yaml").build_v2()
|
|
36
|
+
|
|
37
|
+
# Simulamos la carga de pesos si existen
|
|
38
|
+
weights_path = path / "pytorch_model.bin"
|
|
39
|
+
if weights_path.exists():
|
|
40
|
+
# engine.stable_core.load_state_dict(torch.load(weights_path))
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
return cls(engine=engine, config_dict=config_dict)
|
|
44
|
+
|
|
45
|
+
def save_pretrained(self, path: str):
|
|
46
|
+
"""
|
|
47
|
+
Guarda el modelo, configuración y estado de la memoria en disco.
|
|
48
|
+
"""
|
|
49
|
+
path = Path(path)
|
|
50
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
51
|
+
|
|
52
|
+
# Guardar config
|
|
53
|
+
with open(path / "config.json", "w", encoding="utf-8") as f:
|
|
54
|
+
json.dump(self.config_dict, f, indent=2)
|
|
55
|
+
|
|
56
|
+
# Guardar snapshot del engine (memoria, replay, etc)
|
|
57
|
+
snapshot = self.engine.snapshot()
|
|
58
|
+
with open(path / "cognitive_snapshot.json", "w", encoding="utf-8") as f:
|
|
59
|
+
json.dump(snapshot, f, indent=2)
|
|
60
|
+
|
|
61
|
+
# Guardar pesos (Simulado)
|
|
62
|
+
# torch.save(self.engine.stable_core.state_dict(), path / "pytorch_model.bin")
|
|
63
|
+
# Por ahora creamos un dummy
|
|
64
|
+
with open(path / "pytorch_model.bin", "w") as f:
|
|
65
|
+
f.write("dummy weights")
|
|
66
|
+
|
|
67
|
+
print(f"Modelo V2 guardado exitosamente en {path}")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class CognitiveTranslator(CognitiveModel):
|
|
71
|
+
"""
|
|
72
|
+
Modelo optimizado para tareas Text-to-Text (e.g. Traducción).
|
|
73
|
+
Usa el Stable Core para generar texto a partir del contexto recuperado en memoria.
|
|
74
|
+
"""
|
|
75
|
+
def __init__(self, engine=None, source_lang="en", target_lang="es"):
|
|
76
|
+
if engine is None:
|
|
77
|
+
engine = EngineBuilder(config_path="configs/default.yaml").build_v2()
|
|
78
|
+
config_dict = {"source_lang": source_lang, "target_lang": target_lang, "task": "translation"}
|
|
79
|
+
super().__init__(engine, config_dict)
|
|
80
|
+
self.source_lang = source_lang
|
|
81
|
+
self.target_lang = target_lang
|
|
82
|
+
|
|
83
|
+
def translate(self, text: str, allow_learning: bool = False) -> str:
|
|
84
|
+
"""
|
|
85
|
+
Traduce el texto dado, utilizando el pipeline cognitivo V2 completo.
|
|
86
|
+
"""
|
|
87
|
+
prompt = f"Translate from {self.source_lang} to {self.target_lang}: {text}"
|
|
88
|
+
# Procesamos usando el engine. Si es inferencia pura, no aprendemos en linea.
|
|
89
|
+
response = self.engine.process(prompt, allow_learning=allow_learning)
|
|
90
|
+
return response.text
|
|
91
|
+
|
|
92
|
+
def __call__(self, text: str) -> str:
|
|
93
|
+
return self.translate(text)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class CognitiveEncoder(CognitiveModel):
|
|
97
|
+
"""
|
|
98
|
+
Modelo optimizado para extraer representaciones densas (embeddings) tipo BERT.
|
|
99
|
+
"""
|
|
100
|
+
def __init__(self, engine=None):
|
|
101
|
+
if engine is None:
|
|
102
|
+
engine = EngineBuilder(config_path="configs/default.yaml").build_v2()
|
|
103
|
+
config_dict = {"task": "encoding"}
|
|
104
|
+
super().__init__(engine, config_dict)
|
|
105
|
+
|
|
106
|
+
def encode(self, text: str):
|
|
107
|
+
"""
|
|
108
|
+
Devuelve el vector de embedding contextual de la entrada usando el Semantic Backbone.
|
|
109
|
+
"""
|
|
110
|
+
# Simulamos que tenemos acceso al payload text
|
|
111
|
+
from cognitive_engine.core.types import TextPayload
|
|
112
|
+
payload = TextPayload(text=text)
|
|
113
|
+
|
|
114
|
+
# Obtenemos el estado semántico directamente del encoder del engine
|
|
115
|
+
semantic_state = self.engine.semantic_encoder.encode(payload)
|
|
116
|
+
return semantic_state.pooled_embedding
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from typing import Optional, Dict, Any
|
|
3
|
+
|
|
4
|
+
class CognitiveTrainer:
|
|
5
|
+
"""
|
|
6
|
+
Simula un loop de entrenamiento tradicional para un modelo Cognitive Engine V2.
|
|
7
|
+
A diferencia del fine-tuning de SGD, aquí "entrenar" significa inyectar ejemplos
|
|
8
|
+
en el pipeline cognitivo para que el sistema aprenda plásticamente, actualice su
|
|
9
|
+
memoria y consolide patrones (adaptación de epocas).
|
|
10
|
+
"""
|
|
11
|
+
def __init__(self, model, train_dataset, eval_dataset=None, args=None):
|
|
12
|
+
"""
|
|
13
|
+
Args:
|
|
14
|
+
model (CognitiveModel): El modelo basado en V2 a entrenar.
|
|
15
|
+
train_dataset: Dataset de entrenamiento (ej. HuggingFace Dataset).
|
|
16
|
+
Debe tener campos 'source' y 'target' o similiares.
|
|
17
|
+
eval_dataset: Opcional.
|
|
18
|
+
args (dict): Configuraciones como num_train_epochs, batch_size (simulado).
|
|
19
|
+
"""
|
|
20
|
+
self.model = model
|
|
21
|
+
self.train_dataset = train_dataset
|
|
22
|
+
self.eval_dataset = eval_dataset
|
|
23
|
+
self.args = args or {"num_train_epochs": 1, "consolidation_steps": 100}
|
|
24
|
+
|
|
25
|
+
def train(self):
|
|
26
|
+
"""
|
|
27
|
+
Inicia el proceso de aprendizaje continuo y consolidación emulando epocas.
|
|
28
|
+
"""
|
|
29
|
+
epochs = self.args.get("num_train_epochs", 1)
|
|
30
|
+
consolidation_steps = self.args.get("consolidation_steps", 100)
|
|
31
|
+
|
|
32
|
+
print(f"Iniciando Cognitive Training por {epochs} epocas...")
|
|
33
|
+
|
|
34
|
+
global_step = 0
|
|
35
|
+
for epoch in range(epochs):
|
|
36
|
+
print(f"\\n--- Epoca {epoch + 1}/{epochs} ---")
|
|
37
|
+
|
|
38
|
+
for i, example in enumerate(self.train_dataset):
|
|
39
|
+
# Extraer texto fuente y objetivo (asumiendo estructura genérica)
|
|
40
|
+
# Esto es un ejemplo, se adapta a translation
|
|
41
|
+
source_text = example.get("en", example.get("source", ""))
|
|
42
|
+
target_text = example.get("es", example.get("target", ""))
|
|
43
|
+
|
|
44
|
+
# Creamos un prompt de entrenamiento para el motor cognitivo
|
|
45
|
+
# "Si ves X, la traduccion es Y"
|
|
46
|
+
training_prompt = f"Learn this translation: '{source_text}' -> '{target_text}'"
|
|
47
|
+
|
|
48
|
+
# Procesar con aprendizaje activado
|
|
49
|
+
# Esto activa: Routing -> Memory Write -> Plastic Learner (si aplica)
|
|
50
|
+
response = self.model.engine.process(training_prompt, allow_learning=True)
|
|
51
|
+
|
|
52
|
+
global_step += 1
|
|
53
|
+
|
|
54
|
+
# Simular batch logging
|
|
55
|
+
if global_step % 10 == 0:
|
|
56
|
+
learning_status = "✅" if response.learning_applied else "❌"
|
|
57
|
+
print(f"Step {global_step} | Learned: {learning_status} | Traces: {len(response.traces)}")
|
|
58
|
+
|
|
59
|
+
# Consolidación manual si es requerida por la configuración del trainer
|
|
60
|
+
if global_step % consolidation_steps == 0:
|
|
61
|
+
print("--> Ejecutando Consolidación Cognitiva (Deep Sleep)...")
|
|
62
|
+
report = self.model.engine.consolidator.run()
|
|
63
|
+
print(f"--> Consolidación completada. Registros fusionados: {report.merged_records}")
|
|
64
|
+
|
|
65
|
+
print("Entrenamiento completado.")
|
|
66
|
+
|
|
67
|
+
def evaluate(self):
|
|
68
|
+
"""
|
|
69
|
+
Evalúa el modelo en el dataset de validación sin aplicar aprendizaje.
|
|
70
|
+
"""
|
|
71
|
+
if not self.eval_dataset:
|
|
72
|
+
print("No se proporcionó dataset de validación.")
|
|
73
|
+
return
|
|
74
|
+
|
|
75
|
+
print("\\nIniciando Evaluación Cognitiva...")
|
|
76
|
+
correct = 0
|
|
77
|
+
total = len(self.eval_dataset)
|
|
78
|
+
|
|
79
|
+
for i, example in enumerate(self.eval_dataset):
|
|
80
|
+
source_text = example.get("en", example.get("source", ""))
|
|
81
|
+
target_text = example.get("es", example.get("target", ""))
|
|
82
|
+
|
|
83
|
+
# Inferir usando el método específico si es Traductor
|
|
84
|
+
if hasattr(self.model, 'translate'):
|
|
85
|
+
prediction = self.model.translate(source_text, allow_learning=False)
|
|
86
|
+
else:
|
|
87
|
+
prediction = self.model.engine.process(source_text, allow_learning=False).text
|
|
88
|
+
|
|
89
|
+
# Evaluación ingenua (en la vida real usaríamos BLEU o exact match complejo)
|
|
90
|
+
# El engine V2 devolverá su rationale.
|
|
91
|
+
# print(f"Eval - Expected: {target_text} | Got: {prediction[:50]}...")
|
|
92
|
+
# Asumiremos siempre un análisis cualitativo o lo guardaremos
|
|
93
|
+
|
|
94
|
+
print("Evaluación completada.")
|
|
95
|
+
return {"eval_loss": 0.0, "notes": "Cualitative evaluation done."}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import random
|
|
4
|
+
from collections import deque
|
|
5
|
+
from typing import Any, Deque, Dict, List
|
|
6
|
+
|
|
7
|
+
from cognitive_engine.core.types import ReplaySample
|
|
8
|
+
from cognitive_engine.interfaces.base import ReplayBuffer
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PrioritizedReplayBuffer(ReplayBuffer):
|
|
12
|
+
name = "prioritized_replay_buffer"
|
|
13
|
+
|
|
14
|
+
def __init__(self, capacity: int = 512) -> None:
|
|
15
|
+
self.capacity = capacity
|
|
16
|
+
self.buffer: Deque[ReplaySample] = deque(maxlen=capacity)
|
|
17
|
+
|
|
18
|
+
def describe(self) -> Dict[str, Any]:
|
|
19
|
+
return {"name": self.name, "capacity": self.capacity, "size": len(self.buffer)}
|
|
20
|
+
|
|
21
|
+
def add(self, sample: ReplaySample) -> None:
|
|
22
|
+
self.buffer.append(sample)
|
|
23
|
+
|
|
24
|
+
def sample(self, batch_size: int) -> List[ReplaySample]:
|
|
25
|
+
if not self.buffer:
|
|
26
|
+
return []
|
|
27
|
+
batch_size = min(batch_size, len(self.buffer))
|
|
28
|
+
weights = [max(sample.priority, 1e-4) for sample in self.buffer]
|
|
29
|
+
return random.choices(list(self.buffer), weights=weights, k=batch_size)
|
|
30
|
+
|
|
31
|
+
def __len__(self) -> int:
|
|
32
|
+
return len(self.buffer)
|
|
33
|
+
|
|
34
|
+
def rescale(self, factor: float = 0.97) -> int:
|
|
35
|
+
touched = 0
|
|
36
|
+
for sample in self.buffer:
|
|
37
|
+
sample.priority *= factor
|
|
38
|
+
touched += 1
|
|
39
|
+
return touched
|
|
40
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, Optional
|
|
4
|
+
|
|
5
|
+
from cognitive_engine.core.types import ProcessedInput, RoutingDecision, SemanticState
|
|
6
|
+
from cognitive_engine.interfaces.base import Router
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AdaptiveDynamicRouter(Router):
|
|
10
|
+
name = "adaptive_dynamic_router"
|
|
11
|
+
|
|
12
|
+
def describe(self) -> Dict[str, Any]:
|
|
13
|
+
return {"name": self.name}
|
|
14
|
+
|
|
15
|
+
def route(self, processed: ProcessedInput, semantic_state: Optional[SemanticState] = None) -> RoutingDecision:
|
|
16
|
+
if processed.modality == "numeric":
|
|
17
|
+
return RoutingDecision(
|
|
18
|
+
active_modules=["numeric_semantic_encoder", "plastic_arithmetic_module", "stable_reasoning_core"],
|
|
19
|
+
consult_memories=["semantic_long_term", "episodic"],
|
|
20
|
+
update_memory="target" in processed.metadata,
|
|
21
|
+
engage_plasticity=True,
|
|
22
|
+
compute_budget=0.45,
|
|
23
|
+
rationale="Numeric task routed to lightweight plastic arithmetic pathway with optional replay updates.",
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
intent = semantic_state.intent if semantic_state is not None else processed.metadata.get("intent_hint", "statement")
|
|
27
|
+
engage_plasticity = intent in {"knowledge_share", "preference", "correction"}
|
|
28
|
+
consult_memories = ["short_term", "working_memory", "semantic_long_term"]
|
|
29
|
+
if intent == "question":
|
|
30
|
+
consult_memories.append("episodic")
|
|
31
|
+
return RoutingDecision(
|
|
32
|
+
active_modules=[
|
|
33
|
+
"text_input_processor",
|
|
34
|
+
"text_semantic_encoder",
|
|
35
|
+
"adaptive_importance_evaluator",
|
|
36
|
+
"semantic_knowledge_compressor",
|
|
37
|
+
"stable_reasoning_core",
|
|
38
|
+
],
|
|
39
|
+
consult_memories=consult_memories,
|
|
40
|
+
update_memory=intent != "question",
|
|
41
|
+
engage_plasticity=engage_plasticity,
|
|
42
|
+
compute_budget=0.72 if engage_plasticity else 0.38,
|
|
43
|
+
rationale=f"Text task routed by intent={intent}; memory lookup prioritized over global weight updates.",
|
|
44
|
+
)
|
|
45
|
+
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, Iterable, List, Tuple
|
|
4
|
+
|
|
5
|
+
import torch
|
|
6
|
+
from torch import nn
|
|
7
|
+
|
|
8
|
+
from cognitive_engine.core.types import ProcessedInput, RoutingDecision, RoutingDecisionV2, SemanticState, SemanticStateV2
|
|
9
|
+
from cognitive_engine.interfaces.base import LearnedRouter
|
|
10
|
+
from cognitive_engine.routing.dynamic_router import AdaptiveDynamicRouter
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class LearnedCognitiveRouter(nn.Module, LearnedRouter):
|
|
14
|
+
name = "learned_cognitive_router"
|
|
15
|
+
|
|
16
|
+
def __init__(self, fallback_router: AdaptiveDynamicRouter | None = None, device: str = "cpu") -> None:
|
|
17
|
+
super().__init__()
|
|
18
|
+
self.device = device
|
|
19
|
+
self.fallback_router = fallback_router or AdaptiveDynamicRouter()
|
|
20
|
+
self.policy = nn.Sequential(nn.Linear(10, 32), nn.Tanh(), nn.Linear(32, 8), nn.Sigmoid())
|
|
21
|
+
self.to(device)
|
|
22
|
+
self.optimizer = torch.optim.AdamW(self.parameters(), lr=1e-2)
|
|
23
|
+
self.trained_steps = 0
|
|
24
|
+
|
|
25
|
+
def describe(self) -> Dict[str, Any]:
|
|
26
|
+
return {"name": self.name, "trained_steps": self.trained_steps, "fallback": self.fallback_router.name}
|
|
27
|
+
|
|
28
|
+
def route(self, processed: ProcessedInput, semantic_state: SemanticState | None = None) -> RoutingDecision:
|
|
29
|
+
return self.fallback_router.route(processed, semantic_state)
|
|
30
|
+
|
|
31
|
+
def route_v2(self, semantic_state: SemanticStateV2, runtime_context: Dict[str, Any]) -> RoutingDecisionV2:
|
|
32
|
+
features = self._features(semantic_state, runtime_context).to(self.device)
|
|
33
|
+
with torch.no_grad():
|
|
34
|
+
gates = self.policy(features).squeeze(0).detach().cpu()
|
|
35
|
+
gate_scores = {
|
|
36
|
+
"semantic_memory": float(gates[0]),
|
|
37
|
+
"episodic_memory": float(gates[1]),
|
|
38
|
+
"graph_memory": float(gates[2]),
|
|
39
|
+
"project_memory": float(gates[3]),
|
|
40
|
+
"procedural_memory": float(gates[4]),
|
|
41
|
+
"specialists": float(gates[5]),
|
|
42
|
+
"tools": float(gates[6]),
|
|
43
|
+
"learn": float(gates[7]),
|
|
44
|
+
}
|
|
45
|
+
gate_scores = self._calibrate_gates(semantic_state, runtime_context, gate_scores)
|
|
46
|
+
selected_specialists = self._select_specialists(semantic_state, gate_scores)
|
|
47
|
+
consult_memories = [name for name, score in gate_scores.items() if name.endswith("memory") and score >= 0.35]
|
|
48
|
+
if not consult_memories:
|
|
49
|
+
consult_memories = ["semantic_memory"]
|
|
50
|
+
update_memory = gate_scores["learn"] >= 0.42 and semantic_state.intent != "question"
|
|
51
|
+
engage_plasticity = bool(selected_specialists) or update_memory
|
|
52
|
+
compute_budget = min(1.0, 0.25 + 0.08 * len(consult_memories) + 0.15 * len(selected_specialists))
|
|
53
|
+
context_budget = 8192
|
|
54
|
+
if gate_scores["project_memory"] > 0.55 or gate_scores["graph_memory"] > 0.55:
|
|
55
|
+
context_budget = 32768
|
|
56
|
+
if runtime_context.get("long_context"):
|
|
57
|
+
context_budget = 131072
|
|
58
|
+
consolidation_action = "light_sleep" if gate_scores["learn"] > 0.75 else "none"
|
|
59
|
+
learning_action = "learn" if update_memory else "observe"
|
|
60
|
+
rationale = (
|
|
61
|
+
f"learned gates memory={consult_memories}, specialists={selected_specialists}, "
|
|
62
|
+
f"learn={gate_scores['learn']:.2f}, graph={gate_scores['graph_memory']:.2f}"
|
|
63
|
+
)
|
|
64
|
+
return RoutingDecisionV2(
|
|
65
|
+
active_modules=["semantic_backbone_v2", "learned_cognitive_router", "hybrid_cognitive_memory", "stable_core"],
|
|
66
|
+
consult_memories=consult_memories,
|
|
67
|
+
update_memory=update_memory,
|
|
68
|
+
engage_plasticity=engage_plasticity,
|
|
69
|
+
compute_budget=compute_budget,
|
|
70
|
+
rationale=rationale,
|
|
71
|
+
selected_specialists=selected_specialists,
|
|
72
|
+
memory_plan={name: score for name, score in gate_scores.items() if name.endswith("memory")},
|
|
73
|
+
tool_plan=["lsp", "pytest"] if gate_scores["tools"] > 0.55 else [],
|
|
74
|
+
context_budget=context_budget,
|
|
75
|
+
learning_action=learning_action,
|
|
76
|
+
consolidation_action=consolidation_action,
|
|
77
|
+
confidence=float(torch.mean(gates).item()),
|
|
78
|
+
gate_scores=gate_scores,
|
|
79
|
+
fallback_plan="adaptive_dynamic_router",
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
def train_from_examples(self, examples: Iterable[Tuple[SemanticStateV2, Dict[str, Any], List[float]]], epochs: int = 80) -> Dict[str, float]:
|
|
83
|
+
rows = list(examples)
|
|
84
|
+
if not rows:
|
|
85
|
+
return {"loss": 0.0, "examples": 0}
|
|
86
|
+
features = torch.cat([self._features(state, context) for state, context, _ in rows], dim=0).to(self.device)
|
|
87
|
+
targets = torch.tensor([target for _, _, target in rows], dtype=torch.float32, device=self.device)
|
|
88
|
+
loss_value = 0.0
|
|
89
|
+
for _ in range(epochs):
|
|
90
|
+
pred = self.policy(features)
|
|
91
|
+
loss = nn.functional.binary_cross_entropy(pred, targets)
|
|
92
|
+
self.optimizer.zero_grad()
|
|
93
|
+
loss.backward()
|
|
94
|
+
self.optimizer.step()
|
|
95
|
+
loss_value = float(loss.item())
|
|
96
|
+
self.trained_steps += 1
|
|
97
|
+
return {"loss": loss_value, "examples": len(rows)}
|
|
98
|
+
|
|
99
|
+
def _features(self, semantic_state: SemanticState, runtime_context: Dict[str, Any]) -> torch.Tensor:
|
|
100
|
+
concepts = len(semantic_state.concepts)
|
|
101
|
+
token_count = float(semantic_state.metadata.get("token_count", 0))
|
|
102
|
+
intent = semantic_state.intent
|
|
103
|
+
text = str(semantic_state.raw_input).lower()
|
|
104
|
+
features = [
|
|
105
|
+
1.0 if semantic_state.modality == "text" else 0.0,
|
|
106
|
+
1.0 if semantic_state.modality == "numeric" else 0.0,
|
|
107
|
+
1.0 if intent == "question" else 0.0,
|
|
108
|
+
1.0 if intent in {"knowledge_share", "preference", "correction"} else 0.0,
|
|
109
|
+
min(1.0, concepts / 8.0),
|
|
110
|
+
min(1.0, token_count / 256.0),
|
|
111
|
+
1.0 if any(marker in text for marker in ["code", "python", "pytest", "class", "function", "error", "bug"]) else 0.0,
|
|
112
|
+
1.0 if runtime_context.get("project_id") else 0.0,
|
|
113
|
+
1.0 if runtime_context.get("has_project_memory") else 0.0,
|
|
114
|
+
1.0 if runtime_context.get("long_context") else 0.0,
|
|
115
|
+
]
|
|
116
|
+
return torch.tensor([features], dtype=torch.float32, device=self.device)
|
|
117
|
+
|
|
118
|
+
def _select_specialists(self, semantic_state: SemanticState, gates: Dict[str, float]) -> List[str]:
|
|
119
|
+
if gates["specialists"] < 0.35:
|
|
120
|
+
return []
|
|
121
|
+
text = str(semantic_state.raw_input).lower()
|
|
122
|
+
selected = []
|
|
123
|
+
if any(marker in text for marker in ["python", "pytest", ".py", "pip", "torch"]):
|
|
124
|
+
selected.append("python")
|
|
125
|
+
if "godot" in text or "gdscript" in text:
|
|
126
|
+
selected.append("godot")
|
|
127
|
+
if "rust" in text or "cargo" in text:
|
|
128
|
+
selected.append("rust")
|
|
129
|
+
if "cuda" in text:
|
|
130
|
+
selected.append("cuda")
|
|
131
|
+
return selected or ["general_coding"]
|
|
132
|
+
|
|
133
|
+
def _calibrate_gates(self, semantic_state: SemanticState, runtime_context: Dict[str, Any], gates: Dict[str, float]) -> Dict[str, float]:
|
|
134
|
+
calibrated = dict(gates)
|
|
135
|
+
text = str(semantic_state.raw_input).lower()
|
|
136
|
+
is_noise = semantic_state.intent == "small_talk" or any(marker in text for marker in ["ruido", "spam", "sin valor"])
|
|
137
|
+
is_learning_intent = semantic_state.intent in {"knowledge_share", "preference", "correction"}
|
|
138
|
+
is_code = any(marker in text for marker in ["python", "pytest", "class", "def ", "bug", "error", ".py", "godot", "rust", "cuda"])
|
|
139
|
+
|
|
140
|
+
if is_noise and not is_code:
|
|
141
|
+
calibrated["learn"] = min(calibrated["learn"], 0.12)
|
|
142
|
+
calibrated["specialists"] = min(calibrated["specialists"], 0.12)
|
|
143
|
+
calibrated["tools"] = min(calibrated["tools"], 0.12)
|
|
144
|
+
calibrated["procedural_memory"] = min(calibrated["procedural_memory"], 0.2)
|
|
145
|
+
|
|
146
|
+
if semantic_state.intent == "question":
|
|
147
|
+
calibrated["learn"] = min(calibrated["learn"], 0.18)
|
|
148
|
+
calibrated["semantic_memory"] = max(calibrated["semantic_memory"], 0.72)
|
|
149
|
+
calibrated["episodic_memory"] = max(calibrated["episodic_memory"], 0.55)
|
|
150
|
+
|
|
151
|
+
if is_learning_intent:
|
|
152
|
+
calibrated["learn"] = max(calibrated["learn"], 0.72)
|
|
153
|
+
calibrated["semantic_memory"] = max(calibrated["semantic_memory"], 0.6)
|
|
154
|
+
|
|
155
|
+
if is_code:
|
|
156
|
+
calibrated["specialists"] = max(calibrated["specialists"], 0.76)
|
|
157
|
+
calibrated["tools"] = max(calibrated["tools"], 0.58)
|
|
158
|
+
calibrated["graph_memory"] = max(calibrated["graph_memory"], 0.68)
|
|
159
|
+
calibrated["procedural_memory"] = max(calibrated["procedural_memory"], 0.58)
|
|
160
|
+
|
|
161
|
+
if runtime_context.get("project_id") or runtime_context.get("has_project_memory"):
|
|
162
|
+
calibrated["project_memory"] = max(calibrated["project_memory"], 0.7)
|
|
163
|
+
calibrated["graph_memory"] = max(calibrated["graph_memory"], 0.65)
|
|
164
|
+
|
|
165
|
+
return {key: max(0.0, min(1.0, value)) for key, value in calibrated.items()}
|