package echomeet import ( "context" "fmt" "yunyan/pb" "yunyan/sys/bytedance/audomodel" bdtranslate "yunyan/sys/bytedance/translate" "yunyan/sys/doubao" ) // 字节系单元服务实例(由 provider_registry.go 按服务池字段构造): // 转写=audomodel(短音频 flash 同步 + 长音频异步)、翻译=火山翻译、总结=豆包。 // 语言支持过滤已上移到编排层(服务池 languages 字段),此处只做语言码转换。 // 字节跳动转写:短音频 RecognizeFlash(同步) + 长音频 CreateTask+QueryTask(异步) type byteDanceTranscriber struct { sys audomodel.ISys } const ( byteDanceFlashMaxSeconds = 7200 byteDanceFlashMaxBytes = 1024 * 1024 * 100 ) func (this *byteDanceTranscriber) Submit(_ context.Context, req SubmitRequest) (*SubmitResult, error) { lang, _ := toByteDanceLang(req.Language) // 短音频走 flash 同步通道 if req.Seconds <= byteDanceFlashMaxSeconds && req.SizeBytes <= byteDanceFlashMaxBytes { statusCode, _, contexts, err := this.sys.RecognizeFlash(req.Uid, req.AudioURL, req.EnableSpeaker, lang) if err != nil { return nil, fmt.Errorf("bytedance flash err: status=%s %w", statusCode, err) } out := make([]*pb.ContextStruct, 0, len(contexts)) for _, c := range contexts { out = append(out, &pb.ContextStruct{ Content: c.Content, Starttime: c.StartTime, Endtime: c.EndTime, Speaker: c.Speaker, }) } return &SubmitResult{Done: true, Contexts: out}, nil } // 长音频走异步 submit(CallbackURL 由编排层按部署对外地址拼好传入) taskID, logID, err := this.sys.CreateTask(req.Uid, req.AudioURL, req.EnableSpeaker, lang, req.CallbackURL, req.CallbackData) if err != nil { return nil, fmt.Errorf("bytedance create task: %w", err) } return &SubmitResult{TaskID: taskID, LogID: logID}, nil } func (this *byteDanceTranscriber) Query(_ context.Context, taskID, logID string) (*QueryResult, error) { code, contexts, err := this.sys.QueryTask(taskID, logID) switch code { case "20000000": if err != nil { return &QueryResult{Status: StatusFailed}, err } out := make([]*pb.ContextStruct, 0, len(contexts)) for _, c := range contexts { out = append(out, &pb.ContextStruct{ Content: c.Content, Starttime: c.StartTime, Endtime: c.EndTime, Speaker: c.Speaker, }) } return &QueryResult{Status: StatusSuccess, Contexts: out}, nil case "20000001", "20000002": return &QueryResult{Status: StatusRunning}, nil default: return &QueryResult{Status: StatusFailed}, fmt.Errorf("bytedance query failed code=%s err=%v", code, err) } } // 火山翻译 type byteDanceTranslator struct { sys bdtranslate.ISys } func (this *byteDanceTranslator) Translate(ctx context.Context, from, to string, texts []string) ([]string, error) { return this.sys.Translate(ctx, toByteDanceTranslateLang(from), toByteDanceTranslateLang(to), texts) } // 豆包总结 type doubaoSummarizer struct { sys doubao.ISys } func (this *doubaoSummarizer) Chat(ctx context.Context, messages []ChatMessage) (string, error) { if this.sys == nil { return "", fmt.Errorf("doubao summarizer not initialized") } msgs := make([]doubao.Message, 0, len(messages)) for _, m := range messages { msgs = append(msgs, doubao.Message{Role: m.Role, Content: m.Content, Images: m.Images}) } resp, err := this.sys.Chat(ctx, msgs) if err != nil { return "", err } if resp == nil { return "", fmt.Errorf("doubao chat: empty response") } return resp.Content, nil }