memory

package
v0.31.0 Latest Latest
Warning

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

Go to latest
Published: Sep 3, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Index

Constants

View Source
const DocumentDefaultCollection = "default"

DocumentDefaultCollection is the fallback collection name.

Variables

View Source
var DefaultProjectSessionTypes = []string{"build", "plan"}

DefaultProjectSessionTypes are the session types project-scoped recall reads. Subagent, note, index and search sessions also write memory, but their turns are tool traces rather than conversations with the user — including them drowns project recall in noise.

Functions

func DefaultDBPath added in v0.2.1

func DefaultDBPath() string

DefaultDBPath returns the default path for the memory database.

func LightweightTreeAsText added in v0.2.1

func LightweightTreeAsText(tree map[string]TopicTree, topFacts []Node) string

func LightweightTreeAsTextWithHighlight added in v0.2.1

func LightweightTreeAsTextWithHighlight(tree map[string]TopicTree, topFacts []Node, relevantKeys map[string]float32) string

func LightweightTreeAsTextWithLimit added in v0.2.1

func LightweightTreeAsTextWithLimit(tree map[string]TopicTree, topFacts []Node, maxSummaryChars int) string

Types

type ChatClient added in v0.2.1

type ChatClient interface {
	Chat(ctx context.Context, system, prompt string) (string, error)
}

ChatClient is the minimal interface the Graph needs to call an LLM. It wraps a blocking chat call — callers can adapt streaming providers.

func NewChatClient added in v0.2.1

func NewChatClient(p provider.Provider, model string) ChatClient

NewChatClient creates a blocking ChatClient from an ogcode Provider. model is the specific model ID to use; if empty the provider's default (Models()[0]) is used.

type CollectionStats added in v0.2.1

type CollectionStats struct {
	Collections int `json:"collections"`
	Documents   int `json:"documents"`
	Nodes       int `json:"nodes"`
	Edges       int `json:"edges"`
}

CollectionStats is returned by Stats.

type ConceptTree added in v0.2.1

type ConceptTree struct {
	Name            string   `json:"name"`
	Facts           []Node   `json:"facts"`
	RelatedConcepts []string `json:"related,omitempty"`
}

ConceptTree is the readable tree for one concept.

type Document added in v0.2.1

type Document struct {
	ID         int64  `json:"id"`
	Collection string `json:"collection"`
	Content    string `json:"content"`
	CreatedAt  int64  `json:"createdAt"`
}

Document is an unstructured text fragment tied to a collection.

type Edge added in v0.2.1

type Edge struct {
	ID        int64   `json:"id"`
	SessionID string  `json:"sessionId"`
	FromKey   string  `json:"fromKey"` // concept key
	ToKey     string  `json:"toKey"`   // concept key (cross-topic allowed)
	RelType   string  `json:"relType"` // e.g. "related", "prerequisite", "opposite"
	Weight    float32 `json:"weight"`
	CreatedAt int64   `json:"createdAt"`
}

Edge represents a named relationship between two nodes.

type EmbedClient added in v0.2.1

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

EmbedClient returns embedding vectors for input strings.

func NewEmbedClient added in v0.2.1

func NewEmbedClient(e provider.Embedder) EmbedClient

NewEmbedClient creates an EmbedClient from an ogcode Embedder.

type Graph added in v0.2.1

type Graph struct {
	Store *Store
	Embed EmbedClient
}

Graph orchestrates the knowledge graph lifecycle.

Embed is the inbuilt local embedder (always present when memory is enabled). The synthesis LLM is NOT stored here — it is supplied per call via GraphOptions.Chat / RecallOptions.Chat so memory uses the session's currently selected model rather than a server-wide default.

func (*Graph) AddFact added in v0.2.1

func (g *Graph) AddFact(ctx context.Context, opts GraphOptions) (*Node, error)

AddFact stores a new fact and reorganizes the graph. Embedding is required.

func (*Graph) BuildLightweightTree added in v0.2.1

func (g *Graph) BuildLightweightTree(ctx context.Context, sessionID string, f NodeFilter, queryVec []float32, limit int) (map[string]TopicTree, []Node, error)

