fix(sse): correct UTF-8 handling and chunking in SSE streaming\n\n- Updated splitContentIntoSegments to handle runes instead of bytes\n- Fixed buildWebUIStreamForwarder to send delta chunks instead of cumulative content\n- Ensures proper handling of multi-byte characters in SSE streams

This commit is contained in:
whlaoding
2026-03-14 01:41:51 +08:00
parent 60195f00a0
commit ea88e1dc18
7 changed files with 89 additions and 18 deletions

View File

@@ -731,12 +731,16 @@ func (o *Orchestrator) runUnifiedReActStream(ctx context.Context, chatID, userID
// 推送思考过程事件
if completion.Content != "" {
if err := callback(StreamEvent{
Type: StreamEventTypeThought,
Content: completion.Content,
Step: step,
}); err != nil {
return "", fmt.Errorf("callback error: %w", err)
// 分割内容为逐步推送的片段
segments := splitContentIntoSegments(completion.Content, 50) // 每段50字符
for _, segment := range segments {
if err := callback(StreamEvent{
Type: StreamEventTypeThought,
Content: segment,
Step: step,
}); err != nil {
return "", fmt.Errorf("callback error: %w", err)
}
}
}
@@ -1568,3 +1572,17 @@ func sanitizeUserFacingAnswer(raw string) string {
}
return strings.TrimSpace(strings.Join(cleaned, "\n"))
}
// splitContentIntoSegments splits a string into smaller segments of the specified size (by rune count).
func splitContentIntoSegments(content string, segmentSize int) []string {
runes := []rune(content)
var segments []string
for start := 0; start < len(runes); start += segmentSize {
end := start + segmentSize
if end > len(runes) {
end = len(runes)
}
segments = append(segments, string(runes[start:end]))
}
return segments
}

View File

@@ -48,8 +48,9 @@ type FeishuConfig struct {
}
type WebUIConfig struct {
ListenAddr string
MaxUploadBytes int64
ListenAddr string
MaxUploadBytes int64
ExposeReasoning bool
}
type LLMConfig struct {
@@ -110,8 +111,9 @@ func Load() (Config, error) {
EventPath: defaultIfEmpty(os.Getenv("FEISHU_EVENT_PATH"), "/feishu/events"),
},
WebUI: WebUIConfig{
ListenAddr: defaultIfEmpty(os.Getenv("WEBUI_LISTEN_ADDR"), ":8090"),
MaxUploadBytes: int64(intFromEnv("WEBUI_MAX_UPLOAD_MB", 20)) * 1024 * 1024,
ListenAddr: defaultIfEmpty(os.Getenv("WEBUI_LISTEN_ADDR"), ":8090"),
MaxUploadBytes: int64(intFromEnv("WEBUI_MAX_UPLOAD_MB", 20)) * 1024 * 1024,
ExposeReasoning: boolFromEnv("WEBUI_EXPOSE_REASONING", false),
},
LLM: LLMConfig{
BaseURL: strings.TrimRight(defaultIfEmpty(os.Getenv("LLM_BASE_URL"), "https://api.openai.com/v1"), "/"),