Some checks failed
release-tag / Resolve release metadata (push) Successful in 30s
release-tag / Build knowledge (push) Failing after 4m51s
release-tag / Build control (push) Failing after 5m0s
release-tag / Build agent (push) Failing after 5m0s
release-tag / Build agent-data-init (push) Failing after 5m5s
release-tag / Build neuroforge-worker (push) Failing after 5m7s
release-tag / Build neuroforge (push) Failing after 5m9s
118 lines
3.7 KiB
Go
118 lines
3.7 KiB
Go
package httpapi
|
||
|
||
import (
|
||
"errors"
|
||
"net/http"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
|
||
"neuroforge/internal/core"
|
||
)
|
||
|
||
func (s *Server) adminKnowledgeSummary(w http.ResponseWriter, r *http.Request) {
|
||
s.json(w, http.StatusOK, s.store.KnowledgeSummary())
|
||
}
|
||
|
||
func (s *Server) adminKnowledgeMemories(w http.ResponseWriter, r *http.Request) {
|
||
limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
|
||
var before time.Time
|
||
if raw := strings.TrimSpace(r.URL.Query().Get("before")); raw != "" {
|
||
var err error
|
||
before, err = time.Parse(time.RFC3339Nano, raw)
|
||
if err != nil {
|
||
s.err(w, 400, errors.New("before must be RFC3339"))
|
||
return
|
||
}
|
||
}
|
||
out := s.store.KnowledgeMemories(limit, before, r.URL.Query().Get("memory_type"), r.URL.Query().Get("status"), r.URL.Query().Get("kind"), r.URL.Query().Get("source"))
|
||
s.json(w, http.StatusOK, out)
|
||
}
|
||
|
||
func (s *Server) adminKnowledgeMemory(w http.ResponseWriter, r *http.Request) {
|
||
out, ok := s.store.KnowledgeMemoryDetail(r.PathValue("id"))
|
||
if !ok {
|
||
s.err(w, 404, errors.New("memory not found"))
|
||
return
|
||
}
|
||
s.json(w, http.StatusOK, out)
|
||
}
|
||
|
||
func (s *Server) adminKnowledgeGraph(w http.ResponseWriter, r *http.Request) {
|
||
depth, _ := strconv.Atoi(r.URL.Query().Get("depth"))
|
||
maxNodes, _ := strconv.Atoi(r.URL.Query().Get("max_nodes"))
|
||
s.json(w, http.StatusOK, s.store.KnowledgeGraph(r.URL.Query().Get("center"), depth, maxNodes))
|
||
}
|
||
|
||
func (s *Server) adminKnowledgeEvents(w http.ResponseWriter, r *http.Request) {
|
||
limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
|
||
s.json(w, http.StatusOK, s.store.RecentKnowledgeEvents(limit))
|
||
}
|
||
|
||
func (s *Server) adminKnowledgeSearch(w http.ResponseWriter, r *http.Request) {
|
||
var q struct {
|
||
Text string `json:"text"`
|
||
K int `json:"k"`
|
||
}
|
||
if err := decode(r, &q); err != nil {
|
||
s.err(w, 400, err)
|
||
return
|
||
}
|
||
q.Text = strings.TrimSpace(q.Text)
|
||
if q.Text == "" {
|
||
s.err(w, 400, errors.New("text is required"))
|
||
return
|
||
}
|
||
if q.K <= 0 {
|
||
q.K = 12
|
||
}
|
||
if q.K > 50 {
|
||
q.K = 50
|
||
}
|
||
hits, err := s.brain.Search(r.Context(), q.Text, q.K)
|
||
if err != nil {
|
||
s.err(w, 502, err)
|
||
return
|
||
}
|
||
s.json(w, http.StatusOK, map[string]any{
|
||
"query": q.Text,
|
||
"hits": hits,
|
||
"formula": "score = similarity × salience_factor × type_weight × confidence_factor + graph_boost",
|
||
"note": "candidate_source shows whether a candidate came from HNSW, Disk-PQ, full scan, or a synapse expansion; final similarity is always computed against the original vector when available.",
|
||
})
|
||
}
|
||
|
||
type learningPolicySettings struct {
|
||
AutoLearn bool `json:"auto_learn"`
|
||
Policy core.LearningPolicyConfig `json:"policy"`
|
||
}
|
||
|
||
func (s *Server) adminGetLearningPolicy(w http.ResponseWriter, r *http.Request) {
|
||
c := s.store.Config()
|
||
s.json(w, http.StatusOK, learningPolicySettings{AutoLearn: c.Brain.AutoLearn, Policy: c.Brain.LearningPolicy})
|
||
}
|
||
|
||
func (s *Server) adminPutLearningPolicy(w http.ResponseWriter, r *http.Request) {
|
||
var q learningPolicySettings
|
||
if err := decode(r, &q); err != nil {
|
||
s.err(w, http.StatusBadRequest, err)
|
||
return
|
||
}
|
||
c := s.store.Config()
|
||
c.Brain.AutoLearn = q.AutoLearn
|
||
c.Brain.LearningPolicy = q.Policy
|
||
if err := s.store.ValidateConfig(c); err != nil {
|
||
s.err(w, http.StatusBadRequest, err)
|
||
return
|
||
}
|
||
if err := s.store.UpdateConfig(c); err != nil {
|
||
s.err(w, http.StatusInternalServerError, err)
|
||
return
|
||
}
|
||
_ = s.store.AddKnowledgeEvent(core.KnowledgeEvent{
|
||
Type: "admin.learning_policy_changed", Summary: "Learning policy updated", Reason: "PUT /admin/api/learning-policy", Actor: "admin",
|
||
Metadata: map[string]string{"auto_learn": strconv.FormatBool(q.AutoLearn), "enabled": strconv.FormatBool(q.Policy.Enabled), "duplicate_similarity": strconv.FormatFloat(q.Policy.DuplicateSimilarity, 'f', 4, 64)},
|
||
})
|
||
s.json(w, http.StatusOK, q)
|
||
}
|