bugfix: map bedrock tool-call indexes and tool_choice (#3786)

Signed-off-by: Betula-L <6059935+Betula-L@users.noreply.github.com>
Co-authored-by: Betula-L <6059935+Betula-L@users.noreply.github.com>
This commit is contained in:
Betula-L
2026-05-06 04:48:42 -07:00
committed by GitHub
parent 4aba4a9860
commit 6199fe414d
7 changed files with 557 additions and 34 deletions

View File

@@ -47,6 +47,8 @@ const (
bedrockCachePointPositionSystemPrompt = "systemPrompt"
bedrockCachePointPositionLastUserMessage = "lastUserMessage"
bedrockCachePointPositionLastMessage = "lastMessage"
ctxKeyBedrockToolCallState = "bedrock_tool_call_state"
)
var (
@@ -121,11 +123,13 @@ func (b *bedrockProvider) convertEventFromBedrockToOpenAI(ctx wrapper.HttpContex
chatChoice.Delta.Role = *bedrockEvent.Role
}
if bedrockEvent.Start != nil {
toolCallIndex := getBedrockOpenAIToolCallIndex(ctx, bedrockEvent.ContentBlockIndex)
chatChoice.Delta.Content = nil
chatChoice.Delta.ToolCalls = []toolCall{
{
Id: bedrockEvent.Start.ToolUse.ToolUseID,
Type: "function",
Index: toolCallIndex,
Id: bedrockEvent.Start.ToolUse.ToolUseID,
Type: "function",
Function: functionCall{
Name: bedrockEvent.Start.ToolUse.Name,
Arguments: "",
@@ -152,9 +156,11 @@ func (b *bedrockProvider) convertEventFromBedrockToOpenAI(ctx wrapper.HttpContex
chatChoice.Delta = &chatMessage{Content: &content}
}
if bedrockEvent.Delta.ToolUse != nil {
toolCallIndex := getBedrockOpenAIToolCallIndex(ctx, bedrockEvent.ContentBlockIndex)
chatChoice.Delta.ToolCalls = []toolCall{
{
Type: "function",
Index: toolCallIndex,
Type: "function",
Function: functionCall{
Arguments: bedrockEvent.Delta.ToolUse.Input,
},
@@ -192,6 +198,28 @@ func (b *bedrockProvider) convertEventFromBedrockToOpenAI(ctx wrapper.HttpContex
return []byte(openAIChunk.String()), nil
}
type bedrockToolCallState struct {
indexes map[int]int
next int
}
func getBedrockOpenAIToolCallIndex(ctx wrapper.HttpContext, contentBlockIndex int) int {
state, _ := ctx.GetContext(ctxKeyBedrockToolCallState).(*bedrockToolCallState)
if state == nil {
state = &bedrockToolCallState{indexes: make(map[int]int)}
ctx.SetContext(ctxKeyBedrockToolCallState, state)
}
if toolCallIndex, ok := state.indexes[contentBlockIndex]; ok {
return toolCallIndex
}
toolCallIndex := state.next
state.indexes[contentBlockIndex] = toolCallIndex
state.next++
return toolCallIndex
}
type ConverseStreamEvent struct {
ContentBlockIndex int `json:"contentBlockIndex,omitempty"`
Delta *converseStreamEventContentBlockDelta `json:"delta,omitempty"`
@@ -870,22 +898,25 @@ func (b *bedrockProvider) buildBedrockTextGenerationRequest(origRequest *chatCom
}
}
if origRequest.Tools != nil {
if origRequest.Tools != nil && origRequest.getToolChoiceType() != "none" {
request.ToolConfig = &bedrockToolConfig{}
if origRequest.ToolChoice == nil {
request.ToolConfig.ToolChoice.Auto = &struct{}{}
} else if choice_type, ok := origRequest.ToolChoice.(string); ok {
request.ToolConfig.ToolChoice.Auto = &struct{}{}
if choice_type := origRequest.getToolChoiceType(); choice_type != "" {
switch choice_type {
case "required":
// "any" is accepted for direct Anthropic-compatible callers; OpenAI
// uses "required" for the same "must call at least one tool" behavior.
case "required", "any":
request.ToolConfig.ToolChoice.Auto = nil
request.ToolConfig.ToolChoice.Any = &struct{}{}
case "auto":
request.ToolConfig.ToolChoice.Auto = &struct{}{}
case "none":
request.ToolConfig.ToolChoice.Auto = &struct{}{}
}
} else if choice, ok := origRequest.ToolChoice.(toolChoice); ok {
request.ToolConfig.ToolChoice.Tool = &bedrockToolSpecification{
Name: choice.Function.Name,
case "function":
if choice := origRequest.getToolChoiceObject(); choice != nil && choice.Function.Name != "" {
request.ToolConfig.ToolChoice.Auto = nil
request.ToolConfig.ToolChoice.Tool = &bedrockSpecificToolChoice{
Name: choice.Function.Name,
}
}
}
}
request.ToolConfig.Tools = []bedrockTool{}
@@ -1151,9 +1182,13 @@ type bedrockTool struct {
}
type bedrockToolChoice struct {
Any *struct{} `json:"any,omitempty"`
Auto *struct{} `json:"auto,omitempty"`
Tool *bedrockToolSpecification `json:"tool,omitempty"`
Any *struct{} `json:"any,omitempty"`
Auto *struct{} `json:"auto,omitempty"`
Tool *bedrockSpecificToolChoice `json:"tool,omitempty"`
}
type bedrockSpecificToolChoice struct {
Name string `json:"name"`
}
type bedrockToolSpecification struct {