Delete ui_channel.rs — relocate types, remove all UiMessage/UiSender plumbing
Types relocated: - StreamTarget → mind/mod.rs (Mind decides Conversation vs Autonomous) - SharedActiveTools + shared_active_tools() → agent/tools/mod.rs - ContextSection + SharedContextState → agent/context.rs (already there) - StatusInfo + ContextInfo → user/mod.rs (UI display state) Removed UiSender from: Agent::turn, Mind, learn.rs, all function signatures. The entire message-passing layer is gone. All state flows through Agent fields (activities, entries, streaming) read by the UI via try_lock. Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
parent
cfddb55ed9
commit
f390fa1617
11 changed files with 72 additions and 165 deletions
|
|
@ -16,7 +16,7 @@ use super::{
|
|||
App, HotkeyAction, ScreenAction, ScreenView,
|
||||
screen_legend,
|
||||
};
|
||||
use crate::user::ui_channel::StreamTarget;
|
||||
use crate::mind::StreamTarget;
|
||||
use crate::mind::MindCommand;
|
||||
|
||||
// --- Slash command table ---
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use ratatui::{
|
|||
};
|
||||
|
||||
use super::{App, ScreenAction, ScreenView, screen_legend};
|
||||
use crate::agent::context::ContextSection;
|
||||
|
||||
pub(crate) struct ConsciousScreen {
|
||||
scroll: u16,
|
||||
|
|
@ -25,12 +26,12 @@ impl ConsciousScreen {
|
|||
Self { scroll: 0, selected: None, expanded: std::collections::HashSet::new() }
|
||||
}
|
||||
|
||||
fn read_context_state(&self, app: &App) -> Vec<crate::user::ui_channel::ContextSection> {
|
||||
fn read_context_state(&self, app: &App) -> Vec<ContextSection> {
|
||||
app.shared_context.read().map_or_else(|_| Vec::new(), |s| s.clone())
|
||||
}
|
||||
|
||||
fn item_count(&self, context_state: &[crate::user::ui_channel::ContextSection]) -> usize {
|
||||
fn count_section(section: &crate::user::ui_channel::ContextSection, expanded: &std::collections::HashSet<usize>, idx: &mut usize) -> usize {
|
||||
fn item_count(&self, context_state: &[ContextSection]) -> usize {
|
||||
fn count_section(section: &ContextSection, expanded: &std::collections::HashSet<usize>, idx: &mut usize) -> usize {
|
||||
let my_idx = *idx;
|
||||
*idx += 1;
|
||||
let mut total = 1;
|
||||
|
|
@ -63,7 +64,7 @@ impl ConsciousScreen {
|
|||
|
||||
fn render_section(
|
||||
&self,
|
||||
section: &crate::user::ui_channel::ContextSection,
|
||||
section: &ContextSection,
|
||||
depth: usize,
|
||||
lines: &mut Vec<Line>,
|
||||
idx: &mut usize,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ pub mod context;
|
|||
pub mod subconscious;
|
||||
pub mod unconscious;
|
||||
pub mod thalamus;
|
||||
pub mod ui_channel;
|
||||
|
||||
use anyhow::Result;
|
||||
use ratatui::crossterm::event::{Event, EventStream, KeyEventKind};
|
||||
|
|
@ -30,7 +29,33 @@ use ratatui::{
|
|||
};
|
||||
use std::io;
|
||||
|
||||
use crate::user::ui_channel::{ContextInfo, SharedContextState, StatusInfo};
|
||||
use crate::agent::context::SharedContextState;
|
||||
|
||||
/// Status info for the bottom status bar.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StatusInfo {
|
||||
pub dmn_state: String,
|
||||
pub dmn_turns: u32,
|
||||
pub dmn_max_turns: u32,
|
||||
pub prompt_tokens: u32,
|
||||
pub completion_tokens: u32,
|
||||
pub model: String,
|
||||
pub turn_tools: u32,
|
||||
pub context_budget: String,
|
||||
}
|
||||
|
||||
/// Context loading details for the debug screen.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ContextInfo {
|
||||
pub model: String,
|
||||
pub available_models: Vec<String>,
|
||||
pub prompt_file: String,
|
||||
pub backend: String,
|
||||
pub instruction_files: Vec<(String, usize)>,
|
||||
pub memory_files: Vec<(String, usize)>,
|
||||
pub system_prompt_chars: usize,
|
||||
pub context_message_chars: usize,
|
||||
}
|
||||
|
||||
/// Build the screen legend from screen labels.
|
||||
pub(crate) fn screen_legend_from(interact: &dyn ScreenView, screens: &[Box<dyn ScreenView>]) -> String {
|
||||
|
|
@ -100,7 +125,7 @@ pub struct App {
|
|||
pub temperature: f32,
|
||||
pub top_p: f32,
|
||||
pub top_k: u32,
|
||||
pub(crate) active_tools: crate::user::ui_channel::SharedActiveTools,
|
||||
pub(crate) active_tools: crate::agent::tools::SharedActiveTools,
|
||||
pub should_quit: bool,
|
||||
pub submitted: Vec<String>,
|
||||
pub hotkey_actions: Vec<HotkeyAction>,
|
||||
|
|
@ -112,7 +137,7 @@ pub struct App {
|
|||
}
|
||||
|
||||
impl App {
|
||||
pub fn new(model: String, shared_context: SharedContextState, active_tools: crate::user::ui_channel::SharedActiveTools) -> Self {
|
||||
pub fn new(model: String, shared_context: SharedContextState, active_tools: crate::agent::tools::SharedActiveTools) -> Self {
|
||||
Self {
|
||||
status: StatusInfo {
|
||||
dmn_state: "resting".into(), dmn_turns: 0, dmn_max_turns: 20,
|
||||
|
|
@ -189,11 +214,10 @@ pub async fn start(cli: crate::user::CliArgs) -> Result<()> {
|
|||
unsafe { std::env::set_var("POC_DEBUG", "1") };
|
||||
}
|
||||
|
||||
let (ui_tx, ui_rx) = ui_channel::channel();
|
||||
let (turn_tx, turn_rx) = tokio::sync::mpsc::channel(1);
|
||||
let (mind_tx, mind_rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
|
||||
let mind = crate::mind::Mind::new(config, ui_tx.clone(), turn_tx);
|
||||
let mind = crate::mind::Mind::new(config, turn_tx);
|
||||
|
||||
let shared_context = mind.agent.lock().await.shared_context.clone();
|
||||
let shared_active_tools = mind.agent.lock().await.active_tools.clone();
|
||||
|
|
|
|||
|
|
@ -1,107 +0,0 @@
|
|||
// ui_channel.rs — Output routing for TUI panes
|
||||
//
|
||||
// All output from the agent (streaming text, tool calls, status updates)
|
||||
// goes through a UiMessage enum sent over an mpsc channel. The TUI
|
||||
// receives these messages and routes them to the appropriate pane.
|
||||
//
|
||||
// This replaces direct stdout/stderr printing throughout the codebase.
|
||||
// The agent and API client never touch the terminal directly — they
|
||||
// just send messages that the TUI renders where appropriate.
|
||||
//
|
||||
// The channel also fans out to a broadcast channel so the observation
|
||||
// socket (observe.rs) can subscribe without touching the main path.
|
||||
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{broadcast, mpsc};
|
||||
|
||||
// Re-export context types that moved to agent::context
|
||||
pub use crate::agent::context::{ContextSection, SharedContextState, shared_context_state};
|
||||
|
||||
// ActiveToolCall lives in agent::tools — re-export for TUI access
|
||||
pub use crate::agent::tools::ActiveToolCall;
|
||||
|
||||
/// Shared active tool calls — agent spawns, TUI reads metadata / aborts.
|
||||
pub type SharedActiveTools = Arc<std::sync::Mutex<Vec<ActiveToolCall>>>;
|
||||
|
||||
pub fn shared_active_tools() -> SharedActiveTools {
|
||||
Arc::new(std::sync::Mutex::new(Vec::new()))
|
||||
}
|
||||
|
||||
/// Which pane streaming text should go to.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum StreamTarget {
|
||||
/// User-initiated turn — text goes to conversation pane.
|
||||
Conversation,
|
||||
/// DMN-initiated turn — text goes to autonomous pane.
|
||||
Autonomous,
|
||||
}
|
||||
|
||||
/// Status info for the bottom status bar.
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub struct StatusInfo {
|
||||
pub dmn_state: String,
|
||||
pub dmn_turns: u32,
|
||||
pub dmn_max_turns: u32,
|
||||
pub prompt_tokens: u32,
|
||||
pub completion_tokens: u32,
|
||||
pub model: String,
|
||||
/// Number of tool calls dispatched in the current turn.
|
||||
pub turn_tools: u32,
|
||||
/// Context window budget breakdown (e.g. "id:8% mem:25% jnl:30% conv:37%").
|
||||
pub context_budget: String,
|
||||
}
|
||||
|
||||
/// Context loading details for the debug screen.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ContextInfo {
|
||||
pub model: String,
|
||||
pub available_models: Vec<String>,
|
||||
pub prompt_file: String,
|
||||
pub backend: String,
|
||||
#[allow(dead_code)]
|
||||
pub instruction_files: Vec<(String, usize)>,
|
||||
#[allow(dead_code)]
|
||||
pub memory_files: Vec<(String, usize)>,
|
||||
pub system_prompt_chars: usize,
|
||||
pub context_message_chars: usize,
|
||||
}
|
||||
|
||||
/// Messages sent from agent/API to the TUI for rendering.
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub enum UiMessage {
|
||||
/// Informational message — goes to conversation pane (command output, etc).
|
||||
Info(String),
|
||||
|
||||
}
|
||||
|
||||
/// Sender that fans out to both the TUI (mpsc) and observers (broadcast).
|
||||
#[derive(Clone)]
|
||||
pub struct UiSender {
|
||||
tui: mpsc::UnboundedSender<UiMessage>,
|
||||
observe: broadcast::Sender<UiMessage>,
|
||||
}
|
||||
|
||||
impl UiSender {
|
||||
pub fn send(&self, msg: UiMessage) -> Result<(), mpsc::error::SendError<UiMessage>> {
|
||||
// Broadcast to observers (ignore errors — no subscribers is fine)
|
||||
let _ = self.observe.send(msg.clone());
|
||||
self.tui.send(msg)
|
||||
}
|
||||
|
||||
/// Subscribe to the broadcast side (for the observation socket).
|
||||
pub fn subscribe(&self) -> broadcast::Receiver<UiMessage> {
|
||||
self.observe.subscribe()
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience type for the receiving half.
|
||||
pub type UiReceiver = mpsc::UnboundedReceiver<UiMessage>;
|
||||
|
||||
/// Create a new UI channel pair.
|
||||
pub fn channel() -> (UiSender, UiReceiver) {
|
||||
let (tui_tx, tui_rx) = mpsc::unbounded_channel();
|
||||
let (observe_tx, _) = broadcast::channel(1024);
|
||||
(UiSender { tui: tui_tx, observe: observe_tx }, tui_rx)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue