From 323b5795780487a5511beb9ba72d6c07cc129a65 Mon Sep 17 00:00:00 2001 From: David Turnbull Date: Tue, 17 Feb 2026 01:45:19 +1100 Subject: [PATCH] bedrock: Add placeholder tool when summarising threads with tool history (#48863) Bedrock Converse API requires toolConfig whenever messages have tool use or result blocks. PR #33174 handled the case where tools are defined but tool_choice is None. This change addresses another case: the tools array is empty (e.g. thread summarisation) but messages still have tool blocks from conversation history. A placeholder tool satisfies the requirement. Testing: Tested manually by: 1. Starting a conversation with Bedrock that uses tools 2. Triggering thread summarisation 3. Confirming summarisation now succeeds instead of failing with an API error Release Notes: - Fixed Bedrock thread summarization failing when conversation had tools --- .../language_models/src/provider/bedrock.rs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/crates/language_models/src/provider/bedrock.rs b/crates/language_models/src/provider/bedrock.rs index 0f6a3cc152ae229eb2770057b5249e9bac80614d..677764d32c16d7f8456e45e0e53f1d5d67691599 100644 --- a/crates/language_models/src/provider/bedrock.rs +++ b/crates/language_models/src/provider/bedrock.rs @@ -776,6 +776,10 @@ pub fn into_bedrock( let mut new_messages: Vec = Vec::new(); let mut system_message = String::new(); + // Track whether messages contain tool content - Bedrock requires toolConfig + // when tool blocks are present, so we may need to add a dummy tool + let mut messages_contain_tool_content = false; + for message in request.messages { if message.contents_empty() { continue; @@ -829,6 +833,7 @@ pub fn into_bedrock( Some(BedrockInnerContent::ReasoningContent(redacted)) } MessageContent::ToolUse(tool_use) => { + messages_contain_tool_content = true; let input = if tool_use.input.is_null() { // Bedrock API requires valid JsonValue, not null, for tool use input value_to_aws_document(&serde_json::json!({})) @@ -845,6 +850,7 @@ pub fn into_bedrock( .map(BedrockInnerContent::ToolUse) } MessageContent::ToolResult(tool_result) => { + messages_contain_tool_content = true; BedrockToolResultBlock::builder() .tool_use_id(tool_result.tool_use_id.to_string()) .content(match tool_result.content { @@ -976,6 +982,23 @@ pub fn into_bedrock( }) .collect(); + // Bedrock requires toolConfig when messages contain tool use/result blocks. + // If no tools are defined but messages contain tool content (e.g., when + // summarising a conversation that used tools), add a dummy tool to satisfy + // the API requirement. + if tool_spec.is_empty() && messages_contain_tool_content { + tool_spec.push(BedrockTool::ToolSpec( + BedrockToolSpec::builder() + .name("_placeholder") + .description("Placeholder tool to satisfy Bedrock API requirements when conversation history contains tool usage") + .input_schema(BedrockToolInputSchema::Json(value_to_aws_document( + &serde_json::json!({"type": "object", "properties": {}}), + ))) + .build() + .context("failed to build placeholder tool spec")?, + )); + } + if !tool_spec.is_empty() && supports_caching { tool_spec.push(BedrockTool::CachePoint( CachePointBlock::builder()