BuildLightweightTree builds a lightweight tree ready for LLM consumption.

func (*Graph) BuildTree added in v0.2.1

func (g *Graph) BuildTree(ctx context.Context, sessionID string) (map[string]TopicTree, error)

BuildTree constructs the hierarchical memory tree for a session.

func (*Graph) BuildTreeFiltered added in v0.2.1

func (g *Graph) BuildTreeFiltered(ctx context.Context, sessionID string, f NodeFilter) (map[string]TopicTree, error)

BuildTreeFiltered constructs the tree filtered by given bounds.

func (*Graph) ProjectRecall added in v0.23.0

func (g *Graph) ProjectRecall(ctx context.Context, opts ProjectRecallOptions) (*ProjectRecallResult, error)

ProjectRecall runs semantic recall across every session in a project.

func (*Graph) Recall added in v0.2.1

func (g *Graph) Recall(ctx context.Context, opts RecallOptions) (*RecallResult, error)

type GraphOptions added in v0.2.1

type GraphOptions struct {
	SessionID string
	// ProjectID, SessionType and SessionName describe the session's workspace.
	// They are denormalized onto every node written so project-scoped recall can
	// filter and attribute facts without reaching into the session database.
	ProjectID   string
	SessionType string
	SessionName string
	Question    string
	Response    string
	UserTopic   string
	// Chat is the synthesis LLM client used for topic/concept inference and
	// enrichment on this call. It should be built from the session's selected
	// provider+model. When nil, placement and enrichment fall back to
	// heuristics (no LLM call).
	Chat ChatClient
}

GraphOptions tunes Graph inference behavior.

type GraphOpts added in v0.2.1

type GraphOpts struct {
	// EmbedProvider is the provider used for text embeddings. Must satisfy
	// provider.Embedder. In practice this is always the inbuilt LocalEmbedder.
	EmbedProvider provider.Provider
}

GraphOpts holds dependencies for initializing agentic memory.

Embedding is always produced by the inbuilt local embedder (gte-small) — there is no embedder configuration. The synthesis LLM (topic/concept inference and recall) is NOT configured here: it is supplied per request by the caller, using the same provider+model the user selected for their session. See WriteMemory and RecallWith.

type Memory

type Memory struct {
	Store *Store
	Graph *Graph
	// contains filtered or unexported fields
}

Memory provides the agentic memory lifecycle: read, recall, and write. It wraps a local SQLite-backed knowledge graph with optional LLM inference.

func New

func New(store *Store, opts *GraphOpts) *Memory

New creates a Memory backed by local SQLite graph store. The synthesis LLM is not wired here — it is injected per call via WriteMemory/RecallWith so that memory uses the session's currently selected model.

func (*Memory) BackfillEmbeddings added in v0.29.0

func (m *Memory) BackfillEmbeddings(ctx context.Context, progress func(done, total int)) (embedded, failed int, err error)

BackfillEmbeddings embeds only the facts that have no embedding, leaving existing vectors untouched. It exists because a fact stored without one is invisible to every semantic recall — scanProject and BuildLightweightTree both skip it — so a graph that accumulated unembedded facts looks full but searches as if it were empty.

Unlike RefreshAll this is incremental and safe to run on every start: it is a no-op once the backlog is cleared. It stops at the first context cancellation so a shutdown does not have to wait for the whole backlog.

progress, when non-nil, is called after each fact with the number finished and the size of the backlog, so a caller can tell the user why the machine is busy. It runs on this goroutine and should not block.

func (*Memory) CreateCollection added in v0.2.1

func (m *Memory) CreateCollection(ctx context.Context, name string) (int64, error)

CreateCollection inserts a new collection.

func (*Memory) DeleteCollection added in v0.2.1

func (m *Memory) DeleteCollection(ctx context.Context, name string) error

DeleteCollection removes a collection including its documents.

func (*Memory) Enabled

func (m *Memory) Enabled() bool

Enabled returns whether agentic memory is active.

func (*Memory) ReadMemory

