feat: ActionRequired (#5897)
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
use goose::conversation::message::{Message, MessageContent, ToolRequest, ToolResponse};
|
||||
use goose::conversation::message::{
|
||||
ActionRequiredData, Message, MessageContent, ToolRequest, ToolResponse,
|
||||
};
|
||||
use goose::utils::safe_truncate;
|
||||
use rmcp::model::{RawContent, ResourceContents, Role};
|
||||
use serde_json::Value;
|
||||
@@ -340,6 +342,14 @@ pub fn message_to_markdown(message: &Message, export_all_content: bool) -> Strin
|
||||
let mut md = String::new();
|
||||
for content in &message.content {
|
||||
match content {
|
||||
MessageContent::ActionRequired(action) => match &action.data {
|
||||
ActionRequiredData::ToolConfirmation { tool_name, .. } => {
|
||||
md.push_str(&format!(
|
||||
"**Action Required** (tool_confirmation): {}\n\n",
|
||||
tool_name
|
||||
));
|
||||
}
|
||||
},
|
||||
MessageContent::Text(text) => {
|
||||
md.push_str(&text.text);
|
||||
md.push_str("\n\n");
|
||||
|
||||
@@ -40,7 +40,7 @@ use rmcp::model::ServerNotification;
|
||||
use rmcp::model::{ErrorCode, ErrorData};
|
||||
|
||||
use goose::config::paths::Paths;
|
||||
use goose::conversation::message::{Message, MessageContent};
|
||||
use goose::conversation::message::{ActionRequiredData, Message, MessageContent};
|
||||
use rand::{distributions::Alphanumeric, Rng};
|
||||
use rustyline::EditMode;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -852,12 +852,24 @@ impl CliSession {
|
||||
result = stream.next() => {
|
||||
match result {
|
||||
Some(Ok(AgentEvent::Message(message))) => {
|
||||
// If it's a confirmation request, get approval but otherwise do not render/persist
|
||||
if let Some(MessageContent::ToolConfirmationRequest(confirmation)) = message.content.first() {
|
||||
let tool_call_confirmation = message.content.iter().find_map(|content| {
|
||||
if let MessageContent::ActionRequired(action) = content {
|
||||
#[allow(irrefutable_let_patterns)] // this is a one variant enum right now but it will have more
|
||||
if let ActionRequiredData::ToolConfirmation { id, tool_name, arguments, prompt } = &action.data {
|
||||
Some((id.clone(), tool_name.clone(), arguments.clone(), prompt.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
if let Some((id, _tool_name, _arguments, security_prompt)) = tool_call_confirmation {
|
||||
output::hide_thinking();
|
||||
|
||||
// Format the confirmation prompt - use security message if present, otherwise use generic message
|
||||
let prompt = if let Some(security_message) = &confirmation.prompt {
|
||||
let prompt = if let Some(security_message) = &security_prompt {
|
||||
println!("\n{}", security_message);
|
||||
"Do you allow this tool call?".to_string()
|
||||
} else {
|
||||
@@ -865,7 +877,7 @@ impl CliSession {
|
||||
};
|
||||
|
||||
// Get confirmation from user
|
||||
let permission_result = if confirmation.prompt.is_none() {
|
||||
let permission_result = if security_prompt.is_none() {
|
||||
// No security message - show all options including "Always Allow"
|
||||
cliclack::select(prompt)
|
||||
.item(Permission::AllowOnce, "Allow", "Allow the tool call once")
|
||||
@@ -883,13 +895,12 @@ impl CliSession {
|
||||
};
|
||||
|
||||
let permission = match permission_result {
|
||||
Ok(p) => p, // If Ok, use the selected permission
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
// Check if the error is an interruption (Ctrl+C/Cmd+C, Escape)
|
||||
if e.kind() == std::io::ErrorKind::Interrupted {
|
||||
Permission::Cancel // If interrupted, set permission to Cancel
|
||||
Permission::Cancel
|
||||
} else {
|
||||
return Err(e.into()); // Otherwise, convert and propagate the original error
|
||||
return Err(e.into());
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -899,7 +910,7 @@ impl CliSession {
|
||||
|
||||
let mut response_message = Message::user();
|
||||
response_message.content.push(MessageContent::tool_response(
|
||||
confirmation.id.clone(),
|
||||
id.clone(),
|
||||
Err(ErrorData { code: ErrorCode::INVALID_REQUEST, message: std::borrow::Cow::from("Tool call cancelled by user".to_string()), data: None })
|
||||
));
|
||||
self.messages.push(response_message);
|
||||
@@ -907,10 +918,10 @@ impl CliSession {
|
||||
drop(stream);
|
||||
break;
|
||||
} else {
|
||||
self.agent.handle_confirmation(confirmation.id.clone(), PermissionConfirmation {
|
||||
self.agent.handle_confirmation(id.clone(), PermissionConfirmation {
|
||||
principal_type: PrincipalType::Tool,
|
||||
permission,
|
||||
},).await;
|
||||
}).await;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -2,7 +2,9 @@ use anstream::println;
|
||||
use bat::WrappingMode;
|
||||
use console::{measure_text_width, style, Color, Term};
|
||||
use goose::config::Config;
|
||||
use goose::conversation::message::{Message, MessageContent, ToolRequest, ToolResponse};
|
||||
use goose::conversation::message::{
|
||||
ActionRequiredData, Message, MessageContent, ToolRequest, ToolResponse,
|
||||
};
|
||||
use goose::providers::pricing::get_model_pricing;
|
||||
use goose::providers::pricing::parse_model_id;
|
||||
use goose::utils::safe_truncate;
|
||||
@@ -166,6 +168,11 @@ pub fn render_message(message: &Message, debug: bool) {
|
||||
|
||||
for content in &message.content {
|
||||
match content {
|
||||
MessageContent::ActionRequired(action) => match &action.data {
|
||||
ActionRequiredData::ToolConfirmation { tool_name, .. } => {
|
||||
println!("action_required(tool_confirmation): {}", tool_name)
|
||||
}
|
||||
},
|
||||
MessageContent::Text(text) => print_markdown(&text.text, theme),
|
||||
MessageContent::ToolRequest(req) => render_tool_request(req, theme, debug),
|
||||
MessageContent::ToolResponse(resp) => render_tool_response(resp, theme, debug),
|
||||
|
||||
@@ -19,9 +19,9 @@ use goose::config::declarative_providers::{
|
||||
DeclarativeProviderConfig, LoadedProvider, ProviderEngine,
|
||||
};
|
||||
use goose::conversation::message::{
|
||||
FrontendToolRequest, Message, MessageContent, MessageMetadata, RedactedThinkingContent,
|
||||
SystemNotificationContent, SystemNotificationType, ThinkingContent, TokenState,
|
||||
ToolConfirmationRequest, ToolRequest, ToolResponse,
|
||||
ActionRequired, ActionRequiredData, FrontendToolRequest, Message, MessageContent,
|
||||
MessageMetadata, RedactedThinkingContent, SystemNotificationContent, SystemNotificationType,
|
||||
ThinkingContent, TokenState, ToolConfirmationRequest, ToolRequest, ToolResponse,
|
||||
};
|
||||
|
||||
use crate::routes::recipe_utils::RecipeManifest;
|
||||
@@ -358,7 +358,7 @@ derive_utoipa!(Icon as IconSchema);
|
||||
super::routes::agent::agent_remove_extension,
|
||||
super::routes::agent::update_agent_provider,
|
||||
super::routes::agent::update_router_tool_selector,
|
||||
super::routes::reply::confirm_permission,
|
||||
super::routes::action_required::confirm_tool_action,
|
||||
super::routes::reply::reply,
|
||||
super::routes::session::list_sessions,
|
||||
super::routes::session::get_session,
|
||||
@@ -411,7 +411,7 @@ derive_utoipa!(Icon as IconSchema);
|
||||
super::routes::config_management::UpdateCustomProviderRequest,
|
||||
super::routes::config_management::CheckProviderRequest,
|
||||
super::routes::config_management::SetProviderRequest,
|
||||
super::routes::reply::PermissionConfirmationRequest,
|
||||
super::routes::action_required::ConfirmToolActionRequest,
|
||||
super::routes::reply::ChatRequest,
|
||||
super::routes::session::ImportSessionRequest,
|
||||
super::routes::session::SessionListResponse,
|
||||
@@ -438,6 +438,8 @@ derive_utoipa!(Icon as IconSchema);
|
||||
ToolResponse,
|
||||
ToolRequest,
|
||||
ToolConfirmationRequest,
|
||||
ActionRequired,
|
||||
ActionRequiredData,
|
||||
ThinkingContent,
|
||||
RedactedThinkingContent,
|
||||
FrontendToolRequest,
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
use crate::state::AppState;
|
||||
use axum::{extract::State, http::StatusCode, routing::post, Json, Router};
|
||||
use goose::permission::permission_confirmation::PrincipalType;
|
||||
use goose::permission::{Permission, PermissionConfirmation};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::sync::Arc;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, ToSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ConfirmToolActionRequest {
|
||||
id: String,
|
||||
#[serde(default = "default_principal_type")]
|
||||
principal_type: PrincipalType,
|
||||
action: String,
|
||||
session_id: String,
|
||||
}
|
||||
|
||||
fn default_principal_type() -> PrincipalType {
|
||||
PrincipalType::Tool
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/action-required/tool-confirmation",
|
||||
request_body = ConfirmToolActionRequest,
|
||||
responses(
|
||||
(status = 200, description = "Tool confirmation action is confirmed", body = Value),
|
||||
(status = 401, description = "Unauthorized - invalid secret key"),
|
||||
(status = 500, description = "Internal server error")
|
||||
)
|
||||
)]
|
||||
pub async fn confirm_tool_action(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Json(request): Json<ConfirmToolActionRequest>,
|
||||
) -> Result<Json<Value>, StatusCode> {
|
||||
let agent = state.get_agent_for_route(request.session_id).await?;
|
||||
let permission = match request.action.as_str() {
|
||||
"always_allow" => Permission::AlwaysAllow,
|
||||
"allow_once" => Permission::AllowOnce,
|
||||
"deny" => Permission::DenyOnce,
|
||||
_ => Permission::DenyOnce,
|
||||
};
|
||||
|
||||
agent
|
||||
.handle_confirmation(
|
||||
request.id.clone(),
|
||||
PermissionConfirmation {
|
||||
principal_type: request.principal_type,
|
||||
permission,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(Json(Value::Object(serde_json::Map::new())))
|
||||
}
|
||||
|
||||
pub fn routes(state: Arc<AppState>) -> Router {
|
||||
Router::new()
|
||||
.route(
|
||||
"/action-required/tool-confirmation",
|
||||
post(confirm_tool_action),
|
||||
)
|
||||
.with_state(state)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
mod integration_tests {
|
||||
use super::*;
|
||||
use axum::{body::Body, http::Request};
|
||||
use tower::ServiceExt;
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_tool_confirmation_endpoint() {
|
||||
let state = AppState::new().await.unwrap();
|
||||
|
||||
let app = routes(state);
|
||||
|
||||
let request = Request::builder()
|
||||
.uri("/action-required/tool-confirmation")
|
||||
.method("POST")
|
||||
.header("content-type", "application/json")
|
||||
.header("x-secret-key", "test-secret")
|
||||
.body(Body::from(
|
||||
serde_json::to_string(&ConfirmToolActionRequest {
|
||||
id: "test-id".to_string(),
|
||||
principal_type: PrincipalType::Tool,
|
||||
action: "allow_once".to_string(),
|
||||
session_id: "test-session".to_string(),
|
||||
})
|
||||
.unwrap(),
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
let response = app.oneshot(request).await.unwrap();
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod action_required;
|
||||
pub mod agent;
|
||||
pub mod audio;
|
||||
pub mod config_management;
|
||||
@@ -22,6 +23,7 @@ pub fn configure(state: Arc<crate::state::AppState>, secret_key: String) -> Rout
|
||||
Router::new()
|
||||
.merge(status::routes())
|
||||
.merge(reply::routes(state.clone()))
|
||||
.merge(action_required::routes(state.clone()))
|
||||
.merge(agent::routes(state.clone()))
|
||||
.merge(audio::routes(state.clone()))
|
||||
.merge(config_management::routes(state.clone()))
|
||||
|
||||
@@ -8,17 +8,12 @@ use axum::{
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use futures::{stream::StreamExt, Stream};
|
||||
use goose::agents::{AgentEvent, SessionConfig};
|
||||
use goose::conversation::message::{Message, MessageContent, TokenState};
|
||||
use goose::conversation::Conversation;
|
||||
use goose::permission::{Permission, PermissionConfirmation};
|
||||
use goose::session::SessionManager;
|
||||
use goose::{
|
||||
agents::{AgentEvent, SessionConfig},
|
||||
permission::permission_confirmation::PrincipalType,
|
||||
};
|
||||
use rmcp::model::ServerNotification;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::{
|
||||
convert::Infallible,
|
||||
pin::Pin,
|
||||
@@ -30,7 +25,6 @@ use tokio::sync::mpsc;
|
||||
use tokio::time::timeout;
|
||||
use tokio_stream::wrappers::ReceiverStream;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
fn track_tool_telemetry(content: &MessageContent, all_messages: &[Message]) {
|
||||
match content {
|
||||
@@ -452,60 +446,12 @@ pub async fn reply(
|
||||
Ok(SseResponse::new(stream))
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, ToSchema)]
|
||||
pub struct PermissionConfirmationRequest {
|
||||
id: String,
|
||||
#[serde(default = "default_principal_type")]
|
||||
principal_type: PrincipalType,
|
||||
action: String,
|
||||
session_id: String,
|
||||
}
|
||||
|
||||
fn default_principal_type() -> PrincipalType {
|
||||
PrincipalType::Tool
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/confirm",
|
||||
request_body = PermissionConfirmationRequest,
|
||||
responses(
|
||||
(status = 200, description = "Permission action is confirmed", body = Value),
|
||||
(status = 401, description = "Unauthorized - invalid secret key"),
|
||||
(status = 500, description = "Internal server error")
|
||||
)
|
||||
)]
|
||||
pub async fn confirm_permission(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Json(request): Json<PermissionConfirmationRequest>,
|
||||
) -> Result<Json<Value>, StatusCode> {
|
||||
let agent = state.get_agent_for_route(request.session_id).await?;
|
||||
let permission = match request.action.as_str() {
|
||||
"always_allow" => Permission::AlwaysAllow,
|
||||
"allow_once" => Permission::AllowOnce,
|
||||
"deny" => Permission::DenyOnce,
|
||||
_ => Permission::DenyOnce,
|
||||
};
|
||||
|
||||
agent
|
||||
.handle_confirmation(
|
||||
request.id.clone(),
|
||||
PermissionConfirmation {
|
||||
principal_type: request.principal_type,
|
||||
permission,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
Ok(Json(Value::Object(serde_json::Map::new())))
|
||||
}
|
||||
|
||||
pub fn routes(state: Arc<AppState>) -> Router {
|
||||
Router::new()
|
||||
.route(
|
||||
"/reply",
|
||||
post(reply).layer(DefaultBodyLimit::max(50 * 1024 * 1024)),
|
||||
)
|
||||
.route("/confirm", post(confirm_permission))
|
||||
.with_state(state)
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ impl Agent {
|
||||
});
|
||||
|
||||
let confirmation = Message::assistant()
|
||||
.with_tool_confirmation_request(
|
||||
.with_action_required(
|
||||
request.id.clone(),
|
||||
tool_call.name.to_string().clone(),
|
||||
tool_call.arguments.clone().unwrap_or_default(),
|
||||
|
||||
@@ -375,6 +375,14 @@ fn format_message_for_compacting(msg: &Message) -> String {
|
||||
MessageContent::ToolConfirmationRequest(req) => {
|
||||
format!("tool_confirmation_request: {}", req.tool_name)
|
||||
}
|
||||
MessageContent::ActionRequired(action) => match &action.data {
|
||||
crate::conversation::message::ActionRequiredData::ToolConfirmation {
|
||||
tool_name,
|
||||
..
|
||||
} => {
|
||||
format!("action_required(tool_confirmation): {}", tool_name)
|
||||
}
|
||||
},
|
||||
MessageContent::FrontendToolRequest(req) => {
|
||||
if let Ok(call) = &req.tool_call {
|
||||
format!("frontend_tool_request: {}", call.name)
|
||||
|
||||
@@ -101,6 +101,24 @@ pub struct ToolConfirmationRequest {
|
||||
pub prompt: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)]
|
||||
#[serde(tag = "actionType", rename_all = "camelCase")]
|
||||
pub enum ActionRequiredData {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
ToolConfirmation {
|
||||
id: String,
|
||||
tool_name: String,
|
||||
arguments: JsonObject,
|
||||
prompt: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ActionRequired {
|
||||
pub data: ActionRequiredData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)]
|
||||
pub struct ThinkingContent {
|
||||
pub thinking: String,
|
||||
@@ -144,6 +162,7 @@ pub enum MessageContent {
|
||||
ToolRequest(ToolRequest),
|
||||
ToolResponse(ToolResponse),
|
||||
ToolConfirmationRequest(ToolConfirmationRequest),
|
||||
ActionRequired(ActionRequired),
|
||||
FrontendToolRequest(FrontendToolRequest),
|
||||
Thinking(ThinkingContent),
|
||||
RedactedThinking(RedactedThinkingContent),
|
||||
@@ -169,6 +188,11 @@ impl fmt::Display for MessageContent {
|
||||
MessageContent::ToolConfirmationRequest(r) => {
|
||||
write!(f, "[ToolConfirmationRequest: {}]", r.tool_name)
|
||||
}
|
||||
MessageContent::ActionRequired(a) => match &a.data {
|
||||
ActionRequiredData::ToolConfirmation { tool_name, .. } => {
|
||||
write!(f, "[ActionRequired: ToolConfirmation for {}]", tool_name)
|
||||
}
|
||||
},
|
||||
MessageContent::FrontendToolRequest(r) => match &r.tool_call {
|
||||
Ok(tool_call) => write!(f, "[FrontendToolRequest: {}]", tool_call.name),
|
||||
Err(e) => write!(f, "[FrontendToolRequest: Error: {}]", e),
|
||||
@@ -234,17 +258,19 @@ impl MessageContent {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn tool_confirmation_request<S: Into<String>>(
|
||||
pub fn action_required<S: Into<String>>(
|
||||
id: S,
|
||||
tool_name: String,
|
||||
arguments: JsonObject,
|
||||
prompt: Option<String>,
|
||||
) -> Self {
|
||||
MessageContent::ToolConfirmationRequest(ToolConfirmationRequest {
|
||||
id: id.into(),
|
||||
tool_name,
|
||||
arguments,
|
||||
prompt,
|
||||
MessageContent::ActionRequired(ActionRequired {
|
||||
data: ActionRequiredData::ToolConfirmation {
|
||||
id: id.into(),
|
||||
tool_name,
|
||||
arguments,
|
||||
prompt,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -303,9 +329,9 @@ impl MessageContent {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_tool_confirmation_request(&self) -> Option<&ToolConfirmationRequest> {
|
||||
if let MessageContent::ToolConfirmationRequest(ref tool_confirmation_request) = self {
|
||||
Some(tool_confirmation_request)
|
||||
pub fn as_action_required(&self) -> Option<&ActionRequired> {
|
||||
if let MessageContent::ActionRequired(ref action_required) = self {
|
||||
Some(action_required)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -582,15 +608,15 @@ impl Message {
|
||||
self.with_content(MessageContent::tool_response(id, result))
|
||||
}
|
||||
|
||||
/// Add a tool confirmation request to the message
|
||||
pub fn with_tool_confirmation_request<S: Into<String>>(
|
||||
/// Add an action required message for tool confirmation
|
||||
pub fn with_action_required<S: Into<String>>(
|
||||
self,
|
||||
id: S,
|
||||
tool_name: String,
|
||||
arguments: JsonObject,
|
||||
prompt: Option<String>,
|
||||
) -> Self {
|
||||
self.with_content(MessageContent::tool_confirmation_request(
|
||||
self.with_content(MessageContent::action_required(
|
||||
id, tool_name, arguments, prompt,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -91,6 +91,9 @@ pub fn format_messages(messages: &[Message]) -> Vec<Value> {
|
||||
MessageContent::ToolConfirmationRequest(_tool_confirmation_request) => {
|
||||
// Skip tool confirmation requests
|
||||
}
|
||||
MessageContent::ActionRequired(_action_required) => {
|
||||
// Skip action required messages - they're for UI only
|
||||
}
|
||||
MessageContent::SystemNotification(_) => {
|
||||
// Skip
|
||||
}
|
||||
|
||||
@@ -37,6 +37,9 @@ pub fn to_bedrock_message_content(content: &MessageContent) -> Result<bedrock::C
|
||||
MessageContent::ToolConfirmationRequest(_tool_confirmation_request) => {
|
||||
bedrock::ContentBlock::Text("".to_string())
|
||||
}
|
||||
MessageContent::ActionRequired(_action_required) => {
|
||||
bedrock::ContentBlock::Text("".to_string())
|
||||
}
|
||||
MessageContent::Image(image) => {
|
||||
bedrock::ContentBlock::Image(to_bedrock_image(&image.data, &image.mime_type)?)
|
||||
}
|
||||
|
||||
@@ -208,9 +208,8 @@ fn format_messages(messages: &[Message], image_format: &ImageFormat) -> Vec<Data
|
||||
}
|
||||
}
|
||||
}
|
||||
MessageContent::ToolConfirmationRequest(_) => {
|
||||
// Skip tool confirmation requests
|
||||
}
|
||||
MessageContent::ToolConfirmationRequest(_) => {}
|
||||
MessageContent::ActionRequired(_) => {}
|
||||
MessageContent::Image(image) => {
|
||||
content_array.push(convert_image(image, image_format));
|
||||
}
|
||||
|
||||
@@ -19,10 +19,12 @@ pub fn format_messages(messages: &[Message]) -> Vec<Value> {
|
||||
.iter()
|
||||
.filter(|m| m.is_agent_visible())
|
||||
.filter(|message| {
|
||||
message
|
||||
.content
|
||||
.iter()
|
||||
.any(|content| !matches!(content, MessageContent::ToolConfirmationRequest(_)))
|
||||
message.content.iter().any(|content| {
|
||||
!matches!(
|
||||
content,
|
||||
MessageContent::ToolConfirmationRequest(_) | MessageContent::ActionRequired(_)
|
||||
)
|
||||
})
|
||||
})
|
||||
.map(|message| {
|
||||
let role = if message.role == Role::User {
|
||||
@@ -408,11 +410,11 @@ mod tests {
|
||||
)
|
||||
}
|
||||
|
||||
fn set_up_tool_confirmation_message(id: &str, tool_call: CallToolRequestParam) -> Message {
|
||||
fn set_up_action_required_message(id: &str, tool_call: CallToolRequestParam) -> Message {
|
||||
Message::new(
|
||||
Role::User,
|
||||
0,
|
||||
vec![MessageContent::tool_confirmation_request(
|
||||
vec![MessageContent::action_required(
|
||||
id.to_string(),
|
||||
tool_call.name.to_string().clone(),
|
||||
tool_call.arguments.unwrap_or_default().clone(),
|
||||
@@ -474,7 +476,7 @@ mod tests {
|
||||
arguments: Some(object(arguments.clone())),
|
||||
},
|
||||
),
|
||||
set_up_tool_confirmation_message(
|
||||
set_up_action_required_message(
|
||||
"id2",
|
||||
CallToolRequestParam {
|
||||
name: "tool_name_2".into(),
|
||||
|
||||
@@ -198,9 +198,8 @@ pub fn format_messages(messages: &[Message], image_format: &ImageFormat) -> Vec<
|
||||
}
|
||||
}
|
||||
}
|
||||
MessageContent::ToolConfirmationRequest(_) => {
|
||||
// Skip tool confirmation requests
|
||||
}
|
||||
MessageContent::ToolConfirmationRequest(_) => {}
|
||||
MessageContent::ActionRequired(_) => {}
|
||||
MessageContent::Image(image) => {
|
||||
content_array.push(convert_image(image, image_format));
|
||||
}
|
||||
|
||||
@@ -50,9 +50,8 @@ pub fn format_messages(messages: &[Message]) -> Vec<Value> {
|
||||
}
|
||||
}
|
||||
}
|
||||
MessageContent::ToolConfirmationRequest(_) => {
|
||||
// Skip tool confirmation requests
|
||||
}
|
||||
MessageContent::ToolConfirmationRequest(_) => {}
|
||||
MessageContent::ActionRequired(_) => {}
|
||||
MessageContent::SystemNotification(_) => {
|
||||
// Skip
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user