package eino import ( "context" "io" "strings" "github.com/cloudwego/eino/compose" "github.com/cloudwego/eino/schema" ) // sentenceDelimiters 句子分隔符集合。 var sentenceDelimiters = map[rune]bool{ '。': true, '!': true, '?': true, '\n': true, '.': true, '!': true, '?': true, } // NewSplitterLambda 创建句子分割 Transform Lambda 节点。 // 输入: StreamReader[string](LLM 完整文本的单帧流)→ 输出: StreamReader[[]string](句子数组流) // // 在 Stream 模式下,框架自动将 ChatModel 的 StreamReader[*schema.Message] // concat 为 string 后传入此节点。此节点将文本按句子边界切分, // 每切出一个句子就输出一次,供 TTS 节点实时合成。 func NewSplitterLambda() *compose.Lambda { return compose.TransformableLambda(func(ctx context.Context, input *schema.StreamReader[string]) (*schema.StreamReader[[]string], error) { sr, sw := schema.Pipe[[]string](8) go func() { defer sw.Close() var buffer strings.Builder for { chunk, err := input.Recv() if err != nil { if err == io.EOF { // 流结束,flush 剩余缓冲 if buffer.Len() > 0 { text := strings.TrimSpace(buffer.String()) if text != "" { sw.Send([]string{text}, nil) } } return } sw.Send(nil, err) return } // chunk 是 concat 后的完整文本(单帧流) // 逐字符累积,按句子分隔符切分 for _, r := range chunk { buffer.WriteRune(r) if sentenceDelimiters[r] { text := strings.TrimSpace(buffer.String()) if text != "" { sw.Send([]string{text}, nil) } buffer.Reset() } } } }() return sr, nil }) }