feat: improve OpenAI messages compatibility for Claude Code

This commit is contained in:
lyen1688
2026-05-05 19:36:33 +08:00
parent 94e494319a
commit 0584305e5a
21 changed files with 2525 additions and 136 deletions
@@ -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,