diff --git a/pkg/providers/bedrock/provider_bedrock.go b/pkg/providers/bedrock/provider_bedrock.go index 3798c5fd8..b5221d4fc 100644 --- a/pkg/providers/bedrock/provider_bedrock.go +++ b/pkg/providers/bedrock/provider_bedrock.go @@ -27,6 +27,7 @@ import ( "github.com/aws/aws-sdk-go-v2/service/bedrockruntime/types" "github.com/sipeed/picoclaw/pkg/providers/common" + "github.com/sipeed/picoclaw/pkg/providers/messageutil" "github.com/sipeed/picoclaw/pkg/providers/protocoltypes" ) @@ -319,13 +320,19 @@ func convertMessages(messages []Message) ([]types.Message, []types.SystemContent } // buildUserContent builds Bedrock content blocks for a user message. +// +// Sender attribution from msg.Name is rendered as a `[name] ` prefix on +// the text block. Bedrock's Converse API has no per-message author identity +// shared across all model families, so prefixing keeps the multi-user +// attribution behavior consistent with the Anthropic adapter regardless +// of which underlying model the request lands on. func buildUserContent(msg Message) []types.ContentBlock { var content []types.ContentBlock // Add text content - if msg.Content != "" { + if textValue := messageutil.ApplyUserNamePrefix(msg); textValue != "" { content = append(content, &types.ContentBlockMemberText{ - Value: msg.Content, + Value: textValue, }) } diff --git a/pkg/providers/bedrock/provider_bedrock_test.go b/pkg/providers/bedrock/provider_bedrock_test.go index 38a5e26da..c897177d0 100644 --- a/pkg/providers/bedrock/provider_bedrock_test.go +++ b/pkg/providers/bedrock/provider_bedrock_test.go @@ -294,6 +294,42 @@ func TestBuildUserContent_SkipsNonBase64Data(t *testing.T) { assert.Len(t, content, 1) } +// TestBuildUserContent_UserNamePrefixed verifies that sender attribution +// from Message.Name is rendered as a `[name] ` prefix on the Bedrock text +// block. Bedrock's Converse API has no per-message author identity shared +// across model families, so prefixing keeps multi-user disambiguation +// consistent regardless of the underlying model (Claude, Llama, etc.). +func TestBuildUserContent_UserNamePrefixed(t *testing.T) { + msg := Message{ + Role: "user", + Content: "My name is Alice", + Name: "U_alice", + } + + content := buildUserContent(msg) + + assert.Len(t, content, 1) + textBlock, ok := content[0].(*types.ContentBlockMemberText) + require.True(t, ok) + assert.Equal(t, "[U_alice] My name is Alice", textBlock.Value) +} + +// TestBuildUserContent_NoNameNoPrefix protects backward compatibility for +// direct (single-user) channels: no sender, no prefix. +func TestBuildUserContent_NoNameNoPrefix(t *testing.T) { + msg := Message{ + Role: "user", + Content: "Hello", + } + + content := buildUserContent(msg) + + assert.Len(t, content, 1) + textBlock, ok := content[0].(*types.ContentBlockMemberText) + require.True(t, ok) + assert.Equal(t, "Hello", textBlock.Value) +} + func TestBuildAssistantContent_SkipsEmptyToolName(t *testing.T) { msg := Message{ Content: "Response",