feat(gateway): 添加 show_tool_results 配置以控制工具结果显示
feat(session): 更新 BusToolCallEmitter 以支持工具结果显示控制 feat(ws): 更新 WsToolCallEmitter 以支持工具结果显示控制
This commit is contained in:
parent
65abf017a1
commit
302e6ef6b9
@ -356,7 +356,18 @@ impl OutboundMessage {
|
||||
match message.role.as_str() {
|
||||
"assistant" => {
|
||||
if let Some(tool_calls) = &message.tool_calls {
|
||||
tool_calls
|
||||
let mut outbound = Vec::new();
|
||||
if !message.content.trim().is_empty() {
|
||||
outbound.push(Self::assistant(
|
||||
channel.to_string(),
|
||||
chat_id.to_string(),
|
||||
message.content.clone(),
|
||||
reply_to.clone(),
|
||||
metadata.clone(),
|
||||
));
|
||||
}
|
||||
|
||||
outbound.extend(tool_calls
|
||||
.iter()
|
||||
.map(|tool_call| {
|
||||
Self::tool_call(
|
||||
@ -369,7 +380,8 @@ impl OutboundMessage {
|
||||
metadata.clone(),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
);
|
||||
outbound
|
||||
} else {
|
||||
vec![Self::assistant(
|
||||
channel.to_string(),
|
||||
@ -488,6 +500,32 @@ mod tests {
|
||||
assert_eq!(outbound[1].content, "### file_read\n- path: README.md");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_chat_message_keeps_assistant_content_when_tool_calls_exist() {
|
||||
let message = ChatMessage::assistant_with_tool_calls(
|
||||
"日报已整理完成。",
|
||||
vec![ToolCall {
|
||||
id: "call-1".to_string(),
|
||||
name: "memory_manage".to_string(),
|
||||
arguments: json!({"action": "put"}),
|
||||
}],
|
||||
);
|
||||
|
||||
let outbound = OutboundMessage::from_chat_message(
|
||||
"feishu",
|
||||
"chat-1",
|
||||
None,
|
||||
&HashMap::new(),
|
||||
&message,
|
||||
);
|
||||
|
||||
assert_eq!(outbound.len(), 2);
|
||||
assert_eq!(outbound[0].event_kind, OutboundEventKind::AssistantResponse);
|
||||
assert_eq!(outbound[0].content, "日报已整理完成。");
|
||||
assert_eq!(outbound[1].event_kind, OutboundEventKind::ToolCall);
|
||||
assert_eq!(outbound[1].tool_name.as_deref(), Some("memory_manage"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_chat_message_includes_tool_result() {
|
||||
let message = ChatMessage::tool("call-9", "calculator", "2");
|
||||
|
||||
@ -87,6 +87,7 @@ impl GatewayState {
|
||||
inbound.channel.clone(),
|
||||
inbound.chat_id.clone(),
|
||||
inbound.forwarded_metadata.clone(),
|
||||
session_manager.show_tool_results(),
|
||||
));
|
||||
match session_manager.handle_message(
|
||||
&inbound.channel,
|
||||
|
||||
@ -40,6 +40,7 @@ pub struct BusToolCallEmitter {
|
||||
channel_name: String,
|
||||
chat_id: String,
|
||||
metadata: HashMap<String, String>,
|
||||
show_tool_results: bool,
|
||||
}
|
||||
|
||||
impl BusToolCallEmitter {
|
||||
@ -48,12 +49,14 @@ impl BusToolCallEmitter {
|
||||
channel_name: impl Into<String>,
|
||||
chat_id: impl Into<String>,
|
||||
metadata: HashMap<String, String>,
|
||||
show_tool_results: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
bus,
|
||||
channel_name: channel_name.into(),
|
||||
chat_id: chat_id.into(),
|
||||
metadata,
|
||||
show_tool_results,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -61,6 +64,10 @@ impl BusToolCallEmitter {
|
||||
#[async_trait]
|
||||
impl EmittedMessageHandler for BusToolCallEmitter {
|
||||
async fn handle(&self, message: ChatMessage) {
|
||||
if !should_display_message_to_user(self.show_tool_results, &message) {
|
||||
return;
|
||||
}
|
||||
|
||||
for outbound in OutboundMessage::from_chat_message(
|
||||
&self.channel_name,
|
||||
&self.chat_id,
|
||||
@ -453,6 +460,10 @@ impl SessionManager {
|
||||
self.store.clone()
|
||||
}
|
||||
|
||||
pub fn show_tool_results(&self) -> bool {
|
||||
self.show_tool_results
|
||||
}
|
||||
|
||||
pub fn skills(&self) -> Arc<SkillRuntime> {
|
||||
self.skills.clone()
|
||||
}
|
||||
@ -699,6 +710,7 @@ fn should_display_message_to_user(show_tool_results: bool, message: &ChatMessage
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::bus::MessageBus;
|
||||
use std::collections::HashMap;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
@ -733,6 +745,26 @@ mod tests {
|
||||
assert!(should_display_message_to_user(true, &completed));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_bus_tool_call_emitter_hides_completed_tool_results_when_disabled() {
|
||||
let bus = MessageBus::new(4);
|
||||
let emitter = BusToolCallEmitter::new(
|
||||
bus.clone(),
|
||||
"feishu",
|
||||
"chat-1",
|
||||
HashMap::new(),
|
||||
false,
|
||||
);
|
||||
|
||||
emitter
|
||||
.handle(ChatMessage::tool("call-1", "calculator", "2"))
|
||||
.await;
|
||||
|
||||
assert!(tokio::time::timeout(std::time::Duration::from_millis(50), bus.consume_outbound())
|
||||
.await
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_in_chat_command_aliases() {
|
||||
assert_eq!(parse_in_chat_command("/new"), Some(InChatCommand::FreshConversation));
|
||||
|
||||
@ -13,11 +13,16 @@ use super::{GatewayState, session::{Session, handle_in_chat_command}};
|
||||
|
||||
struct WsToolCallEmitter {
|
||||
sender: mpsc::Sender<WsOutbound>,
|
||||
show_tool_results: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl EmittedMessageHandler for WsToolCallEmitter {
|
||||
async fn handle(&self, message: ChatMessage) {
|
||||
if !should_display_message_to_user(self.show_tool_results, &message) {
|
||||
return;
|
||||
}
|
||||
|
||||
for outbound in ws_outbound_from_chat_message(&message) {
|
||||
let _ = self.sender.send(outbound).await;
|
||||
}
|
||||
@ -162,7 +167,16 @@ fn ws_outbound_from_chat_message(message: &ChatMessage) -> Vec<WsOutbound> {
|
||||
match message.role.as_str() {
|
||||
"assistant" => {
|
||||
if let Some(tool_calls) = &message.tool_calls {
|
||||
tool_calls
|
||||
let mut outbound = Vec::new();
|
||||
if !message.content.trim().is_empty() {
|
||||
outbound.push(WsOutbound::AssistantResponse {
|
||||
id: message.id.clone(),
|
||||
content: message.content.clone(),
|
||||
role: message.role.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
outbound.extend(tool_calls
|
||||
.iter()
|
||||
.map(|tool_call| WsOutbound::ToolCall {
|
||||
id: message.id.clone(),
|
||||
@ -172,7 +186,8 @@ fn ws_outbound_from_chat_message(message: &ChatMessage) -> Vec<WsOutbound> {
|
||||
content: format_tool_call_content(&tool_call.name, &tool_call.arguments),
|
||||
role: message.role.clone(),
|
||||
})
|
||||
.collect()
|
||||
);
|
||||
outbound
|
||||
} else {
|
||||
vec![WsOutbound::AssistantResponse {
|
||||
id: message.id.clone(),
|
||||
@ -262,6 +277,7 @@ async fn handle_inbound(
|
||||
|
||||
let live_emitter = Arc::new(WsToolCallEmitter {
|
||||
sender: session_guard.user_tx.clone(),
|
||||
show_tool_results: state.config.gateway.show_tool_results,
|
||||
});
|
||||
let agent = session_guard
|
||||
.create_agent(&chat_id, None, Some(&user_message_id))?
|
||||
@ -420,12 +436,14 @@ async fn handle_inbound(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{should_display_message_to_user, ws_outbound_from_chat_message};
|
||||
use crate::agent::EmittedMessageHandler;
|
||||
use super::{WsToolCallEmitter, should_display_message_to_user, ws_outbound_from_chat_message};
|
||||
use crate::bus::ChatMessage;
|
||||
use crate::bus::message::ToolMessageState;
|
||||
use crate::providers::ToolCall;
|
||||
use crate::protocol::WsOutbound;
|
||||
use serde_json::json;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
#[test]
|
||||
fn test_ws_outbound_from_chat_message_expands_tool_calls() {
|
||||
@ -452,6 +470,24 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ws_outbound_keeps_assistant_content_when_tool_calls_exist() {
|
||||
let message = ChatMessage::assistant_with_tool_calls(
|
||||
"日报已整理完成。",
|
||||
vec![ToolCall {
|
||||
id: "call-1".to_string(),
|
||||
name: "memory_manage".to_string(),
|
||||
arguments: json!({"action": "put"}),
|
||||
}],
|
||||
);
|
||||
|
||||
let outbound = ws_outbound_from_chat_message(&message);
|
||||
|
||||
assert_eq!(outbound.len(), 2);
|
||||
assert!(matches!(outbound[0], WsOutbound::AssistantResponse { .. }));
|
||||
assert!(matches!(outbound[1], WsOutbound::ToolCall { .. }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ws_outbound_from_chat_message_includes_tool_results() {
|
||||
let message = ChatMessage::tool("call-1", "calculator", "2");
|
||||
@ -491,4 +527,21 @@ mod tests {
|
||||
assert!(should_display_message_to_user(false, &pending));
|
||||
assert!(should_display_message_to_user(true, &completed));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_ws_tool_call_emitter_hides_completed_tool_results_when_disabled() {
|
||||
let (sender, mut receiver) = mpsc::channel(4);
|
||||
let emitter = WsToolCallEmitter {
|
||||
sender,
|
||||
show_tool_results: false,
|
||||
};
|
||||
|
||||
emitter
|
||||
.handle(ChatMessage::tool("call-1", "calculator", "2"))
|
||||
.await;
|
||||
|
||||
assert!(tokio::time::timeout(std::time::Duration::from_millis(50), receiver.recv())
|
||||
.await
|
||||
.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user