Unified · Split
| @@ -69,12 +69,12 @@ func ConvertGeminiCLIResponseToClaude(_ context.Context, _ string, originalReque · 68 unmodified lines | ||
| 69 | 69 | |
| 70 | 70 | // Track whether tools are being used in this response chunk |
| 71 | 71 | usedTool := false |
| 72 | − var sb strings.Builder | |
| 72 | + output := "" | |
| 73 | 73 | |
| 74 | 74 | // Initialize the streaming session with a message_start event |
| 75 | 75 | // This is only sent for the very first response chunk to establish the streaming session |
| 76 | 76 | if !(*param).(*Params).HasFirstResponse { |
| 77 | − sb.WriteString("event: message_start\n") | |
| 77 | + output = "event: message_start\n" | |
| 78 | 78 | |
| 79 | 79 | // Create the initial message structure with default values according to Claude Code API specification |
| 80 | 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 | 87 | if responseIDResult := gjson.GetBytes(rawJSON, "response.responseId"); responseIDResult.Exists() { |
| 88 | 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 | 92 | (*param).(*Params).HasFirstResponse = true |
| 93 | 93 | } |
| @@ -110,7 +110,7 @@ func ConvertGeminiCLIResponseToClaude(_ context.Context, _ string, originalReque · 16 unmodified lines | ||
| 110 | 110 | if partResult.Get("thought").Bool() { |
| 111 | 111 | // Continue existing thinking block if already in thinking state |
| 112 | 112 | if (*param).(*Params).ResponseType == 2 { |
| 113 | − sb.WriteString("event: content_block_delta\n") | |
| 113 | + output = output + "event: content_block_delta\n" | |
| 114 | 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 | 115 | output = output + fmt.Sprintf("data: %s\n\n\n", data) |
| 116 | 116 | (*param).(*Params).HasContent = true |
| @@ -118,19 +118,24 @@ func ConvertGeminiCLIResponseToClaude(_ context.Context, _ string, originalReque · 1 unmodified line | ||
| 118 | 118 | // Transition from another state to thinking |
| 119 | 119 | // First, close any existing content block |
| 120 | 120 | if (*param).(*Params).ResponseType != 0 { |
| 121 | − sb.WriteString("event: content_block_stop\n") | |
| 122 | − sb.WriteString(fmt.Sprintf(`data: {"type":"content_block_stop","index":%d}`, (*param).(*Params).ResponseIndex)) | |
| 123 | − sb.WriteString("\n\n\n") | |
| 121 | + if (*param).(*Params).ResponseType == 2 { | |
| 122 | + // output = output + "event: content_block_delta\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 | 129 | (*param).(*Params).ResponseIndex++ |
| 125 | 130 | } |
| 126 | 131 | |
| 127 | 132 | // Start a new thinking content block |
| 128 | − sb.WriteString("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)) | |
| 130 | − sb.WriteString("\n\n\n") | |
| 131 | − sb.WriteString("event: content_block_delta\n") | |
| 133 | + output = output + "event: content_block_start\n" | |
| 134 | + output = output + fmt.Sprintf(`data: {"type":"content_block_start","index":%d,"content_block":{"type":"thinking","thinking":""}}`, (*param).(*Params).ResponseIndex) | |
| 135 | + output = output + "\n\n\n" | |
| 136 | + output = output + "event: content_block_delta\n" | |
| 132 | 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 | 139 | (*param).(*Params).ResponseType = 2 // Set state to thinking |
| 135 | 140 | (*param).(*Params).HasContent = true |
| 136 | 141 | } |
| @@ -138,7 +143,7 @@ func ConvertGeminiCLIResponseToClaude(_ context.Context, _ string, originalReque · 1 unmodified line | ||
| 138 | 143 | // Process regular text content (user-visible output) |
| 139 | 144 | // Continue existing text block if already in content state |
| 140 | 145 | if (*param).(*Params).ResponseType == 1 { |
| 141 | − sb.WriteString("event: content_block_delta\n") | |
| 146 | + output = output + "event: content_block_delta\n" | |
| 142 | 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 | 148 | output = output + fmt.Sprintf("data: %s\n\n\n", data) |
| 144 | 149 | (*param).(*Params).HasContent = true |
| @@ -146,19 +151,24 @@ func ConvertGeminiCLIResponseToClaude(_ context.Context, _ string, originalReque · 1 unmodified line | ||
| 146 | 151 | // Transition from another state to text content |
| 147 | 152 | // First, close any existing content block |
| 148 | 153 | if (*param).(*Params).ResponseType != 0 { |
| 149 | − sb.WriteString("event: content_block_stop\n") | |
| 150 | − sb.WriteString(fmt.Sprintf(`data: {"type":"content_block_stop","index":%d}`, (*param).(*Params).ResponseIndex)) | |
| 151 | − sb.WriteString("\n\n\n") | |
| 154 | + if (*param).(*Params).ResponseType == 2 { | |
| 155 | + // output = output + "event: content_block_delta\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 | 162 | (*param).(*Params).ResponseIndex++ |
| 153 | 163 | } |
| 154 | 164 | |
| 155 | 165 | // Start a new text content block |
| 156 | − sb.WriteString("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)) | |
| 158 | − sb.WriteString("\n\n\n") | |
| 159 | − sb.WriteString("event: content_block_delta\n") | |
| 166 | + output = output + "event: content_block_start\n" | |
| 167 | + output = output + fmt.Sprintf(`data: {"type":"content_block_start","index":%d,"content_block":{"type":"text","text":""}}`, (*param).(*Params).ResponseIndex) | |
| 168 | + output = output + "\n\n\n" | |
| 169 | + output = output + "event: content_block_delta\n" | |
| 160 | 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 | 172 | (*param).(*Params).ResponseType = 1 // Set state to content |
| 163 | 173 | (*param).(*Params).HasContent = true |
| 164 | 174 | } |
| @@ -172,35 +182,42 @@ func ConvertGeminiCLIResponseToClaude(_ context.Context, _ string, originalReque · 7 unmodified lines | ||
| 172 | 182 | // Handle state transitions when switching to function calls |
| 173 | 183 | // Close any existing function call block first |
| 174 | 184 | if (*param).(*Params).ResponseType == 3 { |
| 175 | − sb.WriteString("event: content_block_stop\n") | |
| 176 | − sb.WriteString(fmt.Sprintf(`data: {"type":"content_block_stop","index":%d}`, (*param).(*Params).ResponseIndex)) | |
| 177 | − sb.WriteString("\n\n\n") | |
| 185 | + output = output + "event: content_block_stop\n" | |
| 186 | + output = output + fmt.Sprintf(`data: {"type":"content_block_stop","index":%d}`, (*param).(*Params).ResponseIndex) | |
| 187 | + output = output + "\n\n\n" | |
| 178 | 188 | (*param).(*Params).ResponseIndex++ |
| 179 | 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 | 199 | // Close any other existing content block |
| 183 | 200 | if (*param).(*Params).ResponseType != 0 { |
| 184 | − sb.WriteString("event: content_block_stop\n") | |
| 185 | − sb.WriteString(fmt.Sprintf(`data: {"type":"content_block_stop","index":%d}`, (*param).(*Params).ResponseIndex)) | |
| 186 | − sb.WriteString("\n\n\n") | |
| 201 | + output = output + "event: content_block_stop\n" | |
| 202 | + output = output + fmt.Sprintf(`data: {"type":"content_block_stop","index":%d}`, (*param).(*Params).ResponseIndex) | |
| 203 | + output = output + "\n\n\n" | |
| 187 | 204 | (*param).(*Params).ResponseIndex++ |
| 188 | 205 | } |
| 189 | 206 | |
| 190 | 207 | // Start a new tool use content block |
| 191 | 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 | 211 | // Create the tool use block with unique ID and function details |
| 195 | 212 | data := fmt.Sprintf(`{"type":"content_block_start","index":%d,"content_block":{"type":"tool_use","id":"","name":"","input":{}}}`, (*param).(*Params).ResponseIndex) |
| 196 | 213 | data, _ = sjson.Set(data, "content_block.id", fmt.Sprintf("%s-%d-%d", fcName, time.Now().UnixNano(), atomic.AddUint64(&toolUseIDCounter, 1))) |
| 197 | 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 | 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 | 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 | 222 | (*param).(*Params).ResponseType = 3 |
| 206 | 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 | 263 | // ConvertGeminiCLIResponseToClaudeNonStream converts a non-streaming Gemini CLI response to a non-streaming Claude response. |