You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

139 lines
4.3 KiB

package echomeet
import (
"context"
"errors"
"strings"
"sync"
"yunyan/comm"
"yunyan/modules"
"yunyan/lego/core"
)
func NewModule() core.IModule {
m := new(Echomeet)
return m
}
type Echomeet struct {
modules.ModuleBase
options *Options
api *apiComp
model *modelComp
cache *modelCacheComp
tasks *tasksComp
providers *providersComp
analyze comm.IAnalyze
service core.IService
mem comm.IMemory // 同进程的 memory 模块(会议纪要 → 待办抽取),惰性取
memOnce sync.Once
}
func (this *Echomeet) GetType() core.M_Modules {
return comm.ModuleEchomeet
}
func (this *Echomeet) NewOptions() (options core.IModuleOptions) {
return new(Options)
}
func (this *Echomeet) Init(service core.IService, module core.IModule, options core.IModuleOptions) (err error) {
this.options = options.(*Options)
this.service = service
if err = this.ModuleBase.Init(service, module, options); err != nil {
return
}
return
}
// memoryModule 惰性取同进程的 memory 模块(会议纪要 → 待办抽取用)。
//
// 惰性而不是 Start 里取:模块 Start 顺序由 home/main.go 的 lego.Run 参数序决定,
// echomeet 排在 memory 前面,Start 阶段去拿会拿到一个还没 Start 完的实例。
// 这里只在真正要用时取一次,缓存住。
func (this *Echomeet) memoryModule() comm.IMemory {
this.memOnce.Do(func() {
if m, e := this.service.GetModule(comm.ModuleMemory); e == nil {
if mm, ok := m.(comm.IMemory); ok {
this.mem = mm
}
}
if this.mem == nil {
this.Warnf("未取到 memory 模块,会议待办抽取不可用(本服务可能没装载它)")
}
})
return this.mem
}
func (this *Echomeet) Start() (err error) {
if err = this.ModuleBase.Start(); err != nil {
return
}
return
}
// MeetCapabilities 实现 comm.IEchomeet:把编排快照里的语言/总结模型汇总给同进程的其他模块
// (user 的 v3 配置下发)。读内存快照,不查库;echomeet_getcapabilities 接口用的是同一份数据。
func (this *Echomeet) MeetCapabilities() (c comm.MeetCapabilities) {
if this.providers == nil {
return
}
c.TranscribeLanguages, c.TranscribeAnyLanguage = this.providers.TranscribeLanguages()
c.TranslateLanguages, c.TranslateAnyLanguage = this.providers.TranslateLanguages()
clientChoice, items := this.providers.SummaryServices()
c.SummaryClientChoice = clientChoice
c.SummaryServices = make([]comm.MeetSummaryService, 0, len(items))
for _, it := range items {
c.SummaryServices = append(c.SummaryServices, comm.MeetSummaryService{
SvcId: it.SvcId, Name: it.Name, IsDefault: it.IsDefault,
})
}
return
}
// ChatLLM 用后台编排选出的总结模型跑一次对话(实现 comm.IEchomeet)。
//
// 开给其它模块用(memory 的周报生成、会议待办抽取),目的是让它们也走同一套编排选路,
// 不必各自 import sys/doubao 把服务商写死。svcId 传空按编排取默认;
// 返回实际用到的 svcId 便于排障时对齐「这条是谁生成的」。
func (this *Echomeet) ChatLLM(ctx context.Context, svcId, systemPrompt, userContent string) (string, string, error) {
if this.providers == nil {
return "", "", errors.New("echomeet providers 未就绪")
}
if strings.TrimSpace(userContent) == "" {
// 空内容送给大模型只会换回一个语焉不详的 400(通义/豆包都直接报
// InternalError.Algo.InvalidParameter),在这里挡掉更好排查。
return "", "", errors.New("userContent 为空")
}
usedSvcId := svcId
var (
s Summarizer
e error
)
if usedSvcId == "" {
usedSvcId, s, e = this.providers.PickLLM("")
} else {
s, e = this.providers.GetSummarizer(usedSvcId)
}
if e != nil {
return "", usedSvcId, e
}
msgs := make([]ChatMessage, 0, 2)
if systemPrompt != "" {
msgs = append(msgs, ChatMessage{Role: "system", Content: systemPrompt})
}
msgs = append(msgs, ChatMessage{Role: "user", Content: userContent})
text, err := s.Chat(ctx, msgs)
return text, usedSvcId, err
}
func (this *Echomeet) OnInstallComp() {
this.ModuleBase.OnInstallComp()
this.api = this.RegisterComp(new(apiComp)).(*apiComp)
this.model = this.RegisterComp(new(modelComp)).(*modelComp)
this.cache = this.RegisterComp(new(modelCacheComp)).(*modelCacheComp)
this.providers = this.RegisterComp(new(providersComp)).(*providersComp)
this.tasks = this.RegisterComp(new(tasksComp)).(*tasksComp)
}