mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-25 14:53:34 +00:00
Add status updates to show agent thinking/processing state
- Add StatusUpdate enum with Thinking, ToolStarted, ToolCompleted, StreamChunk, Status variants - Add send_status method to Channel trait (default no-op) - Implement send_status in TuiChannel to show status in UI - Add send_status to ChannelManager for routing to specific channels - Update handle_message to send "Processing..." status for Chat/CreateJob - Update handle_chat to send "Generating response..." and show errors Now when a user sends a message, they see feedback that the agent is working. Co-Authored-By: Claude Opus 4.5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
d1cb748914
commit
7f9f0cd21e
+44
-9
@@ -7,7 +7,7 @@ use uuid::Uuid;
|
||||
|
||||
use crate::agent::self_repair::DefaultSelfRepair;
|
||||
use crate::agent::{MessageIntent, RepairTask, Router, Scheduler};
|
||||
use crate::channels::{ChannelManager, IncomingMessage, OutgoingResponse};
|
||||
use crate::channels::{ChannelManager, IncomingMessage, OutgoingResponse, StatusUpdate};
|
||||
use crate::config::AgentConfig;
|
||||
use crate::context::ContextManager;
|
||||
use crate::error::Error;
|
||||
@@ -127,6 +127,20 @@ impl Agent {
|
||||
let intent = self.router.route(message);
|
||||
tracing::debug!("Routed to intent: {:?}", intent);
|
||||
|
||||
// Send thinking status for non-trivial operations
|
||||
match &intent {
|
||||
MessageIntent::Chat { .. } | MessageIntent::CreateJob { .. } => {
|
||||
let _ = self
|
||||
.channels
|
||||
.send_status(
|
||||
&message.channel,
|
||||
StatusUpdate::Thinking("Processing...".into()),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Handle based on intent
|
||||
let response = match intent {
|
||||
MessageIntent::CreateJob {
|
||||
@@ -293,19 +307,40 @@ impl Agent {
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_chat(
|
||||
&self,
|
||||
_message: &IncomingMessage,
|
||||
content: &str,
|
||||
) -> Result<String, Error> {
|
||||
async fn handle_chat(&self, message: &IncomingMessage, content: &str) -> Result<String, Error> {
|
||||
// Send thinking status
|
||||
let _ = self
|
||||
.channels
|
||||
.send_status(
|
||||
&message.channel,
|
||||
StatusUpdate::Thinking("Generating response...".into()),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Use LLM for general chat
|
||||
let reasoning = Reasoning::new(self.llm.clone(), self.safety.clone());
|
||||
|
||||
let context = ReasoningContext::new().with_message(ChatMessage::user(content));
|
||||
|
||||
let response = reasoning.respond(&context).await?;
|
||||
|
||||
Ok(response)
|
||||
match reasoning.respond(&context).await {
|
||||
Ok(response) => {
|
||||
let _ = self
|
||||
.channels
|
||||
.send_status(&message.channel, StatusUpdate::Status("Done".into()))
|
||||
.await;
|
||||
Ok(response)
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = self
|
||||
.channels
|
||||
.send_status(
|
||||
&message.channel,
|
||||
StatusUpdate::Status(format!("Error: {}", e)),
|
||||
)
|
||||
.await;
|
||||
Err(e.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_command(
|
||||
|
||||
@@ -99,6 +99,21 @@ impl OutgoingResponse {
|
||||
}
|
||||
}
|
||||
|
||||
/// Status update types for showing agent activity.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum StatusUpdate {
|
||||
/// Agent is thinking/processing.
|
||||
Thinking(String),
|
||||
/// Tool execution started.
|
||||
ToolStarted { name: String },
|
||||
/// Tool execution completed.
|
||||
ToolCompleted { name: String, success: bool },
|
||||
/// Streaming text chunk.
|
||||
StreamChunk(String),
|
||||
/// General status message.
|
||||
Status(String),
|
||||
}
|
||||
|
||||
/// Trait for message channels.
|
||||
///
|
||||
/// Channels receive messages from external sources and convert them to
|
||||
@@ -124,6 +139,13 @@ pub trait Channel: Send + Sync {
|
||||
response: OutgoingResponse,
|
||||
) -> Result<(), ChannelError>;
|
||||
|
||||
/// Send a status update (thinking, tool execution, etc.).
|
||||
///
|
||||
/// Default implementation does nothing (for channels that don't support status).
|
||||
async fn send_status(&self, _status: StatusUpdate) -> Result<(), ChannelError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check if the channel is healthy.
|
||||
async fn health_check(&self) -> Result<(), ChannelError>;
|
||||
|
||||
|
||||
+21
-1
@@ -26,7 +26,7 @@ use ratatui::backend::CrosstermBackend;
|
||||
use tokio::sync::{Mutex, mpsc};
|
||||
use tokio_stream::wrappers::ReceiverStream;
|
||||
|
||||
use crate::channels::{Channel, IncomingMessage, MessageStream, OutgoingResponse};
|
||||
use crate::channels::{Channel, IncomingMessage, MessageStream, OutgoingResponse, StatusUpdate};
|
||||
use crate::error::ChannelError;
|
||||
|
||||
pub use app::{AppEvent, AppState, InputMode};
|
||||
@@ -109,6 +109,26 @@ impl Channel for TuiChannel {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn send_status(&self, status: StatusUpdate) -> Result<(), ChannelError> {
|
||||
let event = match status {
|
||||
StatusUpdate::Thinking(msg) => AppEvent::LogMessage(format!("🤔 {}", msg)),
|
||||
StatusUpdate::ToolStarted { name } => AppEvent::ToolStarted { name },
|
||||
StatusUpdate::ToolCompleted { name, success } => {
|
||||
AppEvent::ToolCompleted { name, success }
|
||||
}
|
||||
StatusUpdate::StreamChunk(chunk) => AppEvent::StreamChunk(chunk),
|
||||
StatusUpdate::Status(msg) => AppEvent::LogMessage(msg),
|
||||
};
|
||||
self.event_tx
|
||||
.send(event)
|
||||
.await
|
||||
.map_err(|e| ChannelError::SendFailed {
|
||||
name: "tui".to_string(),
|
||||
reason: e.to_string(),
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn health_check(&self) -> Result<(), ChannelError> {
|
||||
// Channel is healthy if we haven't been closed
|
||||
if self.event_tx.is_closed() {
|
||||
|
||||
+16
-1
@@ -6,7 +6,7 @@ use std::sync::Arc;
|
||||
use futures::stream;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::channels::{Channel, IncomingMessage, MessageStream, OutgoingResponse};
|
||||
use crate::channels::{Channel, IncomingMessage, MessageStream, OutgoingResponse, StatusUpdate};
|
||||
use crate::error::ChannelError;
|
||||
|
||||
/// Manages multiple input channels and merges their message streams.
|
||||
@@ -82,6 +82,21 @@ impl ChannelManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a status update to a specific channel.
|
||||
pub async fn send_status(
|
||||
&self,
|
||||
channel_name: &str,
|
||||
status: StatusUpdate,
|
||||
) -> Result<(), ChannelError> {
|
||||
let channels = self.channels.read().await;
|
||||
if let Some(channel) = channels.get(channel_name) {
|
||||
channel.send_status(status).await
|
||||
} else {
|
||||
// Silently ignore if channel not found (status is best-effort)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Check health of all channels.
|
||||
pub async fn health_check_all(&self) -> HashMap<String, Result<(), ChannelError>> {
|
||||
let channels = self.channels.read().await;
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ mod manager;
|
||||
mod slack;
|
||||
mod telegram;
|
||||
|
||||
pub use channel::{Channel, IncomingMessage, MessageStream, OutgoingResponse};
|
||||
pub use channel::{Channel, IncomingMessage, MessageStream, OutgoingResponse, StatusUpdate};
|
||||
pub use cli::TuiChannel;
|
||||
pub use http::HttpChannel;
|
||||
pub use manager::ChannelManager;
|
||||
|
||||
Reference in New Issue
Block a user