feat: improve OpenAI messages compatibility for Claude Code
This commit is contained in:
@@ -120,7 +120,7 @@ func responsesStatusToAnthropicStopReason(status string, details *ResponsesIncom
|
||||
}
|
||||
return "end_turn"
|
||||
case "completed":
|
||||
if len(blocks) > 0 && blocks[len(blocks)-1].Type == "tool_use" {
|
||||
if containsAnthropicToolUseBlock(blocks) {
|
||||
return "tool_use"
|
||||
}
|
||||
return "end_turn"
|
||||
@@ -129,6 +129,15 @@ func responsesStatusToAnthropicStopReason(status string, details *ResponsesIncom
|
||||
}
|
||||
}
|
||||
|
||||
func containsAnthropicToolUseBlock(blocks []AnthropicContentBlock) bool {
|
||||
for _, block := range blocks {
|
||||
if block.Type == "tool_use" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func sanitizeAnthropicToolUseInput(name string, raw string) json.RawMessage {
|
||||
if name != "Read" || raw == "" {
|
||||
return json.RawMessage(raw)
|
||||
@@ -161,11 +170,13 @@ type ResponsesEventToAnthropicState struct {
|
||||
MessageStartSent bool
|
||||
MessageStopSent bool
|
||||
|
||||
ContentBlockIndex int
|
||||
ContentBlockOpen bool
|
||||
CurrentBlockType string // "text" | "thinking" | "tool_use"
|
||||
CurrentToolName string
|
||||
CurrentToolArgs string
|
||||
ContentBlockIndex int
|
||||
ContentBlockOpen bool
|
||||
CurrentBlockType string // "text" | "thinking" | "tool_use"
|
||||
CurrentToolName string
|
||||
CurrentToolArgs string
|
||||
CurrentToolHadDelta bool
|
||||
HasToolCall bool
|
||||
|
||||
// OutputIndexToBlockIdx maps Responses output_index → Anthropic content block index.
|
||||
OutputIndexToBlockIdx map[int]int
|
||||
@@ -231,11 +242,16 @@ func FinalizeResponsesAnthropicStream(state *ResponsesEventToAnthropicState) []A
|
||||
var events []AnthropicStreamEvent
|
||||
events = append(events, closeCurrentBlock(state)...)
|
||||
|
||||
stopReason := "end_turn"
|
||||
if state.HasToolCall {
|
||||
stopReason = "tool_use"
|
||||
}
|
||||
|
||||
events = append(events,
|
||||
AnthropicStreamEvent{
|
||||
Type: "message_delta",
|
||||
Delta: &AnthropicDelta{
|
||||
StopReason: "end_turn",
|
||||
StopReason: stopReason,
|
||||
},
|
||||
Usage: &AnthropicUsage{
|
||||
InputTokens: state.InputTokens,
|
||||
@@ -306,6 +322,8 @@ func resToAnthHandleOutputItemAdded(evt *ResponsesStreamEvent, state *ResponsesE
|
||||
state.CurrentBlockType = "tool_use"
|
||||
state.CurrentToolName = evt.Item.Name
|
||||
state.CurrentToolArgs = ""
|
||||
state.CurrentToolHadDelta = false
|
||||
state.HasToolCall = true
|
||||
|
||||
events = append(events, AnthropicStreamEvent{
|
||||
Type: "content_block_start",
|
||||
@@ -390,6 +408,9 @@ func resToAnthHandleFuncArgsDelta(evt *ResponsesStreamEvent, state *ResponsesEve
|
||||
state.CurrentToolArgs += evt.Delta
|
||||
return nil
|
||||
}
|
||||
if state.CurrentBlockType == "tool_use" {
|
||||
state.CurrentToolHadDelta = true
|
||||
}
|
||||
|
||||
blockIdx, ok := state.OutputIndexToBlockIdx[evt.OutputIndex]
|
||||
if !ok {
|
||||
@@ -407,7 +428,7 @@ func resToAnthHandleFuncArgsDelta(evt *ResponsesStreamEvent, state *ResponsesEve
|
||||
}
|
||||
|
||||
func resToAnthHandleFuncArgsDone(evt *ResponsesStreamEvent, state *ResponsesEventToAnthropicState) []AnthropicStreamEvent {
|
||||
if state.CurrentBlockType != "tool_use" || state.CurrentToolName != "Read" {
|
||||
if state.CurrentBlockType != "tool_use" {
|
||||
return resToAnthHandleBlockDone(state)
|
||||
}
|
||||
|
||||
@@ -415,10 +436,16 @@ func resToAnthHandleFuncArgsDone(evt *ResponsesStreamEvent, state *ResponsesEven
|
||||
if raw == "" {
|
||||
raw = state.CurrentToolArgs
|
||||
}
|
||||
sanitized := sanitizeAnthropicToolUseInput(state.CurrentToolName, raw)
|
||||
if len(sanitized) == 0 {
|
||||
if raw == "" || state.CurrentToolHadDelta {
|
||||
return closeCurrentBlock(state)
|
||||
}
|
||||
if state.CurrentToolName == "Read" {
|
||||
sanitized := sanitizeAnthropicToolUseInput(state.CurrentToolName, raw)
|
||||
if len(sanitized) == 0 {
|
||||
return closeCurrentBlock(state)
|
||||
}
|
||||
raw = string(sanitized)
|
||||
}
|
||||
|
||||
idx := state.ContentBlockIndex
|
||||
events := []AnthropicStreamEvent{{
|
||||
@@ -426,7 +453,7 @@ func resToAnthHandleFuncArgsDone(evt *ResponsesStreamEvent, state *ResponsesEven
|
||||
Index: &idx,
|
||||
Delta: &AnthropicDelta{
|
||||
Type: "input_json_delta",
|
||||
PartialJSON: string(sanitized),
|
||||
PartialJSON: raw,
|
||||
},
|
||||
}}
|
||||
events = append(events, closeCurrentBlock(state)...)
|
||||
@@ -553,7 +580,7 @@ func resToAnthHandleCompleted(evt *ResponsesStreamEvent, state *ResponsesEventTo
|
||||
stopReason = "max_tokens"
|
||||
}
|
||||
case "completed":
|
||||
if state.ContentBlockIndex > 0 && state.CurrentBlockType == "tool_use" {
|
||||
if state.HasToolCall {
|
||||
stopReason = "tool_use"
|
||||
}
|
||||
}
|
||||
@@ -586,6 +613,7 @@ func closeCurrentBlock(state *ResponsesEventToAnthropicState) []AnthropicStreamE
|
||||
state.ContentBlockIndex++
|
||||
state.CurrentToolName = ""
|
||||
state.CurrentToolArgs = ""
|
||||
state.CurrentToolHadDelta = false
|
||||
return []AnthropicStreamEvent{{
|
||||
Type: "content_block_stop",
|
||||
Index: &idx,
|
||||
|
||||
Reference in New Issue
Block a user