Skip to content

Commit dcb3a5b

Browse files
committed
fix(chatbot): robust SSE error handling — write errors, escaping, buffer
1 parent 3a4a9d0 commit dcb3a5b

2 files changed

Lines changed: 12 additions & 5 deletions

File tree

internal/admin/chatbot/client.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ func (a *openAIAdapter) ChatCompletionStream(
160160

161161
func (a *openAIAdapter) readSSEStream(r io.Reader, chunkChan chan<- string) error {
162162
scanner := bufio.NewScanner(r)
163+
scanner.Buffer(make([]byte, 0, 64*1024), 1024*1024) // 1MB max
163164

164165
for scanner.Scan() {
165166
line := strings.TrimSpace(scanner.Text())

internal/admin/server_chatbot.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -228,10 +228,13 @@ func (s *Server) handleChatbotChat(w http.ResponseWriter, r *http.Request) {
228228
chunkChan := make(chan string, 64)
229229
errCh := make(chan error, 1)
230230

231+
ctx, cancel := context.WithCancel(r.Context())
232+
defer cancel() // ensure cancel is called on all exit paths
233+
231234
go func() {
232235
// Prepend system prompt as first message when needed.
233236
fullHistory := prependSystemMessage(history, systemPrompt)
234-
errCh <- llm.ChatCompletionStream(r.Context(), req.Prompt, fullHistory, chunkChan)
237+
errCh <- llm.ChatCompletionStream(ctx, req.Prompt, fullHistory, chunkChan)
235238
close(chunkChan)
236239
}()
237240

@@ -240,13 +243,16 @@ func (s *Server) handleChatbotChat(w http.ResponseWriter, r *http.Request) {
240243

241244
for chunk := range chunkChan {
242245
fullResponse.WriteString(chunk)
243-
fmt.Fprintf(w, "data: %s\n\n", chunk)
246+
if _, err := fmt.Fprintf(w, "data: %s\n\n", chunk); err != nil {
247+
cancel() // cancel the LLM request context to stop token generation
248+
return
249+
}
244250
flusher.Flush()
245251
}
246252

247253
if err := <-errCh; err != nil {
248254
s.logger.Error("chatbot streaming error", "error", err)
249-
fmt.Fprintf(w, "event: error\ndata: %s\n\n", err.Error())
255+
fmt.Fprintf(w, "event: error\ndata: %s\n\n", escapeSSEData(err.Error()))
250256
flusher.Flush()
251257
return
252258
}
@@ -272,7 +278,7 @@ func (s *Server) handleChatbotChat(w http.ResponseWriter, r *http.Request) {
272278
} else {
273279
// Validation failed – stream auto-correction progress.
274280
fmt.Fprintf(w, "event: dry_run_status\ndata: {\"success\":false,\"error\":\"%s\"}\n\n",
275-
strings.ReplaceAll(err.Error(), "\"", "\\\""))
281+
escapeSSEData(err.Error()))
276282
fmt.Fprintf(w, "event: manifests\ndata: %s\n\n", escapeSSEData(yamlBlock))
277283
flusher.Flush()
278284

@@ -331,7 +337,7 @@ func runAutoCorrectSSE(
331337

332338
if streamErr := <-errCh; streamErr != nil {
333339
fmt.Fprintf(w, "event: correction_error\ndata: {\"error\":\"%s\"}\n\n",
334-
strings.ReplaceAll(streamErr.Error(), "\"", "\\\""))
340+
escapeSSEData(streamErr.Error()))
335341
flusher.Flush()
336342
return
337343
}

0 commit comments

Comments
 (0)