feat: ActionRequired (#5897)

This commit is contained in:
Alex Hancock
2025-12-02 11:48:28 -05:00
committed by GitHub
parent bf188cd9e2
commit 131c7e7228
22 changed files with 428 additions and 224 deletions
+1 -1
View File
@@ -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(),
+8
View File
@@ -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)
+38 -12
View File
@@ -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));
}
+9 -7
View File
@@ -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(),
+2 -3
View File
@@ -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
}