Enhance streaming message handling in DefaultStreamHandler
- Added a new field to track the current message type (text, thinking, tool_call) in the streamState struct. - Updated message handling methods to set the current message type appropriately. - Modified the group end handling to use the tracked message type when sending messages, ensuring accurate type representation. - Enhanced unit tests to verify the correct handling of group end events and their associated types.
This commit is contained in:
parent
796f6437ef
commit
a824670def
2 changed files with 74 additions and 8 deletions
|
|
@ -62,10 +62,11 @@ func DefaultStreamHandler(ctx *context.Context) context.StreamFunc {
|
|||
|
||||
// streamState manages the state of the streaming process
|
||||
type streamState struct {
|
||||
ctx *context.Context
|
||||
inGroup bool
|
||||
currentID string
|
||||
buffer []byte
|
||||
ctx *context.Context
|
||||
inGroup bool
|
||||
currentID string
|
||||
currentType string // Track the current message type (text, thinking, tool_call)
|
||||
buffer []byte
|
||||
}
|
||||
|
||||
// handleStreamStart handles stream start event
|
||||
|
|
@ -96,6 +97,9 @@ func (s *streamState) handleText(data []byte) int {
|
|||
s.currentID = generateMessageID()
|
||||
}
|
||||
|
||||
// Track current message type
|
||||
s.currentType = message.TypeText
|
||||
|
||||
// Append to buffer
|
||||
s.buffer = append(s.buffer, data...)
|
||||
|
||||
|
|
@ -128,6 +132,9 @@ func (s *streamState) handleThinking(data []byte) int {
|
|||
s.currentID = generateMessageID()
|
||||
}
|
||||
|
||||
// Track current message type
|
||||
s.currentType = message.TypeThinking
|
||||
|
||||
// Append to buffer
|
||||
s.buffer = append(s.buffer, data...)
|
||||
|
||||
|
|
@ -190,9 +197,15 @@ func (s *streamState) handleGroupEnd(data []byte) int {
|
|||
|
||||
// Send done message with complete content
|
||||
if s.currentID != "" && len(s.buffer) > 0 {
|
||||
// Use the tracked message type (thinking, text, tool_call, etc.)
|
||||
msgType := s.currentType
|
||||
if msgType == "" {
|
||||
msgType = message.TypeText // Fallback to text if type not set
|
||||
}
|
||||
|
||||
msg := &message.Message{
|
||||
ID: s.currentID,
|
||||
Type: message.TypeText, // Default to text
|
||||
Type: msgType, // Use the actual message type from the group
|
||||
Done: true,
|
||||
Props: map[string]interface{}{
|
||||
"content": string(s.buffer),
|
||||
|
|
@ -204,6 +217,7 @@ func (s *streamState) handleGroupEnd(data []byte) int {
|
|||
// Reset state
|
||||
s.inGroup = false
|
||||
s.currentID = ""
|
||||
s.currentType = ""
|
||||
s.buffer = []byte{}
|
||||
|
||||
return 0 // Continue
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/yaoapp/gou/connector"
|
||||
"github.com/yaoapp/gou/plan"
|
||||
"github.com/yaoapp/yao/agent/context"
|
||||
|
|
@ -59,20 +60,58 @@ func TestDeepSeekR1StreamBasic(t *testing.T) {
|
|||
// Create context
|
||||
ctx := newDeepSeekTestContext("test-deepseek-r1-basic", "deepseek.r1")
|
||||
|
||||
// Track streaming chunks
|
||||
// Track streaming chunks and group events
|
||||
var reasoningChunks []string
|
||||
var contentChunks []string
|
||||
var thinkingGroupEnded bool
|
||||
var textGroupEnded bool
|
||||
|
||||
handler := func(chunkType context.StreamChunkType, data []byte) int {
|
||||
dataStr := string(data)
|
||||
t.Logf("Stream chunk [%s]: %s", chunkType, dataStr)
|
||||
|
||||
// Track different chunk types
|
||||
if chunkType == context.ChunkThinking {
|
||||
switch chunkType {
|
||||
case context.ChunkThinking:
|
||||
reasoningChunks = append(reasoningChunks, dataStr)
|
||||
} else if chunkType == context.ChunkText {
|
||||
case context.ChunkText:
|
||||
contentChunks = append(contentChunks, dataStr)
|
||||
}
|
||||
|
||||
// Track group_end events to verify type field
|
||||
if chunkType == context.ChunkGroupEnd {
|
||||
// Parse the group_end data to check the type field
|
||||
var groupEndData struct {
|
||||
GroupID string `json:"group_id"`
|
||||
Type string `json:"type"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
DurationMs int64 `json:"duration_ms"`
|
||||
ChunkCount int `json:"chunk_count"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
if err := jsoniter.Unmarshal(data, &groupEndData); err == nil {
|
||||
t.Logf("✓ group_end received: type=%s, chunks=%d, duration=%dms",
|
||||
groupEndData.Type, groupEndData.ChunkCount, groupEndData.DurationMs)
|
||||
|
||||
// Verify the type field matches expected group types
|
||||
switch groupEndData.Type {
|
||||
case "thinking":
|
||||
thinkingGroupEnded = true
|
||||
if groupEndData.ChunkCount == 0 {
|
||||
t.Error("thinking group_end should have chunk_count > 0")
|
||||
}
|
||||
case "text":
|
||||
textGroupEnded = true
|
||||
if groupEndData.ChunkCount == 0 {
|
||||
t.Error("text group_end should have chunk_count > 0")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Failed to parse group_end data: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return 0 // Continue
|
||||
}
|
||||
|
||||
|
|
@ -139,6 +178,19 @@ func TestDeepSeekR1StreamBasic(t *testing.T) {
|
|||
t.Logf("Received %d content chunks", len(contentChunks))
|
||||
}
|
||||
|
||||
// Verify group_end events were received with correct types
|
||||
if !thinkingGroupEnded {
|
||||
t.Error("❌ Expected thinking group_end event but didn't receive it")
|
||||
} else {
|
||||
t.Log("✅ Thinking group_end event received with type='thinking'")
|
||||
}
|
||||
|
||||
if !textGroupEnded {
|
||||
t.Error("❌ Expected text group_end event but didn't receive it")
|
||||
} else {
|
||||
t.Log("✅ Text group_end event received with type='text'")
|
||||
}
|
||||
|
||||
t.Logf("Final response: %+v", response)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue