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.
249 lines
8.3 KiB
249 lines
8.3 KiB
package console
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
|
|
"yunyan/comm"
|
|
"yunyan/lego/sys/postgres"
|
|
"yunyan/pb"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// ============================ 通话翻译配置域 (calltranslate) ============================
|
|
//
|
|
// 数据存 console 主库(postgres.GetSys()),模型见 comm/calltranslate.go。
|
|
//
|
|
// 规则化模型(取代旧的 call_translate_pair 语言对表):
|
|
// 一条规则 = 某应用作用域 + 某区域下的「多源语言 × 多目标语言 → 一个 agent」映射,带优先级。
|
|
// 运行时按 comm.MatchCallTranslateRule 选路:优先级大者先,同优先级精确规则压过 * 通配规则。
|
|
// 规则不再直接拼 asr/mt/tts —— 服务编排收敛在 agent 里,规则只决定「什么语言走哪个 agent」。
|
|
|
|
// 规则可引用的 agent 类型:三段式翻译 与 端到端翻译。chat 类不能用于通话翻译选路。
|
|
var callTranslateAgentTypes = []string{comm.AgentTypeTranslate, comm.AgentTypeAstTranslate}
|
|
|
|
// findAgentForRule 在规则的应用作用域内找 agent,未命中回退全局(与 validateAgentSvcRefs 的解析口径一致)。
|
|
func findAgentForRule(appName, agentId string) (*AgentConfig, error) {
|
|
a := &AgentConfig{}
|
|
err := postgres.FindOne(comm.TableAgentConfig, a, "app_name=? AND id=?", appName, agentId)
|
|
if err != nil && appName != "" {
|
|
err = postgres.FindOne(comm.TableAgentConfig, a, "app_name=? AND id=?", "", agentId)
|
|
}
|
|
return a, err
|
|
}
|
|
|
|
// validateRule 校验规则自身与其引用的 agent,返回错误说明(空串=通过)。
|
|
func validateRule(m *comm.CallTranslateRule) string {
|
|
if len(comm.SplitLangs(m.SrcLangs)) == 0 {
|
|
return "源语言 必选(或选「任意语言」)"
|
|
}
|
|
if len(comm.SplitLangs(m.DstLangs)) == 0 {
|
|
return "目标语言 必选(或选「任意语言」)"
|
|
}
|
|
if strings.TrimSpace(m.AgentId) == "" {
|
|
return "翻译 Agent 必选"
|
|
}
|
|
a, err := findAgentForRule(m.AppName, m.AgentId)
|
|
if err != nil {
|
|
return "引用的 Agent 不存在: " + m.AgentId
|
|
}
|
|
ok := false
|
|
for _, t := range callTranslateAgentTypes {
|
|
if a.Type == t {
|
|
ok = true
|
|
break
|
|
}
|
|
}
|
|
if !ok {
|
|
return "Agent「" + a.Name + "」类型为 " + a.Type + ",通话翻译只能引用 translate(三段式)或 ast-translate(端到端)"
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// getCallTranslateRules 列出某作用域下的规则;region>0 只取该区域 + 不限区域(0)的规则,region<=0 取全量。
|
|
// 返回已按生效顺序排好序(优先级降序 → id 升序),前端直接按序展示即可。
|
|
func (this *serverComp) getCallTranslateRules(c *gin.Context) {
|
|
var req struct {
|
|
AppName string `json:"app_name"`
|
|
Region int32 `json:"region"`
|
|
}
|
|
_ = c.ShouldBindJSON(&req)
|
|
items := make([]*comm.CallTranslateRule, 0)
|
|
var err error
|
|
if req.Region > 0 {
|
|
err = postgres.Find(comm.TableCallTranslateRule, &items, "app_name=? AND (region=? OR region=?)",
|
|
req.AppName, req.Region, comm.RegionAny)
|
|
} else {
|
|
err = postgres.Find(comm.TableCallTranslateRule, &items, "app_name=?", req.AppName)
|
|
}
|
|
if err != nil {
|
|
writeErr(c, pb.ErrorCode_DBError, err.Error())
|
|
return
|
|
}
|
|
sortRulesByEffect(items)
|
|
writeOK(c, gin.H{"items": items})
|
|
}
|
|
|
|
// sortRulesByEffect 按运行时生效顺序排序:优先级降序 → id 升序(插入排序,规则量级很小)。
|
|
func sortRulesByEffect(items []*comm.CallTranslateRule) {
|
|
for i := 1; i < len(items); i++ {
|
|
cur := items[i]
|
|
j := i - 1
|
|
for j >= 0 && (items[j].Priority < cur.Priority || (items[j].Priority == cur.Priority && items[j].Id > cur.Id)) {
|
|
items[j+1] = items[j]
|
|
j--
|
|
}
|
|
items[j+1] = cur
|
|
}
|
|
}
|
|
|
|
// saveCallTranslateRule 新增或更新一条规则(id=0 新增,否则按 id 更新)。
|
|
func (this *serverComp) saveCallTranslateRule(c *gin.Context) {
|
|
var m comm.CallTranslateRule
|
|
if err := c.ShouldBindJSON(&m); err != nil {
|
|
writeErr(c, pb.ErrorCode_ReqParameterError, err.Error())
|
|
return
|
|
}
|
|
if msg := validateRule(&m); msg != "" {
|
|
writeErr(c, pb.ErrorCode_ReqParameterError, msg)
|
|
return
|
|
}
|
|
now := time.Now().UnixMilli()
|
|
m.Updatetime = now
|
|
var err error
|
|
if m.Id == 0 {
|
|
m.Createtime = now
|
|
err = postgres.Insert(comm.TableCallTranslateRule, &m)
|
|
} else {
|
|
old := &comm.CallTranslateRule{}
|
|
if e := postgres.FindOne(comm.TableCallTranslateRule, old, "id=?", m.Id); e != nil {
|
|
writeErr(c, pb.ErrorCode_ReqParameterError, "规则不存在")
|
|
return
|
|
}
|
|
m.Createtime = old.Createtime
|
|
err = postgres.Save(comm.TableCallTranslateRule, &m)
|
|
}
|
|
if err != nil {
|
|
writeErr(c, pb.ErrorCode_DBError, err.Error())
|
|
return
|
|
}
|
|
broadcastConfigChanged(comm.ConfigKindCallTranslate, "update", m.Region)
|
|
writeOK(c, &m)
|
|
}
|
|
|
|
// delCallTranslateRules 按 id 批量删除规则。
|
|
func (this *serverComp) delCallTranslateRules(c *gin.Context) {
|
|
var req struct {
|
|
Ids []uint64 `json:"ids"`
|
|
}
|
|
_ = c.ShouldBindJSON(&req)
|
|
if len(req.Ids) == 0 {
|
|
writeErr(c, pb.ErrorCode_ReqParameterError, "ids 必填")
|
|
return
|
|
}
|
|
if err := postgres.Delete(comm.TableCallTranslateRule, "id IN ?", req.Ids); err != nil {
|
|
writeErr(c, pb.ErrorCode_DBError, err.Error())
|
|
return
|
|
}
|
|
broadcastConfigChanged(comm.ConfigKindCallTranslate, "delete", 0)
|
|
writeOK(c, gin.H{"deleted": len(req.Ids)})
|
|
}
|
|
|
|
// syncCallTranslateRules 把指定规则复制到多个目标区域。
|
|
// 目标区域已有「同 agent + 同源语言集 + 同目标语言集」的规则则覆盖,否则新建;源区域自身跳过。
|
|
func (this *serverComp) syncCallTranslateRules(c *gin.Context) {
|
|
var req struct {
|
|
RuleIds []uint64 `json:"rule_ids"`
|
|
TargetRegions []int32 `json:"target_regions"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil || len(req.RuleIds) == 0 {
|
|
writeErr(c, pb.ErrorCode_ReqParameterError, "rule_ids 必填")
|
|
return
|
|
}
|
|
if len(req.TargetRegions) == 0 {
|
|
writeOK(c, gin.H{"synced": 0})
|
|
return
|
|
}
|
|
srcRules := make([]*comm.CallTranslateRule, 0, len(req.RuleIds))
|
|
if err := postgres.Find(comm.TableCallTranslateRule, &srcRules, "id IN ?", req.RuleIds); err != nil {
|
|
writeErr(c, pb.ErrorCode_DBError, err.Error())
|
|
return
|
|
}
|
|
now := time.Now().UnixMilli()
|
|
synced := 0
|
|
for _, r := range srcRules {
|
|
for _, tgt := range req.TargetRegions {
|
|
if r.Region == tgt {
|
|
continue
|
|
}
|
|
old := &comm.CallTranslateRule{}
|
|
_ = postgres.FindOne(comm.TableCallTranslateRule, old,
|
|
"app_name=? AND region=? AND agent_id=? AND src_langs=? AND dst_langs=?",
|
|
r.AppName, tgt, r.AgentId, r.SrcLangs, r.DstLangs)
|
|
m := &comm.CallTranslateRule{
|
|
Id: old.Id,
|
|
AppName: r.AppName,
|
|
Region: tgt,
|
|
Name: r.Name,
|
|
SrcLangs: r.SrcLangs,
|
|
DstLangs: r.DstLangs,
|
|
Bidir: r.Bidir,
|
|
AgentId: r.AgentId,
|
|
Priority: r.Priority,
|
|
Enable: r.Enable,
|
|
Remark: r.Remark,
|
|
Updatetime: now,
|
|
}
|
|
if old.Id == 0 {
|
|
m.Createtime = now
|
|
if err := postgres.Insert(comm.TableCallTranslateRule, m); err == nil {
|
|
synced++
|
|
}
|
|
} else {
|
|
m.Createtime = old.Createtime
|
|
if err := postgres.Save(comm.TableCallTranslateRule, m); err == nil {
|
|
synced++
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if synced > 0 {
|
|
for _, tgt := range req.TargetRegions {
|
|
broadcastConfigChanged(comm.ConfigKindCallTranslate, "update", tgt)
|
|
}
|
|
}
|
|
writeOK(c, gin.H{"synced": synced})
|
|
}
|
|
|
|
// testCallTranslateRule 选路试算:给定区域与源/目标语言,返回实际会命中的规则及其 agent。
|
|
// 规则一多就很难靠肉眼确认「zh-CN→ja-JP 到底走哪条」,故给后台一个所见即所得的验证入口,
|
|
// 走的是与运行时完全相同的 comm.MatchCallTranslateRule。
|
|
func (this *serverComp) testCallTranslateRule(c *gin.Context) {
|
|
var req struct {
|
|
AppName string `json:"app_name"`
|
|
Region int32 `json:"region"`
|
|
SrcLang string `json:"src_lang"`
|
|
DstLang string `json:"dst_lang"`
|
|
}
|
|
_ = c.ShouldBindJSON(&req)
|
|
if strings.TrimSpace(req.SrcLang) == "" || strings.TrimSpace(req.DstLang) == "" {
|
|
writeErr(c, pb.ErrorCode_ReqParameterError, "src_lang / dst_lang 必填")
|
|
return
|
|
}
|
|
items := make([]*comm.CallTranslateRule, 0)
|
|
if err := postgres.Find(comm.TableCallTranslateRule, &items, "app_name=?", req.AppName); err != nil {
|
|
writeErr(c, pb.ErrorCode_DBError, err.Error())
|
|
return
|
|
}
|
|
hit := comm.MatchCallTranslateRule(items, req.Region, req.SrcLang, req.DstLang)
|
|
if hit == nil {
|
|
writeOK(c, gin.H{"matched": false})
|
|
return
|
|
}
|
|
resp := gin.H{"matched": true, "rule": hit}
|
|
if a, err := findAgentForRule(hit.AppName, hit.AgentId); err == nil {
|
|
resp["agent"] = a
|
|
}
|
|
writeOK(c, resp)
|
|
}
|
|
|