fix: empty tool results can't cause errors in bedrock, claude, openai, or vertexai clients anymore
This commit is contained in:
4 files changed
+203
-19
No files matched your search
+54
-7
@@ -457,10 +457,15 @@ fn build_chat_completions_body(
|
||||
}
|
||||
}));
|
||||
}
|
||||
vec![
|
||||
json!({ "role": "assistant", "content": assistant_parts }),
|
||||
json!({ "role": "user", "content": user_parts }),
|
||||
]
|
||||
// Empty tool_results (reachable via deserialized sessions) must not emit an empty pair.
|
||||
if assistant_parts.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
vec![
|
||||
json!({ "role": "assistant", "content": assistant_parts }),
|
||||
json!({ "role": "user", "content": user_parts }),
|
||||
]
|
||||
}
|
||||
} else {
|
||||
// One pair per round: Claude can reuse tool_use IDs across API calls.
|
||||
// A round boundary is detected by the presence of round text, but
|
||||
@@ -872,7 +877,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn build_body(tool_results: Vec<ToolResult>) -> Value {
|
||||
fn build_body_with(tool_results: Vec<ToolResult>, text: &str, sequence: bool) -> Value {
|
||||
let data = ChatCompletionsData {
|
||||
messages: vec![
|
||||
Message::new(MessageRole::User, MessageContent::Text("hello".to_string())),
|
||||
@@ -880,8 +885,8 @@ mod tests {
|
||||
MessageRole::Assistant,
|
||||
MessageContent::ToolCalls(MessageContentToolCalls {
|
||||
tool_results,
|
||||
text: String::new(),
|
||||
sequence: true,
|
||||
text: text.to_string(),
|
||||
sequence,
|
||||
}),
|
||||
),
|
||||
],
|
||||
@@ -894,6 +899,10 @@ mod tests {
|
||||
build_chat_completions_body(data, &Model::new("bedrock", "test"), false).unwrap()
|
||||
}
|
||||
|
||||
fn build_body(tool_results: Vec<ToolResult>) -> Value {
|
||||
build_body_with(tool_results, "", true)
|
||||
}
|
||||
|
||||
fn tool_result_content_block(output: Value) -> Value {
|
||||
let body = build_body(vec![tool_result(output)]);
|
||||
body["messages"][2]["content"][0]["toolResult"]["content"][0].clone()
|
||||
@@ -1063,6 +1072,44 @@ mod tests {
|
||||
assert_unique_tool_use_ids_per_message(&body);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_sequence_emits_assistant_user_pair() {
|
||||
let body = build_body_with(
|
||||
vec![
|
||||
sequence_tool_result("toolu_A", None),
|
||||
sequence_tool_result("toolu_B", None),
|
||||
],
|
||||
"",
|
||||
false,
|
||||
);
|
||||
|
||||
let messages = body["messages"].as_array().unwrap();
|
||||
|
||||
assert_eq!(messages.len(), 3, "body: {body}");
|
||||
assert_eq!(messages[1]["role"], "assistant");
|
||||
assert_eq!(messages[1]["content"].as_array().unwrap().len(), 2);
|
||||
assert_eq!(messages[2]["role"], "user");
|
||||
assert_eq!(messages[2]["content"].as_array().unwrap().len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_sequence_empty_tool_results_emits_no_messages() {
|
||||
let body = build_body_with(vec![], "leftover text", false);
|
||||
|
||||
let messages = body["messages"].as_array().unwrap();
|
||||
|
||||
assert_eq!(messages.len(), 1, "body: {body}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sequence_empty_tool_results_emits_no_messages() {
|
||||
let body = build_body_with(vec![], "leftover text", true);
|
||||
|
||||
let messages = body["messages"].as_array().unwrap();
|
||||
|
||||
assert_eq!(messages.len(), 1, "body: {body}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bedrock_tool_result_object_uses_json_block() {
|
||||
assert_eq!(
|
||||
|
||||
Reference in new issue
Block a user