ss22345 commited on
Commit
85aaf7e
·
1 Parent(s): ae13261

fix: improve streaming tool_call block detection and add debug logging

Browse files

Support multiple close markers (</tool_call>, </think>, next <tool_call>)
when detecting tool call block boundaries in streaming mode. Add debug
logging for stream payload inspection.

Files changed (1) hide show
  1. internal/handler/chat.go +41 -4
internal/handler/chat.go CHANGED
@@ -101,6 +101,7 @@ func handleStreamResponse(w http.ResponseWriter, body io.ReadCloser, completionI
101
  logger.LogDebug("[Upstream] %s", line)
102
 
103
  if !strings.HasPrefix(line, "data: ") {
 
104
  continue
105
  }
106
 
@@ -111,9 +112,12 @@ func handleStreamResponse(w http.ResponseWriter, body io.ReadCloser, completionI
111
 
112
  var upstreamData model.UpstreamData
113
  if err := json.Unmarshal([]byte(payload), &upstreamData); err != nil {
 
114
  continue
115
  }
116
 
 
 
117
  if upstreamData.Data.Phase == "done" {
118
  break
119
  }
@@ -424,14 +428,37 @@ func handleStreamResponse(w http.ResponseWriter, body io.ReadCloser, completionI
424
  sendContentChunk(w, flusher, completionID, modelName, safeContent)
425
  }
426
  }
427
- // 是否有完整的闭合标签
 
428
  closeIdx := strings.Index(promptToolBuffer, "</tool_call>")
429
- if closeIdx == -1 {
430
- // 未闭合,等待更多数据
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431
  break
432
  }
 
433
  // 提取完整块
434
- blockEnd := closeIdx + len("</tool_call>")
435
  block := promptToolBuffer[:blockEnd]
436
  promptToolBuffer = promptToolBuffer[blockEnd:]
437
 
@@ -586,9 +613,12 @@ func handleNonStreamResponse(w http.ResponseWriter, body io.ReadCloser, completi
586
 
587
  var upstreamData model.UpstreamData
588
  if err := json.Unmarshal([]byte(payload), &upstreamData); err != nil {
 
589
  continue
590
  }
591
 
 
 
592
  if upstreamData.Data.Phase == "done" {
593
  break
594
  }
@@ -762,3 +792,10 @@ func sendContentChunk(w http.ResponseWriter, flusher http.Flusher, completionID,
762
  fmt.Fprintf(w, "data: %s\n\n", data)
763
  flusher.Flush()
764
  }
 
 
 
 
 
 
 
 
101
  logger.LogDebug("[Upstream] %s", line)
102
 
103
  if !strings.HasPrefix(line, "data: ") {
104
+ logger.LogInfo("[DEBUG-Stream] non-data line: %s", truncate(line, 200))
105
  continue
106
  }
107
 
 
112
 
113
  var upstreamData model.UpstreamData
114
  if err := json.Unmarshal([]byte(payload), &upstreamData); err != nil {
115
+ logger.LogInfo("[DEBUG-Stream] JSON parse error: %v, payload=%s", err, truncate(payload, 300))
116
  continue
117
  }
118
 
119
+ logger.LogInfo("[DEBUG-Stream] phase=%s delta_content_len=%d edit_content_len=%d", upstreamData.Data.Phase, len(upstreamData.Data.DeltaContent), len(upstreamData.Data.EditContent))
120
+
121
  if upstreamData.Data.Phase == "done" {
122
  break
123
  }
 
428
  sendContentChunk(w, flusher, completionID, modelName, safeContent)
429
  }
430
  }
431
+ // 查闭合标签:</tool_call>、</think>、或下一个 <tool_call>
432
+ afterOpen := promptToolBuffer[len("<tool_call>"):]
433
  closeIdx := strings.Index(promptToolBuffer, "</tool_call>")
434
+ thinkCloseIdx := strings.Index(afterOpen, "</think>")
435
+ nextOpenIdx := strings.Index(afterOpen, "<tool_call>")
436
+
437
+ // 选择最近的闭合位置
438
+ blockEnd := -1
439
+ if closeIdx != -1 {
440
+ blockEnd = closeIdx + len("</tool_call>")
441
+ }
442
+ if thinkCloseIdx != -1 {
443
+ candidate := len("<tool_call>") + thinkCloseIdx + len("</think>")
444
+ if blockEnd == -1 || candidate < blockEnd {
445
+ blockEnd = candidate
446
+ }
447
+ }
448
+ if nextOpenIdx != -1 {
449
+ // 下一个 <tool_call> 隐式关闭当前块
450
+ candidate := len("<tool_call>") + nextOpenIdx
451
+ if blockEnd == -1 || candidate < blockEnd {
452
+ blockEnd = candidate
453
+ }
454
+ }
455
+
456
+ if blockEnd == -1 {
457
+ // 未找到任何闭合标记,等待更多数据
458
  break
459
  }
460
+
461
  // 提取完整块
 
462
  block := promptToolBuffer[:blockEnd]
463
  promptToolBuffer = promptToolBuffer[blockEnd:]
464
 
 
613
 
614
  var upstreamData model.UpstreamData
615
  if err := json.Unmarshal([]byte(payload), &upstreamData); err != nil {
616
+ logger.LogInfo("[DEBUG-NonStream] JSON parse error: %v, payload=%s", err, truncate(payload, 200))
617
  continue
618
  }
619
 
620
+ logger.LogInfo("[DEBUG-NonStream] phase=%s delta_content_len=%d edit_content_len=%d", upstreamData.Data.Phase, len(upstreamData.Data.DeltaContent), len(upstreamData.Data.EditContent))
621
+
622
  if upstreamData.Data.Phase == "done" {
623
  break
624
  }
 
792
  fmt.Fprintf(w, "data: %s\n\n", data)
793
  flusher.Flush()
794
  }
795
+
796
+ func truncate(s string, maxLen int) string {
797
+ if len(s) <= maxLen {
798
+ return s
799
+ }
800
+ return s[:maxLen] + "..."
801
+ }