History

Merge pull request #479 from router-for-me/claude

fix(claude): prevent final events when no content streamed

Luis Pater committed 11 months ago

Commit 1da03bf2 parents 423ce97 a8951493 files · +83 −42Browse files

Unified · Split

3 files
  1. internal/translator/antigravity/claude/antigravity_claude_response.go +19 −4
  2. internal/translator/gemini-cli/claude/gemini-cli_claude_response.go +35 −22
  3. internal/translator/gemini/claude/gemini_claude_response.go +29 −16
internal/translator/antigravity/claude/antigravity_claude_response.go+19−4
@@ -35,6 +35,7 @@ type Params struct { · 34 unmodified lines
35 TotalTokenCount int64 // Cached total token count from usage metadata35 TotalTokenCount int64 // Cached total token count from usage metadata
36 HasSentFinalEvents bool // Indicates if final content/message events have been sent36 HasSentFinalEvents bool // Indicates if final content/message events have been sent
37 HasToolUse bool // Indicates if tool use was observed in the stream37 HasToolUse bool // Indicates if tool use was observed in the stream
38 HasContent bool // Tracks whether any content (text, thinking, or tool use) has been output
38}39}
39 40
40// toolUseIDCounter provides a process-wide unique counter for tool use identifiers.41// toolUseIDCounter provides a process-wide unique counter for tool use identifiers.
@@ -69,11 +70,14 @@ func ConvertAntigravityResponseToClaude(_ context.Context, _ string, originalReq · 28 unmodified lines
69 70
70 if bytes.Equal(rawJSON, []byte("[DONE]")) {71 if bytes.Equal(rawJSON, []byte("[DONE]")) {
71 output := ""72 output := ""
72 appendFinalEvents(params, &output, true)73 // Only send final events if we have actually output content
73 74 if params.HasContent {
74 return []string{75 appendFinalEvents(params, &output, true)
75 output + "event: message_stop\ndata: {\"type\":\"message_stop\"}\n\n\n",76 return []string{
77 output + "event: message_stop\ndata: {\"type\":\"message_stop\"}\n\n\n",
78 }
76 }79 }
80 return []string{}
77 }81 }
78 82
79 output := ""83 output := ""
@@ -119,10 +123,12 @@ func ConvertAntigravityResponseToClaude(_ context.Context, _ string, originalReq · 39 unmodified lines
119 output = output + "event: content_block_delta\n"123 output = output + "event: content_block_delta\n"
120 data, _ := sjson.Set(fmt.Sprintf(`{"type":"content_block_delta","index":%d,"delta":{"type":"signature_delta","signature":""}}`, params.ResponseIndex), "delta.signature", thoughtSignature.String())124 data, _ := sjson.Set(fmt.Sprintf(`{"type":"content_block_delta","index":%d,"delta":{"type":"signature_delta","signature":""}}`, params.ResponseIndex), "delta.signature", thoughtSignature.String())
121 output = output + fmt.Sprintf("data: %s\n\n\n", data)125 output = output + fmt.Sprintf("data: %s\n\n\n", data)
126 params.HasContent = true
122 } else if params.ResponseType == 2 { // Continue existing thinking block if already in thinking state127 } else if params.ResponseType == 2 { // Continue existing thinking block if already in thinking state
123 output = output + "event: content_block_delta\n"128 output = output + "event: content_block_delta\n"
124 data, _ := sjson.Set(fmt.Sprintf(`{"type":"content_block_delta","index":%d,"delta":{"type":"thinking_delta","thinking":""}}`, params.ResponseIndex), "delta.thinking", partTextResult.String())129 data, _ := sjson.Set(fmt.Sprintf(`{"type":"content_block_delta","index":%d,"delta":{"type":"thinking_delta","thinking":""}}`, params.ResponseIndex), "delta.thinking", partTextResult.String())
125 output = output + fmt.Sprintf("data: %s\n\n\n", data)130 output = output + fmt.Sprintf("data: %s\n\n\n", data)
131 params.HasContent = true
126 } else {132 } else {
127 // Transition from another state to thinking133 // Transition from another state to thinking
128 // First, close any existing content block134 // First, close any existing content block
@@ -146,6 +152,7 @@ func ConvertAntigravityResponseToClaude(_ context.Context, _ string, originalReq · 17 unmodified lines
146 data, _ := sjson.Set(fmt.Sprintf(`{"type":"content_block_delta","index":%d,"delta":{"type":"thinking_delta","thinking":""}}`, params.ResponseIndex), "delta.thinking", partTextResult.String())152 data, _ := sjson.Set(fmt.Sprintf(`{"type":"content_block_delta","index":%d,"delta":{"type":"thinking_delta","thinking":""}}`, params.ResponseIndex), "delta.thinking", partTextResult.String())
147 output = output + fmt.Sprintf("data: %s\n\n\n", data)153 output = output + fmt.Sprintf("data: %s\n\n\n", data)
148 params.ResponseType = 2 // Set state to thinking154 params.ResponseType = 2 // Set state to thinking
155 params.HasContent = true
149 }156 }
150 } else {157 } else {
151 finishReasonResult := gjson.GetBytes(rawJSON, "response.candidates.0.finishReason")158 finishReasonResult := gjson.GetBytes(rawJSON, "response.candidates.0.finishReason")
@@ -156,6 +163,7 @@ func ConvertAntigravityResponseToClaude(_ context.Context, _ string, originalReq · 4 unmodified lines
156 output = output + "event: content_block_delta\n"163 output = output + "event: content_block_delta\n"
157 data, _ := sjson.Set(fmt.Sprintf(`{"type":"content_block_delta","index":%d,"delta":{"type":"text_delta","text":""}}`, params.ResponseIndex), "delta.text", partTextResult.String())164 data, _ := sjson.Set(fmt.Sprintf(`{"type":"content_block_delta","index":%d,"delta":{"type":"text_delta","text":""}}`, params.ResponseIndex), "delta.text", partTextResult.String())
158 output = output + fmt.Sprintf("data: %s\n\n\n", data)165 output = output + fmt.Sprintf("data: %s\n\n\n", data)
166 params.HasContent = true
159 } else {167 } else {
160 // Transition from another state to text content168 // Transition from another state to text content
161 // First, close any existing content block169 // First, close any existing content block
@@ -179,6 +187,7 @@ func ConvertAntigravityResponseToClaude(_ context.Context, _ string, originalReq · 17 unmodified lines
179 data, _ := sjson.Set(fmt.Sprintf(`{"type":"content_block_delta","index":%d,"delta":{"type":"text_delta","text":""}}`, params.ResponseIndex), "delta.text", partTextResult.String())187 data, _ := sjson.Set(fmt.Sprintf(`{"type":"content_block_delta","index":%d,"delta":{"type":"text_delta","text":""}}`, params.ResponseIndex), "delta.text", partTextResult.String())
180 output = output + fmt.Sprintf("data: %s\n\n\n", data)188 output = output + fmt.Sprintf("data: %s\n\n\n", data)
181 params.ResponseType = 1 // Set state to content189 params.ResponseType = 1 // Set state to content
190 params.HasContent = true
182 }191 }
183 }192 }
184 }193 }
@@ -230,6 +239,7 @@ func ConvertAntigravityResponseToClaude(_ context.Context, _ string, originalReq · 45 unmodified lines
230 output = output + fmt.Sprintf("data: %s\n\n\n", data)239 output = output + fmt.Sprintf("data: %s\n\n\n", data)
231 }240 }
232 params.ResponseType = 3241 params.ResponseType = 3
242 params.HasContent = true
233 }243 }
234 }244 }
235 }245 }
@@ -269,6 +279,11 @@ func appendFinalEvents(params *Params, output *string, force bool) { · 33 unmodified lines
269 return279 return
270 }280 }
271 281
282 // Only send final events if we have actually output content
283 if !params.HasContent {
284 return
285 }
286
272 if params.ResponseType != 0 {287 if params.ResponseType != 0 {
273 *output = *output + "event: content_block_stop\n"288 *output = *output + "event: content_block_stop\n"
274 *output = *output + fmt.Sprintf(`data: {"type":"content_block_stop","index":%d}`, params.ResponseIndex)289 *output = *output + fmt.Sprintf(`data: {"type":"content_block_stop","index":%d}`, params.ResponseIndex)
internal/translator/gemini-cli/claude/gemini-cli_claude_response.go+35−22
@@ -26,6 +26,7 @@ type Params struct { · 25 unmodified lines
26 HasFirstResponse bool // Indicates if the initial message_start event has been sent26 HasFirstResponse bool // Indicates if the initial message_start event has been sent
27 ResponseType int // Current response type: 0=none, 1=content, 2=thinking, 3=function27 ResponseType int // Current response type: 0=none, 1=content, 2=thinking, 3=function
28 ResponseIndex int // Index counter for content blocks in the streaming response28 ResponseIndex int // Index counter for content blocks in the streaming response
29 HasContent bool // Tracks whether any content (text, thinking, or tool use) has been output
29}30}
30 31
31// toolUseIDCounter provides a process-wide unique counter for tool use identifiers.32// toolUseIDCounter provides a process-wide unique counter for tool use identifiers.
@@ -57,9 +58,13 @@ func ConvertGeminiCLIResponseToClaude(_ context.Context, _ string, originalReque · 25 unmodified lines
57 }58 }
58 59
59 if bytes.Equal(rawJSON, []byte("[DONE]")) {60 if bytes.Equal(rawJSON, []byte("[DONE]")) {
60 return []string{61 // Only send message_stop if we have actually output content
61 "event: message_stop\ndata: {\"type\":\"message_stop\"}\n\n\n",62 if (*param).(*Params).HasContent {
63 return []string{
64 "event: message_stop\ndata: {\"type\":\"message_stop\"}\n\n\n",
65 }
62 }66 }
67 return []string{}
63 }68 }
64 69
65 // Track whether tools are being used in this response chunk70 // Track whether tools are being used in this response chunk
@@ -108,6 +113,7 @@ func ConvertGeminiCLIResponseToClaude(_ context.Context, _ string, originalReque · 42 unmodified lines
108 output = output + "event: content_block_delta\n"113 output = output + "event: content_block_delta\n"
109 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())
110 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
111 } else {117 } else {
112 // Transition from another state to thinking118 // Transition from another state to thinking
113 // First, close any existing content block119 // First, close any existing content block
@@ -131,6 +137,7 @@ func ConvertGeminiCLIResponseToClaude(_ context.Context, _ string, originalReque · 17 unmodified lines
131 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())
132 output = output + fmt.Sprintf("data: %s\n\n\n", data)138 output = output + fmt.Sprintf("data: %s\n\n\n", data)
133 (*param).(*Params).ResponseType = 2 // Set state to thinking139 (*param).(*Params).ResponseType = 2 // Set state to thinking
140 (*param).(*Params).HasContent = true
134 }141 }
135 } else {142 } else {
136 // Process regular text content (user-visible output)143 // Process regular text content (user-visible output)
@@ -139,6 +146,7 @@ func ConvertGeminiCLIResponseToClaude(_ context.Context, _ string, originalReque · 2 unmodified lines
139 output = output + "event: content_block_delta\n"146 output = output + "event: content_block_delta\n"
140 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())
141 output = output + fmt.Sprintf("data: %s\n\n\n", data)148 output = output + fmt.Sprintf("data: %s\n\n\n", data)
149 (*param).(*Params).HasContent = true
142 } else {150 } else {
143 // Transition from another state to text content151 // Transition from another state to text content
144 // First, close any existing content block152 // First, close any existing content block
@@ -162,6 +170,7 @@ func ConvertGeminiCLIResponseToClaude(_ context.Context, _ string, originalReque · 17 unmodified lines
162 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())
163 output = output + fmt.Sprintf("data: %s\n\n\n", data)171 output = output + fmt.Sprintf("data: %s\n\n\n", data)
164 (*param).(*Params).ResponseType = 1 // Set state to content172 (*param).(*Params).ResponseType = 1 // Set state to content
173 (*param).(*Params).HasContent = true
165 }174 }
166 }175 }
167 } else if functionCallResult.Exists() {176 } else if functionCallResult.Exists() {
@@ -211,6 +220,7 @@ func ConvertGeminiCLIResponseToClaude(_ context.Context, _ string, originalReque · 43 unmodified lines
211 output = output + fmt.Sprintf("data: %s\n\n\n", data)220 output = output + fmt.Sprintf("data: %s\n\n\n", data)
212 }221 }
213 (*param).(*Params).ResponseType = 3222 (*param).(*Params).ResponseType = 3
223 (*param).(*Params).HasContent = true
214 }224 }
215 }225 }
216 }226 }
@@ -219,28 +229,31 @@ func ConvertGeminiCLIResponseToClaude(_ context.Context, _ string, originalReque · 2 unmodified lines
219 // Process usage metadata and finish reason when present in the response229 // Process usage metadata and finish reason when present in the response
220 if usageResult.Exists() && bytes.Contains(rawJSON, []byte(`"finishReason"`)) {230 if usageResult.Exists() && bytes.Contains(rawJSON, []byte(`"finishReason"`)) {
221 if candidatesTokenCountResult := usageResult.Get("candidatesTokenCount"); candidatesTokenCountResult.Exists() {231 if candidatesTokenCountResult := usageResult.Get("candidatesTokenCount"); candidatesTokenCountResult.Exists() {
222 // Close the final content block232 // Only send final events if we have actually output content
223 output = output + "event: content_block_stop\n"233 if (*param).(*Params).HasContent {
224 output = output + fmt.Sprintf(`data: {"type":"content_block_stop","index":%d}`, (*param).(*Params).ResponseIndex)234 // Close the final content block
225 output = output + "\n\n\n"235 output = output + "event: content_block_stop\n"
226 236 output = output + fmt.Sprintf(`data: {"type":"content_block_stop","index":%d}`, (*param).(*Params).ResponseIndex)
227 // Send the final message delta with usage information and stop reason237 output = output + "\n\n\n"
228 output = output + "event: message_delta\n"238
229 output = output + `data: `239 // Send the final message delta with usage information and stop reason
230 240 output = output + "event: message_delta\n"
231 // Create the message delta template with appropriate stop reason241 output = output + `data: `
232 template := `{"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"input_tokens":0,"output_tokens":0}}`242
233 // Set tool_use stop reason if tools were used in this response243 // Create the message delta template with appropriate stop reason
234 if usedTool {244 template := `{"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"input_tokens":0,"output_tokens":0}}`
235 template = `{"type":"message_delta","delta":{"stop_reason":"tool_use","stop_sequence":null},"usage":{"input_tokens":0,"output_tokens":0}}`245 // Set tool_use stop reason if tools were used in this response
236 }246 if usedTool {
247 template = `{"type":"message_delta","delta":{"stop_reason":"tool_use","stop_sequence":null},"usage":{"input_tokens":0,"output_tokens":0}}`
248 }
237 249
238 // Include thinking tokens in output token count if present250 // Include thinking tokens in output token count if present
239 thoughtsTokenCount := usageResult.Get("thoughtsTokenCount").Int()251 thoughtsTokenCount := usageResult.Get("thoughtsTokenCount").Int()
240 template, _ = sjson.Set(template, "usage.output_tokens", candidatesTokenCountResult.Int()+thoughtsTokenCount)252 template, _ = sjson.Set(template, "usage.output_tokens", candidatesTokenCountResult.Int()+thoughtsTokenCount)
241 template, _ = sjson.Set(template, "usage.input_tokens", usageResult.Get("promptTokenCount").Int())253 template, _ = sjson.Set(template, "usage.input_tokens", usageResult.Get("promptTokenCount").Int())
242 254
243 output = output + template + "\n\n\n"255 output = output + template + "\n\n\n"
256 }
244 }257 }
245 }258 }
246 259
internal/translator/gemini/claude/gemini_claude_response.go+29−16
@@ -25,6 +25,7 @@ type Params struct { · 24 unmodified lines
25 HasFirstResponse bool25 HasFirstResponse bool
26 ResponseType int26 ResponseType int
27 ResponseIndex int27 ResponseIndex int
28 HasContent bool // Tracks whether any content (text, thinking, or tool use) has been output
28}29}
29 30
30// toolUseIDCounter provides a process-wide unique counter for tool use identifiers.31// toolUseIDCounter provides a process-wide unique counter for tool use identifiers.
@@ -57,9 +58,13 @@ func ConvertGeminiResponseToClaude(_ context.Context, _ string, originalRequestR · 26 unmodified lines
57 }58 }
58 59
59 if bytes.Equal(rawJSON, []byte("[DONE]")) {60 if bytes.Equal(rawJSON, []byte("[DONE]")) {
60 return []string{61 // Only send message_stop if we have actually output content
61 "event: message_stop\ndata: {\"type\":\"message_stop\"}\n\n\n",62 if (*param).(*Params).HasContent {
63 return []string{
64 "event: message_stop\ndata: {\"type\":\"message_stop\"}\n\n\n",
65 }
62 }66 }
67 return []string{}
63 }68 }
64 69
65 // Track whether tools are being used in this response chunk70 // Track whether tools are being used in this response chunk
@@ -108,6 +113,7 @@ func ConvertGeminiResponseToClaude(_ context.Context, _ string, originalRequestR · 42 unmodified lines
108 output = output + "event: content_block_delta\n"113 output = output + "event: content_block_delta\n"
109 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())
110 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
111 } else {117 } else {
112 // Transition from another state to thinking118 // Transition from another state to thinking
113 // First, close any existing content block119 // First, close any existing content block
@@ -131,6 +137,7 @@ func ConvertGeminiResponseToClaude(_ context.Context, _ string, originalRequestR · 17 unmodified lines
131 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())
132 output = output + fmt.Sprintf("data: %s\n\n\n", data)138 output = output + fmt.Sprintf("data: %s\n\n\n", data)
133 (*param).(*Params).ResponseType = 2 // Set state to thinking139 (*param).(*Params).ResponseType = 2 // Set state to thinking
140 (*param).(*Params).HasContent = true
134 }141 }
135 } else {142 } else {
136 // Process regular text content (user-visible output)143 // Process regular text content (user-visible output)
@@ -139,6 +146,7 @@ func ConvertGeminiResponseToClaude(_ context.Context, _ string, originalRequestR · 2 unmodified lines
139 output = output + "event: content_block_delta\n"146 output = output + "event: content_block_delta\n"
140 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())
141 output = output + fmt.Sprintf("data: %s\n\n\n", data)148 output = output + fmt.Sprintf("data: %s\n\n\n", data)
149 (*param).(*Params).HasContent = true
142 } else {150 } else {
143 // Transition from another state to text content151 // Transition from another state to text content
144 // First, close any existing content block152 // First, close any existing content block
@@ -162,6 +170,7 @@ func ConvertGeminiResponseToClaude(_ context.Context, _ string, originalRequestR · 17 unmodified lines
162 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())
163 output = output + fmt.Sprintf("data: %s\n\n\n", data)171 output = output + fmt.Sprintf("data: %s\n\n\n", data)
164 (*param).(*Params).ResponseType = 1 // Set state to content172 (*param).(*Params).ResponseType = 1 // Set state to content
173 (*param).(*Params).HasContent = true
165 }174 }
166 }175 }
167 } else if functionCallResult.Exists() {176 } else if functionCallResult.Exists() {
@@ -211,6 +220,7 @@ func ConvertGeminiResponseToClaude(_ context.Context, _ string, originalRequestR · 43 unmodified lines
211 output = output + fmt.Sprintf("data: %s\n\n\n", data)220 output = output + fmt.Sprintf("data: %s\n\n\n", data)
212 }221 }
213 (*param).(*Params).ResponseType = 3222 (*param).(*Params).ResponseType = 3
223 (*param).(*Params).HasContent = true
214 }224 }
215 }225 }
216 }226 }
@@ -218,23 +228,26 @@ func ConvertGeminiResponseToClaude(_ context.Context, _ string, originalRequestR · 1 unmodified line
218 usageResult := gjson.GetBytes(rawJSON, "usageMetadata")228 usageResult := gjson.GetBytes(rawJSON, "usageMetadata")
219 if usageResult.Exists() && bytes.Contains(rawJSON, []byte(`"finishReason"`)) {229 if usageResult.Exists() && bytes.Contains(rawJSON, []byte(`"finishReason"`)) {
220 if candidatesTokenCountResult := usageResult.Get("candidatesTokenCount"); candidatesTokenCountResult.Exists() {230 if candidatesTokenCountResult := usageResult.Get("candidatesTokenCount"); candidatesTokenCountResult.Exists() {
221 output = output + "event: content_block_stop\n"231 // Only send final events if we have actually output content
222 output = output + fmt.Sprintf(`data: {"type":"content_block_stop","index":%d}`, (*param).(*Params).ResponseIndex)232 if (*param).(*Params).HasContent {
223 output = output + "\n\n\n"233 output = output + "event: content_block_stop\n"
234 output = output + fmt.Sprintf(`data: {"type":"content_block_stop","index":%d}`, (*param).(*Params).ResponseIndex)
235 output = output + "\n\n\n"
236
237 output = output + "event: message_delta\n"
238 output = output + `data: `
239
240 template := `{"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"input_tokens":0,"output_tokens":0}}`
241 if usedTool {
242 template = `{"type":"message_delta","delta":{"stop_reason":"tool_use","stop_sequence":null},"usage":{"input_tokens":0,"output_tokens":0}}`
243 }
224 244
225 output = output + "event: message_delta\n"245 thoughtsTokenCount := usageResult.Get("thoughtsTokenCount").Int()
226 output = output + `data: `246 template, _ = sjson.Set(template, "usage.output_tokens", candidatesTokenCountResult.Int()+thoughtsTokenCount)
247 template, _ = sjson.Set(template, "usage.input_tokens", usageResult.Get("promptTokenCount").Int())
227 248
228 template := `{"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"input_tokens":0,"output_tokens":0}}`249 output = output + template + "\n\n\n"
229 if usedTool {
230 template = `{"type":"message_delta","delta":{"stop_reason":"tool_use","stop_sequence":null},"usage":{"input_tokens":0,"output_tokens":0}}`
231 }250 }
232
233 thoughtsTokenCount := usageResult.Get("thoughtsTokenCount").Int()
234 template, _ = sjson.Set(template, "usage.output_tokens", candidatesTokenCountResult.Int()+thoughtsTokenCount)
235 template, _ = sjson.Set(template, "usage.input_tokens", usageResult.Get("promptTokenCount").Int())
236
237 output = output + template + "\n\n\n"
238 }251 }
239 }252 }
240 253