- 引入 cloudwego/eino v0.9.9 和 eino-ext/components/model/openai v0.1.13 - 新增 internal/eino/ 包: - types.go: PipelineInput/Output、STTOutput、TokenUsage 类型定义 - state.go: PipelineState 跨节点状态收集(线程安全) - callback.go: ChatModel OnEndWithStreamOutput 回调,逐 token 推送 llm_chunk - nodes_stt.go: STT Lambda,支持文本/语音输入模式 - nodes_history.go: 历史组装 Lambda,含多模态图片支持 - nodes_splitter.go: 句子分割 Transform Lambda - nodes_tts.go: TTS Lambda,逐句合成推送音频 - nodes_done.go: Done Lambda,发送 llm_done 并追加历史 Co-Authored-By: Claude <noreply@anthropic.com>
73 lines
1.7 KiB
Go
73 lines
1.7 KiB
Go
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
|
||
})
|
||
}
|