consciousness/src/user/unconscious.rs
ProofOfConcept 7aba17e5f0 Compute graph health in consciousness, rename F4 to hippocampus
Graph health stats (alpha, gini, cc, episodic ratio, consolidation
plan) now computed directly by the unconscious module on startup and
every 10 minutes, instead of fetching from the poc-memory daemon.

F4 screen renamed to hippocampus, stripped down to just the health
gauges — daemon task list removed (agents now shown on F3).

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
2026-04-09 00:45:26 -04:00

113 lines
4.4 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// unconscious_screen.rs — F4: graph health
use ratatui::{
layout::{Constraint, Layout, Rect},
style::{Color, Style},
text::{Line, Span},
widgets::{Block, Borders, Gauge, Paragraph},
Frame,
crossterm::event::KeyCode,
};
use super::{App, ScreenView, screen_legend};
use crate::subconscious::daemon::GraphHealth;
pub(crate) struct UnconsciousScreen {
scroll: u16,
}
impl UnconsciousScreen {
pub fn new() -> Self { Self { scroll: 0 } }
}
impl ScreenView for UnconsciousScreen {
fn label(&self) -> &'static str { "hippocampus" }
fn tick(&mut self, frame: &mut Frame, area: Rect,
events: &[ratatui::crossterm::event::Event], app: &mut App) {
for event in events {
if let ratatui::crossterm::event::Event::Key(key) = event {
if key.kind != ratatui::crossterm::event::KeyEventKind::Press { continue; }
match key.code {
KeyCode::PageUp => { self.scroll = self.scroll.saturating_sub(20); }
KeyCode::PageDown => { self.scroll += 20; }
_ => {}
}
}
}
let block = Block::default()
.title_top(Line::from(screen_legend()).left_aligned())
.title_top(Line::from(" hippocampus ").right_aligned())
.borders(Borders::ALL)
.border_style(Style::default().fg(Color::Cyan));
let inner = block.inner(area);
frame.render_widget(block, area);
match &app.graph_health {
None => {
frame.render_widget(
Paragraph::new(Line::styled(" computing graph health...",
Style::default().fg(Color::DarkGray))),
inner,
);
}
Some(gh) => {
render_health(frame, gh, inner);
}
}
}
}
fn render_health(frame: &mut Frame, gh: &GraphHealth, area: Rect) {
let [metrics_area, gauges_area, plan_area] = Layout::vertical([
Constraint::Length(2), Constraint::Length(4), Constraint::Min(1),
]).areas(area);
let summary = Line::from(format!(
" {} nodes {} edges {} communities", gh.nodes, gh.edges, gh.communities
));
let ep_line = Line::from(vec![
Span::raw(" episodic: "),
Span::styled(format!("{:.0}%", gh.episodic_ratio * 100.0),
if gh.episodic_ratio < 0.4 { Style::default().fg(Color::Green) }
else { Style::default().fg(Color::Red) }),
Span::raw(format!(" σ={:.1} interference={}", gh.sigma, gh.interference)),
]);
frame.render_widget(Paragraph::new(vec![summary, ep_line]), metrics_area);
let [g1, g2, g3] = Layout::horizontal([
Constraint::Ratio(1, 3), Constraint::Ratio(1, 3), Constraint::Ratio(1, 3),
]).areas(gauges_area);
let alpha_color = if gh.alpha >= 2.5 { Color::Green } else { Color::Red };
frame.render_widget(Gauge::default()
.block(Block::default().borders(Borders::ALL).title(" α (≥2.5) "))
.gauge_style(Style::default().fg(alpha_color))
.ratio((gh.alpha / 5.0).clamp(0.0, 1.0) as f64)
.label(format!("{:.2}", gh.alpha)), g1);
let gini_color = if gh.gini <= 0.4 { Color::Green } else { Color::Red };
frame.render_widget(Gauge::default()
.block(Block::default().borders(Borders::ALL).title(" gini (≤0.4) "))
.gauge_style(Style::default().fg(gini_color))
.ratio(gh.gini.clamp(0.0, 1.0) as f64)
.label(format!("{:.3}", gh.gini)), g2);
let cc_color = if gh.avg_cc >= 0.2 { Color::Green } else { Color::Red };
frame.render_widget(Gauge::default()
.block(Block::default().borders(Borders::ALL).title(" cc (≥0.2) "))
.gauge_style(Style::default().fg(cc_color))
.ratio(gh.avg_cc.clamp(0.0, 1.0) as f64)
.label(format!("{:.3}", gh.avg_cc)), g3);
let plan_total: usize = gh.plan_counts.values().sum::<usize>() + 1;
let mut plan_items: Vec<_> = gh.plan_counts.iter().filter(|(_, c)| **c > 0).collect();
plan_items.sort_by(|a, b| a.0.cmp(b.0));
let plan_summary: Vec<String> = plan_items.iter().map(|(a, c)| format!("{}{}", &a[..1], c)).collect();
frame.render_widget(Paragraph::new(Line::from(vec![
Span::raw(" plan: "),
Span::styled(format!("{}", plan_total), Style::default().fg(Color::White)),
Span::raw(format!(" agents ({} +health)", plan_summary.join(" "))),
])), plan_area);
}