Fix: skip empty CoT nodes, expand AST children in conscious screen, timestamps

Parser skips Thinking nodes that are just whitespace. Conscious screen
now shows assistant children (Content, Thinking, ToolCall) as nested
tree items via recursive node_to_view. Nodes get timestamped in
push_node and on assistant branch creation.

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
Kent Overstreet 2026-04-08 17:18:48 -04:00
parent 5ec2ff95d8
commit 1b6664ee1c
3 changed files with 28 additions and 18 deletions

View file

@ -528,7 +528,9 @@ impl ResponseParser {
self.buf = self.buf[end + 8..].to_string();
self.in_think = false;
let text = std::mem::take(&mut self.think_buf);
self.push_child(ctx, AstNode::thinking(text));
if !text.trim().is_empty() {
self.push_child(ctx, AstNode::thinking(text));
}
continue;
}
None => {

View file

@ -263,6 +263,7 @@ impl Agent {
}
pub async fn push_node(&self, node: AstNode) {
let node = node.with_timestamp(chrono::Utc::now());
let st = self.state.lock().await;
if let Some(ref log) = st.conversation_log {
if let Err(e) = log.append_node(&node) {
@ -319,7 +320,8 @@ impl Agent {
let mut ctx = agent.context.lock().await;
let idx = ctx.len(Section::Conversation);
ctx.push(Section::Conversation,
AstNode::branch(Role::Assistant, vec![]));
AstNode::branch(Role::Assistant, vec![])
.with_timestamp(chrono::Utc::now()));
idx
};

View file

@ -8,7 +8,7 @@ use ratatui::{
Frame,
crossterm::event::KeyCode,
};
use crate::agent::context::{AstNode, NodeBody, Ast};
use crate::agent::context::{AstNode, Ast};
#[derive(Debug, Clone)]
pub struct SectionView {
@ -20,26 +20,32 @@ pub struct SectionView {
pub status: String,
}
pub fn section_to_view(name: &str, nodes: &[AstNode]) -> SectionView {
let children: Vec<SectionView> = nodes.iter().map(|node| {
let content = match node.leaf().map(|l| l.body()) {
Some(NodeBody::Log(_)) => String::new(),
Some(body) => body.text().to_string(),
None => node.children().iter()
.filter_map(|c| c.leaf())
.filter(|l| matches!(l.body(), NodeBody::Content(_)))
.map(|l| l.body().text())
.collect::<Vec<_>>()
.join(""),
};
SectionView {
fn node_to_view(node: &AstNode) -> SectionView {
match node {
AstNode::Leaf(leaf) => SectionView {
name: node.label(),
tokens: node.tokens(),
content,
content: leaf.body().text().to_string(),
children: Vec::new(),
status: String::new(),
},
AstNode::Branch { children, .. } => {
let child_views: Vec<SectionView> = children.iter()
.map(|c| node_to_view(c))
.collect();
SectionView {
name: node.label(),
tokens: node.tokens(),
content: String::new(),
children: child_views,
status: String::new(),
}
}
}).collect();
}
}
pub fn section_to_view(name: &str, nodes: &[AstNode]) -> SectionView {
let children: Vec<SectionView> = nodes.iter().map(|n| node_to_view(n)).collect();
let total_tokens: usize = nodes.iter().map(|n| n.tokens()).sum();
SectionView {
name: name.to_string(),