catalog

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 26, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package catalog loads and searches an OKF knowledge catalog (a directory of markdown files with YAML frontmatter) — the read half of RunLore's Learn pillar.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Catalog

type Catalog struct {
	// contains filtered or unexported fields
}

Catalog is an in-memory BM25 index over OKF entries. It is safe for concurrent Search while a background sync calls Reload (the index is swapped atomically).

func New

func New(dir string) (*Catalog, error)

New loads the OKF bundle at dir and builds an in-memory index.

func NewEmpty

func NewEmpty() *Catalog

NewEmpty returns a catalog with no entries — used before the first git sync.

func (*Catalog) Entries

func (c *Catalog) Entries() []Entry

Entries returns a snapshot of the currently-indexed entries. Used by callers that validate catalog content out-of-band (kbvalidate at load time).

func (*Catalog) HasVectors

func (c *Catalog) HasVectors() bool

HasVectors reports whether hybrid retrieval is live — a vector per entry.

func (*Catalog) Len

func (c *Catalog) Len() int

Len reports the number of indexed entries.

func (*Catalog) Ready

func (c *Catalog) Ready() bool

Ready reports whether the catalog has completed at least one successful load. It stays false for a git-sync catalog (NewEmpty) until the first sync indexes, so readyz can keep the leader out of rotation until its KB is warm.

func (*Catalog) Reload

func (c *Catalog) Reload(dir string) ([]string, error)

Reload rebuilds the index from dir and swaps it in atomically. The new index is built outside the lock so concurrent Search is only blocked for the swap. It returns the list of skipped (unparseable) entry paths so the caller can warn; these are non-fatal — the good entries are still indexed.

func (*Catalog) ReloadContext

func (c *Catalog) ReloadContext(ctx context.Context, dir string) ([]string, error)

ReloadContext is Reload with a caller context bounding the (optional) embedding pass. When an embedder is configured, entry vectors are rebuilt here; an embedding failure is NON-fatal — vectors fall back to nil and the catalog stays BM25-only (hybrid retrieval degrades gracefully rather than breaking the reload).

func (*Catalog) Search

func (c *Catalog) Search(query string, k int) ([]Entry, error)

Search returns up to k entries best matching the query (BM25).

func (*Catalog) SearchHybrid

func (c *Catalog) SearchHybrid(ctx context.Context, query string, k int) ([]ScoredEntry, error)

SearchHybrid combines lexical and semantic retrieval for instant recall. It pools candidates by Reciprocal Rank Fusion of the BM25 and embedding-cosine rankings (so an entry strong in EITHER signal is reachable), then returns up to k of them ORDERED BY COSINE with Score set to the cosine similarity — a [0,1] semantic confidence the recall gate keys off, kept score-descending so the gate's top-vs-runner-up margin stays well-defined. Falls back to BM25 (SearchScored) when vectors are unavailable or the query can't be embedded — hybrid never regresses recall, it only adds a vector signal when one is present.

The fusion choice and the cosine gate thresholds are eval-tunable knobs: recall records its score even on rejection (see recall.lookup) so they can be measured against the live instant-recall scenarios rather than guessed.

func (*Catalog) SearchScored

func (c *Catalog) SearchScored(query string, k int) ([]ScoredEntry, error)

SearchScored returns up to k hits with their relevance scores.

func (*Catalog) SetEmbedder

func (c *Catalog) SetEmbedder(e Embedder)

SetEmbedder enables hybrid retrieval: entry vectors are (re)built on the next Reload. Call once at startup, before the first Reload/Search.

type Embedder

type Embedder interface {
	Embed(ctx context.Context, texts []string) ([][]float32, error)
}

Embedder turns texts into vectors (one per input, in order). Satisfied by internal/embed.Client — defined here (consumer side) so the catalog needn't import a concrete embedder.

type Entry

type Entry struct {
	Type        string   // frontmatter: type (Playbook, Incident, …)
	Title       string   // frontmatter: title
	Description string   // frontmatter: description
	Resource    string   // frontmatter: resource
	Tags        []string // frontmatter: tags
	Body        string   // markdown body (after frontmatter)
	Path        string   // file path relative to the bundle root
}

Entry is one OKF knowledge entry.

func Load

func Load(dir string) (entries []Entry, skipped []string, err error)

Load walks dir and parses every concept .md file into an Entry. The reserved OKF files index.md and log.md are skipped.

A file that fails to parse (e.g. malformed YAML frontmatter) is skipped rather than failing the whole load — its path is returned in skipped so the caller can warn — so one bad entry never empties the catalog. A genuine walk/IO error is still returned as the fatal error.

type HybridSearcher

type HybridSearcher interface {
	SearchHybrid(ctx context.Context, query string, k int) ([]ScoredEntry, error)
	HasVectors() bool
}

HybridSearcher fuses BM25 and embedding similarity. HasVectors reports whether the vector side is live (an embedder configured AND vectors built for the corpus); callers fall back to SearchScored when it is false.

type ScoredEntry

type ScoredEntry struct {
	Entry Entry
	Score float64
}

ScoredEntry is a search hit with its BM25 relevance score.

type ScoredSearcher

type ScoredSearcher interface {
	SearchScored(query string, k int) ([]ScoredEntry, error)
}

ScoredSearcher exposes relevance scores (used by instant recall).

type Searcher

type Searcher interface {
	Search(query string, k int) ([]Entry, error)
}

Searcher is the read surface used by the kb_search tool.

type Syncer

type Syncer struct {
	URL    string
	Branch string
	Dir    string
	Token  TokenFunc // nil / empty => anonymous (public repo)
	Log    *slog.Logger
	// contains filtered or unexported fields
}

Syncer keeps a local mirror of an OKF catalog Git repo up to date, calling onSync after each successful sync so the reader can re-index. This closes the read/write loop: the curator's merged PRs flow back into what the agent searches.

func (*Syncer) Run

func (s *Syncer) Run(ctx context.Context, interval time.Duration, onSync func() error)

Run does an initial sync then re-syncs every interval, calling onSync after each success. It returns when ctx is done. interval <= 0 defaults to 5m.

func (*Syncer) Sync

func (s *Syncer) Sync(ctx context.Context) (bool, error)

Sync clones the repo if the mirror is absent, otherwise fast-forwards it, and reports whether HEAD moved since the previous sync (true on the first sync).

type TokenFunc

type TokenFunc func(ctx context.Context) (string, error)

TokenFunc returns a git credential (e.g. a GitHub App installation token or a read-scoped PAT). Used as the basic-auth password with username x-access-token.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL