feat(server): INGEST_WING_RESOLVE=1 wires the wing resolver into the watcher (#205 S3)
Off by default (retired layout unchanged). On: eligibility from classification.yaml, local ingest LLM as classifier, BM25 as cross-check, INGEST_WING_THRESHOLD (default 0.9, rejected outside [0,1]). Build failure is fatal when explicitly enabled. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
package wing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/mathiasbq/hyperguild/ingestion/internal/classification"
|
||||
)
|
||||
|
||||
// NewConfiguredResolver builds the production Resolver for a brain: Known from
|
||||
// the wiki, Eligible from classification.yaml (a wing is auto-assignable only
|
||||
// when it classifies below Confidential, so an untagged wing never is), the
|
||||
// LLM classifier over complete, and BM25 over the known wings as the scorer.
|
||||
//
|
||||
// Known is read once. A wing created later is unknown to this Resolver until
|
||||
// the process restarts, which fails safe: notes aimed at it are parked.
|
||||
func NewConfiguredResolver(brainDir string, complete CompleteFunc, threshold float64) (*Resolver, error) {
|
||||
known, err := KnownWings(brainDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cls, err := classification.Load(brainDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load classification: %w", err)
|
||||
}
|
||||
return &Resolver{
|
||||
Known: known,
|
||||
Eligible: func(w string) bool { return cls.Wing(w) < classification.Confidential },
|
||||
Classifier: NewLLMClassifier(complete),
|
||||
Scorer: NewBM25Scorer(brainDir, known, ""),
|
||||
Threshold: threshold,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package wing_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mathiasbq/hyperguild/ingestion/internal/wing"
|
||||
)
|
||||
|
||||
func configuredBrain(t *testing.T, classification string) string {
|
||||
dir := t.TempDir()
|
||||
writePage(t, dir, "wiki/homelab/facts/a.md", "x")
|
||||
writePage(t, dir, "wiki/radar/facts/b.md", "x")
|
||||
writePage(t, dir, "wiki/client-x/facts/c.md", "x")
|
||||
if classification != "" {
|
||||
writePage(t, dir, "classification.yaml", classification)
|
||||
}
|
||||
return dir
|
||||
}
|
||||
|
||||
func TestNewConfiguredResolver_EligibilityComesFromClassification(t *testing.T) {
|
||||
dir := configuredBrain(t, "wings:\n radar: internal\n")
|
||||
r, err := wing.NewConfiguredResolver(dir, func(context.Context, string, string) (string, error) { return "", nil }, 0.9)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.ElementsMatch(t, []string{"client-x", "homelab", "radar"}, r.Known)
|
||||
assert.True(t, r.Eligible("radar"), "tagged internal")
|
||||
assert.True(t, r.Eligible("homelab"), "internal by built-in default")
|
||||
assert.False(t, r.Eligible("client-x"), "an untagged wing fails safe to confidential")
|
||||
assert.InDelta(t, 0.9, r.Threshold, 1e-9)
|
||||
}
|
||||
|
||||
func TestNewConfiguredResolver_AMalformedClassificationFileIsAnError(t *testing.T) {
|
||||
dir := configuredBrain(t, "wings: [not, a, map")
|
||||
_, err := wing.NewConfiguredResolver(dir, nil, 0.9)
|
||||
assert.Error(t, err, "a classification source that cannot be trusted must not degrade to 'everything eligible'")
|
||||
}
|
||||
|
||||
func TestNewConfiguredResolver_AConfidentialTagIsNeverEligible(t *testing.T) {
|
||||
dir := configuredBrain(t, "wings:\n radar: confidential\n")
|
||||
r, err := wing.NewConfiguredResolver(dir, nil, 0.9)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, r.Eligible("radar"))
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package wing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// legacyDirs are the retired wiki/{concepts,entities,sources}/ layout, not wings.
|
||||
var legacyDirs = map[string]bool{"concepts": true, "entities": true, "sources": true}
|
||||
|
||||
// KnownWings lists wiki/<wing>/ directories that contain at least one hall
|
||||
// subdirectory, sorted. The retired layout is not a wing. A missing wiki/ is
|
||||
// an error: an unreadable brain must not look like "no wings".
|
||||
func KnownWings(brainDir string) ([]string, error) {
|
||||
entries, err := os.ReadDir(filepath.Join(brainDir, "wiki"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list wings: %w", err)
|
||||
}
|
||||
var out []string
|
||||
for _, e := range entries {
|
||||
if !e.IsDir() || legacyDirs[e.Name()] {
|
||||
continue
|
||||
}
|
||||
subs, err := os.ReadDir(filepath.Join(brainDir, "wiki", e.Name()))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, s := range subs {
|
||||
if s.IsDir() {
|
||||
out = append(out, e.Name())
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Strings(out)
|
||||
return out, nil
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package wing_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mathiasbq/hyperguild/ingestion/internal/wing"
|
||||
)
|
||||
|
||||
func TestKnownWings_ListsWingsWithHallsAndSkipsTheRetiredLayout(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
writePage(t, dir, "wiki/homelab/facts/a.md", "x")
|
||||
writePage(t, dir, "wiki/tapir/decisions/b.md", "x")
|
||||
writePage(t, dir, "wiki/concepts/c.md", "x") // retired layout, a file directly under a legacy dir
|
||||
writePage(t, dir, "wiki/sources/d.md", "x") // retired layout
|
||||
writePage(t, dir, "wiki/entities/e.md", "x") // retired layout
|
||||
writePage(t, dir, "wiki/loose-note.md", "x") // a file, not a wing
|
||||
writePage(t, dir, "wiki/emptywing/_index.md", "x") // a dir with no hall subdirectory
|
||||
got, err := wing.KnownWings(dir)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []string{"homelab", "tapir"}, got)
|
||||
}
|
||||
|
||||
func TestKnownWings_NoWikiDirIsAnError(t *testing.T) {
|
||||
_, err := wing.KnownWings(t.TempDir())
|
||||
assert.Error(t, err, "a brain without wiki/ must not look like a brain with no wings")
|
||||
}
|
||||
Reference in New Issue
Block a user