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.
 
 
 
 
 
 

129 lines
4.8 KiB

package user
import (
"sort"
"yunyan/comm"
"yunyan/lego/sys/log"
"yunyan/lego/sys/postgres"
"yunyan/pb"
)
// 通话翻译配置的下发(user_getappconfig_v3 的 call_translate 字段)。
//
// 对应后台「通话翻译配置」的两层模型:规则(call_translate_rule)只管「什么语言走哪个 agent」,
// 具体调哪套 asr/mt/tts(或端到端 ast)在翻译 agent(agent_config, type=translate|ast-translate) 里。
// 两层一起下发,客户端按 comm.MatchCallTranslateRule 的口径选路(口径同步写在 proto 注释里)。
//
// 之所以下发规则集而不是让客户端每次通话来问一次:通话建立时选路要即时,多一次往返就是首句延迟。
// callTranslateV3 组装下发客户端的通话翻译配置:本区域生效的规则 + 这些规则引用到的翻译 agent。
//
// 三处筛选都在服务端做掉,客户端不必再判:
// - 区域:只留本区域的规则与不限区域(RegionAny)的兜底规则;
// - 启停:停用的规则、停用的 agent 都不下发;
// - 悬空引用:agent 找不到或类型不对的规则整条丢掉——下发一条指向不存在 agent 的规则,
// 客户端要到通话中途才发现没法翻译。
//
// 取不到配置不让整个 appconfig 失败:通话翻译是可选能力,没配它的应用(乃至库里还没建这张表的
// 老部署)应当照常拿到其余配置,故这里只记日志、返回空壳(字段都在、列表为空)。
func (this *apiV3Comp) callTranslateV3(region pb.Region) *pb.CallTranslateItem {
out := &pb.CallTranslateItem{
Rules: []*pb.CallTranslateRuleItem{},
Agents: []*pb.AgentItemV3{},
}
app := comm.AppName()
rules, err := loadCallTranslateRules(app, region)
if err != nil {
this.module.Error("加载通话翻译规则失败!",
log.Field{Key: "app", Value: app}, log.Field{Key: "err", Value: err.Error()})
return out
}
if len(rules) == 0 {
return out
}
list, err := pickAgentsV3(app, comm.AgentTypeIsTranslate)
if err != nil {
this.module.Error("加载翻译 agent 失败!",
log.Field{Key: "app", Value: app}, log.Field{Key: "err", Value: err.Error()})
return out
}
byId := make(map[string]*comm.AgentConfig, len(list))
for _, a := range list {
byId[a.Id] = a
}
providerOf := this.svcProviders(app)
added := make(map[string]bool, len(byId))
for _, r := range rules {
a, ok := byId[r.AgentId]
if !ok {
this.module.Warn("通话翻译规则引用不到可用的翻译 agent,已跳过",
log.Field{Key: "rule", Value: r.Id}, log.Field{Key: "agent", Value: r.AgentId})
continue
}
out.Rules = append(out.Rules, &pb.CallTranslateRuleItem{
Id: r.Id,
Name: r.Name,
SrcLangs: r.SrcLangs,
DstLangs: r.DstLangs,
Bidir: r.Bidir,
AgentId: r.AgentId,
Priority: r.Priority,
})
if !added[a.Id] {
added[a.Id] = true
out.Agents = append(out.Agents, agentItemV3Of(a, providerOf[voiceSvcIdOf(a)]))
}
}
return out
}
// loadCallTranslateRules 取本应用作用域下、本区域生效且启用的规则,按生效顺序(优先级降序 → id 升序)排好。
//
// 作用域是「整套覆盖」而非按条合并:应用自己配了规则就只用应用那套,一条都没有才回退全局默认。
// 规则之间靠优先级互相压制,两套混在一起优先级会打架——全局的兜底规则可能压过应用的精确规则。
// (规则引用的 agent 仍是按条解析、应用优先回退全局,见 pickAgentsV3,与 console 的 findAgentForRule 一致。)
func loadCallTranslateRules(app string, region pb.Region) ([]*comm.CallTranslateRule, error) {
rows := make([]*comm.CallTranslateRule, 0)
if err := postgres.Find(comm.TableCallTranslateRule, &rows, "(app_name=? OR app_name='')", app); err != nil && err != postgres.ErrNoDocuments {
return nil, err
}
return selectCallTranslateRules(rows, app, region), nil
}
// selectCallTranslateRules 从「应用行 + 全局行」的原始规则里挑出实际下发的那批(作用域覆盖 → 区域/启停过滤 → 排序)。
// 与查库分开,便于直接用数据验证选取口径。
func selectCallTranslateRules(rows []*comm.CallTranslateRule, app string, region pb.Region) []*comm.CallTranslateRule {
scoped := make([]*comm.CallTranslateRule, 0, len(rows))
if app != "" {
for _, r := range rows {
if r.AppName == app {
scoped = append(scoped, r)
}
}
}
if len(scoped) == 0 {
for _, r := range rows {
if r.AppName == "" {
scoped = append(scoped, r)
}
}
}
out := make([]*comm.CallTranslateRule, 0, len(scoped))
for _, r := range scoped {
if r.Enable && (r.Region == comm.RegionAny || r.Region == int32(region)) {
out = append(out, r)
}
}
sort.Slice(out, func(i, j int) bool {
if out[i].Priority != out[j].Priority {
return out[i].Priority > out[j].Priority
}
return out[i].Id < out[j].Id
})
return out
}