Unified · Split
| @@ -69,12 +69,12 @@ func ConvertGeminiCLIResponseToClaude(_ context.Context, _ string, originalReque · 68 unmodified lines | |||
| 69 | 69 | ||
| 70 | // Track whether tools are being used in this response chunk | 70 | // Track whether tools are being used in this response chunk |
| 71 | usedTool := false | 71 | usedTool := false |
| 72 | var sb strings.Builder | 72 | output := "" |
| 73 | 73 | ||
| 74 | // Initialize the streaming session with a message_start event | 74 | // Initialize the streaming session with a message_start event |
| 75 | // This is only sent for the very first response chunk to establish the streaming session | 75 | // This is only sent for the very first response chunk to establish the streaming session |
| 76 | if !(*param).(*Params).HasFirstResponse { | 76 | if !(*param).(*Params).HasFirstResponse { |
| 77 | sb.WriteString("event: message_start\n") | 77 | output = "event: message_start\n" |
| 78 | 78 | ||
| 79 | // Create the initial message structure with default values according to Claude Code API specification | 79 | // Create the initial message structure with default values according to Claude Code API specification |
| 80 | // This follows the Claude Code API specification for streaming message initialization | 80 | // This follows the Claude Code API specification for streaming message initialization |
| @@ -87,7 +87,7 @@ func ConvertGeminiCLIResponseToClaude(_ context.Context, _ string, originalReque · 6 unmodified lines | |||
| 87 | if responseIDResult := gjson.GetBytes(rawJSON, "response.responseId"); responseIDResult.Exists() { | 87 | if responseIDResult := gjson.GetBytes(rawJSON, "response.responseId"); responseIDResult.Exists() { |
| 88 | messageStartTemplate, _ = sjson.Set(messageStartTemplate, "message.id", responseIDResult.String()) | 88 | messageStartTemplate, _ = sjson.Set(messageStartTemplate, "message.id", responseIDResult.String()) |
| 89 | } | 89 | } |
| 90 | sb.WriteString(fmt.Sprintf("data: %s\n\n\n", messageStartTemplate)) | 90 | output = output + fmt.Sprintf("data: %s\n\n\n", messageStartTemplate) |
| 91 | 91 | ||
| 92 | (*param).(*Params).HasFirstResponse = true | 92 | (*param).(*Params).HasFirstResponse = true |
| 93 | } | 93 | } |
| @@ -110,7 +110,7 @@ func ConvertGeminiCLIResponseToClaude(_ context.Context, _ string, originalReque · 16 unmodified lines | |||
| 110 | if partResult.Get("thought").Bool() { | 110 | if partResult.Get("thought").Bool() { |
| 111 | // Continue existing thinking block if already in thinking state | 111 | // Continue existing thinking block if already in thinking state |
| 112 | if (*param).(*Params).ResponseType == 2 { | 112 | if (*param).(*Params).ResponseType == 2 { |
| 113 | sb.WriteString("event: content_block_delta\n") | 113 | output = output + "event: content_block_delta\n" |
| 114 | data, _ := sjson.Set(fmt.Sprintf(`{"type":"content_block_delta","index":%d,"delta":{"type":"thinking_delta","thinking":""}}`, (*param).(*Params).ResponseIndex), "delta.thinking", partTextResult.String()) | 114 | data, _ := sjson.Set(fmt.Sprintf(`{"type":"content_block_delta","index":%d,"delta":{"type":"thinking_delta","thinking":""}}`, (*param).(*Params).ResponseIndex), "delta.thinking", partTextResult.String()) |
| 115 | output = output + fmt.Sprintf("data: %s\n\n\n", data) | 115 | output = output + fmt.Sprintf("data: %s\n\n\n", data) |
| 116 | (*param).(*Params).HasContent = true | 116 | (*param).(*Params).HasContent = true |
| @@ -118,19 +118,24 @@ func ConvertGeminiCLIResponseToClaude(_ context.Context, _ string, originalReque · 1 unmodified line | |||
| 118 | // Transition from another state to thinking | 118 | // Transition from another state to thinking |
| 119 | // First, close any existing content block | 119 | // First, close any existing content block |
| 120 | if (*param).(*Params).ResponseType != 0 { | 120 | if (*param).(*Params).ResponseType != 0 { |
| 121 | sb.WriteString("event: content_block_stop\n") | 121 | if (*param).(*Params).ResponseType == 2 { |
| 122 | sb.WriteString(fmt.Sprintf(`data: {"type":"content_block_stop","index":%d}`, (*param).(*Params).ResponseIndex)) | 122 | // output = output + "event: content_block_delta\n" |
| 123 | sb.WriteString("\n\n\n") | 123 | // output = output + fmt.Sprintf(`data: {"type":"content_block_delta","index":%d,"delta":{"type":"signature_delta","signature":null}}`, (*param).(*Params).ResponseIndex) |
| 124 | // output = output + "\n\n\n" | ||
| 125 | } | ||
| 126 | output = output + "event: content_block_stop\n" | ||
| 127 | output = output + fmt.Sprintf(`data: {"type":"content_block_stop","index":%d}`, (*param).(*Params).ResponseIndex) | ||
| 128 | output = output + "\n\n\n" | ||
| 124 | (*param).(*Params).ResponseIndex++ | 129 | (*param).(*Params).ResponseIndex++ |
| 125 | } | 130 | } |
| 126 | 131 | ||
| 127 | // Start a new thinking content block | 132 | // Start a new thinking content block |
| 128 | sb.WriteString("event: content_block_start\n") | 133 | output = output + "event: content_block_start\n" |
| 129 | sb.WriteString(fmt.Sprintf(`data: {"type":"content_block_start","index":%d,"content_block":{"type":"thinking","thinking":""}}`, (*param).(*Params).ResponseIndex)) | 134 | output = output + fmt.Sprintf(`data: {"type":"content_block_start","index":%d,"content_block":{"type":"thinking","thinking":""}}`, (*param).(*Params).ResponseIndex) |
| 130 | sb.WriteString("\n\n\n") | 135 | output = output + "\n\n\n" |
| 131 | sb.WriteString("event: content_block_delta\n") | 136 | output = output + "event: content_block_delta\n" |
| 132 | data, _ := sjson.Set(fmt.Sprintf(`{"type":"content_block_delta","index":%d,"delta":{"type":"thinking_delta","thinking":""}}`, (*param).(*Params).ResponseIndex), "delta.thinking", partTextResult.String()) | 137 | data, _ := sjson.Set(fmt.Sprintf(`{"type":"content_block_delta","index":%d,"delta":{"type":"thinking_delta","thinking":""}}`, (*param).(*Params).ResponseIndex), "delta.thinking", partTextResult.String()) |
| 133 | sb.WriteString(fmt.Sprintf("data: %s\n\n\n", data)) | 138 | output = output + fmt.Sprintf("data: %s\n\n\n", data) |
| 134 | (*param).(*Params).ResponseType = 2 // Set state to thinking | 139 | (*param).(*Params).ResponseType = 2 // Set state to thinking |
| 135 | (*param).(*Params).HasContent = true | 140 | (*param).(*Params).HasContent = true |
| 136 | } | 141 | } |
| @@ -138,7 +143,7 @@ func ConvertGeminiCLIResponseToClaude(_ context.Context, _ string, originalReque · 1 unmodified line | |||
| 138 | // Process regular text content (user-visible output) | 143 | // Process regular text content (user-visible output) |
| 139 | // Continue existing text block if already in content state | 144 | // Continue existing text block if already in content state |
| 140 | if (*param).(*Params).ResponseType == 1 { | 145 | if (*param).(*Params).ResponseType == 1 { |
| 141 | sb.WriteString("event: content_block_delta\n") | 146 | output = output + "event: content_block_delta\n" |
| 142 | data, _ := sjson.Set(fmt.Sprintf(`{"type":"content_block_delta","index":%d,"delta":{"type":"text_delta","text":""}}`, (*param).(*Params).ResponseIndex), "delta.text", partTextResult.String()) | 147 | data, _ := sjson.Set(fmt.Sprintf(`{"type":"content_block_delta","index":%d,"delta":{"type":"text_delta","text":""}}`, (*param).(*Params).ResponseIndex), "delta.text", partTextResult.String()) |
| 143 | output = output + fmt.Sprintf("data: %s\n\n\n", data) | 148 | output = output + fmt.Sprintf("data: %s\n\n\n", data) |
| 144 | (*param).(*Params).HasContent = true | 149 | (*param).(*Params).HasContent = true |
| @@ -146,19 +151,24 @@ func ConvertGeminiCLIResponseToClaude(_ context.Context, _ string, originalReque · 1 unmodified line | |||
| 146 | // Transition from another state to text content | 151 | // Transition from another state to text content |
| 147 | // First, close any existing content block | 152 | // First, close any existing content block |
| 148 | if (*param).(*Params).ResponseType != 0 { | 153 | if (*param).(*Params).ResponseType != 0 { |
| 149 | sb.WriteString("event: content_block_stop\n") | 154 | if (*param).(*Params).ResponseType == 2 { |
| 150 | sb.WriteString(fmt.Sprintf(`data: {"type":"content_block_stop","index":%d}`, (*param).(*Params).ResponseIndex)) | 155 | // output = output + "event: content_block_delta\n" |
| 151 | sb.WriteString("\n\n\n") | 156 | // output = output + fmt.Sprintf(`data: {"type":"content_block_delta","index":%d,"delta":{"type":"signature_delta","signature":null}}`, (*param).(*Params).ResponseIndex) |
| 157 | // output = output + "\n\n\n" | ||
| 158 | } | ||
| 159 | output = output + "event: content_block_stop\n" | ||
| 160 | output = output + fmt.Sprintf(`data: {"type":"content_block_stop","index":%d}`, (*param).(*Params).ResponseIndex) | ||
| 161 | output = output + "\n\n\n" | ||
| 152 | (*param).(*Params).ResponseIndex++ | 162 | (*param).(*Params).ResponseIndex++ |
| 153 | } | 163 | } |
| 154 | 164 | ||
| 155 | // Start a new text content block | 165 | // Start a new text content block |
| 156 | sb.WriteString("event: content_block_start\n") | 166 | output = output + "event: content_block_start\n" |
| 157 | sb.WriteString(fmt.Sprintf(`data: {"type":"content_block_start","index":%d,"content_block":{"type":"text","text":""}}`, (*param).(*Params).ResponseIndex)) | 167 | output = output + fmt.Sprintf(`data: {"type":"content_block_start","index":%d,"content_block":{"type":"text","text":""}}`, (*param).(*Params).ResponseIndex) |
| 158 | sb.WriteString("\n\n\n") | 168 | output = output + "\n\n\n" |
| 159 | sb.WriteString("event: content_block_delta\n") | 169 | output = output + "event: content_block_delta\n" |
| 160 | data, _ := sjson.Set(fmt.Sprintf(`{"type":"content_block_delta","index":%d,"delta":{"type":"text_delta","text":""}}`, (*param).(*Params).ResponseIndex), "delta.text", partTextResult.String()) | 170 | data, _ := sjson.Set(fmt.Sprintf(`{"type":"content_block_delta","index":%d,"delta":{"type":"text_delta","text":""}}`, (*param).(*Params).ResponseIndex), "delta.text", partTextResult.String()) |
| 161 | sb.WriteString(fmt.Sprintf("data: %s\n\n\n", data)) | 171 | output = output + fmt.Sprintf("data: %s\n\n\n", data) |
| 162 | (*param).(*Params).ResponseType = 1 // Set state to content | 172 | (*param).(*Params).ResponseType = 1 // Set state to content |
| 163 | (*param).(*Params).HasContent = true | 173 | (*param).(*Params).HasContent = true |
| 164 | } | 174 | } |
| @@ -172,35 +182,42 @@ func ConvertGeminiCLIResponseToClaude(_ context.Context, _ string, originalReque · 7 unmodified lines | |||
| 172 | // Handle state transitions when switching to function calls | 182 | // Handle state transitions when switching to function calls |
| 173 | // Close any existing function call block first | 183 | // Close any existing function call block first |
| 174 | if (*param).(*Params).ResponseType == 3 { | 184 | if (*param).(*Params).ResponseType == 3 { |
| 175 | sb.WriteString("event: content_block_stop\n") | 185 | output = output + "event: content_block_stop\n" |
| 176 | sb.WriteString(fmt.Sprintf(`data: {"type":"content_block_stop","index":%d}`, (*param).(*Params).ResponseIndex)) | 186 | output = output + fmt.Sprintf(`data: {"type":"content_block_stop","index":%d}`, (*param).(*Params).ResponseIndex) |
| 177 | sb.WriteString("\n\n\n") | 187 | output = output + "\n\n\n" |
| 178 | (*param).(*Params).ResponseIndex++ | 188 | (*param).(*Params).ResponseIndex++ |
| 179 | (*param).(*Params).ResponseType = 0 | 189 | (*param).(*Params).ResponseType = 0 |
| 180 | } | 190 | } |
| 181 | 191 | ||
| 192 | // Special handling for thinking state transition | ||
| 193 | if (*param).(*Params).ResponseType == 2 { | ||
| 194 | // output = output + "event: content_block_delta\n" | ||
| 195 | // output = output + fmt.Sprintf(`data: {"type":"content_block_delta","index":%d,"delta":{"type":"signature_delta","signature":null}}`, (*param).(*Params).ResponseIndex) | ||
| 196 | // output = output + "\n\n\n" | ||
| 197 | } | ||
| 198 | |||
| 182 | // Close any other existing content block | 199 | // Close any other existing content block |
| 183 | if (*param).(*Params).ResponseType != 0 { | 200 | if (*param).(*Params).ResponseType != 0 { |
| 184 | sb.WriteString("event: content_block_stop\n") | 201 | output = output + "event: content_block_stop\n" |
| 185 | sb.WriteString(fmt.Sprintf(`data: {"type":"content_block_stop","index":%d}`, (*param).(*Params).ResponseIndex)) | 202 | output = output + fmt.Sprintf(`data: {"type":"content_block_stop","index":%d}`, (*param).(*Params).ResponseIndex) |
| 186 | sb.WriteString("\n\n\n") | 203 | output = output + "\n\n\n" |
| 187 | (*param).(*Params).ResponseIndex++ | 204 | (*param).(*Params).ResponseIndex++ |
| 188 | } | 205 | } |
| 189 | 206 | ||
| 190 | // Start a new tool use content block | 207 | // Start a new tool use content block |
| 191 | // This creates the structure for a function call in Claude Code format | 208 | // This creates the structure for a function call in Claude Code format |
| 192 | sb.WriteString("event: content_block_start\n") | 209 | output = output + "event: content_block_start\n" |
| 193 | 210 | ||
| 194 | // Create the tool use block with unique ID and function details | 211 | // Create the tool use block with unique ID and function details |
| 195 | data := fmt.Sprintf(`{"type":"content_block_start","index":%d,"content_block":{"type":"tool_use","id":"","name":"","input":{}}}`, (*param).(*Params).ResponseIndex) | 212 | data := fmt.Sprintf(`{"type":"content_block_start","index":%d,"content_block":{"type":"tool_use","id":"","name":"","input":{}}}`, (*param).(*Params).ResponseIndex) |
| 196 | data, _ = sjson.Set(data, "content_block.id", fmt.Sprintf("%s-%d-%d", fcName, time.Now().UnixNano(), atomic.AddUint64(&toolUseIDCounter, 1))) | 213 | data, _ = sjson.Set(data, "content_block.id", fmt.Sprintf("%s-%d-%d", fcName, time.Now().UnixNano(), atomic.AddUint64(&toolUseIDCounter, 1))) |
| 197 | data, _ = sjson.Set(data, "content_block.name", fcName) | 214 | data, _ = sjson.Set(data, "content_block.name", fcName) |
| 198 | sb.WriteString(fmt.Sprintf("data: %s\n\n\n", data)) | 215 | output = output + fmt.Sprintf("data: %s\n\n\n", data) |
| 199 | 216 | ||
| 200 | if fcArgsResult := functionCallResult.Get("args"); fcArgsResult.Exists() { | 217 | if fcArgsResult := functionCallResult.Get("args"); fcArgsResult.Exists() { |
| 201 | sb.WriteString("event: content_block_delta\n") | 218 | output = output + "event: content_block_delta\n" |
| 202 | data, _ = sjson.Set(fmt.Sprintf(`{"type":"content_block_delta","index":%d,"delta":{"type":"input_json_delta","partial_json":""}}`, (*param).(*Params).ResponseIndex), "delta.partial_json", fcArgsResult.Raw) | 219 | data, _ = sjson.Set(fmt.Sprintf(`{"type":"content_block_delta","index":%d,"delta":{"type":"input_json_delta","partial_json":""}}`, (*param).(*Params).ResponseIndex), "delta.partial_json", fcArgsResult.Raw) |
| 203 | sb.WriteString(fmt.Sprintf("data: %s\n\n\n", data)) | 220 | output = output + fmt.Sprintf("data: %s\n\n\n", data) |
| 204 | } | 221 | } |
| 205 | (*param).(*Params).ResponseType = 3 | 222 | (*param).(*Params).ResponseType = 3 |
| 206 | (*param).(*Params).HasContent = true | 223 | (*param).(*Params).HasContent = true |
| @@ -240,7 +257,7 @@ func ConvertGeminiCLIResponseToClaude(_ context.Context, _ string, originalReque · 33 unmodified lines | |||
| 240 | } | 257 | } |
| 241 | } | 258 | } |
| 242 | 259 | ||
| 243 | return []string{sb.String()} | 260 | return []string{output} |
| 244 | } | 261 | } |
| 245 | 262 | ||
| 246 | // ConvertGeminiCLIResponseToClaudeNonStream converts a non-streaming Gemini CLI response to a non-streaming Claude response. | 263 | // ConvertGeminiCLIResponseToClaudeNonStream converts a non-streaming Gemini CLI response to a non-streaming Claude response. |