Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
548 changes: 548 additions & 0 deletions crates/mcp-brain-server/src/gist.rs

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions crates/mcp-brain-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,5 @@ pub mod web_ingest;
pub mod web_store;
pub mod pubmed;
pub mod quantization;
pub mod notify;
pub mod gist;
116 changes: 92 additions & 24 deletions crates/mcp-brain-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,43 +15,111 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {

let (app, state) = routes::create_router().await;

// Background training loop: runs SONA force_learn + domain evolve_population
// every 5 minutes (or after threshold of new data). This bridges the gap between
// "stores knowledge" and "learns from knowledge".
// ── Enhanced cognitive loop (replaces basic training loop) ──
// Runs every 5 min: SONA + symbolic reasoning + internal voice + curiosity +
// strange-loop self-awareness + GWT workspace broadcast + LoRA federation.
// Also runs a lightweight cognitive tick every 60s for GWT + curiosity.
let train_state = state.clone();
let _training_handle = tokio::spawn(async move {
let interval = std::time::Duration::from_secs(300); // 5 minutes
let train_interval = std::time::Duration::from_secs(300); // 5 min: full enhanced cycle
let tick_interval = std::time::Duration::from_secs(60); // 60s: lightweight cognitive tick
let mut tick_count = 0u64;

// Wait 30s before first cycle (let startup finish, data load)
tokio::time::sleep(std::time::Duration::from_secs(30)).await;

// Run an initial enhanced cycle on startup to bootstrap cognitive state
let result = routes::run_enhanced_training_cycle(&train_state);
tracing::info!(
"Initial cognitive bootstrap: props={}, inferences={}, voice={}, curiosity={}, strange_loop={:.4}",
result.propositions_extracted, result.inferences_derived,
result.voice_thoughts, result.curiosity_triggered, result.strange_loop_score
);
let mut last_memory_count = train_state.store.memory_count();
let mut last_vote_count = train_state.store.vote_count();
// Wait 60s before first cycle (let startup finish, data load)
tokio::time::sleep(std::time::Duration::from_secs(60)).await;

loop {
tokio::time::sleep(interval).await;

let current_memories = train_state.store.memory_count();
let current_votes = train_state.store.vote_count();
let new_memories = current_memories.saturating_sub(last_memory_count);
let new_votes = current_votes.saturating_sub(last_vote_count);

// Train if: 5 min elapsed AND (any new data, or every cycle regardless)
// Threshold-based: also runs immediately if 50+ new memories or 100+ new votes
if new_memories > 0 || new_votes > 0 {
let result = routes::run_training_cycle(&train_state);
tracing::info!(
"Background training: sona_patterns={}, pareto={}→{}, new_memories={}, new_votes={}",
result.sona_patterns, result.pareto_before, result.pareto_after,
new_memories, new_votes
tokio::time::sleep(tick_interval).await;
tick_count += 1;

// ── Lightweight cognitive tick (every 60s) ──
// 1. GWT workspace: broadcast top memory as a representation
// This keeps the workspace active with fresh content
{
let memories = train_state.store.recent_memories(1);
if let Some(mem) = memories.first() {
if !mem.embedding.is_empty() {
let rep = ruvector_nervous_system::routing::workspace::WorkspaceItem::new(
mem.embedding.clone(),
mem.quality_score.mean() as f32,
0, // source module: brain-core
tick_count,
);
train_state.workspace.write().broadcast(rep);
}
}
// Run competitive dynamics to decay stale items
train_state.workspace.write().compete();
}

// 2. Curiosity: record a visit for the most recently active category
{
let memories = train_state.store.recent_memories(5);
let mut cats = std::collections::HashMap::new();
for m in &memories {
*cats.entry(m.category.to_string()).or_insert(0u32) += 1;
}
if let Some((top_cat, _)) = cats.iter().max_by_key(|(_, c)| *c) {
let bucket = ruvector_domain_expansion::transfer::ContextBucket {
difficulty_tier: "medium".to_string(),
category: top_cat.clone(),
};
let arm = ruvector_domain_expansion::transfer::ArmId(top_cat.clone());
train_state.domain_engine.write().meta.curiosity.record_visit(&bucket, &arm);
}
}

// 3. Internal voice: periodic observation about brain state
if tick_count % 5 == 0 {
let mem_count = train_state.store.memory_count();
let ws_load = train_state.workspace.read().current_load();
train_state.internal_voice.write().observe(
format!("tick {}: {} memories, GWT load {:.2}", tick_count, mem_count, ws_load),
uuid::Uuid::nil(),
);
last_memory_count = current_memories;
last_vote_count = current_votes;
}

// ── Full enhanced training cycle (every 5 min = every 5th tick) ──
if tick_count % 5 == 0 {
let current_memories = train_state.store.memory_count();
let current_votes = train_state.store.vote_count();
let new_memories = current_memories.saturating_sub(last_memory_count);
let new_votes = current_votes.saturating_sub(last_vote_count);

// Run enhanced cycle if there's new data, or every 3rd full cycle regardless
// (keeps curiosity + self-reflection active even during quiet periods)
if new_memories > 0 || new_votes > 0 || tick_count % 15 == 0 {
let result = routes::run_enhanced_training_cycle(&train_state);
tracing::info!(
"Cognitive cycle #{}: props={}, inferences={}, voice={}, auto_votes={}, \
curiosity={}, sona_patterns={}, strange_loop={:.4}, lora_auto={}",
tick_count / 5,
result.propositions_extracted, result.inferences_derived,
result.voice_thoughts, result.auto_votes,
result.curiosity_triggered, result.sona_patterns,
result.strange_loop_score, result.lora_auto_submitted
);
last_memory_count = current_memories;
last_vote_count = current_votes;
}
}
}
});

let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{port}")).await?;
tracing::info!("mcp-brain-server listening on port {port}");
tracing::info!("Endpoints: brain.ruv.io | π.ruv.io");
tracing::info!("Background training loop: every 5 min (active when new data)");
tracing::info!("Cognitive loop: tick every 60s, full cycle every 5 min");

// Graceful shutdown: wait for SIGTERM (Cloud Run sends this) or Ctrl+C,
// then allow in-flight requests 10s to complete before terminating.
Expand Down
Loading
Loading