brain

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 9, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Overview

Package brain is Tacklr's knowledge-base retrieval engine.

Public surface

Hosts use Engine (NewEngine + options), Store implementations (MemoryStore / PostgresStore), graph backends via WithGraph, kind registration (ApplyKinds / WithKinds), and composition helpers (LandingIDs, ExpandMany, ExpandByRecipe, SortRichObjects). Agent tools are registered by the harness when AgentOptions.Brain is set — they call Engine methods only.

Graph backend packages (e.g. helixgraph) implement GraphReader / GraphWriter / GraphObjectSearcher / GraphEdgeSearcher. Dual-write property keys and Helix schema details stay inside those packages.

Hosts attach an Engine via AgentOptions.Brain. This package does not import the harness, session, or telemetry packages; Scope is passed in by the caller. Optional Observer (telemetry.NewBrainObserver) records retrieval ops without domain coupling: search, find_exact, find_objects, find_links, continue, expand, expand_many.

SearchContext is the retrieval session surface: host namespace + active ResultSet for continue (replaced on each search, find_exact, find_objects, or large expand).

Kind schemas (host migrations)

Object kinds are host/user-defined for determinism. Register with ApplyKinds (or WithKinds). Agent-defined kinds are out of scope.

Explicit writes (no handoff side effects)

Durable objects are written only via Engine.Put / SoftDelete (host SDK) or kind-scoped agent tools. Context handoff never writes the knowledge base.

Hosts map save_* tools via AgentOptions.BrainWriteKinds. Write for retrieval: fill title and summary (and useful properties) so search and find_objects work.

Graph nodes are live, not static: every parent Put dual-writes node props in place (edges preserved). SoftDelete removes the graph node first, then soft-deletes the store row. Revive via Put recreates the graph node.

Postgres vs Helix (complementary)

Postgres is the source of truth and document corpus: full rows, parts/chunks, BM25 + dense hybrid search, property filters, soft-delete, containment (parent_id). Tools: search, find_exact, read; expand children. search may pass ScopeIDs to limit hits to a parent neighborhood.

Helix holds first-class entity nodes and cross-object edges only (not chunks). Helix owns: native text/vector indexes, $distance ranking, graph topology, edge props, BothE neighbor walks, optional tenant indexes on namespace_id. Tacklr does not reimplement BM25/HNSW or in-process neighbor indexes for Helix. We dual-write searchable props (EntityIndexText + embedding), Link edges, fuse Helix text+vector channels with RRF (Helix has no single hybrid op), then hydrate full rows from Postgres under Scope.

Tools: find_objects (after Bootstrap), expand with relation_types, link. Optional: helixgraph.EnsureEdgeTextIndex(rel) + SearchEdgesText for note search on a known relation label.

GraphRAG-style composition (host-agnostic):

find_objects (entity land; filters via schema filterable_fields)
  or search/find_exact (corpus) → LandingIDs / LandingIDsFromPage (parent promote)
→ expand / ExpandMany (max_hops, direction, WantContainment)
  or ExpandByRecipe (host-named ExpandRequest template) → hydrate Postgres
→ optional find_links (edge text) for relationship-first land
→ search(scope_ids=…) for neighborhood corpus
→ optional host Reranker after hydrate; SortRichObjects for peer ordering

Graph-first then Postgres drill-down:

find_objects / expand(graph) → Helix ids → hydrate from Postgres
expand() containment → Postgres ListChildren (chunks)
read / search → Postgres

schema() returns filter_usage.tools listing search, find_exact, and find_objects so agents know filterable_fields apply to entity find as well as corpus search.

Embeddings: WithEmbedder on NewEngine. Parents embed EntityIndexText; parts embed IndexText with parent title prefix (corpus only). One embedding dimension per process. Helix hosts must call helixgraph.Graph.Bootstrap (or EnsureSearchIndexes) so HasObjectSearch is true; MemoryGraph is always ready when attached. Bootstrap(true) enables Helix tenant filtering when the image supports it.

Boot sketch

store, err := brain.NewPostgresStore(pool)
g, err := helixgraph.New(helixURL)
if err := g.Bootstrap(ctx, false); err != nil { return err }
eng, err := brain.NewEngine(store, brain.WithEmbedder(emb), brain.WithGraph(g))
if err := eng.ApplyKinds(ctx, specs...); err != nil { return err }

Integration tests (skipped under -short / without Docker):

  • PostgresStore: Testcontainers + brain/testdata/Dockerfile.postgres
  • helixgraph: Testcontainers + ghcr.io/helixdb/enterprise-dev (in-memory)

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNotFound is returned when an object is missing, soft-deleted, or outside scope.
	ErrNotFound = errors.New("brain: object not found")
	// ErrObjectIDRequired is returned when a UUID argument is the nil UUID.
	ErrObjectIDRequired = errors.New("brain: object id is required")
	// ErrQueryRequired is returned when a search/find query string is empty.
	ErrQueryRequired = errors.New("brain: query is required")
	// ErrResultSetRequired is returned when paging needs a ResultSetStore and none was provided.
	ErrResultSetRequired = errors.New("brain: result set store is required")
	// ErrResultSetIDRequired is returned when continue is called with a nil result set id.
	ErrResultSetIDRequired = errors.New("brain: result_set_id is required")
	// ErrGraphWriterRequired is returned when Link is called without a GraphWriter.
	ErrGraphWriterRequired = errors.New("brain: graph writer is required for Link")
	// ErrObjectSearchUnavailable is returned when FindObjects lacks a GraphObjectSearcher.
	ErrObjectSearchUnavailable = errors.New("brain: graph object search is not available")
	// ErrGraphRequired is returned when expand needs graph labels but no GraphReader is set.
	ErrGraphRequired = errors.New("brain: graph backend is required")
	// ErrWritesUnsupported is returned when Put/SoftDelete is used on a read-only store.
	ErrWritesUnsupported = errors.New("brain: store does not support object writes")
	// ErrSoftDeletedPut is returned when Put is called with DeletedAt already set.
	ErrSoftDeletedPut = errors.New("brain: put refuses soft-deleted objects; use SoftDelete")
	// ErrLinkNotFirstClass is returned when a link endpoint is a part (has parent_id).
	ErrLinkNotFirstClass = errors.New("brain: link endpoint must be a first-class object (not a part)")
	// ErrLinkArgs is returned when from/to/relation are incomplete.
	ErrLinkArgs = errors.New("brain: from, to, and relation type are required")
	// ErrGraphEnsure and ErrGraphRemove wrap dual-write failures (cause via errors.Unwrap).
	ErrGraphEnsure = errors.New("brain: graph ensure object")
	ErrGraphRemove = errors.New("brain: graph remove object")
	// ErrEdgeSearchUnavailable is returned when FindLinks lacks a GraphEdgeSearcher.
	ErrEdgeSearchUnavailable = errors.New("brain: graph edge search is not available")
	// ErrLinkQueryRequired is returned when FindLinks is missing relation type or query.
	ErrLinkQueryRequired = errors.New("brain: relation type and query are required")
	// ErrExpandRecipeNotFound is returned when ExpandByRecipe names an unknown recipe.
	ErrExpandRecipeNotFound = errors.New("brain: expand recipe not found")
	// ErrExpandRecipeNameRequired is returned when registering a recipe without a name.
	ErrExpandRecipeNameRequired = errors.New("brain: expand recipe name is required")
)

Sentinel errors for Engine / store / graph call sites. Callers should use errors.Is / errors.As — messages stay stable and wrap-friendly.

View Source
var ErrResultSetNotFound = errors.New("brain: result set not found")

ErrResultSetNotFound is returned when a ResultSet id is unknown or replaced.

Functions

func EntityIndexText

func EntityIndexText(obj Object) string

EntityIndexText builds text for first-class graph nodes and parent embeddings: title, summary, scalar properties (sorted keys), and capped content. Keeps entity find sensitive to attributes (stage, amount, …) without dumping huge bodies.

func IndexText

func IndexText(obj Object) string

IndexText joins non-empty title, summary, and content for corpus part embeds.

func IndexTextWithParent

func IndexTextWithParent(obj Object, parentTitle string) string

IndexTextWithParent prefixes parent context (bursting-style) when parentTitle is set.

func IsContainmentRelation

func IsContainmentRelation(rel string) bool

IsContainmentRelation is true for contains / part_of (and partof).

func LandingIDs

func LandingIDs(objects []RichObject) []uuid.UUID

LandingIDs returns unique first-class object ids suitable for graph expand / link endpoints from rich hits (search, find_exact, find_objects). Parts use ParentID; parents use their own ID. Nil / empty parent pointers are skipped.

Use after corpus search so Phase 1 can land on chunks while Phase 2 expands from the dual-written parent entity on Helix.

func LandingIDsFromPage

func LandingIDsFromPage(page SearchPage) []uuid.UUID

LandingIDsFromPage is LandingIDs(page.Objects).

func NormalizeRelationTypes

func NormalizeRelationTypes(rels []string) []string

NormalizeRelationTypes trims, drops empties, and dedupes labels (case-insensitive). Exported so backends (e.g. helixgraph) share one normalizer.

func PersistKinds

func PersistKinds(ctx context.Context, w KindWriter, specs ...KindSpec) error

PersistKinds upserts validated kind specs into any KindWriter (additive).

func SortRichObjects

func SortRichObjects(objects []RichObject, key string, desc bool)

SortRichObjects sorts objects in place by a well-known or property key. Keys: "updated_at", "created_at", "title", "position", or a property name.

func SplitRelationTypes

func SplitRelationTypes(rels []string) (wantContainment bool, graphLabels []string)

SplitRelationTypes returns whether containment apply and remaining graph labels. Empty input means containment-only.

func ValidateFilters

func ValidateFilters(f Filters) error

ValidateFilters checks filter keys and value shapes. Empty/nil is valid.

func ValidateFiltersAgainst

func ValidateFiltersAgainst(f Filters, cat *KindCatalog) error

ValidateFiltersAgainst runs structural validation, then catalog rules when non-empty.

func ValidateObject

func ValidateObject(obj Object, cat *KindCatalog) error

ValidateObject checks an object against the kind catalog when non-empty.

Types

type DegradeMode

type DegradeMode string

DegradeMode is a closed enum for soft-fail retrieval paths (string values match telemetry labels).

const (
	DegradeNone            DegradeMode = "none"
	DegradeLexicalOnly     DegradeMode = "lexical_only"
	DegradeContainmentOnly DegradeMode = "containment_only"
)

func (DegradeMode) String

func (m DegradeMode) String() string

type EdgeMeta

type EdgeMeta struct {
	Note       string     `json:"note,omitempty"`
	Status     string     `json:"status,omitempty"`     // e.g. active, resolved
	Role       string     `json:"role,omitempty"`       // e.g. primary buyer vs cc
	Confidence float64    `json:"confidence,omitempty"` // 0 means unset; otherwise typically (0,1]
	EvidenceID *uuid.UUID `json:"evidence_id,omitempty"`
	CreatedAt  time.Time  `json:"created_at,omitempty"`
	UpdatedAt  time.Time  `json:"updated_at,omitempty"`
}

EdgeMeta is optional metadata on a non-containment relationship (why/how/when linked). Kept short and relational — full bodies stay on objects in Postgres.

func (EdgeMeta) IsZero

func (m EdgeMeta) IsZero() bool

IsZero reports whether meta carries no meaningful fields.

type EdgeSearchHit

type EdgeSearchHit struct {
	FromID       uuid.UUID
	ToID         uuid.UUID
	RelationType string
	Meta         EdgeMeta
	Score        float64
}

EdgeSearchHit is one graph edge search result (endpoints + meta + score).

type Engine

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

Engine is the retrieval facade over a Store.

func NewEngine

func NewEngine(store Store, opts ...EngineOption) (*Engine, error)

