Kill TextDelta, Info — UiMessage is dead. RAII ActivityGuards replace all status feedback
Streaming text now goes directly to agent entries via append_streaming(). sync_from_agent diffs the growing entry each tick. The streaming entry is popped when the response completes; build_response_message pushes the final version. All status feedback uses RAII ActivityGuards: - push_activity() for long-running work (thinking, streaming, scoring) - notify() for instant feedback (compacted, DMN state changes, commands) - Guards auto-remove on Drop, appending "(complete)" and lingering 5s - expire_activities() cleans up timed-out notifications on render tick UiMessage enum reduced to a single Info variant with zero sends. The channel infrastructure remains for now (Mind/Agent still take UiSender in signatures) — mechanical cleanup for a follow-up. Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
parent
e7914e3d58
commit
cfddb55ed9
9 changed files with 201 additions and 186 deletions
|
|
@ -20,7 +20,7 @@ use tokio::sync::mpsc;
|
|||
|
||||
use crate::agent::tools::{self as agent_tools, summarize_args, ActiveToolCall};
|
||||
pub use types::ToolCall;
|
||||
use crate::user::ui_channel::{UiMessage, UiSender, StreamTarget};
|
||||
use crate::user::ui_channel::UiSender;
|
||||
|
||||
/// A JoinHandle that aborts its task when dropped.
|
||||
pub struct AbortOnDrop(tokio::task::JoinHandle<()>);
|
||||
|
|
@ -130,7 +130,7 @@ impl ApiClient {
|
|||
&reasoning_effort, sampling, priority,
|
||||
).await;
|
||||
if let Err(e) = result {
|
||||
let _ = tx.send(StreamEvent::Error(e.to_string();
|
||||
let _ = tx.send(StreamEvent::Error(e.to_string()));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -207,7 +207,6 @@ pub(crate) async fn send_and_check(
|
|||
body: &impl serde::Serialize,
|
||||
auth_header: (&str, &str),
|
||||
extra_headers: &[(&str, &str)],
|
||||
ui_tx: &UiSender,
|
||||
debug_label: &str,
|
||||
request_json: Option<&str>,
|
||||
) -> Result<reqwest::Response> {
|
||||
|
|
@ -619,8 +618,6 @@ pub struct StreamResult {
|
|||
/// - UI forwarding (text deltas, reasoning, tool call notifications)
|
||||
pub async fn collect_stream(
|
||||
rx: &mut mpsc::UnboundedReceiver<StreamEvent>,
|
||||
ui_tx: &UiSender,
|
||||
target: StreamTarget,
|
||||
agent: &std::sync::Arc<tokio::sync::Mutex<super::Agent>>,
|
||||
active_tools: &crate::user::ui_channel::SharedActiveTools,
|
||||
) -> StreamResult {
|
||||
|
|
@ -633,12 +630,13 @@ pub async fn collect_stream(
|
|||
let mut error = None;
|
||||
let mut first_content = true;
|
||||
let mut display_buf = String::new();
|
||||
let mut _streaming_guard: Option<super::ActivityGuard> = None;
|
||||
|
||||
while let Some(event) = rx.recv().await {
|
||||
match event {
|
||||
StreamEvent::Content(text) => {
|
||||
if first_content {
|
||||
if let Ok(mut ag) = agent.try_lock() { ag.activity = "streaming...".into(); }
|
||||
_streaming_guard = Some(super::start_activity(agent, "streaming...").await);
|
||||
first_content = false;
|
||||
}
|
||||
content.push_str(&text);
|
||||
|
|
@ -683,7 +681,7 @@ pub async fn collect_stream(
|
|||
if let Some(pos) = display_buf.find("<tool_call>") {
|
||||
let before = &display_buf[..pos];
|
||||
if !before.is_empty() {
|
||||
let _ = ui_tx.send(UiMessage::TextDelta(before.to_string(), target));
|
||||
if let Ok(mut ag) = agent.try_lock() { ag.append_streaming(before); }
|
||||
}
|
||||
display_buf.clear();
|
||||
in_tool_call = true;
|
||||
|
|
@ -693,7 +691,7 @@ pub async fn collect_stream(
|
|||
if safe > 0 {
|
||||
let flush = display_buf[..safe].to_string();
|
||||
display_buf = display_buf[safe..].to_string();
|
||||
let _ = ui_tx.send(UiMessage::TextDelta(flush, target));
|
||||
if let Ok(mut ag) = agent.try_lock() { ag.append_streaming(&flush); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue