| @@ -22,8 +22,9 @@ var ( · 21 unmodified lines |
| 22 | 22 | |
| 23 | 23 | // ConvertCodexResponseToClaudeParams holds parameters for response conversion. |
| 24 | 24 | type ConvertCodexResponseToClaudeParams struct { |
| 25 | | − HasToolCall bool |
| 26 | | − BlockIndex int |
| 25 | + HasToolCall bool |
| 26 | + BlockIndex int |
| 27 | + HasReceivedArgumentsDelta bool |
| 27 | 28 | } |
| 28 | 29 | |
| 29 | 30 | // ConvertCodexResponseToClaude performs sophisticated streaming response format conversion. |
| @@ -137,6 +138,7 @@ func ConvertCodexResponseToClaude(_ context.Context, _ string, originalRequestRa · 107 unmodified lines |
| 137 | 138 | itemType := itemResult.Get("type").String() |
| 138 | 139 | if itemType == "function_call" { |
| 139 | 140 | (*param).(*ConvertCodexResponseToClaudeParams).HasToolCall = true |
| 141 | + (*param).(*ConvertCodexResponseToClaudeParams).HasReceivedArgumentsDelta = false |
| 140 | 142 | template = `{"type":"content_block_start","index":0,"content_block":{"type":"tool_use","id":"","name":"","input":{}}}` |
| 141 | 143 | template, _ = sjson.Set(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) |
| 142 | 144 | template, _ = sjson.Set(template, "content_block.id", itemResult.Get("call_id").String()) |
| @@ -171,12 +173,29 @@ func ConvertCodexResponseToClaude(_ context.Context, _ string, originalRequestRa · 28 unmodified lines |
| 171 | 173 | output += fmt.Sprintf("data: %s\n\n", template) |
| 172 | 174 | } |
| 173 | 175 | } else if typeStr == "response.function_call_arguments.delta" { |
| 176 | + (*param).(*ConvertCodexResponseToClaudeParams).HasReceivedArgumentsDelta = true |
| 174 | 177 | template = `{"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":""}}` |
| 175 | 178 | template, _ = sjson.Set(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) |
| 176 | 179 | template, _ = sjson.Set(template, "delta.partial_json", rootResult.Get("delta").String()) |
| 177 | 180 | |
| 178 | 181 | output += "event: content_block_delta\n" |
| 179 | 182 | output += fmt.Sprintf("data: %s\n\n", template) |
| 183 | + } else if typeStr == "response.function_call_arguments.done" { |
| 184 | + // Some models (e.g. gpt-5.3-codex-spark) send function call arguments |
| 185 | + // in a single "done" event without preceding "delta" events. |
| 186 | + // Emit the full arguments as a single input_json_delta so the |
| 187 | + // downstream Claude client receives the complete tool input. |
| 188 | + // When delta events were already received, skip to avoid duplicating arguments. |
| 189 | + if !(*param).(*ConvertCodexResponseToClaudeParams).HasReceivedArgumentsDelta { |
| 190 | + if args := rootResult.Get("arguments").String(); args != "" { |
| 191 | + template = `{"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":""}}` |
| 192 | + template, _ = sjson.Set(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) |
| 193 | + template, _ = sjson.Set(template, "delta.partial_json", args) |
| 194 | + |
| 195 | + output += "event: content_block_delta\n" |
| 196 | + output += fmt.Sprintf("data: %s\n\n", template) |
| 197 | + } |
| 198 | + } |
| 180 | 199 | } |
| 181 | 200 | |
| 182 | 201 | return []string{output} |