NewEngine builds an Engine over a Store. store must be non-nil.

func (*Engine) ApplyKinds

func (e *Engine) ApplyKinds(ctx context.Context, specs ...KindSpec) error

ApplyKinds is the host migration entry point: desired process catalog + optional durable upsert.

func (*Engine) Catalog

func (e *Engine) Catalog() *KindCatalog

Catalog returns the process kind catalog for inspection and host filter validation (e.g. ValidateFiltersAgainst). Empty means open mode. Prefer ApplyKinds / Schema for normal host setup; do not mutate catalog fields directly.

func (*Engine) Continue

func (e *Engine) Continue(ctx context.Context, scope Scope, resultSetID uuid.UUID, limit int, results ResultSetStore) (page SearchPage, err error)

Continue returns the next page of a prior ResultSet under scope.

func (*Engine) Expand

func (e *Engine) Expand(ctx context.Context, scope Scope, req ExpandRequest, results ResultSetStore) (res ExpandResult, err error)

Expand returns the structural neighborhood of object_id under scope.

func (*Engine) ExpandByRecipe

func (e *Engine) ExpandByRecipe(ctx context.Context, scope Scope, objectID uuid.UUID, recipeName string, results ResultSetStore) (ExpandResult, error)

ExpandByRecipe looks up a host-registered ExpandRecipe and runs Expand with it.

func (*Engine) ExpandMany

func (e *Engine) ExpandMany(ctx context.Context, scope Scope, req ExpandManyRequest) (res ExpandManyResult, err error)

ExpandMany walks the graph from many landing ids without paging / SearchContext. First seed to claim a neighbor wins Relation.SourceID. Out-of-scope seeds are skipped.

func (*Engine) FindExact

func (e *Engine) FindExact(ctx context.Context, scope Scope, req SearchRequest, results ResultSetStore) (page SearchPage, err error)

FindExact runs equality-first exact retrieval (no dense channel), then lexical + trigram fusion, promotion, and ResultSet materialization.

func (e *Engine) FindLinks(ctx context.Context, scope Scope, req FindLinksRequest) (res FindLinksResult, err error)

FindLinks lands on relationships via GraphEdgeSearcher, then hydrates endpoints under Scope.

func (*Engine) FindObjects

func (e *Engine) FindObjects(ctx context.Context, scope Scope, req FindObjectsRequest, results ResultSetStore) (page SearchPage, err error)

FindObjects ranks knowledge objects as entities via GraphObjectSearcher (Helix text/vector or MemoryGraph), then hydrates under Scope from the store. Filters use the same catalog rules as search/find_exact (schema filterable_fields). Not a substitute for corpus Search: no part promotion evidence path.

func (*Engine) FreezeCatalog

func (e *Engine) FreezeCatalog()

FreezeCatalog rejects further RegisterKinds / LoadKindsFromStore. Also auto-frozen on first search/find_exact when the catalog is non-empty.

func (*Engine) HasEdgeSearch

func (e *Engine) HasEdgeSearch() bool

HasEdgeSearch reports whether FindLinks is available.

func (*Engine) HasGraphWriter

func (e *Engine) HasGraphWriter() bool

HasGraphWriter reports whether Put dual-write and Link are available.

func (*Engine) HasObjectSearch

func (e *Engine) HasObjectSearch() bool

HasObjectSearch reports whether FindObjects / find_objects is available.

func (e *Engine) Link(ctx context.Context, scope Scope, from, to uuid.UUID, relationType string) error

Link creates a non-containment edge from→to between first-class, visible objects. Both endpoints must exist under scope, must not be soft-deleted, and must not be parts. Equivalent to LinkWith with zero EdgeMeta.

func (*Engine) LinkWith

func (e *Engine) LinkWith(ctx context.Context, scope Scope, from, to uuid.UUID, relationType string, meta EdgeMeta) error

LinkWith is Link plus optional relationship metadata (note, status, role, …).

func (*Engine) ListChildren

func (e *Engine) ListChildren(ctx context.Context, scope Scope, parentID uuid.UUID) ([]RichObject, error)

ListChildren returns ordered children for a parent visible under scope.

func (*Engine) LoadKindsFromStore

func (e *Engine) LoadKindsFromStore(ctx context.Context) error

LoadKindsFromStore replaces the process catalog from the store.

func (*Engine) Put

func (e *Engine) Put(ctx context.Context, scope Scope, obj Object) (Object, error)

Put upserts a knowledge object under scope. Catalog non-empty → ValidateObject. Namespace filled from scope when missing. ID generated when nil. Put refuses objects that already have DeletedAt set. When WithEmbedder is set and index text is non-empty, embeds and stores the vector; embed errors fail the Put (fail closed). Parent Puts dual-write the graph node (in-place upsert; edges preserved). If the graph Ensure fails after a successful store write, the store row remains (source of truth); callers should re-Put after fixing the graph.

func (*Engine) Read

func (e *Engine) Read(ctx context.Context, scope Scope, id uuid.UUID) (RichObject, error)

Read returns the full rich object for id under scope.

func (*Engine) RegisterExpandRecipe

func (e *Engine) RegisterExpandRecipe(r ExpandRecipe) error

RegisterExpandRecipe adds or replaces a named expand view. Safe for concurrent use with ExpandByRecipe.

func (*Engine) RegisterKinds

func (e *Engine) RegisterKinds(_ context.Context, specs ...KindSpec) error

RegisterKinds merges host kind definitions into the process catalog. Re-registering an existing kind name replaces that kind. Fails if the catalog is frozen.

func (*Engine) Schema

func (e *Engine) Schema(ctx context.Context, kind string) (SchemaResult, error)

Schema returns kind documentation. Empty kind lists all registered kinds. When the process catalog is non-empty it is the source of truth; otherwise the store registry is used.

func (*Engine) Search

func (e *Engine) Search(ctx context.Context, scope Scope, req SearchRequest, results ResultSetStore) (page SearchPage, err error)

Search runs hybrid retrieval (BM25 + optional vector), RRF, temporal decay, parent promotion, and materializes a ResultSet into results.

func (*Engine) SoftDelete

func (e *Engine) SoftDelete(ctx context.Context, scope Scope, id uuid.UUID) error

SoftDelete removes the graph node first (when present), then marks the store row deleted. Graph-first keeps store intact if graph removal fails. If store SoftDelete fails after a successful graph remove, re-Put re-creates the graph node.

func (*Engine) SyncKindsToStore

func (e *Engine) SyncKindsToStore(ctx context.Context) error

SyncKindsToStore pushes the process catalog to the store.

type EngineConfig

type EngineConfig struct {
	CandidateK          int
	RRFk                int
	Lambda              *float64
	EvidenceN           int
	DefaultLimit        int
	MaxLimit            int
	ExpandInlineMax     int
	SiblingRadius       int
	GraphNeighborK      int
	MaxExpandHops       int // max MaxHops on expand (default 4)
	MaxGraphExpandRPCs  int // cap Neighbors calls per multi-hop expand (default 64)
	MaxResultSetSize    int
	FailOnEmbedderError bool
	FailOnGraphError    bool
	Now                 func() time.Time
}

EngineConfig holds engine-owned ranking knobs (not tool arguments). Lambda nil → default mild decay; explicit 0 disables temporal bias. FailOn* false (default) soft-degrades embedder/graph failures; true surfaces errors.

func DefaultEngineConfig

func DefaultEngineConfig() EngineConfig

DefaultEngineConfig returns mild production defaults.

type EngineOption

type EngineOption func(*Engine)

EngineOption configures NewEngine.

func WithConfig

func WithConfig(cfg EngineConfig) EngineOption

WithConfig sets ranking configuration (normalized by NewEngine).

func WithEmbedder

func WithEmbedder(e QueryEmbedder) EngineOption

WithEmbedder sets the optional query embedder for hybrid search.

func WithExpandRecipes

func WithExpandRecipes(recipes ...ExpandRecipe) EngineOption

WithExpandRecipes registers host-named expand views at construct time. Each recipe is a named ExpandRequest template (ObjectID filled at call time). Invalid recipes (empty name) are ignored here; use RegisterExpandRecipe for errors.

func WithGraph

func WithGraph(g GraphReader) EngineOption

WithGraph sets the optional non-containment graph backend (Helix or MemoryGraph). Writer and object-search capabilities are resolved once here (not re-asserted per call).

func WithKinds

func WithKinds(specs ...KindSpec) EngineOption

WithKinds registers host-defined object kinds at construct time. Invalid specs cause NewEngine to fail. Kinds are host/user-owned for determinism.

func WithObserver

func WithObserver(o Observer) EngineOption

WithObserver sets retrieval observability (default no-op).

func WithReranker

func WithReranker(r Reranker) EngineOption

WithReranker sets an optional post-hydrate reranker for search and find_objects.

type Evidence

type Evidence struct {
	PartID     uuid.UUID      `json:"part_id"`
	Title      string         `json:"title,omitempty"`
	Snippet    string         `json:"snippet,omitempty"`
	Score      float64        `json:"score,omitempty"`
	Position   *int           `json:"position,omitempty"`
	Properties map[string]any `json:"properties,omitempty"`
}

Evidence is a part that justified a parent hit during search.

type ExpandManyRequest

type ExpandManyRequest struct {
	ObjectIDs       []uuid.UUID
	RelationTypes   []string
	MaxHops         int
	Direction       string
	NeighborBudget  int  // max unique neighbors total; default MaxResultSetSize
	WantContainment bool // same semantics as ExpandRequest.WantContainment
}

ExpandManyRequest expands several landing objects with shared hop parameters.

type ExpandManyResult

type ExpandManyResult struct {
	Objects []RichObject `json:"objects"`
}

ExpandManyResult is a flat neighbor list; Relation.SourceID is the landing id.

type ExpandRecipe

type ExpandRecipe struct {
	Name            string
	RelationTypes   []string
	MaxHops         int
	Direction       string
	WantContainment bool
}

ExpandRecipe is a host-registered named ExpandRequest template. ObjectID (and optional Limit / ResultSetStore) are supplied at call time. Register at construct via WithExpandRecipes, or later via RegisterExpandRecipe.

type ExpandRequest

type ExpandRequest struct {
	ObjectID      uuid.UUID
	RelationTypes []string // graph labels; contains/part_of also request containment
	MaxHops       int      // graph depth; default 1; capped by MaxExpandHops
	Direction     string   // out | in | both (default both)
	Limit         int
	// WantContainment forces Postgres containment (children / parent+siblings)
	// alongside any graph labels. When RelationTypes is empty, containment is
	// always applied (default expand). Prefer this flag over smuggling "contains"
	// into RelationTypes when registering recipes or ExpandMany.
	WantContainment bool
}

ExpandRequest is the engine input for expand.

type ExpandResult

type ExpandResult struct {
	Objects     []RichObject `json:"objects"`
	ResultSetID uuid.UUID    `json:"result_set_id,omitempty"`
	HasMore     bool         `json:"has_more"`
	Mode        string       `json:"mode"` // children | neighborhood | graph | mixed
}

ExpandResult is the agent-facing expand payload.

type FieldSpec

type FieldSpec struct {
	Name        string    `json:"name"`
	Type        FieldType `json:"type"`
	Description string    `json:"description,omitempty"`
	Required    bool      `json:"required,omitempty"`
	Operators   []string  `json:"operators,omitempty"` // always eq, or eq+in (see NormalizeKindSpec)
	Examples    []string  `json:"examples,omitempty"`
}

FieldSpec describes one filterable (and later writable) property on a kind.

type FieldType

type FieldType string

FieldType is the closed set of property types for kind schemas.

const (
	FieldTypeString   FieldType = "string"
	FieldTypeNumber   FieldType = "number"
	FieldTypeBoolean  FieldType = "boolean"
	FieldTypeDateTime FieldType = "datetime"
)

type FilterUsage

type FilterUsage struct {
	// Tools list knowledge tools that accept the same property filter keys as filterable_fields.
	Tools []string `json:"tools"`
	// Note is short instruction text for the agent.
	Note string `json:"note"`
}

FilterUsage is agent-facing guidance for structured filters (shared by corpus and entity find).

func DefaultFilterUsage

func DefaultFilterUsage() FilterUsage

DefaultFilterUsage is embedded in every SchemaResult.

type Filters

type Filters map[string]any

Filters narrows retrieval. Keys are field names; values are equality targets (or a list for match-any). Special keys: kind, title, created_after, created_before, updated_after, updated_before. Any other key matches properties.

type FindLinksRequest

type FindLinksRequest struct {
	RelationType string // required edge label (e.g. about, references)
	Query        string
	Limit        int
}

FindLinksRequest searches graph edges by text (Helix edge text index or MemoryGraph).

type FindLinksResult

type FindLinksResult struct {
	Links []LinkHit `json:"links"`
}

FindLinksResult is the agent-facing edge search payload.

type FindObjectsRequest

type FindObjectsRequest struct {
	Query   string
	Kinds   []string // optional host kind names; empty = all kinds
	Filters Filters  // same property keys as search; see schema filterable_fields
	Limit   int
}

FindObjectsRequest is the engine input for entity/object find (graph node search).

type GraphEdgeSearcher

type GraphEdgeSearcher interface {
	SearchEdgesText(ctx context.Context, relationType, query string, limit int) ([]EdgeSearchHit, error)
}

GraphEdgeSearcher finds edges by text (e.g. Helix TextSearchEdges on note).

type GraphNeighbor

type GraphNeighbor struct {
	ObjectID     uuid.UUID
	RelationType string
	Direction    string // "out" | "in"
	Meta         EdgeMeta
}

GraphNeighbor is one edge-adjacent object from the knowledge graph.

type GraphObjectSearcher

type GraphObjectSearcher interface {
	SearchText(ctx context.Context, query string, limit int, namespace *uuid.UUID) ([]ScoredID, error)
	SearchVector(ctx context.Context, embedding []float32, limit int, namespace *uuid.UUID) ([]ScoredID, error)
}

GraphObjectSearcher finds entity nodes by text and/or vector (Helix native indexes or MemoryGraph in-process). Results are ranked best-first; Engine hydrates under Scope. Optional namespace isolates multi-tenant graphs when the backend supports it.

type GraphReader

type GraphReader interface {
	Neighbors(ctx context.Context, objectID uuid.UUID, relationTypes []string, limit int) ([]GraphNeighbor, error)
}

GraphReader traverses non-containment relations. Engine hydrates ids under Scope.

type GraphWriter

type GraphWriter interface {
	GraphReader
	// EnsureObject upserts a graph node for obj.ID (searchable props when available).
	// Must preserve incident edges (update in place, not drop+recreate).
	EnsureObject(ctx context.Context, obj Object) error
	// RemoveObject drops the node (and incident edges) after/with Postgres soft-delete.
	RemoveObject(ctx context.Context, id uuid.UUID) error
	// AddEdge creates a directed edge from→to with optional relationship metadata.
	AddEdge(ctx context.Context, from, to uuid.UUID, relationType string, meta EdgeMeta) error
}

GraphWriter persists graph nodes and non-containment edges (Helix dual-write / MemoryGraph). Embeds GraphReader so a single WithGraph value can satisfy both read and write.

type KindCatalog

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

KindCatalog is the process-local enforcement view of registered kinds. Empty means open mode. Specs are treated as immutable after registration.

func (*KindCatalog) All

func (c *KindCatalog) All() []KindSpec

func (*KindCatalog) Empty

func (c *KindCatalog) Empty() bool

func (*KindCatalog) Freeze

func (c *KindCatalog) Freeze()

func (*KindCatalog) Get

func (c *KindCatalog) Get(kind string) (KindSpec, bool)

func (*KindCatalog) Names

func (c *KindCatalog) Names() []string

type KindReader

type KindReader interface {
	GetKind(ctx context.Context, kind string) (ObjectKind, error)
	ListKinds(ctx context.Context) ([]ObjectKind, error)
}

KindReader reads durable kind schema rows (schema fallback, LoadKindsFromStore).

type KindRegistry

type KindRegistry interface {
	KindReader
	KindWriter
}

KindRegistry is KindReader + KindWriter for durable kind schemas.

type KindSpec

type KindSpec struct {
	Kind        string
	Description string
	IsParent    bool
	IsPart      bool
	Fields      []FieldSpec
}

KindSpec is the host-facing definition of a knowledge object kind.

func KindSpecFromObjectKind

func KindSpecFromObjectKind(k ObjectKind) (KindSpec, error)

KindSpecFromObjectKind parses a registry row into a KindSpec.

func NormalizeKindSpec

func NormalizeKindSpec(spec KindSpec) (KindSpec, error)

NormalizeKindSpec validates a kind and fills default operators (eq / eq+in).

func (KindSpec) Field

func (s KindSpec) Field(name string) (FieldSpec, bool)

Field returns the named field when present.

type KindWriter

type KindWriter interface {
	PutKind(ctx context.Context, k ObjectKind) error
}

KindWriter upserts durable kind schema rows (ApplyKinds / PersistKinds). Not required for open-mode or process-only catalogs. Together with KindReader this is KindRegistry — implement both for a custom durable backend.

type LinkHit

type LinkHit struct {
	From         RichObject `json:"from"`
	To           RichObject `json:"to"`
	RelationType string     `json:"relation_type"`
	Meta         EdgeMeta   `json:"meta,omitempty"`
	Score        float64    `json:"score,omitempty"`
}

LinkHit is one edge land result with hydrated endpoints when visible under scope.

type MemoryGraph

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

MemoryGraph is an in-process GraphReader/GraphWriter/GraphObjectSearcher (tests / offline). Edges are a single map; directions are derived on Neighbors.

func NewMemoryGraph

func NewMemoryGraph() *MemoryGraph

NewMemoryGraph returns an empty graph.

func (*MemoryGraph) AddEdge

func (g *MemoryGraph) AddEdge(ctx context.Context, from, to uuid.UUID, relationType string, meta EdgeMeta) error

AddEdge implements GraphWriter. Upserts the edge for (from, to, relationType).

func (*MemoryGraph) EnsureObject

func (g *MemoryGraph) EnsureObject(ctx context.Context, obj Object) error

EnsureObject implements GraphWriter and stores searchable props for FindObjects. Replaces any prior node for the same id (live update; edges are independent).

func (*MemoryGraph) Neighbors

func (g *MemoryGraph) Neighbors(ctx context.Context, objectID uuid.UUID, relationTypes []string, limit int) ([]GraphNeighbor, error)

Neighbors implements GraphReader (both directions, deduped by object id). Single scan of the edge map, then ordered by request relation list / out-before-in.

func (*MemoryGraph) RemoveObject

func (g *MemoryGraph) RemoveObject(ctx context.Context, id uuid.UUID) error

RemoveObject implements GraphWriter.

func (*MemoryGraph) SearchEdgesText

func (g *MemoryGraph) SearchEdgesText(ctx context.Context, relationType, query string, limit int) ([]EdgeSearchHit, error)

SearchEdgesText implements GraphEdgeSearcher (substring match on edge note).

func (*MemoryGraph) SearchText

func (g *MemoryGraph) SearchText(ctx context.Context, query string, limit int, namespace *uuid.UUID) ([]ScoredID, error)

SearchText implements GraphObjectSearcher (case-fold substring on entity index text).

func (*MemoryGraph) SearchVector

func (g *MemoryGraph) SearchVector(ctx context.Context, embedding []float32, limit int, namespace *uuid.UUID) ([]ScoredID, error)

SearchVector implements GraphObjectSearcher via cosine similarity on stored embeddings.

type MemoryStore

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

MemoryStore is an in-process Store (tests, fixtures, and ObjectWriter for Engine.Put).

func NewMemoryStore

func NewMemoryStore() *MemoryStore

NewMemoryStore returns an empty memory-backed store.

func (*MemoryStore) Get

func (s *MemoryStore) Get(_ context.Context, scope Scope, id uuid.UUID) (Object, error)

Get implements ObjectReader.

func (*MemoryStore) GetKind

func (s *MemoryStore) GetKind(_ context.Context, kind string) (ObjectKind, error)

GetKind implements KindReader.

func (*MemoryStore) GetMany

func (s *MemoryStore) GetMany(_ context.Context, scope Scope, ids []uuid.UUID) ([]Object, error)

GetMany implements ObjectReader.

func (*MemoryStore) ListChildren

func (s *MemoryStore) ListChildren(_ context.Context, scope Scope, parentID uuid.UUID) ([]Object, error)

ListChildren implements ObjectReader.

func (*MemoryStore) ListKinds

func (s *MemoryStore) ListKinds(_ context.Context) ([]ObjectKind, error)

ListKinds implements KindReader.

func (*MemoryStore) Put

func (s *MemoryStore) Put(_ context.Context, obj Object) error

Put implements ObjectWriter. Soft-deleted rows may be stored; Get hides them. Clones maps/slices so callers cannot mutate the store through shared references.

func (*MemoryStore) PutKind

func (s *MemoryStore) PutKind(_ context.Context, k ObjectKind) error

PutKind implements KindWriter.

func (*MemoryStore) SearchLexical

func (s *MemoryStore) SearchLexical(_ context.Context, scope Scope, query string, filters Filters, k int) ([]ScoredID, error)

SearchLexical implements PartSearcher with a deterministic TF×IDF-style score. Only content-bearing parts (parent_id set) are candidates.

func (*MemoryStore) SearchTrigram

func (s *MemoryStore) SearchTrigram(_ context.Context, scope Scope, query string, filters Filters, k int) ([]ScoredID, error)

SearchTrigram implements PartSearcher with case-fold substring / trigram overlap.

func (*MemoryStore) SearchVector

func (s *MemoryStore) SearchVector(_ context.Context, scope Scope, embedding []float32, filters Filters, k int) ([]ScoredID, error)

SearchVector implements PartSearcher via cosine similarity on Object.Embedding.

func (*MemoryStore) SoftDelete

func (s *MemoryStore) SoftDelete(_ context.Context, scope Scope, id uuid.UUID) error

SoftDelete implements ObjectWriter.

type Object

type Object struct {
	ID          uuid.UUID
	Kind        string
	Title       string
	Summary     string
	Properties  map[string]any
	Content     string
	ContentType string
	ParentID    *uuid.UUID
	Position    *int
	// Embedding is optional dense vector for hybrid search fixtures / stores.
	Embedding   []float32
	NamespaceID uuid.UUID
	CreatedAt   time.Time
	UpdatedAt   time.Time
	DeletedAt   *time.Time
}

Object is one row from the generic objects store (parent or part).

func (Object) IsPart

func (o Object) IsPart() bool

IsPart reports whether the object has a parent containment link.

type ObjectKind

type ObjectKind struct {
	Kind             string
	Description      string
	IsPart           bool
	IsParent         bool
	FilterableFields json.RawMessage // JSON array from object_kinds.filterable_fields
}

ObjectKind documents a free-form kind for schema() discovery.

func ObjectKindFromSpec

func ObjectKindFromSpec(spec KindSpec) (ObjectKind, error)

ObjectKindFromSpec maps a typed kind into the durable ObjectKind row shape.

type ObjectKindInfo

type ObjectKindInfo struct {
	Kind             string          `json:"kind"`
	Description      string          `json:"description,omitempty"`
	IsPart           bool            `json:"is_part"`
	IsParent         bool            `json:"is_parent"`
	FilterableFields json.RawMessage `json:"filterable_fields,omitempty"`
}

