fix: 6 Go code quality fixes + 4 plugin bug fixes
This commit is contained in:
parent
12c4c58c1c
commit
dc8d074cf5
|
|
@ -60,7 +60,7 @@ func (ga *GraphAPI) Query(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
// 默认跨 namespace 搜索(空 = 匹配所有,§2.5.6)
|
||||
results := ga.Graph.Query(normalizeEntity(req.Entity), req.Relation, req.Namespace)
|
||||
results := ga.Graph.Query(NormalizeEntity(req.Entity), req.Relation, req.Namespace)
|
||||
respond(w, 200, map[string]interface{}{"results": results, "count": len(results)})
|
||||
}
|
||||
|
||||
|
|
@ -89,8 +89,8 @@ func (ga *GraphAPI) Navigate(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// 模式 2: 双向 BFS(source + target)
|
||||
if req.Source != "" && req.Target != "" {
|
||||
source := normalizeEntity(req.Source)
|
||||
target := normalizeEntity(req.Target)
|
||||
source := NormalizeEntity(req.Source)
|
||||
target := NormalizeEntity(req.Target)
|
||||
paths, err := ga.Graph.NavigateBiDir(source, target, req.MaxHops, req.Namespace, req.RelationFilter)
|
||||
if err != nil {
|
||||
respondError(w, 500, "navigate failed: "+err.Error())
|
||||
|
|
@ -109,7 +109,7 @@ func (ga *GraphAPI) Navigate(w http.ResponseWriter, r *http.Request) {
|
|||
respondError(w, 400, "entity (or source+target) required")
|
||||
return
|
||||
}
|
||||
entity := normalizeEntity(req.Entity)
|
||||
entity := NormalizeEntity(req.Entity)
|
||||
paths, err := ga.Graph.Navigate(entity, req.MaxHops, req.Namespace, req.RelationFilter)
|
||||
if err != nil {
|
||||
respondError(w, 500, "navigate failed: "+err.Error())
|
||||
|
|
@ -162,8 +162,8 @@ func stripPrefix(id string) string {
|
|||
return strings.TrimPrefix(id, "n_")
|
||||
}
|
||||
|
||||
// normalizeEntity 规整实体名:去特殊字符 + n_前缀,保留中文和 Unicode 字符
|
||||
func normalizeEntity(entity string) string {
|
||||
// NormalizeEntity 规整实体名:去特殊字符 + n_前缀,保留中文和 Unicode 字符
|
||||
func NormalizeEntity(entity string) string {
|
||||
if strings.HasPrefix(entity, "n_") {
|
||||
entity = entity[2:]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,10 +149,26 @@ func applyForgettingLinkage(skill *BetaSkill, success bool) {
|
|||
|
||||
// applyDegreePenalty 降低记忆 degree(retired skill → degree -2)
|
||||
func applyDegreePenalty(memID string, penalty int) error {
|
||||
// degree 保护通过 BayesianSkills.GetSkillForMemory() 在 Forgetter.ShouldForget 中生效
|
||||
_ = memID
|
||||
_ = penalty
|
||||
return nil
|
||||
ldb := getLDB()
|
||||
if ldb == nil {
|
||||
return fmt.Errorf("ldb not available")
|
||||
}
|
||||
candidates, err := ldb.GetSkillCandidates(1, 1000)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, mem := range candidates {
|
||||
if mem.ID == memID {
|
||||
newImportance := mem.Importance - float64(penalty)*0.05
|
||||
if newImportance < 0.01 {
|
||||
newImportance = 0.01
|
||||
}
|
||||
return ldb.Update("memories", memID, map[string]any{
|
||||
"importance": newImportance,
|
||||
})
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("memory not found: %s", memID)
|
||||
}
|
||||
|
||||
// applyDecayAcceleration 加速记忆衰减(通过更新 importance)
|
||||
|
|
|
|||
|
|
@ -29,26 +29,6 @@ import (
|
|||
// cachedGraphStoreRef 全局图谱缓存引用(用于 graph write 操作时主动失效)
|
||||
var cachedGraphStoreRef *storage.GraphCacheRef
|
||||
|
||||
// normalizeEntity 规整实体名:去特殊字符 + n_前缀
|
||||
func normalizeEntity(entity string) string {
|
||||
if strings.HasPrefix(entity, "n_") {
|
||||
entity = entity[2:]
|
||||
}
|
||||
// 保留中文、字母、数字、下划线、连字符,过滤其他特殊字符
|
||||
clean := strings.Map(func(r rune) rune {
|
||||
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_' || r == '-' ||
|
||||
(r >= 0x4e00 && r <= 0x9fa5) { // 中文 Unicode 范围
|
||||
return r
|
||||
}
|
||||
return '_'
|
||||
}, strings.ToLower(strings.TrimSpace(entity)))
|
||||
// 合并连续下划线
|
||||
for strings.Contains(clean, "__") {
|
||||
clean = strings.ReplaceAll(clean, "__", "_")
|
||||
}
|
||||
return strings.Trim(clean, "_")
|
||||
}
|
||||
|
||||
// skillByNameHandler GET/DELETE /api/v1/skills/{name} 的 method 分支分发器
|
||||
func skillByNameHandler(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
|
|
@ -260,19 +240,6 @@ func NewServer() http.Handler {
|
|||
// ─── 跨 Agent 缓存失效回调 ───────────────────
|
||||
// 收到其他 Agent 的广播 → 失效本地缓存
|
||||
governance.GlobalEventBus.Subscribe("cache.invalidate", "self")
|
||||
go func() {
|
||||
// 本地处理 cache.invalidate 事件(通过 HTTP self-call)
|
||||
http.HandleFunc("/_internal/cache/invalidate", func(w http.ResponseWriter, r *http.Request) {
|
||||
var evt struct {
|
||||
Payload map[string]string `json:"payload"`
|
||||
}
|
||||
json.NewDecoder(r.Body).Decode(&evt)
|
||||
ns := evt.Payload["namespace"]
|
||||
storage.SearchCacheInstance.Invalidate(ns)
|
||||
log.Printf("[cache] 跨 Agent 失效: %s", ns)
|
||||
w.WriteHeader(200)
|
||||
})
|
||||
}()
|
||||
|
||||
// ─── 路由注册 ──────────────────────────────
|
||||
|
||||
|
|
@ -442,10 +409,10 @@ func NewServer() http.Handler {
|
|||
var noteResults []map[string]interface{}
|
||||
var paths []map[string]interface{}
|
||||
if entity != "" {
|
||||
p, err := graphStore.Navigate(normalizeEntity(entity), maxHops, "", nil)
|
||||
p, err := graphStore.Navigate(routes.NormalizeEntity(entity), maxHops, "", nil)
|
||||
if err == nil {
|
||||
paths = p
|
||||
entities := []string{normalizeEntity(entity)}
|
||||
entities := []string{routes.NormalizeEntity(entity)}
|
||||
for _, path := range p {
|
||||
if s, ok := path["source"].(string); ok {
|
||||
entities = append(entities, s)
|
||||
|
|
@ -499,7 +466,7 @@ func NewServer() http.Handler {
|
|||
var entityA, entityB string
|
||||
// 模式1: "A和B是什么关系" / "A和B有什么关系" / "A和B的关系"
|
||||
hasAnd := strings.Contains(cleanQuery, "和") || strings.Contains(cleanQuery, "与")
|
||||
if hasAnd && (strings.Contains(cleanQuery, "关系") || strings.Contains(cleanQuery, "关系")) {
|
||||
if hasAnd && (strings.Contains(cleanQuery, "关系") || strings.Contains(cleanQuery, "关联")) {
|
||||
delim := "和"
|
||||
if strings.Contains(cleanQuery, "与") {
|
||||
delim = "与"
|
||||
|
|
@ -524,13 +491,13 @@ func NewServer() http.Handler {
|
|||
var result map[string]interface{}
|
||||
if entityA != "" && entityB != "" {
|
||||
// 双向查询:A → B 的关系
|
||||
paths, _ := graphStore.Navigate(normalizeEntity(entityA), 2, "", nil)
|
||||
relB, _ := graphStore.Navigate(normalizeEntity(entityB), 2, "", nil)
|
||||
paths, _ := graphStore.Navigate(routes.NormalizeEntity(entityA), 2, "", nil)
|
||||
relB, _ := graphStore.Navigate(routes.NormalizeEntity(entityB), 2, "", nil)
|
||||
// 找 A → B 的直接边
|
||||
var directPath map[string]interface{}
|
||||
for _, p := range paths {
|
||||
toNorm := normalizeEntity(p["to"].(string))
|
||||
targetNorm := normalizeEntity(entityB)
|
||||
toNorm := routes.NormalizeEntity(p["to"].(string))
|
||||
targetNorm := routes.NormalizeEntity(entityB)
|
||||
if toNorm == targetNorm {
|
||||
directPath = p
|
||||
break
|
||||
|
|
@ -548,7 +515,7 @@ func NewServer() http.Handler {
|
|||
} else {
|
||||
// 单实体查询:找 entity 的关系网
|
||||
entity := cleanQuery
|
||||
paths, _ := graphStore.Navigate(normalizeEntity(entity), 2, "", nil)
|
||||
paths, _ := graphStore.Navigate(routes.NormalizeEntity(entity), 2, "", nil)
|
||||
// 按 relation 分组
|
||||
relCounts := make(map[string]int)
|
||||
for _, p := range paths {
|
||||
|
|
@ -702,7 +669,10 @@ func NewServer() http.Handler {
|
|||
EventType string `json:"event_type"`
|
||||
Callback string `json:"callback_url"`
|
||||
}
|
||||
json.NewDecoder(r.Body).Decode(&req)
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
governance.GlobalEventBus.Subscribe(req.EventType, req.Callback)
|
||||
respondJSON(w, 200, map[string]string{"status": "subscribed"})
|
||||
})
|
||||
|
|
@ -711,7 +681,10 @@ func NewServer() http.Handler {
|
|||
EventType string `json:"event_type"`
|
||||
Callback string `json:"callback_url"`
|
||||
}
|
||||
json.NewDecoder(r.Body).Decode(&req)
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
governance.GlobalEventBus.Unsubscribe(req.EventType, req.Callback)
|
||||
respondJSON(w, 200, map[string]string{"status": "unsubscribed"})
|
||||
})
|
||||
|
|
@ -726,7 +699,10 @@ func NewServer() http.Handler {
|
|||
Type string `json:"type"`
|
||||
Payload map[string]string `json:"payload"`
|
||||
}
|
||||
json.NewDecoder(r.Body).Decode(&evt)
|
||||
if err := json.NewDecoder(r.Body).Decode(&evt); err != nil {
|
||||
http.Error(w, "invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
ns := evt.Payload["namespace"]
|
||||
storage.SearchCacheInstance.Invalidate(ns)
|
||||
log.Printf("[cache] 跨 Agent 失效接收: %s", ns)
|
||||
|
|
@ -777,7 +753,10 @@ func NewServer() http.Handler {
|
|||
AgentType string `json:"agent_type"`
|
||||
}
|
||||
if r.Method == "POST" {
|
||||
json.NewDecoder(r.Body).Decode(&req)
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
forgetter.SetAgentType(req.AgentType)
|
||||
}
|
||||
respondJSON(w, 200, map[string]interface{}{
|
||||
|
|
@ -796,7 +775,10 @@ func NewServer() http.Handler {
|
|||
Namespace string `json:"namespace"`
|
||||
AgentID string `json:"agent_id"`
|
||||
}
|
||||
json.NewDecoder(r.Body).Decode(&req)
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if req.Content == "" {
|
||||
respondJSON(w, 400, map[string]string{"error": "content required"})
|
||||
return
|
||||
|
|
@ -964,7 +946,24 @@ func NewServer() http.Handler {
|
|||
|
||||
// G6: ephemeral namespace 管理
|
||||
mux.HandleFunc("/api/v1/admin/ephemeral/clean", func(w http.ResponseWriter, r *http.Request) {
|
||||
cleaned := []string{}
|
||||
now := time.Now().UnixMilli()
|
||||
agents := agentRegistry.ListAgents()
|
||||
var cleaned []string
|
||||
for _, agent := range agents {
|
||||
if now-agent.LastSeen > 5*60*1000 {
|
||||
ns := agent.AgentID + "-ephemeral"
|
||||
zeroVec := make([]float32, 1024)
|
||||
memories, _ := ldb.Search("memories", zeroVec, 1000, ns)
|
||||
for _, mem := range memories {
|
||||
ldb.SoftDelete(mem.ID, "ephemeral_expired")
|
||||
}
|
||||
if len(memories) > 0 {
|
||||
cleaned = append(cleaned, ns)
|
||||
log.Printf("[ephemeral] cleaned %d memories from %s (inactive for %ds)",
|
||||
len(memories), ns, (now-agent.LastSeen)/1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
respondJSON(w, 200, map[string]interface{}{
|
||||
"status": "cleaned",
|
||||
"cleaned_namespaces": cleaned,
|
||||
|
|
@ -981,7 +980,10 @@ func NewServer() http.Handler {
|
|||
var evt struct {
|
||||
Payload map[string]string `json:"payload"`
|
||||
}
|
||||
json.NewDecoder(r.Body).Decode(&evt)
|
||||
if err := json.NewDecoder(r.Body).Decode(&evt); err != nil {
|
||||
http.Error(w, "invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
ns := evt.Payload["namespace"]
|
||||
storage.SearchCacheInstance.Invalidate(ns)
|
||||
log.Printf("[cache] 内部跨 Agent 失效: %s", ns)
|
||||
|
|
|
|||
BIN
go/zhiyid-new
BIN
go/zhiyid-new
Binary file not shown.
Loading…
Reference in New Issue