package echomeet import ( "context" "yunyan/lego/sys/log" "yunyan/pb" "yunyan/sys/bytedance/audomodel" "yunyan/sys/bytedance/translate" "yunyan/sys/doubao" "fmt" ) // newByteDanceProviders 初始化字节系单元服务(转写=audomodel、翻译=火山翻译、总结=豆包), // 返回组合后的转写/翻译/总结三个 provider。 func newByteDanceProviders(opt ProviderOptions) (Transcriber, Translator, Summarizer) { if err := audomodel.OnInit(opt.Transcribe); err != nil { log.Errorf("echomeet: init bytedance audomodel failed: %v", err) } if err := translate.OnInit(opt.Translate); err != nil { log.Errorf("echomeet: init bytedance translate failed: %v", err) } sys, err := doubao.NewSys( doubao.SetApikey(opt.Summary.Apikey), doubao.SetModel(opt.Summary.Model), ) if err != nil { log.Errorf("echomeet: init doubao summarizer failed: %v", err) } return &byteDanceTranscriber{}, &byteDanceTranslator{}, &doubaoSummarizer{sys: sys} } // 字节跳动转写:短音频 RecognizeFlash(同步) + 长音频 CreateTask+QueryTask(异步) type byteDanceTranscriber struct{} func (this *byteDanceTranscriber) Name() pb.TranscribeService { return pb.TranscribeService_TranscribeService_ByteDance } func (this *byteDanceTranscriber) SupportsLanguage(bcp47 string) bool { _, ok := toByteDanceLang(bcp47) return ok } 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 := audomodel.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 taskID, logID, err := audomodel.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 := audomodel.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{} func (this *byteDanceTranslator) Name() pb.TranscribeService { return pb.TranscribeService_TranscribeService_ByteDance } func (this *byteDanceTranslator) Translate(ctx context.Context, from, to string, texts []string) ([]string, error) { return translate.Translate(ctx, toByteDanceTranslateLang(from), toByteDanceTranslateLang(to), texts) } // 豆包总结 type doubaoSummarizer struct { sys doubao.ISys } func (this *doubaoSummarizer) Name() pb.TranscribeService { return pb.TranscribeService_TranscribeService_ByteDance } 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 }