From d6188a2bd757a42bf9278678e60f045e60824082 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E5=94=AF?= Date: Tue, 16 Jun 2026 16:16:35 +0800 Subject: [PATCH] fix: health endpoint + graph/edge API + normalizeEntity Chinese + nl_query comparison 1. Add /api/v1/health endpoint (maps to HandleHealth) 2. Add POST /api/v1/graph/edge for adding relation edges 3. Fix normalizeEntity to preserve Chinese characters (0x4e00-0x9fa5) 4. Fix nl_query direct_path comparison to use normalizeEntity on both sides --- go/internal/api/server.go | 42 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/go/internal/api/server.go b/go/internal/api/server.go index fda375f..af2a78e 100644 --- a/go/internal/api/server.go +++ b/go/internal/api/server.go @@ -31,9 +31,10 @@ 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 == '-' { + if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_' || r == '-' || + (r >= 0x4e00 && r <= 0x9fa5) { // 中文 Unicode 范围 return r } return '_' @@ -278,6 +279,7 @@ func NewServer() http.Handler { // 后续在 catch-all 中处理(见文件末尾) mux.HandleFunc("/health", routes.HandleHealth) + mux.HandleFunc("/api/v1/health", routes.HandleHealth) // ─── Prometheus /metrics ───────────────────── mux.Handle("/metrics", metrics.Handler()) @@ -333,6 +335,38 @@ func NewServer() http.Handler { mux.HandleFunc("/api/v1/graph/stats", graphAPI.Stats) mux.HandleFunc("/api/v1/graph/query", graphAPI.Query) mux.HandleFunc("/api/v1/graph/navigate", graphAPI.Navigate) + // 新增:添加关系边(POST JSON body: {"from":"实体A","to":"实体B","relation":"关系类型","namespace":""}) + mux.HandleFunc("/api/v1/graph/edge", func(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + http.Error(w, "POST only", 405) + return + } + var req struct { + From string `json:"from"` + To string `json:"to"` + Relation string `json:"relation"` + Namespace string `json:"namespace"` + } + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + http.Error(w, err.Error(), 400) + return + } + if req.From == "" || req.To == "" || req.Relation == "" { + http.Error(w, "from, to, relation required", 400) + return + } + ns := req.Namespace + if ns == "" { + ns = "default" + } + edgeID := fmt.Sprintf("e_%s_%s_%d", req.From, req.Relation, time.Now().UnixNano()) + err := graphStore.AddEdge(edgeID, req.From, req.To, req.Relation, ns, 1.0) + if err != nil { + http.Error(w, err.Error(), 500) + return + } + json.NewEncoder(w).Encode(map[string]interface{}{"status": "ok", "edge_id": edgeID}) + }) // 新增:pagerank + evidence_count mux.HandleFunc("/api/v1/graph/pagerank", func(w http.ResponseWriter, r *http.Request) { rank := graphStore.PageRank(0.85, 20) @@ -478,7 +512,9 @@ func NewServer() http.Handler { // 找 A → B 的直接边 var directPath map[string]interface{} for _, p := range paths { - if strings.TrimPrefix(p["to"].(string), "n_") == normalizeEntity(entityB) { + toNorm := normalizeEntity(p["to"].(string)) + targetNorm := normalizeEntity(entityB) + if toNorm == targetNorm { directPath = p break }