ObjectKindInfo is the JSON form of ObjectKind for agents.

func KindInfoFrom

func KindInfoFrom(k ObjectKind) ObjectKindInfo

KindInfoFrom maps a registry row to the agent-facing shape.

func KindInfoFromSpec

func KindInfoFromSpec(spec KindSpec) ObjectKindInfo

KindInfoFromSpec builds the agent-facing schema payload for one kind.

type ObjectReader

type ObjectReader interface {
	Get(ctx context.Context, scope Scope, id uuid.UUID) (Object, error)
	// GetMany returns objects for ids in the same order. Missing/out-of-scope ids are omitted.
	GetMany(ctx context.Context, scope Scope, ids []uuid.UUID) ([]Object, error)
	// ListChildren returns parts ordered by position.
	ListChildren(ctx context.Context, scope Scope, parentID uuid.UUID) ([]Object, error)
}

ObjectReader is the read port for knowledge objects.

type ObjectWriter

type ObjectWriter interface {
	Put(ctx context.Context, obj Object) error
	SoftDelete(ctx context.Context, scope Scope, id uuid.UUID) error
}

ObjectWriter persists knowledge objects (Engine.Put / SoftDelete). PostgresStore and MemoryStore implement it. Custom backends implement for non-Postgres deployments. Not required for read-only engines.

type Observer

type Observer interface {
	StartOp(ctx context.Context, op Op) (context.Context, OpSpan)
}

Observer records retrieval ops. Nil/noop is fine for tests and offline hosts.

type Op

type Op string

Op is a closed enum for retrieval operations (matches telemetry label values). Keep in sync with telemetry.BrainOp* closed enum.

const (
	OpSearch      Op = "search"
	OpFindExact   Op = "find_exact"
	OpFindObjects Op = "find_objects"
	OpFindLinks   Op = "find_links"
	OpContinue    Op = "continue"
	OpExpand      Op = "expand"
	OpExpandMany  Op = "expand_many"
)

type OpSpan

type OpSpan interface {
	End(hits int, degrade DegradeMode, err error)
}

OpSpan ends one retrieval operation. Implementations must be safe for a single End call.

type PartSearcher

type PartSearcher interface {
	SearchLexical(ctx context.Context, scope Scope, query string, filters Filters, k int) ([]ScoredID, error)
	SearchVector(ctx context.Context, scope Scope, embedding []float32, filters Filters, k int) ([]ScoredID, error)
	SearchTrigram(ctx context.Context, scope Scope, query string, filters Filters, k int) ([]ScoredID, error)
}

PartSearcher is the candidate retrieval port for hybrid / exact search.

type PgxDB

type PgxDB interface {
	Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error)
	QueryRow(ctx context.Context, sql string, args ...any) pgx.Row
	Exec(ctx context.Context, sql string, args ...any) (pgconn.CommandTag, error)
}

PgxDB is satisfied by *pgx.Conn and *pgxpool.Pool.

type PostgresStore

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

PostgresStore implements Store against the objects / object_kinds schema.

func NewPostgresStore

func NewPostgresStore(db PgxDB) (*PostgresStore, error)

NewPostgresStore wraps a pgx pool or connection.

func (*PostgresStore) Get

func (s *PostgresStore) Get(ctx context.Context, scope Scope, id uuid.UUID) (Object, error)

Get implements ObjectReader.

func (*PostgresStore) GetKind

func (s *PostgresStore) GetKind(ctx context.Context, kind string) (ObjectKind, error)

GetKind implements KindReader.

func (*PostgresStore) GetMany

func (s *PostgresStore) GetMany(ctx context.Context, scope Scope, ids []uuid.UUID) ([]Object, error)

GetMany implements ObjectReader.

func (*PostgresStore) ListChildren

func (s *PostgresStore) ListChildren(ctx context.Context, scope Scope, parentID uuid.UUID) ([]Object, error)

ListChildren implements ObjectReader.

func (*PostgresStore) ListKinds

func (s *PostgresStore) ListKinds(ctx context.Context) ([]ObjectKind, error)

ListKinds implements KindReader.

func (*PostgresStore) Put

func (s *PostgresStore) Put(ctx context.Context, obj Object) error

Put implements ObjectWriter (full-column upsert; clears soft-delete on revive).

func (*PostgresStore) PutKind

func (s *PostgresStore) PutKind(ctx context.Context, k ObjectKind) error

PutKind implements KindWriter.

func (*PostgresStore) SearchLexical

func (s *PostgresStore) SearchLexical(ctx context.Context, scope Scope, query string, filters Filters, k int) ([]ScoredID, error)

SearchLexical implements PartSearcher using pg_textsearch BM25.

func (*PostgresStore) SearchTrigram

func (s *PostgresStore) SearchTrigram(ctx context.Context, scope Scope, query string, filters Filters, k int) ([]ScoredID, error)

SearchTrigram implements PartSearcher using pg_trgm similarity.

func (*PostgresStore) SearchVector

func (s *PostgresStore) SearchVector(ctx context.Context, scope Scope, embedding []float32, filters Filters, k int) ([]ScoredID, error)

SearchVector implements PartSearcher using pgvector cosine distance.

func (*PostgresStore) SoftDelete

func (s *PostgresStore) SoftDelete(ctx context.Context, scope Scope, id uuid.UUID) error

SoftDelete implements ObjectWriter.

type QueryEmbedder

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

QueryEmbedder embeds a query string for the dense search channel. When nil on the Engine, search runs lexical-only.

type Relation

type Relation struct {
	Type      string     `json:"type"`
	Direction string     `json:"direction,omitempty"` // out | in
	Depth     int        `json:"depth,omitempty"`     // hops from expand seed
	SourceID  *uuid.UUID `json:"source_id,omitempty"` // ExpandMany landing id
	EdgeMeta
}

Relation describes a non-containment hop used to reach a neighbor on expand. EdgeMeta fields are embedded so agent JSON stays flat (note, status, role, …).

