package echomeet import ( "context" "yunyan/lego/core" "yunyan/lego/core/cbase" "yunyan/pb" "fmt" "strings" ) // providersComp 服务聚合组件: // - Init 阶段按 echomeet 模块配置初始化字节/阿里/Google/微软四套单元服务(转写+翻译+总结), // 并把它们组合成「按服务商成套」的 provider 集合; // - 运行期把转写/翻译/总结三类能力按 ServiceType 路由到同一服务商的 provider。 type providersComp struct { cbase.ModuleCompBase module *Echomeet transcribers map[pb.TranscribeService]Transcriber translators map[pb.TranscribeService]Translator summarizers map[pb.TranscribeService]Summarizer } // 任务状态常量,对齐底层 sys 包 const ( StatusRunning = "RUNNING" StatusSuccess = "SUCCESS" StatusFailed = "FAILED" ) // SubmitRequest 转写提交请求 type SubmitRequest struct { Uid string AudioURL string Language string // BCP-47 EnableSpeaker bool Seconds int32 SizeBytes int64 CallbackURL string CallbackData string } // SubmitResult 转写提交结果。Done=true 时同步完成(仅 ByteDance flash),直接取 Contexts; // 否则需要后续调用 Query 拉取异步结果。 type SubmitResult struct { Done bool TaskID string LogID string Contexts []*pb.ContextStruct } // QueryResult 转写查询结果 type QueryResult struct { Status string Contexts []*pb.ContextStruct } // ChatMessage 对齐 sys/doubao / sys/google/gemini 的统一消息结构。 // Images 为可选的图片 url 列表(多模态总结用):非空时该条消息以「文本 + 图片」的多模态形式 // 发给模型;为空时退化为纯文本,行为与以前一致。要求所配模型支持视觉输入。 type ChatMessage struct { Role string Content string Images []string } // Transcriber 转写 provider 接口 type Transcriber interface { Name() pb.TranscribeService SupportsLanguage(bcp47 string) bool Submit(ctx context.Context, req SubmitRequest) (*SubmitResult, error) Query(ctx context.Context, taskID, logID string) (*QueryResult, error) } // Translator 翻译 provider 接口 type Translator interface { Name() pb.TranscribeService Translate(ctx context.Context, from, to string, texts []string) ([]string, error) } // Summarizer AI 总结 provider 接口 type Summarizer interface { Name() pb.TranscribeService Chat(ctx context.Context, messages []ChatMessage) (string, error) } // ==================== 组件生命周期 ==================== func (this *providersComp) Init(service core.IService, module core.IModule, comp core.IModuleComp, options core.IModuleOptions) (err error) { this.ModuleCompBase.Init(service, module, comp, options) this.module = module.(*Echomeet) opt := options.(*Options) // 按服务商成套初始化:每套各自完成转写/翻译/总结三个单元服务的装配。 bdTranscriber, bdTranslator, bdSummarizer := newByteDanceProviders(opt.ByteDance) aliTranscriber, aliTranslator, aliSummarizer := newAliProviders(opt.Ali) ggTranscriber, ggTranslator, ggSummarizer := newGoogleProviders(opt.Google) msTranscriber, msTranslator, msSummarizer := newMicrosoftProviders(opt.Microsoft) this.transcribers = map[pb.TranscribeService]Transcriber{ pb.TranscribeService_TranscribeService_ByteDance: bdTranscriber, pb.TranscribeService_TranscribeService_Ali: aliTranscriber, pb.TranscribeService_TranscribeService_Google: ggTranscriber, pb.TranscribeService_TranscribeService_Microsoft: msTranscriber, } this.translators = map[pb.TranscribeService]Translator{ pb.TranscribeService_TranscribeService_ByteDance: bdTranslator, pb.TranscribeService_TranscribeService_Ali: aliTranslator, pb.TranscribeService_TranscribeService_Google: ggTranslator, pb.TranscribeService_TranscribeService_Microsoft: msTranslator, } this.summarizers = map[pb.TranscribeService]Summarizer{ pb.TranscribeService_TranscribeService_ByteDance: bdSummarizer, pb.TranscribeService_TranscribeService_Ali: aliSummarizer, pb.TranscribeService_TranscribeService_Google: ggSummarizer, pb.TranscribeService_TranscribeService_Microsoft: msSummarizer, } this.module.Infof("echomeet providers 初始化完成 DefaultService=%s", opt.DefaultService) return } // ==================== 服务商解析 ==================== // parseServiceType 把配置里的服务商名称解析为枚举;空 / auto / 未识别返回 Auto。 func parseServiceType(s string) pb.TranscribeService { switch strings.ToLower(strings.TrimSpace(s)) { case "bytedance", "byte", "字节": return pb.TranscribeService_TranscribeService_ByteDance case "ali", "aliyun", "阿里", "阿里云": return pb.TranscribeService_TranscribeService_Ali case "google", "谷歌": return pb.TranscribeService_TranscribeService_Google case "microsoft", "azure", "微软": return pb.TranscribeService_TranscribeService_Microsoft default: return pb.TranscribeService_TranscribeService_Auto } } // ResolveServiceType 解析本次会议最终使用的服务商。优先级: // 1. 请求显式指定 ServiceType → 直接采用(覆盖配置); // 2. 模块配置 DefaultService 指定了服务商 → 采用配置; // 3. 默认模式(以前的方案):按源语言自动路由——字节支持该语言走字节,否则走阿里。 // // 解析结果一旦确定,会议撰写、翻译、总结三步都走同一服务商。 func (this *providersComp) ResolveServiceType(svcType pb.TranscribeService, language string) pb.TranscribeService { if svcType != pb.TranscribeService_TranscribeService_Auto && svcType != pb.TranscribeService_TranscribeService_Unknown { return svcType } if d := parseServiceType(this.module.options.DefaultService); d != pb.TranscribeService_TranscribeService_Auto { return d } if _, ok := toByteDanceLang(language); ok { return pb.TranscribeService_TranscribeService_ByteDance } return pb.TranscribeService_TranscribeService_Ali } // ==================== 工厂方法 ==================== // GetTranscriber 取转写 provider;svcType=Auto/Unknown 时先 ResolveServiceType。 func (this *providersComp) GetTranscriber(svcType pb.TranscribeService, language string) (Transcriber, error) { resolved := this.ResolveServiceType(svcType, language) if p, ok := this.transcribers[resolved]; ok && p != nil { return p, nil } return nil, fmt.Errorf("transcriber not registered: service=%s", ServiceTypeName(resolved)) } // GetTranslator 取翻译 provider;与转写同服务商,svcType=Auto/Unknown 时先 ResolveServiceType。 func (this *providersComp) GetTranslator(svcType pb.TranscribeService, language string) (Translator, error) { resolved := this.ResolveServiceType(svcType, language) if p, ok := this.translators[resolved]; ok && p != nil { return p, nil } return nil, fmt.Errorf("translator not registered: service=%s", ServiceTypeName(resolved)) } // GetSummarizer 取总结 provider;与转写同服务商,svcType=Auto/Unknown 时先 ResolveServiceType。 func (this *providersComp) GetSummarizer(svcType pb.TranscribeService, language string) (Summarizer, error) { resolved := this.ResolveServiceType(svcType, language) if p, ok := this.summarizers[resolved]; ok && p != nil { return p, nil } return nil, fmt.Errorf("summarizer not registered: service=%s", ServiceTypeName(resolved)) } // ServiceTypeName 返回服务类型的友好名称,便于日志输出 func ServiceTypeName(svcType pb.TranscribeService) string { switch svcType { case pb.TranscribeService_TranscribeService_ByteDance: return "ByteDance" case pb.TranscribeService_TranscribeService_Ali: return "Ali" case pb.TranscribeService_TranscribeService_Auto: return "Auto" case pb.TranscribeService_TranscribeService_Google: return "Google" case pb.TranscribeService_TranscribeService_Microsoft: return "Microsoft" default: return fmt.Sprintf("Unknown(%d)", int32(svcType)) } }