Documentation
¶
Index ¶
- Constants
- func CopyMetadata(metadata map[string]string) map[string]string
- func DedupeLinkIds(ids []string) []string
- func LinksToProto(links []Link) []*contract.Link
- func MarshalMetadata(metadata map[string]string) (any, error)
- func MetadataSerialisedLen(metadata map[string]string) int
- func MetadataToTerms(metadata map[string]string) []string
- func ParseMetadataFilters(pairs []string) (map[string]string, error)
- func UnmarshalMetadata(src any) (map[string]string, error)
- func ValidateLinks(links []Link, owner string, kind string) error
- func ValidateMetadata(metadata map[string]string, kind string) error
- type Event
- type Link
- type LinkDirection
- type LinkEdge
- type Memory
Constants ¶
const ( MaxLinks = 128 MaxLinkSignificance = 1_000_000 )
Link validation bounds. Links are a client-supplied input into the decay maths: an item's summed link significance is damped and weighted into its value, and for a memory its event's is too. An uncapped count would let one item's links dominate every scan that reads them, and an uncapped per-link significance would push the damped contribution up without limit, so both are bounded here. The damping (see hippocampus.linkContribution) is what stops a merely large number of links making an item unforgettable; these bounds are what stop the graph itself growing without limit.
The same two bounds apply to memory links and event links: they are one mechanism, and an asymmetry between them would only ever be a trap.
const ( MaxMetadataKeys = 32 MaxMetadataKeyLength = 64 MaxMetadataValueLength = 512 MaxMetadataBytes = 4096 )
Metadata validation bounds. Metadata is the multi-dimensional classification that group - one freeform 128-character label - could only ever carry one of, so it is client-supplied, opaque to the server, and stored per item. That combination is exactly what needs bounding: unbounded metadata is a body by another name. It would escape memory.limit.sizeBytes, distort UsedBytes and therefore capacity pressure, and become an eviction-accounting problem - which is why the serialised length is counted toward the body limit (see Memory.ValidateInsert) and toward the store's byte accounting rather than absorbed into db.evictionRowOverheadBytes' flat allowance.
These are constants rather than configuration, like the link bounds above them: they exist to keep a client from turning the store into something it cannot account for, and a deployment that could raise them could defeat that. MaxMetadataBytes is the binding constraint - the per-key and per-value caps only stop one pathological entry consuming the whole budget - and is deliberately far above db.evictionRowOverheadBytes, which is why it must be measured rather than absorbed.
Variables ¶
This section is empty.
Functions ¶
func CopyMetadata ¶ added in v0.26.0
CopyMetadata returns a copy of a metadata map, or nil for an empty one. Conversions to and from the proto messages copy rather than alias, because the proto map belongs to the caller and a stored item aliasing it would change under a caller that reused the message.
func DedupeLinkIds ¶ added in v0.26.0
DedupeLinkIds returns ids with duplicates removed, preserving first-occurrence order. Unlinking the same target twice is harmless but pointless work, and the deduplicated list is what the aggregate recalculation should be handed.
func LinksToProto ¶ added in v0.26.0
func MarshalMetadata ¶ added in v0.26.0
MarshalMetadata encodes metadata for storage, returning nil for an empty map.
nil - stored as SQL NULL - rather than "" or "{}" is load-bearing, not tidiness. SQLite's json_extract raises "malformed JSON" on an empty string, so a column defaulting to the empty string the way group_name does would make the FIRST metadata-filtered query fail against every row written before the column existed. NULL is what json_extract, ->> and JSON_EXTRACT all return nothing for, so a row with no metadata is uniformly excluded by a key predicate on all three drivers.
func MetadataSerialisedLen ¶ added in v0.26.0
MetadataSerialisedLen is the byte length metadata occupies once stored, and is what both the memory.limit.sizeBytes check and the transfer batch sizer measure. It is the length of exactly what MarshalMetadata produces, so the figure a write is validated against is the figure the store will hold.
func MetadataToTerms ¶ added in v0.26.0
MetadataToTerms renders metadata as the sorted "key=value" strings the search index holds.
The index stores metadata as a keyword array in exactly this shape rather than as an object, because the keys are client-supplied: an object mapping would mint a new index field per distinct key and a client generating keys per request would exhaust the cluster's field limit. A keyword array cannot, term-filters exactly, ANDs naturally as several term filters, and is byte-identical to the filter's wire form - so a filter needs no conversion to become a term.
func ParseMetadataFilters ¶ added in v0.26.0
ParseMetadataFilters turns the repeated "key=value" filter strings into the map the store filters on, splitting each on the first separator. Every pair must match for an item to be returned.
The key is validated with the same rule a written key is - see validMetadataKey for why that matters here and not only on the write path. The value is not: it is bound as a query parameter and compared for equality, so it needs no charset, and a value that could never have been stored simply matches nothing.
func UnmarshalMetadata ¶ added in v0.26.0
UnmarshalMetadata decodes a stored metadata column. A NULL or empty column reads as nil rather than an empty map, so a round trip through the store leaves an item exactly as it was written.
src is whatever the driver scanned: []byte on SQLite and MySQL, and either []byte or string on Postgres depending on how the JSONB column comes back.
func ValidateLinks ¶ added in v0.26.0
ValidateLinks checks a set of links declared by one item. owner is the id of the item declaring them, so a self-link can be rejected: an item linked to itself would count its own significance twice in its own value, and means nothing as an association. kind names the item type for the error message ("memory"/"event").
It does NOT check that the targets exist - that needs the store, so the RPC layer does it and returns NotFound. Duplicate targets are rejected rather than silently collapsed: the store upserts per pair, so a duplicate in one request would otherwise mean the last one silently wins.
func ValidateMetadata ¶ added in v0.26.0
ValidateMetadata checks one item's metadata against the bounds above. kind names the item type for the error message ("memory"/"event"), matching ValidateLinks.
It does NOT check the total against memory.limit.sizeBytes - that limit covers the body too, so the combined check belongs with the body, in Memory.ValidateInsert.
Types ¶
type Event ¶
type Event struct {
Id string // if not provided, will be a uuid
TimeStart int64 // time.Time.Now().UnixNano()
TimeEnd int64 // time.Time.Now().UnixNano()
Significance int32
Name string // limited to 256 characters
Description string // limited to 1024 characters
Links []Link
LinkSignificance int64 // Sum of the significances of this event's links, both directions. This is a calculated value, maintained by the store.
MemoriesConsolidated bool // if true, some memories related to this event have been consolidated (deleted)
Group string // optional grouping/context label; limited to 128 characters
// Metadata, ClearMetadata and ClearGroup mirror the same three fields on Memory exactly - one
// mechanism, same bounds (types/metadata.go), same write-only clearing semantics. An asymmetry
// between the two would only ever be a trap.
Metadata map[string]string
ClearMetadata bool
ClearGroup bool
// SignificanceLevelID is the resolved significance registry level id, set by the RPC layer via
// db.ResolveSignificanceLevel before a create/update reaches the store. nil means unranked on a
// create, or "leave significance unchanged" on a partial update. It is internal - never part of
// the proto conversion.
SignificanceLevelID *int64
}
func EventFromProto ¶
func (*Event) SetDefaults ¶
func (e *Event) SetDefaults()
type Link ¶ added in v0.26.0
Link is one directed edge from the item carrying it to the item it names. The near end is implied by whatever holds the link, so only the far end is recorded here; LinkEdge is the read form that says which way a link points.
func LinksFromProto ¶ added in v0.26.0
type LinkDirection ¶ added in v0.26.0
type LinkDirection int
LinkDirection mirrors contract.LinkDirection without the db package having to depend on the contract package, and is what a read uses to say which way a returned link points.
const ( LinkDirectionBoth LinkDirection = iota LinkDirectionOutbound LinkDirectionInbound )
func LinkDirectionFromProto ¶ added in v0.26.0
func LinkDirectionFromProto(d contract.LinkDirection) LinkDirection
LinkDirectionFromProto resolves a requested direction, defaulting UNSPECIFIED to BOTH: the graph is stored directed but valued symmetrically, so both directions are what a caller asking about an item's links almost always means.
func (LinkDirection) ToProto ¶ added in v0.26.0
func (d LinkDirection) ToProto() contract.LinkDirection
type LinkEdge ¶ added in v0.26.0
type LinkEdge struct {
Id string
Significance int32
Direction LinkDirection
Created int64
}
LinkEdge is a stored link as read back: the far end, the weight, and which way it points relative to the item that was asked about.
type Memory ¶
type Memory struct {
Id string // if not provided, will be a uuid
TimeStamp int64 // time.Time.Now().UnixNano()
Significance int32 // the level's rank on read; on write the requested absolute value (0 = unranked)
EventId string
Body string // optionally limited "memory.limit.sizeBytes"
IsBinary bool
TimeRecalled int64 // time of the most recent recall; zero if never recalled
RecallCount int32 // number of times the memory has been recalled
IsSummary bool // set on the memory created by ReplaceMemoriesWithSummary
Group string // optional grouping/context label; limited to 128 characters
// Metadata is the multi-dimensional classification group could only carry one dimension of;
// see types/metadata.go for the bounds and why they are constants. ClearMetadata and ClearGroup
// are write-only update instructions, never populated on a read: every other updatable field
// reads its zero value as "leave unchanged", so without them neither field could be unset once
// set.
Metadata map[string]string
ClearMetadata bool
ClearGroup bool
// Links are this memory's associative links. On a write they are the links to create (targets
// must already exist); on a read they are populated only when the caller asked for them.
// LinkSignificance is the store-maintained sum of this memory's link significances in both
// directions - a calculated value, never accepted from a client.
Links []Link
LinkSignificance int64
// SignificanceLevelID is the resolved significance registry level id, set by the RPC layer via
// db.ResolveSignificanceLevel before a create/update reaches the store. nil means unranked on a
// create, or "leave significance unchanged" on a partial update. It is internal - never part of
// the proto conversion.
SignificanceLevelID *int64
}
func MemoryFromProto ¶
func (*Memory) SetDefaults ¶
func (m *Memory) SetDefaults()