feat(providers/bedrock): prefix user content with sender name
Bedrock's Converse API multiplexes Claude, Llama, Titan, Mistral and others through a single content-block protocol. None of those families expose a per-message author identity in a unified way, so prefixing in the text content block keeps multi-user attribution consistent regardless of which underlying model the request lands on. buildUserContent now passes msg through messageutil.ApplyUserNamePrefix before constructing the text ContentBlockMemberText. Tool result paths go through a separate code path (toolResultBlock) and are unaffected. Image media handling is unchanged. Tests: - buildUserContent with Name set produces the prefixed text block - buildUserContent without Name preserves byte-identical behavior - existing image and base64 cases continue to pass The Bedrock build is gated behind //go:build bedrock so this path is only exercised in builds that opt in via that tag. Refs #2702.
This commit is contained in:
parent
cd4372368e
commit
5b0634286c
2 changed files with 45 additions and 2 deletions
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue