Documentation
¶
Overview ¶
Package service implements the MemoryService — the transport-neutral input port (DESIGN.md §9.1). It composes the Embedder, VectorIndex, and Graph ports to deliver Write / Recall / Forget / Stats operations with lazy decay, Hebbian reinforcement, and weight-aware retrieval.
The service is stateless across calls (DESIGN.md §0 #3): no caches, no in-memory accumulators. All state lives in the database.
Index ¶
- type Config
- type ErrTenantInvalid
- type MemoryService
- type Service
- func (s *Service) Demote(ctx context.Context, req types.DemoteRequest) (types.DemoteResult, error)
- func (s *Service) Forget(ctx context.Context, req types.ForgetRequest) (types.ForgetResult, error)
- func (s *Service) Mark(ctx context.Context, req types.MarkRequest) (types.MarkResult, error)
- func (s *Service) Recall(ctx context.Context, req types.RecallRequest) (types.RecallResult, error)
- func (s *Service) Reinforce(ctx context.Context, req types.ReinforceRequest) (types.ReinforceResult, error)
- func (s *Service) Schema() *TenantSchema
- func (s *Service) Stats(ctx context.Context, req types.StatsRequest) (types.StatsResult, error)
- func (s *Service) Write(ctx context.Context, req types.WriteRequest) (types.WriteResult, error)
- type TenantKey
- type TenantSchema
- type TenantStats
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
// Chunking
ChunkWindowSize int
ChunkStride int
// Retrieval defaults
DefaultK int
DefaultHops int
DefaultOversample int
SimAlpha float64
WeightBeta float64
DepthPenaltyFactor float64 // multiplier per hop, default 2.0 → /2 per depth
// Decay (per-second)
NodeLambda float64
EdgeStructuralLambda float64
EdgeCoRetrievalLambda float64
EdgeCoTraversalLambda float64
// Reinforcement
NodeDelta float64
EdgeCoRetrievalBase float64
EdgeCoTraversalMultiplier float64
EdgeStructuralWeight float64
EdgeStructuralTemporalWeight float64
// Pruning
EdgeFloor float64
NodeFloor float64
WeightCap float64
// Structural temporal recency
StructuralRecentN int
StructuralRecentDelta time.Duration
// Explicit-bump throttling (Reinforce/Demote/Mark only).
// RefractoryPeriod blocks repeated explicit bumps on the same node
// within the window — the call still updates LastTouched and
// AccessCount but applies no delta. Implicit Recall co-retrieval
// bumps are NOT refractory-gated. Set to 0 to disable.
RefractoryPeriod time.Duration
// LogDampening makes positive bumps approach WeightCap asymptotically
// instead of hitting a hard wall: effective_delta = delta * (1 - w/cap).
// Demote (negative delta) is unaffected. Implicit Recall co-retrieval
// bumps are NOT log-dampened.
LogDampening bool
// MarkMaxNodes caps the number of recent nodes Mark walks per call.
MarkMaxNodes int
}
Config bundles the tunables for chunking, retrieval, decay, and reinforcement. See DESIGN.md §12.
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns the documented defaults from DESIGN.md §12.
type ErrTenantInvalid ¶
type ErrTenantInvalid struct {
Code string // tenant_unknown_key | tenant_enum_mismatch | ...
Field string // empty when the error isn't field-specific
Got string // the offending value (when applicable)
Message string
}
ErrTenantInvalid is the typed error returned when a tuple is rejected. It carries enough structured detail that MCP handlers can surface a corrective payload back to the LLM.
func (*ErrTenantInvalid) Error ¶
func (e *ErrTenantInvalid) Error() string
func (*ErrTenantInvalid) Payload ¶
func (e *ErrTenantInvalid) Payload(expected *jsonschema.Schema) ([]byte, error)
Payload returns a JSON-serializable detail object suitable for inclusion in an MCP CallToolResult error payload alongside the expected JSON Schema.
type MemoryService ¶
type MemoryService interface {
Write(ctx context.Context, req types.WriteRequest) (types.WriteResult, error)
Recall(ctx context.Context, req types.RecallRequest) (types.RecallResult, error)
Forget(ctx context.Context, req types.ForgetRequest) (types.ForgetResult, error)
Stats(ctx context.Context, req types.StatsRequest) (types.StatsResult, error)
Reinforce(ctx context.Context, req types.ReinforceRequest) (types.ReinforceResult, error)
Demote(ctx context.Context, req types.DemoteRequest) (types.DemoteResult, error)
Mark(ctx context.Context, req types.MarkRequest) (types.MarkResult, error)
}
MemoryService is the transport-neutral application port. All transport adapters (MCP, gRPC, HTTP) call into this interface.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service is the concrete MemoryService implementation.
func New ¶
func New(g graph.Graph, v vectorindex.VectorIndex, e embed.Embedder, c clock.Clock, cfg Config, schema *TenantSchema) (*Service, error)
New constructs a Service. All four ports are required. The schema is optional — pass nil to accept any tenant tuple (today's behavior).
func (*Service) Demote ¶
func (s *Service) Demote(ctx context.Context, req types.DemoteRequest) (types.DemoteResult, error)
Demote applies an explicit caller-driven inhibition to a single node. Used to record "this hit was misleading or wrong."
Bump magnitude is -NodeDelta. The post-update weight is clamped at NodeFloor — Demote never deletes the node. The same refractory window applies (a single retrieval can't be repeatedly demoted within the window). Demote bypasses LogDampening so its effect is not damped near WeightCap. See DESIGN.md §8.2.
func (*Service) Forget ¶
func (s *Service) Forget(ctx context.Context, req types.ForgetRequest) (types.ForgetResult, error)
Forget removes either a single message (and all derived nodes/vectors/ HNSW records/edges) by ID, or all messages created strictly before a given timestamp.
func (*Service) Mark ¶
func (s *Service) Mark(ctx context.Context, req types.MarkRequest) (types.MarkResult, error)
Mark retroactively boosts every node in the tenant whose CreatedAt falls within [Since, now]. Per-node delta is `Strength * NodeDelta`, scaled linearly by recency: most recently created nodes within the window get the full bump; nodes near the edge of the window get a fractional bump.
Each per-node application goes through the same refractory and log-dampening path as Reinforce.
See DESIGN.md §8.2 (synaptic-tag capture analog).
func (*Service) Recall ¶
func (s *Service) Recall(ctx context.Context, req types.RecallRequest) (types.RecallResult, error)
Recall implements the full retrieval pipeline (DESIGN.md §6).
Phases:
- Embed query, normalize.
- Vector search with oversample.
- For each candidate: lazy-decay-then-reinforce node weight.
- Re-rank by sim^α × weight^β; take top-K → seeds.
- Hebbian co-retrieval: reinforce edges between every seed pair.
- BFS expansion via memory edges, with edge-floor pruning + depth penalty.
- Co-traversal reinforcement on edges that delivered nodes into the final returned result set.
- Build provenance hits with score breakdowns + path.
func (*Service) Reinforce ¶
func (s *Service) Reinforce(ctx context.Context, req types.ReinforceRequest) (types.ReinforceResult, error)
Reinforce applies an explicit caller-driven LTP bump to a single node. Used to record "this hit was useful in my answer."
Bump magnitude is the configured NodeDelta. Repeated bumps approach WeightCap asymptotically (LogDampening). A per-node refractory window (RefractoryPeriod) prevents double-counting when a single retrieval is reinforced multiple times in quick succession; the call still updates LastTouched and AccessCount so the system knows the access happened. See DESIGN.md §8.2.
func (*Service) Schema ¶
func (s *Service) Schema() *TenantSchema
Schema returns the active TenantSchema, or nil if none is configured. Used by transport adapters to render the schema into wire-level input schemas and to surface corrective errors back to clients.
func (*Service) Stats ¶
func (s *Service) Stats(ctx context.Context, req types.StatsRequest) (types.StatsResult, error)
Stats aggregates counts for one tenant or all tenants.
func (*Service) Write ¶
func (s *Service) Write(ctx context.Context, req types.WriteRequest) (types.WriteResult, error)
Write ingests a message: chunk it, embed each chunk, persist nodes + vectors + HNSW records, and create structural memory edges.
Order matters:
- Embed BEFORE any storage write (DESIGN.md §5).
- Persist message + nodes + vectors + HNSW.
- Create structural edges (sequential within message + recent within tenant).
type TenantKey ¶
type TenantKey struct {
Description string
Pattern *regexp.Regexp // nil if no pattern
PatternRaw string
Enum []string
Required bool
}
TenantKey is one declared tuple key.
type TenantSchema ¶
type TenantSchema struct {
// contains filtered or unexported fields
}
TenantSchema validates a tenant tuple against a configured shape and renders the equivalent JSON Schema for MCP InputSchema patching.
The schema only validates incoming tuples — it does NOT transform them. TenantID is derived from the validated tuple as today (see DESIGN.md §3). Stored memories are not migrated when the schema changes; data written under one schema remains addressable if the schema is later changed back to one that accepts the original tuple shape.
func NewTenantSchemaFromConfig ¶
func NewTenantSchemaFromConfig(cfg config.TenantSchemaConfig) (*TenantSchema, error)
NewTenantSchemaFromConfig compiles a TenantSchema from config. Returns (nil, nil) when no schema is configured — callers treat nil as "accept any tuple."
func (*TenantSchema) Description ¶
func (s *TenantSchema) Description() string
Description returns the top-level description string for use in the rendered JSON Schema and in MCP tool descriptions.
func (*TenantSchema) JSONSchema ¶
func (s *TenantSchema) JSONSchema() *jsonschema.Schema
JSONSchema renders the schema as a *jsonschema.Schema suitable for patching into an MCP tool's InputSchema (the `tenant` property).