Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions core/providers/bedrock/bedrock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3923,6 +3923,71 @@ func TestMultiTurnReasoningContentPassthrough(t *testing.T) {
assert.True(t, foundReasoning, "Expected reasoning content block in assistant message")
})

t.Run("AssistantMessage_WithReasoningAndToolCalls_ReasoningComesFirst", func(t *testing.T) {
reasoningText := "I need to call a tool to answer this."
signature := "sig_abc123"
assistantContent := "Let me check that for you."
toolCallID := "tooluse_abc123"

bifrostReq := &schemas.BifrostChatRequest{
Provider: schemas.Bedrock,
Model: "anthropic.claude-sonnet-4-6",
Input: []schemas.ChatMessage{
{
Role: schemas.ChatMessageRoleUser,
Content: &schemas.ChatMessageContent{ContentStr: schemas.Ptr("What time is it?")},
},
{
Role: schemas.ChatMessageRoleAssistant,
Content: &schemas.ChatMessageContent{ContentStr: &assistantContent},
ChatAssistantMessage: &schemas.ChatAssistantMessage{
ReasoningDetails: []schemas.ChatReasoningDetails{
{
Index: 0,
Type: schemas.BifrostReasoningDetailsTypeText,
Text: &reasoningText,
Signature: &signature,
},
},
ToolCalls: []schemas.ChatAssistantMessageToolCall{
{
ID: &toolCallID,
Type: schemas.Ptr("function"),
Function: schemas.ChatAssistantMessageToolCallFunction{
Name: schemas.Ptr("get_time"),
Arguments: "{}",
},
},
},
},
},
},
}

ctx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline)
result, err := bedrock.ToBedrockChatCompletionRequest(ctx, bifrostReq)
require.NoError(t, err)
require.NotNil(t, result)

assistantMsg := result.Messages[1]
// reasoning + text + tool_use = at least 3 blocks
require.GreaterOrEqual(t, len(assistantMsg.Content), 3)

// Reasoning MUST be the first block
assert.NotNil(t, assistantMsg.Content[0].ReasoningContent,
"reasoning block must be first content block; got %+v", assistantMsg.Content[0])

// tool_use must appear after reasoning
var foundToolUse bool
for _, block := range assistantMsg.Content[1:] {
if block.ToolUse != nil {
foundToolUse = true
break
}
}
assert.True(t, foundToolUse, "tool_use block must appear after reasoning block")
Comment thread
TejasGhatte marked this conversation as resolved.
})

t.Run("AssistantMessage_WithoutReasoningDetails_NoReasoningContent", func(t *testing.T) {
assistantContent := "Simple response"

Expand Down
34 changes: 17 additions & 17 deletions core/providers/bedrock/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -739,25 +739,9 @@ func convertMessage(ctx context.Context, msg schemas.ChatMessage) (BedrockMessag
Role: BedrockMessageRole(msg.Role),
}

// Convert content
var contentBlocks []BedrockContentBlock
if msg.Content != nil {
var err error
contentBlocks, err = convertContent(ctx, *msg.Content)
if err != nil {
return BedrockMessage{}, fmt.Errorf("failed to convert content: %w", err)
}
}

// Add tool calls if present (for assistant messages)
if msg.ChatAssistantMessage != nil && msg.ChatAssistantMessage.ToolCalls != nil {
for _, toolCall := range msg.ChatAssistantMessage.ToolCalls {
toolUseBlock := convertToolCallToContentBlock(toolCall)
contentBlocks = append(contentBlocks, toolUseBlock)
}
}

// Add reasoning content if present (for multi-turn conversations with thinking)
// Add reasoning content first
if msg.ChatAssistantMessage != nil && len(msg.ChatAssistantMessage.ReasoningDetails) > 0 {
for _, detail := range msg.ChatAssistantMessage.ReasoningDetails {
if detail.Type == schemas.BifrostReasoningDetailsTypeText {
Expand All @@ -773,6 +757,22 @@ func convertMessage(ctx context.Context, msg schemas.ChatMessage) (BedrockMessag
}
}

// Convert text/image content
if msg.Content != nil {
textBlocks, err := convertContent(ctx, *msg.Content)
if err != nil {
return BedrockMessage{}, fmt.Errorf("failed to convert content: %w", err)
}
contentBlocks = append(contentBlocks, textBlocks...)
}

// Add tool calls last (for assistant messages)
if msg.ChatAssistantMessage != nil && msg.ChatAssistantMessage.ToolCalls != nil {
for _, toolCall := range msg.ChatAssistantMessage.ToolCalls {
contentBlocks = append(contentBlocks, convertToolCallToContentBlock(toolCall))
}
}

bedrockMsg.Content = contentBlocks
return bedrockMsg, nil
}
Expand Down
Loading