func RelationFromNeighbor

func RelationFromNeighbor(n GraphNeighbor) Relation

RelationFromNeighbor maps a graph hop to the agent-facing Relation payload.

type Reranker

type Reranker interface {
	Rerank(ctx context.Context, objects []RichObject) ([]RichObject, error)
}

Reranker optionally reorders/filters hydrated rich objects after search or find_objects. Host-owned product scoring; default nil leaves engine ranking unchanged.

type ResultSet

type ResultSet struct {
	ID        uuid.UUID   `json:"id"`
	ObjectIDs []uuid.UUID `json:"object_ids"`
	// Relations carries expand hop metadata keyed by object id so continue
	// re-attaches relation fields on later pages (JSON keys are UUID strings).
	Relations map[uuid.UUID]Relation `json:"relations,omitempty"`
	Offset    int                    `json:"offset"`
	CreatedAt time.Time              `json:"created_at"`
}

ResultSet is a ranked-list snapshot for deterministic continue() pagination.

type ResultSetStore

type ResultSetStore interface {
	Put(ctx context.Context, set ResultSet) error
	Get(ctx context.Context, id uuid.UUID) (ResultSet, error)
}

ResultSetStore holds ResultSet snapshots for continue(). SearchContext is the production implementation (single active set). Offset is advanced by Put of the same id with an updated Offset field.

type RichObject

type RichObject struct {
	ID          uuid.UUID      `json:"id"`
	Kind        string         `json:"kind"`
	Title       string         `json:"title,omitempty"`
	Summary     string         `json:"summary,omitempty"`
	Score       *float64       `json:"score,omitempty"`
	Properties  map[string]any `json:"properties,omitempty"`
	Content     string         `json:"content,omitempty"` // set by read; omitted on search hits
	ContentType string         `json:"content_type,omitempty"`
	ParentID    *uuid.UUID     `json:"parent_id,omitempty"`
	Position    *int           `json:"position,omitempty"`
	Evidence    []Evidence     `json:"evidence,omitempty"`
	// Relation is set on expand graph neighbors (how this object was reached).
	Relation  *Relation `json:"relation,omitempty"`
	CreatedAt time.Time `json:"created_at,omitempty"`
	UpdatedAt time.Time `json:"updated_at,omitempty"`
}

RichObject is the agent-facing object reference (never a bare id).

func RichFromObject

func RichFromObject(o Object, includeContent bool) RichObject

RichFromObject maps a stored object to a rich reference.

type SchemaResult

type SchemaResult struct {
	Kinds []ObjectKindInfo `json:"kinds"`
	// FilterUsage tells agents which tools accept filterable_fields and how.
	FilterUsage FilterUsage `json:"filter_usage"`
}

SchemaResult is the payload for the schema tool.

type Scope

type Scope struct {
	Namespace *uuid.UUID
}

Scope is optional retrieval isolation for Engine methods. When Namespace is non-nil, results are limited to that namespace.

type ScoredID

type ScoredID struct {
	ID        uuid.UUID
	Score     float64
	UpdatedAt time.Time
	ParentID  *uuid.UUID
	Title     string
	Content   string
	Position  *int
}

ScoredID is a candidate from a retrieval channel before fusion.

type SearchContext

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

SearchContext is the single retrieval session surface for one agent thread: host namespace isolation + the active ResultSet for continue.

func NewSearchContext

func NewSearchContext() *SearchContext

NewSearchContext returns an empty search context.

func (*SearchContext) ClearNamespace

func (c *SearchContext) ClearNamespace()

ClearNamespace clears retrieval isolation.

func (*SearchContext) Export

func (c *SearchContext) Export() ([]byte, error)

Export serializes namespace + active ResultSet for session checkpoints.

func (*SearchContext) Get

Get implements ResultSetStore.

func (*SearchContext) Namespace

func (c *SearchContext) Namespace() (uuid.UUID, bool)

Namespace returns the host-set search namespace, if any.

func (*SearchContext) Put

func (c *SearchContext) Put(_ context.Context, set ResultSet) error

Put implements ResultSetStore: stores set as the sole active ResultSet.

func (*SearchContext) Restore

func (c *SearchContext) Restore(raw []byte) error

Restore loads a prior Export. Empty/nil clears the context. Accepts the current envelope and the legacy ResultSet-only JSON.

func (*SearchContext) Scope

func (c *SearchContext) Scope() Scope

Scope returns the retrieval Scope for engine calls.

func (*SearchContext) SetNamespace

func (c *SearchContext) SetNamespace(id uuid.UUID)

SetNamespace sets host retrieval isolation.

type SearchPage

type SearchPage struct {
	ResultSetID uuid.UUID    `json:"result_set_id"`
	HasMore     bool         `json:"has_more"`
	Objects     []RichObject `json:"objects"`
}

SearchPage is one page of ranked rich objects plus ResultSet identity.

type SearchRequest

type SearchRequest struct {
	Query   string
	Filters Filters
	Limit   int
	// ScopeIDs, when non-empty, keeps only candidates whose id or parent_id is in the set.
	// Use after expand/find_objects to restrict corpus search to a deal-local neighborhood.
	ScopeIDs []uuid.UUID
}

SearchRequest is the engine input for search and find_exact.

type Store

type Store interface {
	ObjectReader
	KindReader
	PartSearcher
}

Store is the full read + search surface required by Engine.

type WriteKinds

type WriteKinds struct {
	Discovery string // save_discovery
	Fact      string // save_fact
	Memory    string // save_memory
}

WriteKinds maps agent save_* tools to host kind names. Empty fields omit that tool. When the process catalog is non-empty, named kinds must already be registered (ApplyKinds / WithKinds).

Directories

Path Synopsis
Package helixgraph adapts HelixDB to brain.GraphReader / GraphWriter / searchers.
Package helixgraph adapts HelixDB to brain.GraphReader / GraphWriter / searchers.

Jump to

Keyboard shortcuts

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