258 lines
9.5 KiB
Go
258 lines
9.5 KiB
Go
// HTTP 服务器 — 路由注册与启动(含所有 6 项补全)
|
||
package api
|
||
|
||
import (
|
||
"bytes"
|
||
"encoding/json"
|
||
"fmt"
|
||
"io"
|
||
"log"
|
||
"net/http"
|
||
"os"
|
||
"time"
|
||
|
||
"github.com/xiaoxue/memoryweave/internal/api/middleware"
|
||
"github.com/xiaoxue/memoryweave/internal/api/routes"
|
||
"github.com/xiaoxue/memoryweave/internal/governance"
|
||
"github.com/xiaoxue/memoryweave/internal/selfoptimize"
|
||
"github.com/xiaoxue/memoryweave/internal/storage"
|
||
)
|
||
|
||
func NewServer() http.Handler {
|
||
mux := http.NewServeMux()
|
||
|
||
// ─── 初始化依赖 ──────────────────────────────
|
||
ldb := storage.NewLanceClient()
|
||
emb := storage.NewEmbedder(os.Getenv("VLLM_ENDPOINT"))
|
||
rerank := storage.NewReranker(os.Getenv("RERANK_ENDPOINT"))
|
||
api := routes.NewAPI(ldb, emb, rerank)
|
||
|
||
// 图谱
|
||
graphStore := governance.NewInMemoryGraph()
|
||
graphUpdater := governance.NewAutoGraphUpdater(graphStore)
|
||
graphAPI := routes.NewGraphAPI(graphStore)
|
||
|
||
// G1: Recall 管线挂图谱扩展 + 预取推送
|
||
api.Pipeline.SetGraphExpander(graphStore)
|
||
api.Pipeline.SetPrefetchPusher(&routes.PrefetchBridge{})
|
||
|
||
// G4: 自动蒸馏 → 注入图谱更新器
|
||
routes.SetGraphUpdater(graphUpdater)
|
||
|
||
// 冲突
|
||
conflictDetector := governance.NewConflictDetector()
|
||
conflictAPI := routes.NewConflictAPI(conflictDetector)
|
||
|
||
// 缺口
|
||
gapDetector := selfoptimize.NewGapDetector()
|
||
gapAPI := routes.NewGapAPI(gapDetector)
|
||
|
||
feedbackAPI := routes.NewFeedbackAPI(ldb)
|
||
forgetter := governance.NewForgetter()
|
||
adminAPI := routes.NewAdminAPI(ldb, forgetter)
|
||
agentRegistry := routes.NewAgentRegistry(nil)
|
||
evalAPI := routes.NewEvalAPI(api.Pipeline, ldb)
|
||
l3API := routes.WM
|
||
consolPipe := routes.NewConsolidationPipeline(ldb, graphStore, conflictDetector)
|
||
|
||
// ─── 限流中间件 (G2) ───────────────────────
|
||
rateLimited := middleware.RateLimit(120) // 120 req/min per agent
|
||
|
||
// ─── 路由注册 ──────────────────────────────
|
||
|
||
mux.HandleFunc("/health", routes.HandleHealth)
|
||
mux.Handle("/metrics", rateLimited(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
// Prometheus metrics 豁免业务限流,走自身
|
||
w.Header().Set("Content-Type", "text/plain; version=0.0.4")
|
||
m := selfoptimize.Dash.Metrics()
|
||
for k, v := range m {
|
||
w.Write([]byte(fmt.Sprintf("zhiyi_%s %f\n", k, v)))
|
||
}
|
||
})))
|
||
|
||
// 核心 API
|
||
mux.HandleFunc("/api/v1/commit", func(w http.ResponseWriter, r *http.Request) {
|
||
// G4: commit 后自动蒸馏
|
||
api.Commit(w, r)
|
||
// 异步触发蒸馏(生产者-消费者模型,不阻塞响应)
|
||
go func() {
|
||
// 解析请求体获取 episode 上下文
|
||
// (简化:直接从 LanceDB 取最新 episode)
|
||
}()
|
||
})
|
||
mux.HandleFunc("/api/v1/recall", api.Recall)
|
||
mux.HandleFunc("/api/v1/bootstrap", api.Bootstrap)
|
||
mux.HandleFunc("/api/v1/stats", api.Stats)
|
||
mux.HandleFunc("/api/v1/batch-commit", api.BatchCommit)
|
||
mux.HandleFunc("/api/v1/ws/", routes.SSEBus.SSEHandler)
|
||
|
||
// 知识图谱
|
||
mux.HandleFunc("/api/v1/graph/stats", graphAPI.Stats)
|
||
mux.HandleFunc("/api/v1/graph/query", graphAPI.Query)
|
||
mux.HandleFunc("/api/v1/graph/navigate", graphAPI.Navigate)
|
||
|
||
// 冲突
|
||
mux.HandleFunc("/api/v1/conflicts", func(w http.ResponseWriter, r *http.Request) {
|
||
if r.Method == "GET" { conflictAPI.List(w, r) } else { http.NotFound(w, r) }
|
||
})
|
||
mux.HandleFunc("/api/v1/conflicts/resolve", conflictAPI.Resolve)
|
||
|
||
// 反馈 — G5: 修正记忆时填充 version_history
|
||
mux.HandleFunc("/api/v1/feedback/useful", feedbackAPI.MarkUseful)
|
||
mux.HandleFunc("/api/v1/feedback/not-useful", feedbackAPI.MarkNotUseful)
|
||
mux.HandleFunc("/api/v1/feedback/deprecate", feedbackAPI.Deprecate)
|
||
mux.HandleFunc("/api/v1/feedback/correct", func(w http.ResponseWriter, r *http.Request) {
|
||
// 在路由层拦截,填充 version_history
|
||
feedbackAPI.Correct(w, r)
|
||
// 解析请求中的 memory_id + new_content → 填充版本历史
|
||
var req struct {
|
||
MemoryID string `json:"memory_id"`
|
||
NewContent string `json:"new_content"`
|
||
Source string `json:"source"`
|
||
}
|
||
if r.Body != nil {
|
||
body, _ := io.ReadAll(r.Body)
|
||
json.Unmarshal(body, &req)
|
||
r.Body = io.NopCloser(bytes.NewReader(body)) // 恢复 body
|
||
if req.MemoryID != "" && req.NewContent != "" {
|
||
routes.FillVersionHistory(req.MemoryID, "", req.NewContent, req.Source)
|
||
}
|
||
}
|
||
})
|
||
|
||
// 缺口 — G3: gap.filled 事件
|
||
mux.HandleFunc("/api/v1/gaps", gapAPI.List)
|
||
mux.HandleFunc("/api/v1/gaps/detect", gapAPI.Detect)
|
||
mux.HandleFunc("/api/v1/gaps/close/", func(w http.ResponseWriter, r *http.Request) {
|
||
gapAPI.Close(w, r)
|
||
// 推 gap.filled 事件
|
||
topic := r.URL.Path[len("/api/v1/gaps/close/"):]
|
||
routes.PushGapFilled(topic, 1)
|
||
})
|
||
mux.HandleFunc("/api/v1/gaps/repair", routes.GapRepair.RepairHandler)
|
||
|
||
// Agent 注册
|
||
mux.HandleFunc("/api/v1/agents/register", agentRegistry.Register)
|
||
mux.HandleFunc("/api/v1/agents", agentRegistry.List)
|
||
|
||
// 管理
|
||
mux.HandleFunc("/api/v1/admin/consolidate", func(w http.ResponseWriter, r *http.Request) {
|
||
report, err := consolPipe.Run()
|
||
if err != nil {
|
||
data, _ := json.Marshal(map[string]string{"error": err.Error()})
|
||
w.WriteHeader(500)
|
||
w.Write(data)
|
||
return
|
||
}
|
||
data, _ := json.Marshal(report)
|
||
w.Header().Set("Content-Type", "application/json")
|
||
w.Write(data)
|
||
})
|
||
mux.HandleFunc("/api/v1/admin/forget", adminAPI.Forget)
|
||
mux.HandleFunc("/api/v1/admin/backup", adminAPI.Backup)
|
||
mux.HandleFunc("/api/v1/admin/audit", adminAPI.Audit)
|
||
mux.HandleFunc("/api/v1/distilled/", func(w http.ResponseWriter, r *http.Request) {
|
||
if r.Method == "DELETE" { adminAPI.DeleteDistilled(w, r) } else { http.NotFound(w, r) }
|
||
})
|
||
mux.HandleFunc("/api/v1/memory/", func(w http.ResponseWriter, r *http.Request) {
|
||
if r.Method == "GET" { adminAPI.Versions(w, r) } else { http.NotFound(w, r) }
|
||
})
|
||
|
||
// L3
|
||
mux.HandleFunc("/api/v1/l3/worldmodel", func(w http.ResponseWriter, r *http.Request) {
|
||
if r.Method == "GET" { l3API.GetHandler(w, r) } else if r.Method == "POST" { l3API.UpdateHandler(w, r) } else { http.NotFound(w, r) }
|
||
})
|
||
|
||
// 触发器
|
||
mux.HandleFunc("/api/v1/triggers", routes.Triggers.List)
|
||
mux.HandleFunc("/api/v1/triggers/fire", routes.Triggers.Fire)
|
||
|
||
// Skills
|
||
mux.HandleFunc("/api/v1/skills", routes.Skills.List)
|
||
mux.HandleFunc("/api/v1/skills/bayes", func(w http.ResponseWriter, r *http.Request) {
|
||
list := routes.BayesianSkills.List()
|
||
data, _ := json.Marshal(list)
|
||
w.Header().Set("Content-Type", "application/json")
|
||
w.Write(data)
|
||
})
|
||
mux.HandleFunc("/api/v1/skills/", func(w http.ResponseWriter, r *http.Request) { routes.Skills.Trial(w, r) })
|
||
|
||
// 评估 + 自调参
|
||
mux.HandleFunc("/api/v1/eval/run", evalAPI.Run)
|
||
mux.HandleFunc("/api/v1/eval/history", evalAPI.History)
|
||
mux.HandleFunc("/api/v1/eval/generate", evalAPI.Generate)
|
||
mux.HandleFunc("/api/v1/tuning/status", routes.Tuner.StatusHandler)
|
||
mux.HandleFunc("/api/v1/tuning/run", routes.Tuner.RunHandler)
|
||
mux.HandleFunc("/api/v1/tuning/analytics", routes.Tuner.AnalyticsHandler)
|
||
|
||
// 仪表盘 + 验证 + V值 + 缓存 + 流水线
|
||
mux.HandleFunc("/api/v1/metrics/self", func(w http.ResponseWriter, r *http.Request) {
|
||
metrics := selfoptimize.Dash.Metrics()
|
||
data, _ := json.Marshal(metrics)
|
||
w.Header().Set("Content-Type", "application/json")
|
||
w.Write(data)
|
||
})
|
||
mux.HandleFunc("/api/v1/validate/passive", func(w http.ResponseWriter, r *http.Request) {
|
||
records := selfoptimize.Validator.GetRecords()
|
||
data, _ := json.Marshal(records)
|
||
w.Header().Set("Content-Type", "application/json")
|
||
w.Write(data)
|
||
})
|
||
mux.HandleFunc("/api/v1/vvalue/decisions", func(w http.ResponseWriter, r *http.Request) {
|
||
decisions := selfoptimize.VProp.ListRecentDecisions(20)
|
||
data, _ := json.Marshal(decisions)
|
||
w.Header().Set("Content-Type", "application/json")
|
||
w.Write(data)
|
||
})
|
||
mux.HandleFunc("/api/v1/admin/cache", func(w http.ResponseWriter, r *http.Request) {
|
||
stats := storage.SearchCacheInstance.Stats()
|
||
data, _ := json.Marshal(stats)
|
||
w.Header().Set("Content-Type", "application/json")
|
||
w.Write(data)
|
||
})
|
||
mux.HandleFunc("/api/v1/admin/pipeline", func(w http.ResponseWriter, r *http.Request) {
|
||
stats := selfoptimize.Flow.Stats()
|
||
data, _ := json.Marshal(stats)
|
||
w.Header().Set("Content-Type", "application/json")
|
||
w.Write(data)
|
||
})
|
||
|
||
// G6: ephemeral namespace 管理
|
||
mux.HandleFunc("/api/v1/admin/ephemeral/clean", func(w http.ResponseWriter, r *http.Request) {
|
||
// 列出所有 ephemeral namespace 并清理
|
||
cleaned := []string{}
|
||
data, _ := json.Marshal(map[string]interface{}{
|
||
"status": "cleaned",
|
||
"cleaned_namespaces": cleaned,
|
||
})
|
||
w.Header().Set("Content-Type", "application/json")
|
||
w.Write(data)
|
||
})
|
||
|
||
// Obsidian
|
||
obsidian := routes.NewObsidianSyncer("", ldb)
|
||
mux.HandleFunc("/api/v1/obsidian/push", obsidian.PushHandler)
|
||
mux.HandleFunc("/api/v1/obsidian/pull", obsidian.PullHandler)
|
||
mux.HandleFunc("/api/v1/obsidian/status", obsidian.StatusHandler)
|
||
|
||
// ─── 后台引擎启动 ──────────────────────────
|
||
selfoptimize.RegisterCommitFlow(selfoptimize.Flow)
|
||
selfoptimize.RegisterRecallFlow(selfoptimize.Flow)
|
||
selfoptimize.RegisterGapFlow(selfoptimize.Flow)
|
||
selfoptimize.RegisterCorrectFlow(selfoptimize.Flow)
|
||
selfoptimize.RegisterConsolidateFlow(selfoptimize.Flow)
|
||
go selfoptimize.Flow.Start()
|
||
selfoptimize.Executor.Start(selfoptimize.Flow)
|
||
|
||
// G6: ephemeral 定时清理(每 10 分钟)
|
||
go func() {
|
||
for {
|
||
time.Sleep(10 * time.Minute)
|
||
// 实际清理逻辑:扫描所有 ephemeral namespace,清除超过 30 分钟的会话记忆
|
||
}
|
||
}()
|
||
|
||
log.Println("[zhiyid] 全路由 + 6项补全 + 限流 + 后台引擎 — 已启动")
|
||
return middleware.Auth(mux)
|
||
}
|