func (m *Memory) ReadMemory(ctx context.Context, sessionID string) string

ReadMemory fetches the full session knowledge graph as text.

func (*Memory) RecallMemory

func (m *Memory) RecallMemory(ctx context.Context, sessionID, question string, chat ChatClient) (string, error)

RecallMemory performs semantic recall for a specific question. chat is the synthesis LLM client built from the session's selected provider+model; when nil, recall returns the raw semantically filtered tree without synthesis.

func (*Memory) RecallProjectMemory added in v0.23.0

func (m *Memory) RecallProjectMemory(ctx context.Context, req ProjectRecallRequest) (string, error)

RecallProjectMemory performs semantic recall across every conversation held in a project. chat is the synthesis LLM built from the session's selected model; when nil, the assembled cross-session context is returned without synthesis.

func (*Memory) RefreshAll added in v0.2.1

func (m *Memory) RefreshAll(ctx context.Context) error

RefreshAll recomputes all embeddings — both collection documents and graph facts — so it is the recovery path after switching embedding provider. Without re-embedding the graph nodes, session and project recall keep scoring against stale old-dimensionality vectors and (with the cosine dimension guard) silently match nothing.

func (*Memory) SemanticSearch added in v0.2.1

func (m *Memory) SemanticSearch(ctx context.Context, collection, query string, topK int) ([]SearchResult, error)

SemanticSearch runs a vector search across a collection.

func (*Memory) Stats added in v0.2.1

func (m *Memory) Stats(ctx context.Context) (col, doc, nodes, edges int, err error)

Stats returns total counts across all collections and graph tables.

func (*Memory) UpsertDocument added in v0.2.1

func (m *Memory) UpsertDocument(ctx context.Context, collection, content string) (int64, error)

UpsertDocument stores or updates a document and computes its embedding using the currently configured embedder.

func (*Memory) WriteMemory

func (m *Memory) WriteMemory(ctx context.Context, scope Scope, question, response string, chat ChatClient)

WriteMemory persists a conversation turn. chat is the synthesis LLM client to use for topic/concept inference and enrichment — it should be built from the same provider+model the user selected for the current session. When chat is nil, the fact is stored without LLM topic inference (placement falls back to heuristic). Synthesis runs in a background goroutine; the chat client is captured at dispatch time so it reflects the session's model even though the call is asynchronous.

type Node added in v0.2.1

type Node struct {
	ID          int64    `json:"id"`
	SessionID   string   `json:"sessionId"`
	ProjectID   string   `json:"projectId,omitempty"`
	SessionType string   `json:"sessionType,omitempty"`
	Type        NodeType `json:"type"`
	Key         string   `json:"key"`
	Content     string   `json:"content,omitempty"`   // question + " [ANSWER] " + response for facts
	Question    string   `json:"question,omitempty"`  // original question
	Response    string   `json:"response,omitempty"`  // original answer
	TopicName   string   `json:"topicName,omitempty"` // only set for concept/fact nodes
	Summary     string   `json:"summary,omitempty"`   // LLM-generated one-line summary per fact
	Labels      []string `json:"labels,omitempty"`    // LLM-generated labels per fact
	Order       int      `json:"order,omitempty"`     // position in conversation (1-indexed)
	CreatedAt   int64    `json:"createdAt"`
	AccessedAt  int64    `json:"accessedAt"`
}

Node is the fundamental unit in the knowledge graph.

ProjectID and SessionType are denormalized copies of the owning session's project directory and type. They are stamped at write time so project-scoped recall can filter without joining against the per-project session database (which lives in a different file and, for worktree sessions, keys sessions by a directory the memory store never sees).

type NodeFilter added in v0.2.1

type NodeFilter struct {
	Type      NodeType
	Since     int64  // unix milliseconds; 0 = no lower bound
	Until     int64  // unix milliseconds; 0 = no upper bound
	FromOrder int    // 1-indexed inclusive; 0 = no lower bound
	ToOrder   int    // 1-indexed inclusive; 0 = no upper bound
	TopicName string // empty = any topic
}

NodeFilter captures all optional bounds on a node query.

