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:
@@ -220,7 +220,7 @@ func runMessageChannel(ctx context.Context, cfg config.Config, engine *agent.Orc
|
||||
return engine.HandleMessage(ctx, msg.ChatID, msg.UserID, msg.Text)
|
||||
},
|
||||
func(ctx context.Context, msg webui.IncomingMessage, callback webui.StreamEventCallback) (string, error) {
|
||||
return engine.HandleMessageStream(ctx, msg.ChatID, msg.UserID, msg.Text, buildWebUIStreamForwarder(callback))
|
||||
return engine.HandleMessageStream(ctx, msg.ChatID, msg.UserID, msg.Text, buildWebUIStreamForwarder(callback, cfg.WebUI.ExposeReasoning))
|
||||
},
|
||||
func(ctx context.Context, chatID, userID string, files []llm.InputFile) ([]string, error) {
|
||||
return engine.UploadAndCacheFiles(ctx, chatID, userID, files)
|
||||
@@ -231,16 +231,54 @@ func runMessageChannel(ctx context.Context, cfg config.Config, engine *agent.Orc
|
||||
}
|
||||
}
|
||||
|
||||
func buildWebUIStreamForwarder(callback webui.StreamEventCallback) agent.StreamEventCallback {
|
||||
func buildWebUIStreamForwarder(callback webui.StreamEventCallback, exposeReasoning bool) agent.StreamEventCallback {
|
||||
const finalChunkRunes = 12
|
||||
const finalChunkInterval = 25 * time.Millisecond
|
||||
|
||||
return func(event agent.StreamEvent) error {
|
||||
if callback == nil {
|
||||
return nil
|
||||
}
|
||||
switch event.Type {
|
||||
case agent.StreamEventTypeFinal, agent.StreamEventTypeError:
|
||||
case agent.StreamEventTypeThought, agent.StreamEventTypeToolCall, agent.StreamEventTypeToolResult:
|
||||
if !exposeReasoning {
|
||||
return nil
|
||||
}
|
||||
return callback(webui.StreamEvent{
|
||||
Type: webui.StreamEventType(event.Type),
|
||||
Type: webui.StreamEventType(event.Type),
|
||||
Content: event.Content,
|
||||
Step: event.Step,
|
||||
ToolName: event.ToolName,
|
||||
})
|
||||
case agent.StreamEventTypeFinal:
|
||||
runes := []rune(event.Content)
|
||||
if len(runes) == 0 {
|
||||
return callback(webui.StreamEvent{Type: webui.StreamEventTypeFinal, Content: "", Step: event.Step})
|
||||
}
|
||||
start := 0
|
||||
for start < len(runes) {
|
||||
end := start + finalChunkRunes
|
||||
if end > len(runes) {
|
||||
end = len(runes)
|
||||
}
|
||||
if err := callback(webui.StreamEvent{
|
||||
Type: webui.StreamEventTypeFinal,
|
||||
Content: string(runes[start:end]),
|
||||
Step: event.Step,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
start = end
|
||||
if start < len(runes) {
|
||||
time.Sleep(finalChunkInterval)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case agent.StreamEventTypeError:
|
||||
return callback(webui.StreamEvent{
|
||||
Type: webui.StreamEventTypeError,
|
||||
Content: event.Content,
|
||||
Step: event.Step,
|
||||
})
|
||||
default:
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user