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
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
|
|
}
|
|
|