package echomeet import ( "context" "yunyan/lego/sys/log" "yunyan/pb" "yunyan/sys/aliyun/filetrans" "yunyan/sys/aliyun/translate" "yunyan/sys/openai" "fmt" "strings" ) // newAliProviders 初始化阿里系单元服务(转写=DashScope 录音文件转写、翻译=阿里翻译、 // 总结=通义千问 DashScope OpenAI 兼容端点),返回组合后的转写/翻译/总结三个 provider。 func newAliProviders(opt ProviderOptions) (Transcriber, Translator, Summarizer) { if err := filetrans.OnInit(opt.Transcribe); err != nil { log.Errorf("echomeet: init ali filetrans failed: %v", err) } if err := translate.OnInit(opt.Translate); err != nil { log.Errorf("echomeet: init ali translate failed: %v", err) } sys, err := openai.NewSys( openai.SetBaseURL(opt.Summary.BaseURL), openai.SetToken(opt.Summary.Token), openai.SetModel(opt.Summary.Model), openai.SetMaxfunccall(1), ) if err != nil { log.Errorf("echomeet: init ali(qwen) summarizer failed: %v", err) } return &aliTranscriber{}, &aliTranslator{}, &aliSummarizer{sys: sys} } // 阿里云 DashScope 转写(paraformer-v2,异步) type aliTranscriber struct{} func (this *aliTranscriber) Name() pb.TranscribeService { return pb.TranscribeService_TranscribeService_Ali } // dashScopeParaformerV2Langs 是 filetrans 所用模型 paraformer-v2 **实际**支持的语种(DashScope 语言码)。 // // 坑:core.go 的 bcp47ToDashScope 里还映射了 es/pt/it/vi/tr/pl/nl/uk/th/ar/id/ms/fil, // 但 paraformer-v2 并不支持它们——把这些语种的音频送进去,引擎识别不出任何内容, // 转写查询会返回 SUCCESS_WITH_NO_VALID_FRAGMENT(看起来像"音频没声音",实则是模型不认识该语种)。 // 若将来把 filetrans.go 的 Model 换成 Fun-ASR / Qwen-ASR(支持更多语种),需同步放开这里。 var dashScopeParaformerV2Langs = map[string]bool{ "zh": true, "yue": true, "en": true, "ja": true, "ko": true, "de": true, "fr": true, "ru": true, } func (this *aliTranscriber) SupportsLanguage(bcp47 string) bool { if strings.TrimSpace(bcp47) == "" { return true // 空 = 不指定 hints,交给 DashScope 自动检测 } return dashScopeParaformerV2Langs[toDashScopeLang(bcp47)] } func (this *aliTranscriber) Submit(_ context.Context, req SubmitRequest) (*SubmitResult, error) { aliLang := toDashScopeLang(req.Language) taskID, err := filetrans.CreateTask(req.AudioURL, aliLang, req.EnableSpeaker, req.CallbackURL) if err != nil { return nil, fmt.Errorf("ali filetrans create task: %w", err) } return &SubmitResult{TaskID: taskID}, nil } func (this *aliTranscriber) Query(_ context.Context, taskID, _ string) (*QueryResult, error) { status, contexts, err := filetrans.QueryTask(taskID) switch status { case filetrans.StatusSuccess: 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 filetrans.StatusRunning: return &QueryResult{Status: StatusRunning}, nil default: return &QueryResult{Status: StatusFailed}, fmt.Errorf("ali filetrans query failed: %w", err) } } // 阿里翻译 type aliTranslator struct{} func (this *aliTranslator) Name() pb.TranscribeService { return pb.TranscribeService_TranscribeService_Ali } func (this *aliTranslator) Translate(ctx context.Context, from, to string, texts []string) ([]string, error) { return translate.Translate(ctx, toAliTranslateLang(from), toAliTranslateLang(to), texts) } // 通义千问总结(DashScope OpenAI 兼容端点) type aliSummarizer struct { sys openai.ISys } func (this *aliSummarizer) Name() pb.TranscribeService { return pb.TranscribeService_TranscribeService_Ali } func (this *aliSummarizer) Chat(ctx context.Context, messages []ChatMessage) (string, error) { if this.sys == nil { return "", fmt.Errorf("ali summarizer not initialized") } msgs := make([]openai.ChatReq, 0, len(messages)) for _, m := range messages { msgs = append(msgs, openai.ChatReq{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("ali(qwen) chat: empty response") } return resp.Content, nil }