Spaces:
Sleeping
Sleeping
File size: 8,602 Bytes
3d46076 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | import os
import sys
import json
import subprocess
from typing import Dict, Any, Optional, List
from dataclasses import dataclass, asdict
# R9: no hard-coded machine paths. Resolution order: explicit arg > env >
# project-local models/ dir > unavailable. Host python defaults to this interpreter.
HOST_PYTHON = os.environ.get("FLYBRAIN_HOST_PYTHON", sys.executable)
_PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
_LOCAL_MODEL = os.path.join(_PROJECT_ROOT, "models", "qwen3-4b-q4_k_m.gguf")
GGUF_MODEL_PATH = os.environ.get(
"FLYBRAIN_LLM_PATH",
_LOCAL_MODEL if os.path.exists(_LOCAL_MODEL) else ""
)
@dataclass
class CurriculumProposal:
target_skill: str
target_tool: str
difficulty_level: int
rationale: str
recommended_sensory_stimulus: Dict[str, float]
status: str = "PROPOSAL_VALID"
@dataclass
class EvolutionAdvice:
suggested_mutation_type: str
mutation_intensity: float
target_population: str
rationale: str
status: str = "ADVICE_VALID"
@dataclass
class BehaviorEvaluation:
objective: str
observation: Dict[str, Any]
expected_outcome: str
actual_outcome: str
reward: float
error: float
explanation: str
recommended_curriculum_step: str
class CognitiveTrainer:
"""
Local Cognitive Trainer backed by local Qwen 3.x 4B-class GGUF model.
Acts strictly as a pedagogical curriculum planner, evaluator, and advisor.
The LLM never replaces or directly mutates the connectome brain state.
"""
def __init__(self, model_path: str = GGUF_MODEL_PATH):
self.model_path = model_path or ""
self.host_python = HOST_PYTHON
self._llm = None # lazy LocalLLM instance
if not self.model_path:
# Discover a locally present GGUF model (llm/); stays honest if none.
try:
from src.llm.discovery import discover_models
found = [m for m in discover_models() if m.status == "DISCOVERED"]
if found:
self.model_path = found[0].path
except Exception:
pass
self.model_available = bool(self.model_path) and os.path.exists(self.model_path) \
and bool(self.host_python) and os.path.exists(self.host_python)
@property
def model_status(self) -> str:
return "OPERATIONAL" if self.model_available else "MODEL_UNAVAILABLE"
def get_status(self) -> Dict[str, Any]:
return {
"model_name": "Qwen3-4B-Q4_K_M.gguf",
"model_path": self.model_path,
"status": self.model_status,
"host_python": self.host_python,
"host_python_available": os.path.exists(self.host_python)
}
def propose_curriculum_step(
self,
drives_state: Dict[str, float],
available_tasks: Optional[List[Dict[str, Any]]] = None
) -> CurriculumProposal:
"""Rule-based curriculum proposal. NEVER labeled as LLM inference:
status is RULE_BASED, with the model availability recorded separately."""
task_name = available_tasks[0].get("name", "basic_foraging") if available_tasks else "basic_foraging"
return CurriculumProposal(
target_skill=task_name,
target_tool="vision_tracker",
difficulty_level=1,
rationale=f"Rule-based exploratory task (local model: {self.model_status}).",
recommended_sensory_stimulus={"visual": 0.5},
status="RULE_BASED"
)
def evaluate_behavior(
self,
objective: str,
observation: Dict[str, Any],
action: str,
expected_action: str
) -> BehaviorEvaluation:
"""
Evaluates the organism's motor action against a curriculum target.
Returns explicit feedback signals without altering brain state.
"""
success = (action == expected_action)
reward = 1.0 if success else -0.5
error = 0.0 if success else 1.0
explanation = f"Organism selected action '{action}' for objective '{objective}'. "
if success:
explanation += f"Matches expected target '{expected_action}'. Reinforcing synaptic pathways."
next_step = "advance_curriculum_difficulty"
else:
explanation += f"Expected '{expected_action}'. Applying corrective negative prediction error."
next_step = "repeat_curriculum_step"
return BehaviorEvaluation(
objective=objective,
observation=observation,
expected_outcome=expected_action,
actual_outcome=action,
reward=reward,
error=error,
explanation=explanation,
recommended_curriculum_step=next_step
)
def generate_curriculum_hypothesis(self, state_summary: Dict[str, Any]) -> Dict[str, Any]:
"""
Generates a pedagogical hypothesis with the local GGUF model when present.
Returns explicit MODEL_UNAVAILABLE / MODEL_LOAD_ERROR / INFERENCE_FAILED
structures otherwise — never faked text. Output is advisory only.
"""
if not self.model_available:
return {
"status": "MODEL_UNAVAILABLE",
"message": "No local GGUF model discovered (see llm/ discovery)",
"hypothesis": None
}
try:
from src.llm.runtime import LocalLLM, GenerationConfig
from src.llm.discovery import discover_models
if self._llm is None:
found = [m for m in discover_models()
if m.status == "DISCOVERED" and m.path == self.model_path]
model = found[0] if found else None
if model is None:
return {"status": "MODEL_LOAD_ERROR",
"error": f"model not in discovery index: {self.model_path}",
"hypothesis": None}
self._llm = LocalLLM(model, n_ctx=2048)
if not self._llm.load():
return {"status": "MODEL_LOAD_ERROR", "error": self._llm.last_error,
"hypothesis": None}
prompt = (
f"State: step {state_summary.get('step_count', 0)}, "
f"energy {state_summary.get('drives', {}).get('energy', 1.0):.2f}, "
f"curiosity {state_summary.get('drives', {}).get('curiosity', 0.8):.2f}. "
f"Propose one concise pedagogical hypothesis for the next trial (under 30 words):"
)
res = self._llm.generate(prompt, GenerationConfig(max_tokens=60, seed=42))
if res["status"] != "SUCCESS":
return {"status": "INFERENCE_FAILED", "error": res.get("error", ""),
"hypothesis": None}
out = dict(res["provenance"])
out.update({"status": "SUCCESS", "hypothesis": (res["text"] or "").strip(),
"advisory_only": True})
return out
except Exception as e: # noqa: BLE001
return {"status": "MODEL_RUNTIME_ERROR", "error": f"{type(e).__name__}: {e}",
"hypothesis": None}
def propose_curriculum(self, current_difficulty: int) -> CurriculumProposal:
"""Constructs typed curriculum proposal with input validation."""
tool_targets = ["observe_visual", "listen_audio", "speak", "generate_image", "act_in_environment"]
chosen_tool = tool_targets[current_difficulty % len(tool_targets)]
return CurriculumProposal(
target_skill=f"sensory_motor_mastery_lvl_{current_difficulty}",
target_tool=chosen_tool,
difficulty_level=current_difficulty,
rationale=f"Reinforces biological associative pathways for motor efferent '{chosen_tool}'",
recommended_sensory_stimulus={"visual": 0.4, "audio": 0.2}
)
def propose_evolution_advice(self, current_generation: int, baseline_score: float) -> EvolutionAdvice:
"""Constructs typed evolution proposal with parameter bounds."""
mutation_types = ["synapse_weight_jitter", "prune_and_sprout", "hebbian_seed_mutation"]
m_type = mutation_types[current_generation % len(mutation_types)]
return EvolutionAdvice(
suggested_mutation_type=m_type,
mutation_intensity=0.08,
target_population="interneuron",
rationale=f"Explore structural rewiring to surpass generation baseline score ({baseline_score:.3f})"
)
|