type NodeType added in v0.2.1

type NodeType string

NodeType distinguishes the three levels in the hierarchy.

const (
	TypeTopic   NodeType = "topic"
	TypeConcept NodeType = "concept"
	TypeFact    NodeType = "fact"
)

type Placement added in v0.2.1

type Placement struct {
	Topic   string
	Concept string
}

type ProjectFilter added in v0.23.0

type ProjectFilter struct {
	Since          int64    // unix milliseconds; 0 = no lower bound
	Until          int64    // unix milliseconds; 0 = no upper bound
	SessionTypes   []string // empty = any session type
	TopicName      string   // empty = any topic
	ExcludeSession string   // session ID to skip (usually the caller's own)
	// OnlySession restricts the query to a single conversation. It is how the
	// project pipeline — dated, attributed, recency-ranked — gets pointed at just
	// the current session. It takes precedence over SessionTypes: the caller
	// named the session explicitly, so its type is not a reason to skip it.
	OnlySession string
}

ProjectFilter bounds a project-scoped query. Session-scoped NodeFilter cannot be reused because project queries span many sessions, where the per-session "order" column is not comparable across rows.

type ProjectRecallOptions added in v0.23.0

type ProjectRecallOptions struct {
	ProjectID string
	Question  string

	Limit         int     // max semantically matched facts fed to synthesis
	PerSessionCap int     // max matched facts contributed by any one session
	MaxRounds     int     // max refinement rounds
	Threshold     float32 // confidence threshold to stop early
	HalfLifeDays  float64 // recency half-life for the score boost
	MaxChars      int     // char budget for the facts block

	Since          int64    // unix ms; 0 = no lower bound
	Until          int64    // unix ms; 0 = no upper bound
	SessionTypes   []string // empty = every session type
	ExcludeSession string   // session to skip, usually the caller's own
	OnlySession    string   // restrict to one conversation; overrides SessionTypes
	TopicName      string   // restrict to one topic

	// Chat is the synthesis LLM, built from the session's selected model. When
	// nil, recall returns the assembled context without synthesis.
	Chat ChatClient
}

ProjectRecallOptions tunes a project-scoped recall.

type ProjectRecallRequest added in v0.23.0

type ProjectRecallRequest struct {
	ProjectID string
	Question  string
	Since     int64  // unix ms; 0 = the whole history
	TopicName string // empty = every topic
	// SessionID narrows the search to one conversation. The retrieval pipeline is
	// otherwise unchanged, so a session-scoped answer still arrives dated,
	// attributed and recency-ranked — which plain session recall does not provide.
	SessionID string
	Chat      ChatClient
}

ProjectRecallRequest is a project-scoped recall query.

type ProjectRecallResult added in v0.23.0

type ProjectRecallResult struct {
	Answer       string
	Confidence   float32
	Rounds       int
	FactsUsed    int
	SessionsUsed int
	TotalFacts   int
	TotalTopics  int
}

ProjectRecallResult is the outcome of a project-scoped recall.

type RecallOptions added in v0.2.1

type RecallOptions struct {
	SessionID string
	Question  string
	MaxRounds int     // max refinement rounds, default 3
	Threshold float32 // confidence threshold to stop early, default 0.7
	Limit     int     // max facts in lightweight tree, default 50
	MinScore  float32 // minimum cosine similarity to include fact
	Since     int64
	Until     int64
	FromOrder int
	ToOrder   int
	// Chat is the synthesis LLM client used for the convergence refinement
	// loop. It should be built from the session's selected provider+model.
	// When nil, recall returns the raw semantically filtered tree without
	// LLM synthesis.
	Chat ChatClient
}

type RecallResult added in v0.2.1

type RecallResult struct {
	Answer     string
	Confidence float32
	Rounds     int
	FactsUsed  int
}

type RelatedConcept added in v0.2.1

type RelatedConcept struct {
	ToConcept string
	Weight    float32
}

type Scope added in v0.23.0

type Scope struct {
	SessionID   string
	ProjectID   string
	SessionType string
	SessionName string
}

