diff --git a/internal/translator/codex/claude/codex_claude_response.go b/internal/translator/codex/claude/codex_claude_response.go index e3909d45..c700ef84 100644 --- a/internal/translator/codex/claude/codex_claude_response.go +++ b/internal/translator/codex/claude/codex_claude_response.go @@ -20,6 +20,12 @@ var ( dataTag = []byte("data:") ) +// ConvertCodexResponseToClaudeParams holds parameters for response conversion. +type ConvertCodexResponseToClaudeParams struct { + HasToolCall bool + BlockIndex int +} + // ConvertCodexResponseToClaude performs sophisticated streaming response format conversion. // This function implements a complex state machine that translates Codex API responses // into Claude Code-compatible Server-Sent Events (SSE) format. It manages different response types @@ -38,8 +44,10 @@ var ( // - []string: A slice of strings, each containing a Claude Code-compatible JSON response func ConvertCodexResponseToClaude(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) []string { if *param == nil { - hasToolCall := false - *param = &hasToolCall + *param = &ConvertCodexResponseToClaudeParams{ + HasToolCall: false, + BlockIndex: 0, + } } // log.Debugf("rawJSON: %s", string(rawJSON)) @@ -62,46 +70,49 @@ func ConvertCodexResponseToClaude(_ context.Context, _ string, originalRequestRa output += fmt.Sprintf("data: %s\n\n", template) } else if typeStr == "response.reasoning_summary_part.added" { template = `{"type":"content_block_start","index":0,"content_block":{"type":"thinking","thinking":""}}` - template, _ = sjson.Set(template, "index", rootResult.Get("output_index").Int()) + template, _ = sjson.Set(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) output = "event: content_block_start\n" output += fmt.Sprintf("data: %s\n\n", template) } else if typeStr == "response.reasoning_summary_text.delta" { template = `{"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":""}}` - template, _ = sjson.Set(template, "index", rootResult.Get("output_index").Int()) + template, _ = sjson.Set(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) template, _ = sjson.Set(template, "delta.thinking", rootResult.Get("delta").String()) output = "event: content_block_delta\n" output += fmt.Sprintf("data: %s\n\n", template) } else if typeStr == "response.reasoning_summary_part.done" { template = `{"type":"content_block_stop","index":0}` - template, _ = sjson.Set(template, "index", rootResult.Get("output_index").Int()) + template, _ = sjson.Set(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) + (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex++ output = "event: content_block_stop\n" output += fmt.Sprintf("data: %s\n\n", template) + } else if typeStr == "response.content_part.added" { template = `{"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}` - template, _ = sjson.Set(template, "index", rootResult.Get("output_index").Int()) + template, _ = sjson.Set(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) output = "event: content_block_start\n" output += fmt.Sprintf("data: %s\n\n", template) } else if typeStr == "response.output_text.delta" { template = `{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":""}}` - template, _ = sjson.Set(template, "index", rootResult.Get("output_index").Int()) + template, _ = sjson.Set(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) template, _ = sjson.Set(template, "delta.text", rootResult.Get("delta").String()) output = "event: content_block_delta\n" output += fmt.Sprintf("data: %s\n\n", template) } else if typeStr == "response.content_part.done" { template = `{"type":"content_block_stop","index":0}` - template, _ = sjson.Set(template, "index", rootResult.Get("output_index").Int()) + template, _ = sjson.Set(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) + (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex++ output = "event: content_block_stop\n" output += fmt.Sprintf("data: %s\n\n", template) } else if typeStr == "response.completed" { template = `{"type":"message_delta","delta":{"stop_reason":"tool_use","stop_sequence":null},"usage":{"input_tokens":0,"output_tokens":0}}` - p := (*param).(*bool) - if *p { + p := (*param).(*ConvertCodexResponseToClaudeParams).HasToolCall + if p { template, _ = sjson.Set(template, "delta.stop_reason", "tool_use") } else { template, _ = sjson.Set(template, "delta.stop_reason", "end_turn") @@ -118,10 +129,9 @@ func ConvertCodexResponseToClaude(_ context.Context, _ string, originalRequestRa itemResult := rootResult.Get("item") itemType := itemResult.Get("type").String() if itemType == "function_call" { - p := true - *param = &p + (*param).(*ConvertCodexResponseToClaudeParams).HasToolCall = true template = `{"type":"content_block_start","index":0,"content_block":{"type":"tool_use","id":"","name":"","input":{}}}` - template, _ = sjson.Set(template, "index", rootResult.Get("output_index").Int()) + template, _ = sjson.Set(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) template, _ = sjson.Set(template, "content_block.id", itemResult.Get("call_id").String()) { // Restore original tool name if shortened @@ -137,7 +147,7 @@ func ConvertCodexResponseToClaude(_ context.Context, _ string, originalRequestRa output += fmt.Sprintf("data: %s\n\n", template) template = `{"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":""}}` - template, _ = sjson.Set(template, "index", rootResult.Get("output_index").Int()) + template, _ = sjson.Set(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) output += "event: content_block_delta\n" output += fmt.Sprintf("data: %s\n\n", template) @@ -147,14 +157,15 @@ func ConvertCodexResponseToClaude(_ context.Context, _ string, originalRequestRa itemType := itemResult.Get("type").String() if itemType == "function_call" { template = `{"type":"content_block_stop","index":0}` - template, _ = sjson.Set(template, "index", rootResult.Get("output_index").Int()) + template, _ = sjson.Set(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) + (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex++ output = "event: content_block_stop\n" output += fmt.Sprintf("data: %s\n\n", template) } } else if typeStr == "response.function_call_arguments.delta" { template = `{"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":""}}` - template, _ = sjson.Set(template, "index", rootResult.Get("output_index").Int()) + template, _ = sjson.Set(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) template, _ = sjson.Set(template, "delta.partial_json", rootResult.Get("delta").String()) output += "event: content_block_delta\n"