package eino import ( "context" "fmt" "strings" "github.com/cloudwego/eino/compose" "github.com/hhs/camtalk/internal/ai/stt" "github.com/hhs/camtalk/internal/logger" "github.com/hhs/camtalk/internal/models" ) // NewSTTLambda 创建 STT Lambda 节点。 // 输入: PipelineInput → 输出: STTOutput // // 文本输入模式:跳过 STT,直接返回用户输入文本。 // 语音模式:调用 sttService.Recognize() 进行语音识别。 // 识别结果通过 Sender 发送 stt_result 到客户端。 func NewSTTLambda(sttService stt.Service) *compose.Lambda { return compose.InvokableLambda(func(ctx context.Context, input PipelineInput) (STTOutput, error) { log := logger.Log sender := senderFromCtx(ctx) requestID := requestIDFromCtx(ctx) // 将输入元数据写入 State,供下游节点(History、Done)读取 if state := stateFromCtx(ctx); state != nil { state.mu.Lock() state.SessionID = input.SessionID state.RequestID = input.RequestID state.ImageData = input.ImageData state.Scenario = input.Scenario state.DetailLevel = "low" state.Language = input.Language state.TTSEnabled = input.TTSEnabled state.mu.Unlock() } // 文本输入模式:跳过 STT if input.Text != "" { log.Infow("使用文本输入,跳过 STT", "request_id", requestID, "text", input.Text) // 发送 stt_result 保持前端消息流一致性 if sender != nil { if err := sender.SendSTTResult(models.WsSTTResult{ Type: "stt_result", RequestID: requestID, Text: input.Text, IsFinal: true, }); err != nil { log.Errorw("发送 stt_result 失败", "error", err) } } // 写入 State if state := stateFromCtx(ctx); state != nil { state.mu.Lock() state.TranscribedText = input.Text state.mu.Unlock() } return STTOutput{ Text: input.Text, Language: input.Language, IsSkipped: true, }, nil } // 语音模式:解码音频 if len(input.AudioData) == 0 { return STTOutput{}, fmt.Errorf("stt: no audio data provided") } log.Infow("开始语音识别", "request_id", requestID, "audio_bytes", len(input.AudioData)) // 调用 STT 服务 text, err := sttService.Recognize(ctx, input.AudioData, stt.Options{ Encoding: "pcm_s16le", SampleRate: 16000, Language: input.Language, }) if err != nil { log.Errorw("语音识别失败", "error", err, "request_id", requestID) if sender != nil { sender.SendError(models.WsError{ Type: "error", RequestID: requestID, Code: "STT_ERROR", Message: "语音识别失败: " + err.Error(), }) } return STTOutput{}, fmt.Errorf("stt: recognize: %w", err) } // STT 返回空文本 if strings.TrimSpace(text) == "" { log.Infow("语音识别结果为空", "request_id", requestID) text = "(未识别到语音)" } log.Infow("语音识别完成", "request_id", requestID, "text", text) // 发送 stt_result if sender != nil { if err := sender.SendSTTResult(models.WsSTTResult{ Type: "stt_result", RequestID: requestID, Text: text, IsFinal: true, }); err != nil { log.Errorw("发送 stt_result 失败", "error", err) } } // 写入 State if state := stateFromCtx(ctx); state != nil { state.mu.Lock() state.TranscribedText = text state.mu.Unlock() } return STTOutput{ Text: text, Language: input.Language, IsSkipped: false, }, nil }) }