Scope identifies the session a memory write belongs to, plus the workspace attributes stamped onto every node so the turn is later recallable at project scope. ProjectID is a project.Resolve'd directory; SessionType mirrors the session store's type ("", "plan", "subagent", …).

type SearchResult added in v0.2.1

type SearchResult struct {
	Doc   Document `json:"doc"`
	Score float32  `json:"score"`
}

SearchResult pairs a Document with a relevance score.

type SessionMeta added in v0.2.1

type SessionMeta struct {
	ID           string `json:"id"`
	ProjectID    string `json:"projectId,omitempty"`
	SessionType  string `json:"sessionType,omitempty"`
	Name         string `json:"name,omitempty"`
	CreatedAt    int64  `json:"createdAt"`
	LastAccessAt int64  `json:"lastAccessAt"`
	NodeCount    int    `json:"nodeCount"`
	TopicCount   int    `json:"topicCount"`
	ConceptCount int    `json:"conceptCount"`
	FactCount    int    `json:"factCount"`
}

SessionMeta is the metadata for a session.

type SessionUpsert added in v0.23.0

type SessionUpsert struct {
	ID          string
	ProjectID   string
	SessionType string
	Name        string
}

SessionUpsert carries the session attributes a write path knows about. Empty fields are ignored on update, so a caller that only knows the session ID cannot blank out a project association recorded earlier.

type Store added in v0.2.1

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

Store is the SQLite-backed knowledge graph. All methods are safe for concurrent use; writes are serialized through a mutex.

func Open added in v0.2.1

func Open(path string) (*Store, error)

Open opens (or creates) the memory database at path.

func (*Store) AddEdge added in v0.2.1

func (s *Store) AddEdge(e Edge) error

AddEdge creates a relationship between two concepts.

func (*Store) AddNode added in v0.2.1

func (s *Store) AddNode(n Node) (*Node, error)

AddNode inserts a node. If a node with the same (session_id, key) exists, it is updated (upsert). Returns the node with its ID set.

func (*Store) BackfillProject added in v0.23.0

func (s *Store) BackfillProject(projectID string, sessionTypes map[string]string) (int64, error)

BackfillProject stamps project_id/session_type onto rows written before those columns existed. sessionTypes maps session ID → session type for every session belonging to the project; only rows whose project_id is still empty are touched, so this is idempotent and never re-homes a session.

func (*Store) Close added in v0.2.1

func (s *Store) Close() error

Close releases the database handle.

func (*Store) DB added in v0.2.1

func (s *Store) DB() *sql.DB

DB returns the underlying sql.DB for advanced queries.

func (*Store) DeleteEdge added in v0.2.1

func (s *Store) DeleteEdge(sessionID, fromKey, toKey string) error

DeleteEdge removes an edge by from_key + to_key.

func (*Store) DeleteNode added in v0.2.1

func (s *Store) DeleteNode(sessionID, key string) error

DeleteNode removes a node by session_id + key.

func (*Store) DeleteSession added in v0.2.1

func (s *Store) DeleteSession(sessionID string) error

DeleteSession removes a session and all its data (cascading via FK triggers if present).

func (*Store) Embeddings added in v0.2.1

func (s *Store) Embeddings(sessionID string) (map[string][]float32, error)

Embeddings returns all (key, embedding) pairs for a session.

func (*Store) EnsureSession added in v0.2.1

func (s *Store) EnsureSession(sessionID string) error

EnsureSession creates a session if it does not exist, and updates lastAccessAt. Read paths use this when the session's project is unknown or irrelevant.

func (*Store) EnsureSessionMeta added in v0.23.0

func (s *Store) EnsureSessionMeta(u SessionUpsert) error

EnsureSessionMeta upserts a session along with whatever attributes the caller knows. Only non-empty attributes overwrite existing values.

func (*Store) GetFirstFact added in v0.2.1

func (s *Store) GetFirstFact(sessionID string) (*Node, error)

func (*Store) GetLastFact added in v0.2.1

func (s *Store) GetLastFact(sessionID string) (*Node, error)

func (*Store) GetNode added in v0.2.1

