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.
 
 
 
 
 
 

201 lines
6.5 KiB

package console
import (
"strconv"
"strings"
"time"
"yunyan/comm"
"yunyan/lego/sys/postgres"
"yunyan/pb"
"github.com/gin-gonic/gin"
)
// ============================ Agent 配置域 (agent) ============================
//
// yunyan 自管 agents 的管理端 CRUD,存 console 主库(postgres.GetSys())。
// agent 编排引用第三方服务(ThirdSvcConfig)的 id;保存时按 Type 校验必需的服务引用「存在且类别匹配」。
// 配置「下发客户端」由后续阶段的下发接口 + modules/agents 改造承接。
// agentSvcRef 一个待校验的服务引用(哪个位置、引用的 svcId、期望的服务类别)。
type agentSvcRef struct {
label string
svcId string
cat int32
}
// collectAgentSvcRefs 按 agent 类型收集其应校验的服务引用集合。
func collectAgentSvcRefs(a *AgentConfig) []agentSvcRef {
switch a.Type {
case AgentTypeChat:
return []agentSvcRef{
{"STT 语音识别", a.SttSvcId, svcCatSTT},
{"LLM 大语言", a.LlmSvcId, svcCatLLM},
{"TTS 语音合成", a.TtsSvcId, svcCatTTS},
}
case AgentTypeTranslate:
return []agentSvcRef{
{"STT 语音识别", a.SttSvcId, svcCatSTT},
{"MT 机器翻译", a.MtSvcId, svcCatMT},
{"TTS 语音合成", a.TtsSvcId, svcCatTTS},
}
case AgentTypeStsChat:
return []agentSvcRef{{"STS 端到端对话", a.StsSvcId, svcCatSTS}}
case AgentTypeAstTranslate:
return []agentSvcRef{{"AST 端到端翻译", a.AstSvcId, svcCatAST}}
}
return nil
}
// svcHasCategory 判断 ThirdSvcConfig.Categories("1,2,3") 是否包含目标类别。
func svcHasCategory(categories string, cat int32) bool {
want := strconv.Itoa(int(cat))
for _, s := range strings.Split(categories, ",") {
if strings.TrimSpace(s) == want {
return true
}
}
return false
}
// validateAgentSvcRefs 校验 agent 引用的服务:必填、存在、类别匹配。返回错误说明(空串=通过)。
// 引用在 agent 自己的应用作用域内解析,未命中回退全局(app_name='')——与运行时「应用优先、无则全局」一致。
func validateAgentSvcRefs(a *AgentConfig) string {
refs := collectAgentSvcRefs(a)
if len(refs) == 0 {
return "未知的智能体类型: " + a.Type
}
for _, r := range refs {
if strings.TrimSpace(r.svcId) == "" {
return r.label + " 必选"
}
cfg := &ThirdSvcConfig{}
err := postgres.FindOne(comm.TableSvcConfig, cfg, "app_name=? AND id=?", a.AppName, r.svcId)
if err != nil && a.AppName != "" { // 应用作用域未命中 → 回退全局
err = postgres.FindOne(comm.TableSvcConfig, cfg, "app_name=? AND id=?", "", r.svcId)
}
if err != nil {
return r.label + " 引用的服务不存在: " + r.svcId
}
if !svcHasCategory(cfg.Categories, r.cat) {
return r.label + " 引用的服务类别不匹配: " + r.svcId
}
}
return ""
}
// ============================== Agent CRUD ==============================
// getAgents 列出某作用域(app_name)下全部 agent 配置。app_name='' 为全局默认。
func (this *serverComp) getAgents(c *gin.Context) {
var req struct {
AppName string `json:"app_name"`
}
_ = c.ShouldBindJSON(&req)
items := make([]*AgentConfig, 0)
if err := postgres.Find(comm.TableAgentConfig, &items, "app_name=?", req.AppName); err != nil {
writeErr(c, pb.ErrorCode_DBError, err.Error())
return
}
writeOK(c, gin.H{"items": items})
}
// getAgent 取单个 agent 配置(按 app_name+id)。
func (this *serverComp) getAgent(c *gin.Context) {
var req struct {
AppName string `json:"app_name"`
Id string `json:"id"`
}
if err := c.ShouldBindJSON(&req); err != nil || strings.TrimSpace(req.Id) == "" {
writeErr(c, pb.ErrorCode_ReqParameterError, "id 必填")
return
}
m := &AgentConfig{}
if err := postgres.FindOne(comm.TableAgentConfig, m, "app_name=? AND id=?", req.AppName, req.Id); err != nil {
writeErr(c, pb.ErrorCode_DBError, "智能体不存在: "+req.Id)
return
}
writeOK(c, gin.H{"agent": m})
}
// addAgent 新增 agent 配置(作用域内 app_name+id 唯一)。
func (this *serverComp) addAgent(c *gin.Context) {
var m AgentConfig
if err := c.ShouldBindJSON(&m); err != nil {
writeErr(c, pb.ErrorCode_ReqParameterError, err.Error())
return
}
if strings.TrimSpace(m.Id) == "" || strings.TrimSpace(m.Name) == "" {
writeErr(c, pb.ErrorCode_ReqParameterError, "智能体ID 和 名称 必填")
return
}
exist := &AgentConfig{}
if err := postgres.FindOne(comm.TableAgentConfig, exist, "app_name=? AND id=?", m.AppName, m.Id); err == nil {
writeErr(c, pb.ErrorCode_ReqParameterError, "智能体ID 已存在: "+m.Id)
return
}
if msg := validateAgentSvcRefs(&m); msg != "" {
writeErr(c, pb.ErrorCode_ReqParameterError, msg)
return
}
m.RowId = 0 // 代理主键由库自增回填
now := time.Now().UnixMilli()
m.Createtime = now
m.Updatetime = now
if err := postgres.Insert(comm.TableAgentConfig, &m); err != nil {
writeErr(c, pb.ErrorCode_DBError, err.Error())
return
}
broadcastConfigChanged(comm.ConfigKindAgent, "add", 0)
writeOK(c, &m)
}
// updateAgent 更新 agent 配置(按 app_name+id)。
func (this *serverComp) updateAgent(c *gin.Context) {
var m AgentConfig
if err := c.ShouldBindJSON(&m); err != nil {
writeErr(c, pb.ErrorCode_ReqParameterError, err.Error())
return
}
if strings.TrimSpace(m.Id) == "" {
writeErr(c, pb.ErrorCode_ReqParameterError, "智能体ID 必填")
return
}
old := &AgentConfig{}
if err := postgres.FindOne(comm.TableAgentConfig, old, "app_name=? AND id=?", m.AppName, m.Id); err != nil {
writeErr(c, pb.ErrorCode_DBError, "智能体不存在: "+m.Id)
return
}
if msg := validateAgentSvcRefs(&m); msg != "" {
writeErr(c, pb.ErrorCode_ReqParameterError, msg)
return
}
m.RowId = old.RowId // 按代理主键更新本行
m.Createtime = old.Createtime
m.Updatetime = time.Now().UnixMilli()
if err := postgres.Save(comm.TableAgentConfig, &m); err != nil {
writeErr(c, pb.ErrorCode_DBError, err.Error())
return
}
broadcastConfigChanged(comm.ConfigKindAgent, "update", 0)
writeOK(c, &m)
}
// delAgent 删除 agent 配置(按 app_name+id)。
func (this *serverComp) delAgent(c *gin.Context) {
var req struct {
AppName string `json:"app_name"`
Id string `json:"id"`
}
if err := c.ShouldBindJSON(&req); err != nil || strings.TrimSpace(req.Id) == "" {
writeErr(c, pb.ErrorCode_ReqParameterError, "id 必填")
return
}
if err := postgres.Delete(comm.TableAgentConfig, "app_name=? AND id=?", req.AppName, req.Id); err != nil {
writeErr(c, pb.ErrorCode_DBError, err.Error())
return
}
broadcastConfigChanged(comm.ConfigKindAgent, "delete", 0)
writeOK(c, gin.H{"id": req.Id})
}