Honeycomb und Control-Patch
This commit is contained in:
@@ -161,6 +161,10 @@ func (s *Store) ClearVectorsByDimension(dim int) int {
|
||||
}
|
||||
|
||||
func (s *Store) NodesForEmbedding() []model.Node {
|
||||
return s.NodesForEmbeddingFiltered(nil)
|
||||
}
|
||||
|
||||
func (s *Store) NodesForEmbeddingFiltered(categories []string) []model.Node {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
out := []model.Node{}
|
||||
@@ -168,6 +172,9 @@ func (s *Store) NodesForEmbedding() []model.Node {
|
||||
if n.Kind != "knowledge" && n.Kind != "ai-think" && n.Kind != "external" {
|
||||
continue
|
||||
}
|
||||
if !nodeMatchesCategories(n, categories) {
|
||||
continue
|
||||
}
|
||||
if _, ok := s.vectors[n.ID]; !ok {
|
||||
out = append(out, n)
|
||||
}
|
||||
@@ -332,6 +339,10 @@ func (s *Store) Similar(query []float64, limit int) []model.Hit {
|
||||
// selection responsive even for tens of thousands of knowledge nodes while the
|
||||
// rotating cursor eventually visits the complete corpus.
|
||||
func (s *Store) NextPair(min float64, anchorLimit int) (model.Node, model.Node, float64, bool, int) {
|
||||
return s.NextPairFiltered(min, anchorLimit, nil)
|
||||
}
|
||||
|
||||
func (s *Store) NextPairFiltered(min float64, anchorLimit int, categories []string) (model.Node, model.Node, float64, bool, int) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
@@ -340,6 +351,9 @@ func (s *Store) NextPair(min float64, anchorLimit int) (model.Node, model.Node,
|
||||
if n.Kind != "knowledge" && n.Kind != "ai-think" {
|
||||
continue
|
||||
}
|
||||
if !nodeMatchesCategories(n, categories) {
|
||||
continue
|
||||
}
|
||||
if v, ok := s.vectors[n.ID]; ok && len(v) > 0 {
|
||||
nodes = append(nodes, n)
|
||||
}
|
||||
@@ -439,6 +453,35 @@ func (s *Store) ConnectingEdges(ids []string) []string {
|
||||
}
|
||||
return out
|
||||
}
|
||||
func nodeMatchesCategories(n model.Node, filters []string) bool {
|
||||
if len(filters) == 0 {
|
||||
return true
|
||||
}
|
||||
wanted := make(map[string]struct{}, len(filters))
|
||||
for _, filter := range filters {
|
||||
filter = strings.ToLower(strings.TrimSpace(filter))
|
||||
if filter != "" {
|
||||
wanted[filter] = struct{}{}
|
||||
}
|
||||
}
|
||||
if len(wanted) == 0 {
|
||||
return true
|
||||
}
|
||||
if _, ok := wanted["*"]; ok {
|
||||
return true
|
||||
}
|
||||
if len(n.Categories) == 0 {
|
||||
_, ok := wanted["__uncategorized__"]
|
||||
return ok
|
||||
}
|
||||
for _, category := range n.Categories {
|
||||
if _, ok := wanted[strings.ToLower(strings.TrimSpace(category))]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func edgeBetweenLocked(edges map[string]model.Edge, a, b string) bool {
|
||||
for _, e := range edges {
|
||||
if (e.Source == a && e.Target == b) || (e.Source == b && e.Target == a) {
|
||||
|
||||
@@ -69,3 +69,34 @@ func TestNextPairUsesBoundedRotatingAnchors(t *testing.T) {
|
||||
t.Fatalf("rotating anchor did not reach the next semantic region, ok=%v comparisons=%d", ok, comparisons)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCategoryFiltersLimitEmbeddingAndThinkingCandidates(t *testing.T) {
|
||||
s, err := Open(t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
nodes := []model.Node{
|
||||
{ID: "net-a", Kind: "knowledge", Label: "Net A", Categories: []string{"Netzwerk"}, Origin: "test"},
|
||||
{ID: "net-b", Kind: "knowledge", Label: "Net B", Categories: []string{"Netzwerk"}, Origin: "test"},
|
||||
{ID: "app-a", Kind: "knowledge", Label: "App A", Categories: []string{"Applikation"}, Origin: "test"},
|
||||
{ID: "none", Kind: "knowledge", Label: "Ohne", Origin: "test"},
|
||||
}
|
||||
for _, node := range nodes {
|
||||
s.UpsertNode(node)
|
||||
}
|
||||
pending := s.NodesForEmbeddingFiltered([]string{"Netzwerk"})
|
||||
if len(pending) != 2 {
|
||||
t.Fatalf("expected two network embeddings, got %d", len(pending))
|
||||
}
|
||||
uncategorized := s.NodesForEmbeddingFiltered([]string{"__uncategorized__"})
|
||||
if len(uncategorized) != 1 || uncategorized[0].ID != "none" {
|
||||
t.Fatalf("unexpected uncategorized nodes: %#v", uncategorized)
|
||||
}
|
||||
for _, node := range nodes {
|
||||
s.SetVector(node.ID, []float64{1, .01})
|
||||
}
|
||||
a, b, _, ok, _ := s.NextPairFiltered(.5, 8, []string{"Netzwerk"})
|
||||
if !ok || a.Categories[0] != "Netzwerk" || b.Categories[0] != "Netzwerk" {
|
||||
t.Fatalf("thinking filter returned wrong pair: ok=%v a=%+v b=%+v", ok, a, b)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user