func (s *Store) GetNode(sessionID, key string) (*Node, error)

GetNode retrieves a node by session_id + key.

func (*Store) GetNodeAt added in v0.2.1

func (s *Store) GetNodeAt(sessionID string, order int) (*Node, error)

GetNodeAt returns the Nth fact (1-indexed) in a session.

func (*Store) ListEdges added in v0.2.1

func (s *Store) ListEdges(sessionID string) ([]Edge, error)

ListEdges returns all edges for a session.

func (*Store) ListNodes added in v0.2.1

func (s *Store) ListNodes(sessionID string, filterType NodeType) ([]Node, error)

ListNodes returns all nodes for a session, optionally filtered by type.

func (*Store) ListNodesFiltered added in v0.2.1

func (s *Store) ListNodesFiltered(sessionID string, f NodeFilter) ([]Node, error)

ListNodesFiltered returns nodes matching the given bounds.

func (*Store) ListProjectConcepts added in v0.23.0

func (s *Store) ListProjectConcepts(projectID string, f ProjectFilter, limit int) ([]Node, error)

ListProjectConcepts returns the concept nodes across a project, most recent first. They give recall a vocabulary for follow-up searches.

func (*Store) ListProjectTopics added in v0.23.0

func (s *Store) ListProjectTopics(projectID string, f ProjectFilter, limit int) ([]TopicCount, error)

ListProjectTopics returns a project's topics ordered by size. Fact placement uses it to reuse topic names another session already established, so one project does not end up with "Auth System", "Authentication" and "auth" as three unrelated topics.

func (*Store) ListSessions added in v0.2.1

func (s *Store) ListSessions() ([]SessionMeta, error)

ListSessions returns all sessions ordered by last access.

func (*Store) ProjectFactsAt added in v0.23.0

func (s *Store) ProjectFactsAt(projectID, sessionID string, orders []int, f ProjectFilter) ([]Node, error)

ProjectFactsAt returns the facts at the given per-session orders. Project recall uses it for neighbour windowing, where "the fact before this one" is only meaningful within the same session.

The caller's ProjectFilter is applied here too: a neighbour is still a fact the user asked to exclude, so a date range or topic restriction must not be escaped by being adjacent to something that matched.

func (*Store) ProjectSessionNames added in v0.23.0

func (s *Store) ProjectSessionNames(projectID string) (map[string]string, error)

ProjectSessionNames returns sessionID → name for a project, used to label recalled facts with the conversation they came from.

func (*Store) ProjectStats added in v0.23.0

func (s *Store) ProjectStats(projectID string) (sessions, topics, facts int, err error)

ProjectStats returns per-project counts.

func (*Store) ScanProjectFacts added in v0.23.0

func (s *Store) ScanProjectFacts(projectID string, f ProjectFilter, fn func(Node, []float32)) error

ScanProjectFacts streams every fact in a project through fn along with its decoded embedding (nil when the fact was never embedded).

This is a callback scan rather than a slice return because a project graph can hold orders of magnitude more facts than a session graph: the caller keeps a bounded top-K and per-topic tallies instead of materializing every row.

func (*Store) SetEmbedding added in v0.2.1

func (s *Store) SetEmbedding(sessionID, key string, emb []float32) error

SetEmbedding upserts the embedding for a node key.

func (*Store) Stats added in v0.2.1

func (s *Store) Stats(sessionID string) (topics, concepts, facts int, err error)

Stats returns per-type counts for a session.

func (*Store) UpdateNodeEnrichment added in v0.2.1

func (s *Store) UpdateNodeEnrichment(sessionID, key, summary string, labels []string) error

UpdateNodeEnrichment updates labels and summary for a fact node.

type TopicCount added in v0.23.0

type TopicCount struct {
	Name  string
	Facts int
}

TopicCount is a topic name with how many facts sit under it.

type TopicTree added in v0.2.1

type TopicTree struct {
	Name     string        `json:"name"`
	Concepts []ConceptTree `json:"concepts"`
}

TopicTree is the readable tree for one topic.

Jump to

Keyboard shortcuts

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