feat: 引入 Eino 框架并实现 AI 编排层基础设施与节点

- 引入 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>
This commit is contained in:
2026-06-19 21:49:28 +08:00
parent b10a508356
commit fd5c7712f8
11 changed files with 946 additions and 24 deletions

View File

@@ -0,0 +1,72 @@
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
})
}