Update to rmcp 1.1.0 (#7619)
Co-authored-by: Alex Hancock <alexhancock@block.xyz>
This commit is contained in:
@@ -877,12 +877,10 @@ mod tests {
|
||||
if let Some(img_data) = s.strip_prefix("*img:") {
|
||||
MessageContent::image(format!("http://example.com/{}", img_data), "image/png")
|
||||
} else if let Some(tool_name) = s.strip_prefix("*tool:") {
|
||||
let tool_call = Ok(rmcp::model::CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: tool_name.to_string().into(),
|
||||
arguments: Some(serde_json::Map::new()),
|
||||
});
|
||||
let tool_call = Ok(
|
||||
rmcp::model::CallToolRequestParams::new(tool_name.to_string())
|
||||
.with_arguments(serde_json::Map::new()),
|
||||
);
|
||||
MessageContent::tool_request(format!("tool_{}", tool_name), tool_call)
|
||||
} else {
|
||||
MessageContent::text(s)
|
||||
|
||||
@@ -972,11 +972,7 @@ mod tests {
|
||||
Message::user().with_text("user text"),
|
||||
Message::assistant().with_text("assistant prelude").with_tool_request(
|
||||
"call-1",
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None, task: None,
|
||||
name: "tool_name".into(),
|
||||
arguments: Some(object!({"param": "value"})),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new("tool_name").with_arguments(object!({"param": "value"}))),
|
||||
),
|
||||
Message::user().with_tool_response(
|
||||
"call-1",
|
||||
@@ -998,11 +994,7 @@ mod tests {
|
||||
Message::user().with_text("user text"),
|
||||
Message::assistant().with_tool_request(
|
||||
"call-1",
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None, task: None,
|
||||
name: "tool_name".into(),
|
||||
arguments: Some(object!({"param": "value"})),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new("tool_name").with_arguments(object!({"param": "value"}))),
|
||||
),
|
||||
Message::user().with_tool_response(
|
||||
"call-1",
|
||||
@@ -1023,11 +1015,7 @@ mod tests {
|
||||
Message::user().with_text("user text"),
|
||||
Message::assistant().with_tool_request(
|
||||
"call-1",
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None, task: None,
|
||||
name: "tool_name".into(),
|
||||
arguments: Some(object!({"param": "value"})),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new("tool_name").with_arguments(object!({"param": "value"}))),
|
||||
),
|
||||
Message::user().with_tool_response(
|
||||
"call-1",
|
||||
|
||||
@@ -1019,21 +1019,14 @@ mod tests {
|
||||
)]
|
||||
#[test_case(
|
||||
vec![Message::new(Role::Assistant, 0, vec![
|
||||
MessageContent::tool_request("call_123", Ok(rmcp::model::CallToolRequestParams {
|
||||
name: "developer__shell".into(),
|
||||
arguments: Some(serde_json::from_value(json!({"cmd": "ls"})).unwrap()),
|
||||
meta: None, task: None,
|
||||
}))
|
||||
MessageContent::tool_request("call_123", Ok(rmcp::model::CallToolRequestParams::new("developer__shell").with_arguments(serde_json::from_value(json!({"cmd": "ls"})).unwrap())))
|
||||
])],
|
||||
&[json!({"type":"text","text":"Assistant: [tool_use: developer__shell id=call_123]"})]
|
||||
; "tool_request_no_user_fallback"
|
||||
)]
|
||||
#[test_case(
|
||||
vec![Message::new(Role::User, 0, vec![
|
||||
MessageContent::tool_response("call_123", Ok(rmcp::model::CallToolResult {
|
||||
content: vec![rmcp::model::Content::text("file1.txt\nfile2.txt")],
|
||||
is_error: None, structured_content: None, meta: None,
|
||||
}))
|
||||
MessageContent::tool_response("call_123", Ok(rmcp::model::CallToolResult::success(vec![rmcp::model::Content::text("file1.txt\nfile2.txt")])))
|
||||
])],
|
||||
&[json!({"type":"text","text":"Human: [tool_result id=call_123] file1.txt\nfile2.txt"})]
|
||||
; "tool_response"
|
||||
|
||||
@@ -876,12 +876,8 @@ mod tests {
|
||||
fn test_prepare_input_tool_request() {
|
||||
use rmcp::model::CallToolRequestParams;
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let tool_call = Ok(CallToolRequestParams {
|
||||
name: "developer__shell".into(),
|
||||
arguments: Some(serde_json::from_value(json!({"cmd": "ls"})).unwrap()),
|
||||
meta: None,
|
||||
task: None,
|
||||
});
|
||||
let tool_call = Ok(CallToolRequestParams::new("developer__shell")
|
||||
.with_arguments(serde_json::from_value(json!({"cmd": "ls"})).unwrap()));
|
||||
let messages = vec![Message::new(
|
||||
Role::Assistant,
|
||||
0,
|
||||
@@ -896,12 +892,7 @@ mod tests {
|
||||
fn test_prepare_input_tool_response() {
|
||||
use rmcp::model::{CallToolResult, Content};
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let result = CallToolResult {
|
||||
content: vec![Content::text("file1.txt\nfile2.txt")],
|
||||
is_error: None,
|
||||
structured_content: None,
|
||||
meta: None,
|
||||
};
|
||||
let result = CallToolResult::success(vec![Content::text("file1.txt\nfile2.txt")]);
|
||||
let messages = vec![Message::new(
|
||||
Role::User,
|
||||
0,
|
||||
|
||||
@@ -325,12 +325,8 @@ pub fn response_to_message(response: &Value) -> Result<Message> {
|
||||
.get(INPUT_FIELD)
|
||||
.ok_or_else(|| anyhow!("Missing tool_use input"))?;
|
||||
|
||||
let tool_call = CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: name.into(),
|
||||
arguments: Some(object(input.clone())),
|
||||
};
|
||||
let tool_call =
|
||||
CallToolRequestParams::new(name).with_arguments(object(input.clone()));
|
||||
message = message.with_tool_request(id, Ok(tool_call));
|
||||
}
|
||||
Some(THINKING_TYPE) => {
|
||||
@@ -692,11 +688,7 @@ where
|
||||
}
|
||||
};
|
||||
|
||||
let tool_call = CallToolRequestParams{
|
||||
meta: None, task: None,
|
||||
name: name.into(),
|
||||
arguments: Some(object(parsed_args))
|
||||
};
|
||||
let tool_call = CallToolRequestParams::new(name).with_arguments(object(parsed_args));
|
||||
|
||||
let mut message = Message::new(
|
||||
rmcp::model::Role::Assistant,
|
||||
@@ -1126,12 +1118,8 @@ mod tests {
|
||||
let messages = vec![
|
||||
Message::assistant().with_tool_request(
|
||||
"tool_1",
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: "calculator".into(),
|
||||
arguments: Some(object!({"expression": "2 + 2"})),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new("calculator")
|
||||
.with_arguments(object!({"expression": "2 + 2"}))),
|
||||
),
|
||||
Message::user().with_tool_response(
|
||||
"tool_1",
|
||||
@@ -1168,22 +1156,10 @@ mod tests {
|
||||
Message::user().with_text("Hello"),
|
||||
Message::assistant().with_text("").with_tool_request(
|
||||
"tool_1",
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: "search".into(),
|
||||
arguments: Some(object!({"query": "test"})),
|
||||
}),
|
||||
),
|
||||
Message::user().with_tool_response(
|
||||
"tool_1",
|
||||
Ok(rmcp::model::CallToolResult {
|
||||
content: vec![],
|
||||
structured_content: None,
|
||||
is_error: Some(false),
|
||||
meta: None,
|
||||
}),
|
||||
Ok(CallToolRequestParams::new("search").with_arguments(object!({"query": "test"}))),
|
||||
),
|
||||
Message::user()
|
||||
.with_tool_response("tool_1", Ok(rmcp::model::CallToolResult::success(vec![]))),
|
||||
];
|
||||
|
||||
let spec = format_messages(&messages);
|
||||
|
||||
@@ -312,12 +312,8 @@ pub fn from_bedrock_content_block(block: &bedrock::ContentBlock) -> Result<Messa
|
||||
bedrock::ContentBlock::Text(text) => MessageContent::text(text),
|
||||
bedrock::ContentBlock::ToolUse(tool_use) => MessageContent::tool_request(
|
||||
tool_use.tool_use_id.to_string(),
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: tool_use.name.clone().into(),
|
||||
arguments: Some(object(from_bedrock_json(&tool_use.input.clone())?)),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new(tool_use.name.clone())
|
||||
.with_arguments(object(from_bedrock_json(&tool_use.input.clone())?))),
|
||||
),
|
||||
bedrock::ContentBlock::ToolResult(tool_res) => MessageContent::tool_response(
|
||||
tool_res.tool_use_id.to_string(),
|
||||
@@ -333,12 +329,7 @@ pub fn from_bedrock_content_block(block: &bedrock::ContentBlock) -> Result<Messa
|
||||
.iter()
|
||||
.map(from_bedrock_tool_result_content_block)
|
||||
.collect::<ToolResult<Vec<_>>>()
|
||||
.map(|content| rmcp::model::CallToolResult {
|
||||
content,
|
||||
structured_content: None,
|
||||
is_error: Some(false),
|
||||
meta: None,
|
||||
})
|
||||
.map(rmcp::model::CallToolResult::success)
|
||||
},
|
||||
),
|
||||
bedrock::ContentBlock::CachePoint(_) => {
|
||||
@@ -612,12 +603,8 @@ mod tests {
|
||||
MessageContent::text("I'll use a tool"),
|
||||
MessageContent::tool_request(
|
||||
"tool_1".to_string(),
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: "test_tool".into(),
|
||||
arguments: Some(object(json!({"param": "value"}))),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new("test_tool")
|
||||
.with_arguments(object(json!({"param": "value"})))),
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -652,12 +639,9 @@ mod tests {
|
||||
Utc::now().timestamp(),
|
||||
vec![MessageContent::tool_response(
|
||||
"tool_1".to_string(),
|
||||
Ok(CallToolResult {
|
||||
content: vec![Content::text("Tool result text".to_string())],
|
||||
structured_content: None,
|
||||
is_error: Some(false),
|
||||
meta: None,
|
||||
}),
|
||||
Ok(CallToolResult::success(vec![Content::text(
|
||||
"Tool result text".to_string(),
|
||||
)])),
|
||||
)],
|
||||
);
|
||||
|
||||
@@ -690,21 +674,13 @@ mod tests {
|
||||
MessageContent::text("Using tools"),
|
||||
MessageContent::tool_request(
|
||||
"tool_1".to_string(),
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: "tool_a".into(),
|
||||
arguments: Some(object(json!({"key": "val"}))),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new("tool_a")
|
||||
.with_arguments(object(json!({"key": "val"})))),
|
||||
),
|
||||
MessageContent::tool_request(
|
||||
"tool_2".to_string(),
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: "tool_b".into(),
|
||||
arguments: Some(object(json!({"key": "val"}))),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new("tool_b")
|
||||
.with_arguments(object(json!({"key": "val"})))),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -366,12 +366,8 @@ pub fn response_to_message(response: &Value) -> anyhow::Result<Message> {
|
||||
Ok(params) => {
|
||||
content.push(MessageContent::tool_request(
|
||||
id,
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: function_name.into(),
|
||||
arguments: Some(object(params)),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new(function_name)
|
||||
.with_arguments(object(params))),
|
||||
));
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -755,12 +751,8 @@ mod tests {
|
||||
Message::user().with_text("How are you?"),
|
||||
Message::assistant().with_tool_request(
|
||||
"tool1",
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: "example".into(),
|
||||
arguments: Some(object!({"param1": "value1"})),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new("example")
|
||||
.with_arguments(object!({"param1": "value1"}))),
|
||||
),
|
||||
];
|
||||
|
||||
@@ -772,12 +764,7 @@ mod tests {
|
||||
|
||||
messages.push(Message::user().with_tool_response(
|
||||
tool_id,
|
||||
Ok(CallToolResult {
|
||||
content: vec![Content::text("Result")],
|
||||
structured_content: None,
|
||||
is_error: Some(false),
|
||||
meta: None,
|
||||
}),
|
||||
Ok(CallToolResult::success(vec![Content::text("Result")])),
|
||||
));
|
||||
|
||||
let as_value =
|
||||
@@ -802,12 +789,7 @@ mod tests {
|
||||
fn test_format_messages_multiple_content() -> anyhow::Result<()> {
|
||||
let mut messages = vec![Message::assistant().with_tool_request(
|
||||
"tool1",
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: "example".into(),
|
||||
arguments: Some(object!({"param1": "value1"})),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new("example").with_arguments(object!({"param1": "value1"}))),
|
||||
)];
|
||||
|
||||
let tool_id = if let MessageContent::ToolRequest(request) = &messages[0].content[0] {
|
||||
@@ -818,12 +800,7 @@ mod tests {
|
||||
|
||||
messages.push(Message::user().with_tool_response(
|
||||
tool_id,
|
||||
Ok(CallToolResult {
|
||||
content: vec![Content::text("Result")],
|
||||
structured_content: None,
|
||||
is_error: Some(false),
|
||||
meta: None,
|
||||
}),
|
||||
Ok(CallToolResult::success(vec![Content::text("Result")])),
|
||||
));
|
||||
|
||||
let as_value =
|
||||
@@ -1182,15 +1159,8 @@ mod tests {
|
||||
#[test]
|
||||
fn test_format_messages_tool_request_with_none_arguments() -> anyhow::Result<()> {
|
||||
// Test that tool calls with None arguments are formatted as "{}" string
|
||||
let message = Message::assistant().with_tool_request(
|
||||
"tool1",
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: "test_tool".into(),
|
||||
arguments: None, // This is the key case the fix addresses
|
||||
}),
|
||||
);
|
||||
let message = Message::assistant()
|
||||
.with_tool_request("tool1", Ok(CallToolRequestParams::new("test_tool")));
|
||||
|
||||
let spec = format_messages(&[message], &ImageFormat::OpenAi);
|
||||
let as_value = serde_json::to_value(spec)?;
|
||||
@@ -1215,12 +1185,8 @@ mod tests {
|
||||
// Test that tool calls with Some arguments are properly JSON-serialized
|
||||
let message = Message::assistant().with_tool_request(
|
||||
"tool1",
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: "test_tool".into(),
|
||||
arguments: Some(object!({"param": "value", "number": 42})),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new("test_tool")
|
||||
.with_arguments(object!({"param": "value", "number": 42}))),
|
||||
);
|
||||
|
||||
let spec = format_messages(&[message], &ImageFormat::OpenAi);
|
||||
@@ -1397,12 +1363,7 @@ mod tests {
|
||||
|
||||
let message = Message::assistant().with_tool_request_with_metadata(
|
||||
"tool1",
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: "test_tool".into(),
|
||||
arguments: Some(object!({"param": "value"})),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new("test_tool").with_arguments(object!({"param": "value"}))),
|
||||
Some(&metadata),
|
||||
None,
|
||||
);
|
||||
@@ -1534,12 +1495,7 @@ mod tests {
|
||||
|
||||
let message = Message::assistant().with_tool_request_with_metadata(
|
||||
"tool1",
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: "test_tool".into(),
|
||||
arguments: None,
|
||||
}),
|
||||
Ok(CallToolRequestParams::new("test_tool")),
|
||||
Some(&metadata),
|
||||
None,
|
||||
);
|
||||
|
||||
@@ -292,11 +292,12 @@ fn process_response_part_impl(
|
||||
|
||||
Some(MessageContent::tool_request_with_metadata(
|
||||
id,
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: name.to_string().into(),
|
||||
arguments,
|
||||
Ok({
|
||||
let mut params = CallToolRequestParams::new(name.to_string());
|
||||
if let Some(args) = arguments {
|
||||
params = params.with_arguments(args);
|
||||
}
|
||||
params
|
||||
}),
|
||||
metadata.as_ref(),
|
||||
))
|
||||
@@ -640,12 +641,7 @@ mod tests {
|
||||
0,
|
||||
vec![MessageContent::tool_response(
|
||||
id.to_string(),
|
||||
Ok(CallToolResult {
|
||||
content: tool_response,
|
||||
structured_content: None,
|
||||
is_error: Some(false),
|
||||
meta: None,
|
||||
}),
|
||||
Ok(CallToolResult::success(tool_response)),
|
||||
)],
|
||||
)
|
||||
}
|
||||
@@ -719,21 +715,11 @@ mod tests {
|
||||
let messages = vec![
|
||||
set_up_tool_request_message(
|
||||
"id",
|
||||
CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: "tool_name".into(),
|
||||
arguments: Some(object(arguments.clone())),
|
||||
},
|
||||
CallToolRequestParams::new("tool_name").with_arguments(object(arguments.clone())),
|
||||
),
|
||||
set_up_action_required_message(
|
||||
"id2",
|
||||
CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: "tool_name_2".into(),
|
||||
arguments: Some(object(arguments.clone())),
|
||||
},
|
||||
CallToolRequestParams::new("tool_name_2").with_arguments(object(arguments.clone())),
|
||||
),
|
||||
];
|
||||
let payload = format_messages(&messages);
|
||||
@@ -970,12 +956,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn tool_result(text: &str) -> CallToolResult {
|
||||
CallToolResult {
|
||||
content: vec![Content::text(text)],
|
||||
structured_content: None,
|
||||
is_error: Some(false),
|
||||
meta: None,
|
||||
}
|
||||
CallToolResult::success(vec![Content::text(text)])
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -58,12 +58,8 @@ pub fn parse_xml_tool_calls(content: &str) -> (Option<String>, Vec<MessageConten
|
||||
if is_valid_function_name(&function_name) {
|
||||
tool_calls.push(MessageContent::tool_request(
|
||||
id,
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: function_name.into(),
|
||||
arguments: Some(object(serde_json::Value::Object(arguments))),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new(function_name)
|
||||
.with_arguments(object(serde_json::Value::Object(arguments)))),
|
||||
));
|
||||
} else {
|
||||
let error = ErrorData {
|
||||
|
||||
@@ -409,12 +409,8 @@ pub fn response_to_message(response: &Value) -> anyhow::Result<Message> {
|
||||
Ok(params) => {
|
||||
content.push(MessageContent::tool_request_with_metadata(
|
||||
id,
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: function_name.into(),
|
||||
arguments: Some(object(params)),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new(function_name)
|
||||
.with_arguments(object(params))),
|
||||
metadata.as_ref(),
|
||||
));
|
||||
}
|
||||
@@ -670,12 +666,7 @@ where
|
||||
Ok(params) => {
|
||||
MessageContent::tool_request_with_metadata(
|
||||
id.clone(),
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: function_name.clone().into(),
|
||||
arguments: Some(object(params)),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new(function_name.clone()).with_arguments(object(params))),
|
||||
metadata,
|
||||
)
|
||||
},
|
||||
@@ -984,12 +975,8 @@ mod tests {
|
||||
Message::user().with_text("How are you?"),
|
||||
Message::assistant().with_tool_request(
|
||||
"tool1",
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: "example".into(),
|
||||
arguments: Some(object!({"param1": "value1"})),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new("example")
|
||||
.with_arguments(object!({"param1": "value1"}))),
|
||||
),
|
||||
];
|
||||
|
||||
@@ -1002,12 +989,7 @@ mod tests {
|
||||
|
||||
messages.push(Message::user().with_tool_response(
|
||||
tool_id,
|
||||
Ok(CallToolResult {
|
||||
content: vec![Content::text("Result")],
|
||||
structured_content: None,
|
||||
is_error: Some(false),
|
||||
meta: None,
|
||||
}),
|
||||
Ok(CallToolResult::success(vec![Content::text("Result")])),
|
||||
));
|
||||
|
||||
let spec = format_messages(&messages, &ImageFormat::OpenAi);
|
||||
@@ -1030,12 +1012,7 @@ mod tests {
|
||||
fn test_format_messages_multiple_content() -> anyhow::Result<()> {
|
||||
let mut messages = vec![Message::assistant().with_tool_request(
|
||||
"tool1",
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: "example".into(),
|
||||
arguments: Some(object!({"param1": "value1"})),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new("example").with_arguments(object!({"param1": "value1"}))),
|
||||
)];
|
||||
|
||||
// Get the ID from the tool request to use in the response
|
||||
@@ -1047,12 +1024,7 @@ mod tests {
|
||||
|
||||
messages.push(Message::user().with_tool_response(
|
||||
tool_id,
|
||||
Ok(CallToolResult {
|
||||
content: vec![Content::text("Result")],
|
||||
structured_content: None,
|
||||
is_error: Some(false),
|
||||
meta: None,
|
||||
}),
|
||||
Ok(CallToolResult::success(vec![Content::text("Result")])),
|
||||
));
|
||||
|
||||
let spec = format_messages(&messages, &ImageFormat::OpenAi);
|
||||
@@ -1340,15 +1312,8 @@ mod tests {
|
||||
#[test]
|
||||
fn test_format_messages_tool_request_with_none_arguments() -> anyhow::Result<()> {
|
||||
// Test that tool calls with None arguments are formatted as "{}" string
|
||||
let message = Message::assistant().with_tool_request(
|
||||
"tool1",
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: "test_tool".into(),
|
||||
arguments: None, // This is the key case the fix addresses
|
||||
}),
|
||||
);
|
||||
let message = Message::assistant()
|
||||
.with_tool_request("tool1", Ok(CallToolRequestParams::new("test_tool")));
|
||||
|
||||
let spec = format_messages(&[message], &ImageFormat::OpenAi);
|
||||
|
||||
@@ -1371,12 +1336,8 @@ mod tests {
|
||||
// Test that tool calls with Some arguments are properly JSON-serialized
|
||||
let message = Message::assistant().with_tool_request(
|
||||
"tool1",
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: "test_tool".into(),
|
||||
arguments: Some(object!({"param": "value", "number": 42})),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new("test_tool")
|
||||
.with_arguments(object!({"param": "value", "number": 42}))),
|
||||
);
|
||||
|
||||
let spec = format_messages(&[message], &ImageFormat::OpenAi);
|
||||
@@ -1403,12 +1364,7 @@ mod tests {
|
||||
// Test that FrontendToolRequest with None arguments are formatted as "{}" string
|
||||
let message = Message::assistant().with_frontend_tool_request(
|
||||
"frontend_tool1",
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: "frontend_test_tool".into(),
|
||||
arguments: None, // This is the key case the fix addresses
|
||||
}),
|
||||
Ok(CallToolRequestParams::new("frontend_test_tool")),
|
||||
);
|
||||
|
||||
let spec = format_messages(&[message], &ImageFormat::OpenAi);
|
||||
@@ -1432,12 +1388,8 @@ mod tests {
|
||||
// Test that FrontendToolRequest with Some arguments are properly JSON-serialized
|
||||
let message = Message::assistant().with_frontend_tool_request(
|
||||
"frontend_tool1",
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: "frontend_test_tool".into(),
|
||||
arguments: Some(object!({"action": "click", "element": "button"})),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new("frontend_test_tool")
|
||||
.with_arguments(object!({"action": "click", "element": "button"}))),
|
||||
);
|
||||
|
||||
let spec = format_messages(&[message], &ImageFormat::OpenAi);
|
||||
@@ -1925,12 +1877,8 @@ data: [DONE]"#;
|
||||
// Add a tool call to test that reasoning_content works with tool calls
|
||||
message = message.with_tool_request(
|
||||
"tool1",
|
||||
Ok(rmcp::model::CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: "test_tool".into(),
|
||||
arguments: Some(rmcp::object!({"param": "value"})),
|
||||
}),
|
||||
Ok(rmcp::model::CallToolRequestParams::new("test_tool")
|
||||
.with_arguments(rmcp::object!({"param": "value"}))),
|
||||
);
|
||||
|
||||
let spec = format_messages(&[message], &ImageFormat::OpenAi);
|
||||
|
||||
@@ -473,12 +473,8 @@ pub fn responses_api_to_message(response: &ResponsesApiResponse) -> anyhow::Resu
|
||||
ResponseContentBlock::ToolCall { id, name, input } => {
|
||||
content.push(MessageContent::tool_request(
|
||||
id.clone(),
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: name.clone().into(),
|
||||
arguments: Some(object(input.clone())),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new(name.clone())
|
||||
.with_arguments(object(input.clone()))),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -499,12 +495,8 @@ pub fn responses_api_to_message(response: &ResponsesApiResponse) -> anyhow::Resu
|
||||
|
||||
content.push(MessageContent::tool_request(
|
||||
id.clone(),
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: name.clone().into(),
|
||||
arguments: Some(object(parsed_args)),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new(name.clone())
|
||||
.with_arguments(object(parsed_args))),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -559,12 +551,8 @@ fn process_streaming_output_items(
|
||||
|
||||
content.push(MessageContent::tool_request(
|
||||
id,
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: name.into(),
|
||||
arguments: Some(object(parsed_args)),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new(name)
|
||||
.with_arguments(object(parsed_args))),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -584,12 +572,7 @@ fn process_streaming_output_items(
|
||||
|
||||
content.push(MessageContent::tool_request(
|
||||
call_id,
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: name.into(),
|
||||
arguments: Some(object(parsed_args)),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new(name).with_arguments(object(parsed_args))),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -835,23 +818,15 @@ mod tests {
|
||||
.with_text("I'll create that file.")
|
||||
.with_tool_request(
|
||||
"call_1",
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: "shell".into(),
|
||||
arguments: Some(object!({"command": "echo hello"})),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new("shell")
|
||||
.with_arguments(object!({"command": "echo hello"}))),
|
||||
),
|
||||
Message::assistant()
|
||||
.with_text("Now let me verify.")
|
||||
.with_tool_request(
|
||||
"call_2",
|
||||
Ok(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: "shell".into(),
|
||||
arguments: Some(object!({"command": "cat file.txt"})),
|
||||
}),
|
||||
Ok(CallToolRequestParams::new("shell")
|
||||
.with_arguments(object!({"command": "cat file.txt"}))),
|
||||
),
|
||||
];
|
||||
|
||||
|
||||
@@ -187,21 +187,11 @@ pub fn parse_streaming_response(sse_data: &str) -> Result<Message> {
|
||||
if !tool_input.is_empty() {
|
||||
let input_value = serde_json::from_str::<Value>(&tool_input)
|
||||
.unwrap_or_else(|_| Value::String(tool_input.clone()));
|
||||
let tool_call = CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: name.into(),
|
||||
arguments: Some(object(input_value)),
|
||||
};
|
||||
let tool_call = CallToolRequestParams::new(name).with_arguments(object(input_value));
|
||||
message = message.with_tool_request(&id, Ok(tool_call));
|
||||
} else {
|
||||
// Tool with no input - use empty object
|
||||
let tool_call = CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: name.into(),
|
||||
arguments: Some(object!({})),
|
||||
};
|
||||
let tool_call = CallToolRequestParams::new(name).with_arguments(object!({}));
|
||||
message = message.with_tool_request(&id, Ok(tool_call));
|
||||
}
|
||||
}
|
||||
@@ -258,12 +248,7 @@ pub fn response_to_message(response: &Value) -> Result<Message> {
|
||||
.ok_or_else(|| anyhow!("Missing tool input"))?
|
||||
.clone();
|
||||
|
||||
let tool_call = CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: name.into(),
|
||||
arguments: Some(object(input)),
|
||||
};
|
||||
let tool_call = CallToolRequestParams::new(name).with_arguments(object(input));
|
||||
message = message.with_tool_request(id, Ok(tool_call));
|
||||
}
|
||||
Some("thinking") => {
|
||||
@@ -700,12 +685,8 @@ data: {"id":"a9537c2c-2017-4906-9817-2456168d89fa","model":"claude-sonnet-4-2025
|
||||
use crate::conversation::message::Message;
|
||||
|
||||
// Create a conversation with text, tool requests, and tool responses
|
||||
let tool_call = CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: "calculator".into(),
|
||||
arguments: Some(object!({"expression": "2 + 2"})),
|
||||
};
|
||||
let tool_call = CallToolRequestParams::new("calculator")
|
||||
.with_arguments(object!({"expression": "2 + 2"}));
|
||||
|
||||
let messages = vec![
|
||||
Message::user().with_text("Calculate 2 + 2"),
|
||||
|
||||
@@ -313,12 +313,8 @@ fn send_emulator_action(
|
||||
let tool_id = Uuid::new_v4().to_string();
|
||||
let mut args = serde_json::Map::new();
|
||||
args.insert("command".to_string(), json!(command));
|
||||
let tool_call = CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: Cow::Borrowed(SHELL_TOOL),
|
||||
arguments: Some(args),
|
||||
};
|
||||
let tool_call =
|
||||
CallToolRequestParams::new(Cow::Borrowed(SHELL_TOOL)).with_arguments(args);
|
||||
let mut message = Message::assistant();
|
||||
message
|
||||
.content
|
||||
@@ -337,12 +333,8 @@ fn send_emulator_action(
|
||||
};
|
||||
let mut args = serde_json::Map::new();
|
||||
args.insert("code".to_string(), json!(wrapped));
|
||||
let tool_call = CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: Cow::Borrowed(CODE_EXECUTION_TOOL),
|
||||
arguments: Some(args),
|
||||
};
|
||||
let tool_call =
|
||||
CallToolRequestParams::new(Cow::Borrowed(CODE_EXECUTION_TOOL)).with_arguments(args);
|
||||
let mut message = Message::assistant();
|
||||
message
|
||||
.content
|
||||
|
||||
@@ -172,12 +172,8 @@ pub(super) fn extract_tool_call_messages(tool_calls_json: &str, message_id: &str
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_else(|| Uuid::new_v4().to_string());
|
||||
|
||||
let tool_call = CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: Cow::Owned(name),
|
||||
arguments,
|
||||
};
|
||||
let tool_call = CallToolRequestParams::new(Cow::Owned(name))
|
||||
.with_arguments(arguments.unwrap_or_default());
|
||||
|
||||
let mut msg = Message::assistant();
|
||||
msg.content
|
||||
@@ -319,11 +315,10 @@ pub(super) fn extract_xml_tool_call_messages(
|
||||
tool_calls
|
||||
.into_iter()
|
||||
.map(|(name, args)| {
|
||||
let tool_call = CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: Cow::Owned(name),
|
||||
arguments: if args.is_empty() { None } else { Some(args) },
|
||||
let tool_call = if args.is_empty() {
|
||||
CallToolRequestParams::new(Cow::Owned(name))
|
||||
} else {
|
||||
CallToolRequestParams::new(Cow::Owned(name)).with_arguments(args)
|
||||
};
|
||||
let mut msg = Message::assistant();
|
||||
msg.content.push(MessageContent::tool_request(
|
||||
|
||||
@@ -59,11 +59,11 @@ fn create_sample_weather_tool() -> Tool {
|
||||
}
|
||||
}),
|
||||
)
|
||||
.annotate(ToolAnnotations {
|
||||
title: Some("Get weather".to_string()),
|
||||
read_only_hint: Some(true),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
})
|
||||
.annotate(
|
||||
ToolAnnotations::with_title("Get weather".to_string())
|
||||
.read_only(true)
|
||||
.destructive(false)
|
||||
.idempotent(false)
|
||||
.open_world(false),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -11,10 +11,10 @@ use std::sync::{Arc, Mutex};
|
||||
use super::base::stream_from_single_message;
|
||||
use super::base::{MessageStream, Provider, ProviderDef, ProviderMetadata, ProviderUsage};
|
||||
use super::errors::ProviderError;
|
||||
use crate::conversation::message::Message;
|
||||
use crate::conversation::message::{Message, ToolResponse};
|
||||
use crate::model::ModelConfig;
|
||||
use futures::future::BoxFuture;
|
||||
use rmcp::model::Tool;
|
||||
use rmcp::model::{CallToolResult, Tool};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct TestInput {
|
||||
@@ -83,18 +83,28 @@ impl TestProvider {
|
||||
let stable_messages: Vec<_> = messages
|
||||
.iter()
|
||||
.map(|msg| {
|
||||
let cleaned_content: Vec<_> = msg
|
||||
.content
|
||||
.iter()
|
||||
.map(|c| match c {
|
||||
MessageContent::ToolRequest(req) => {
|
||||
let mut req = req.clone();
|
||||
let mut cleaned_content: Vec<_> = msg.content.to_vec();
|
||||
|
||||
for content in &mut cleaned_content {
|
||||
match content {
|
||||
MessageContent::ToolRequest(ref mut req) => {
|
||||
req.tool_meta = None;
|
||||
MessageContent::ToolRequest(req)
|
||||
}
|
||||
other => other.clone(),
|
||||
})
|
||||
.collect();
|
||||
MessageContent::ToolResponse(ToolResponse {
|
||||
tool_result:
|
||||
Ok(
|
||||
ref mut result @ CallToolResult {
|
||||
is_error: Some(false),
|
||||
..
|
||||
},
|
||||
),
|
||||
..
|
||||
}) => {
|
||||
result.is_error = None;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
(msg.role.clone(), cleaned_content)
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -226,12 +226,10 @@ impl OllamaInterpreter {
|
||||
let arguments = item["arguments"].clone();
|
||||
|
||||
// Add the tool call to our result vector
|
||||
tool_calls.push(CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: name.into(),
|
||||
arguments: Some(object(arguments)),
|
||||
});
|
||||
tool_calls.push(
|
||||
CallToolRequestParams::new(name)
|
||||
.with_arguments(object(arguments)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,12 +492,8 @@ impl Provider for VeniceProvider {
|
||||
function["arguments"].clone()
|
||||
};
|
||||
|
||||
let tool_call = CallToolRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
name: name.into(),
|
||||
arguments: Some(object(arguments)),
|
||||
};
|
||||
let tool_call =
|
||||
CallToolRequestParams::new(name).with_arguments(object(arguments));
|
||||
|
||||
// Create a ToolRequest MessageContent
|
||||
let tool_request = MessageContent::tool_request(id, ToolResult::Ok(tool_call));
|
||||
|
||||
Reference in New Issue
Block a user