Documentation
¶
Overview ¶
Package graph provides a knowledge graph store (entities and relations) for agent memory. The store is interface-driven so backends can be swapped (in-memory now; Trigo or SQLite later). When persistence is configured, the in-memory implementation snapshots to ~/.genie/<agent>/memory.bin.zst (gob+zstd).
Index ¶
- Constants
- Variables
- func DataDirForAgent(agentName string) string
- func PendingGraphLearnPath(agentName string) string
- type BatchQuery
- type BatchResult
- type BatchStoreItem
- type BatchStoreResult
- type Config
- type Entity
- type GraphQueryRequest
- type GraphQueryResponse
- type GraphStoreRequest
- type GraphStoreResponse
- type IStore
- type InMemoryStore
- func (s *InMemoryStore) AddEntity(ctx context.Context, e Entity) error
- func (s *InMemoryStore) AddRelation(ctx context.Context, r Relation) error
- func (s *InMemoryStore) Close(ctx context.Context) error
- func (s *InMemoryStore) GetEntity(ctx context.Context, id string) (*Entity, error)
- func (s *InMemoryStore) Neighbors(ctx context.Context, id string, limit int) ([]Neighbor, error)
- func (s *InMemoryStore) RelationsIn(ctx context.Context, id string) ([]Relation, error)
- func (s *InMemoryStore) RelationsOut(ctx context.Context, id string) ([]Relation, error)
- func (s *InMemoryStore) ShortestPath(ctx context.Context, sourceID, targetID string) ([]string, error)
- type InMemoryStoreOption
- type Neighbor
- type Relation
- type Subgraph
- type ToolProvider
- type VectorBackedStore
- func (s *VectorBackedStore) AddEntities(ctx context.Context, entities []Entity) error
- func (s *VectorBackedStore) AddEntity(ctx context.Context, e Entity) error
- func (s *VectorBackedStore) AddRelation(ctx context.Context, r Relation) error
- func (s *VectorBackedStore) Close(_ context.Context) error
- func (s *VectorBackedStore) GetEntity(ctx context.Context, id string) (*Entity, error)
- func (s *VectorBackedStore) Neighbors(ctx context.Context, id string, limit int) ([]Neighbor, error)
- func (s *VectorBackedStore) RelationsIn(ctx context.Context, id string) ([]Relation, error)
- func (s *VectorBackedStore) RelationsOut(ctx context.Context, id string) ([]Relation, error)
- func (s *VectorBackedStore) ShortestPath(ctx context.Context, sourceID, targetID string) ([]string, error)
Constants ¶
const ( // GraphStoreToolName covers entity and relation storage. GraphStoreToolName = "graph_store" // GraphQueryToolName covers neighbor queries, entity lookups, and shortest path. GraphQueryToolName = "graph_query" )
Tool names for the consolidated graph tools. Instead of 5 separate tools (graph_store_entity, graph_store_relation, graph_query, graph_get_entity, graph_shortest_path), we expose 2 action-routed tools to reduce tool explosion while preserving functionality.
const GraphLearnPendingFilename = "graph_learn_pending"
GraphLearnPendingFilename is the name of the file written by setup when the user opts into "build knowledge graph from data"; when present, the app runs one graph-learn pass after the first successful data sources sync.
Variables ¶
var ErrInvalidInput = errors.New("invalid input: required fields missing")
ErrInvalidInput is returned when a tool request is missing required fields.
Functions ¶
func DataDirForAgent ¶
DataDirForAgent returns a directory path suitable for graph persistence for the given agent name: ~/.genie/<sanitized_agent>/ (or ~/.genie/genie/ if agentName is empty). Callers can pass this as Config.DataDir.
func PendingGraphLearnPath ¶
PendingGraphLearnPath returns the path to the graph-learn pending flag file for the given agent. Setup writes this file when the user opts in; the app removes it after running the one-time graph-learn pass.
Types ¶
type BatchQuery ¶ added in v0.1.7
type BatchQuery struct {
Action string `` /* 235-byte string literal not displayed */
EntityID string `json:"entity_id,omitempty" jsonschema:"description=Entity ID to query (required for explore and neighbors and get_entity)"`
Limit int `json:"limit,omitempty" jsonschema:"description=Max neighbors to return (default 20)"`
Depth int `json:"depth,omitempty" jsonschema:"description=Traversal depth for explore: 1 (default) or 2. Max 2"`
SourceID string `json:"source_id,omitempty" jsonschema:"description=Source entity ID for shortest_path"`
TargetID string `json:"target_id,omitempty" jsonschema:"description=Target entity ID for shortest_path"`
}
BatchQuery represents a single query within a batch request.
type BatchResult ¶ added in v0.1.7
type BatchResult struct {
Index int `json:"index"`
Action string `json:"action"`
Response *GraphQueryResponse `json:"response,omitempty"`
Error string `json:"error,omitempty"`
}
BatchResult contains the result (or error) for one sub-query within a batch. Index corresponds to the position in the input queries array.
type BatchStoreItem ¶ added in v0.1.7
type BatchStoreItem struct {
Action string `` /* 144-byte string literal not displayed */
// Fields for action=entity
ID string `json:"id,omitempty" jsonschema:"description=Entity ID (required when action=entity)"`
Type string `json:"type,omitempty" jsonschema:"description=Entity type (required when action=entity)"`
Attrs map[string]string `json:"attrs,omitempty" jsonschema:"description=Optional key-value attributes"`
// Fields for action=relation
SubjectID string `json:"subject_id,omitempty" jsonschema:"description=Subject entity ID (required when action=relation)"`
Predicate string `json:"predicate,omitempty" jsonschema:"description=Relation type (required when action=relation)"`
ObjectID string `json:"object_id,omitempty" jsonschema:"description=Object entity ID (required when action=relation)"`
}
BatchStoreItem represents a single entity or relation within a batch store request.
type BatchStoreResult ¶ added in v0.1.7
type BatchStoreResult struct {
Index int `json:"index"`
Action string `json:"action"`
Message string `json:"message,omitempty"`
Error string `json:"error,omitempty"`
}
BatchStoreResult contains the result (or error) for one item within a batch.
type Config ¶
type Config struct {
// Disabled turns off the knowledge graph and graph_* tools. When true, no
// graph store is created and no graph tools are registered.
Disabled bool `yaml:"disabled,omitempty" toml:"disabled,omitempty"`
// Backend selects the knowledge graph storage backend. Options:
// "inmemory" (default) — in-process dominikbraun/graph with gob+zstd snapshot persistence.
// "vectorstore" — reuses the configured vector store (Qdrant); entities and
// relations are stored as vector documents with metadata discriminators.
// When Backend is "vectorstore", DataDir is ignored (persistence is handled by the vector store).
Backend string `yaml:"backend,omitempty" toml:"backend,omitempty"`
// DataDir is the directory for persistence (e.g. ~/.genie/<agent>). The
// in-memory implementation writes memory.bin.zst (gob+zstd) here. Empty means no persistence.
// Ignored when Backend is "vectorstore".
DataDir string `yaml:"data_dir,omitempty" toml:"data_dir,omitempty"`
}
Config holds configuration for the knowledge graph store. When Disabled is false and DataDir is set, the in-memory store persists to DataDir/memory.bin.zst (gob+zstd). Empty DataDir means no persistence (in-memory only). The implementation is interface-driven so DataDir can be ignored by a future backend.
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns a config with the graph enabled by default (Disabled: false) and no DataDir (no persistence). Callers typically set DataDir from GenieConfig (e.g. DataDir = filepath.Join(GenieDir(), SanitizeForFilename(agentName))) when persistence is desired.
func (Config) IsVectorStoreBackend ¶ added in v0.1.7
IsVectorStoreBackend returns true when the Backend field requests the vector-backed store (case-insensitive, trimmed). Used by app.go to decide whether to delegate graph storage to the shared vector store instance.
type Entity ¶
type Entity struct {
ID string `json:"id"`
Type string `json:"type"`
Attrs map[string]string `json:"attrs,omitempty"`
}
Entity represents a node in the knowledge graph with an id, type, and optional JSON attributes. Used for people, repos, issues, documents, etc.
type GraphQueryRequest ¶
type GraphQueryRequest struct {
Action string `` /* 496-byte string literal not displayed */
// Fields for action=neighbors, get_entity, and explore
EntityID string `` /* 165-byte string literal not displayed */
Limit int `` /* 132-byte string literal not displayed */
// Fields for action=explore
Depth int `` /* 178-byte string literal not displayed */
// Fields for action=shortest_path
SourceID string `json:"source_id,omitempty" jsonschema:"description=Starting entity ID for shortest_path"`
TargetID string `json:"target_id,omitempty" jsonschema:"description=Destination entity ID for shortest_path"`
// Fields for action=batch
Queries []BatchQuery `` /* 219-byte string literal not displayed */
}
GraphQueryRequest is the input for the unified graph_query tool. Set Action to "neighbors", "get_entity", "shortest_path", "explore", or "batch".
type GraphQueryResponse ¶
type GraphQueryResponse struct {
// For action=neighbors
Neighbors []Neighbor `json:"neighbors,omitempty"`
Count int `json:"count,omitempty"`
// For action=get_entity
Entity *Entity `json:"entity,omitempty"`
// Shared: true when the query matched data, false when entity not found
Found bool `json:"found"`
// For action=shortest_path
Path []string `json:"path,omitempty"`
// For action=explore — contains root entity, all connected entities,
// their relations, and neighbor details in a single response.
Subgraph *Subgraph `json:"subgraph,omitempty"`
// For action=batch — one result per sub-query, in the same order.
BatchResults []BatchResult `json:"batch_results,omitempty"`
}
GraphQueryResponse is the output for the graph_query tool.
type GraphStoreRequest ¶ added in v0.1.7
type GraphStoreRequest struct {
Action string `` /* 170-byte string literal not displayed */
// Fields for action=entity
ID string `json:"id,omitempty" jsonschema:"description=Unique identifier for the entity (required when action=entity)"`
Type string `json:"type,omitempty" jsonschema:"description=Entity type e.g. person repo issue document (required when action=entity)"`
Attrs map[string]string `json:"attrs,omitempty" jsonschema:"description=Optional key-value attributes for the entity"`
// Fields for action=relation
SubjectID string `json:"subject_id,omitempty" jsonschema:"description=ID of the subject entity (required when action=relation)"`
Predicate string `` /* 126-byte string literal not displayed */
ObjectID string `json:"object_id,omitempty" jsonschema:"description=ID of the object entity (required when action=relation)"`
// Fields for action=batch
Items []BatchStoreItem `` /* 213-byte string literal not displayed */
}
GraphStoreRequest is the input for the unified graph_store tool. Set Action to "entity", "relation", or "batch" to choose what to store.
type GraphStoreResponse ¶ added in v0.1.7
type GraphStoreResponse struct {
Message string `json:"message"`
BatchResults []BatchStoreResult `json:"batch_results,omitempty"`
}
GraphStoreResponse is the output for the graph_store tool.
type IStore ¶
type IStore interface {
// AddEntity stores an entity by id; overwrites if id exists.
AddEntity(ctx context.Context, e Entity) error
// AddRelation stores a directed relation; idempotent (same triple is a no-op).
AddRelation(ctx context.Context, r Relation) error
// GetEntity returns the entity by id, or nil if not found.
GetEntity(ctx context.Context, id string) (*Entity, error)
// RelationsOut returns relations where subject_id equals id (outgoing edges).
RelationsOut(ctx context.Context, id string) ([]Relation, error)
// RelationsIn returns relations where object_id equals id (incoming edges).
RelationsIn(ctx context.Context, id string) ([]Relation, error)
// Neighbors returns entities reachable in one hop from id (outgoing and
// incoming), with the connecting predicate and direction. Limit caps the
// total number of neighbors returned.
Neighbors(ctx context.Context, id string, limit int) ([]Neighbor, error)
// ShortestPath returns the shortest path of entity IDs from source to target.
// Returns nil path and nil error if no path exists. Returns an error if
// source or target vertex does not exist. Not all implementations may support
// this; they may return an error.
ShortestPath(ctx context.Context, sourceID, targetID string) ([]string, error)
// Close releases resources (e.g. flush snapshot, close connection).
Close(ctx context.Context) error
}
IStore is the interface for the knowledge graph store. All callers use this interface so the implementation can be swapped (in-memory, Trigo, SQLite, etc.) without changing tool or app code.
type InMemoryStore ¶
type InMemoryStore struct {
// contains filtered or unexported fields
}
InMemoryStore implements IStore using github.com/dominikbraun/graph for the in-memory structure and algorithms. Persistence is gob+zstd to memory.bin.zst. Use ShortestPath for path finding between entities.
func NewInMemoryStore ¶
func NewInMemoryStore(opts ...InMemoryStoreOption) (*InMemoryStore, error)
NewInMemoryStore creates an in-memory graph store backed by dominikbraun/graph. When persistence dir is set, state is loaded from memory.bin.zst if present and saved on Add and Close (gob+zstd only).
func (*InMemoryStore) AddEntity ¶
func (s *InMemoryStore) AddEntity(ctx context.Context, e Entity) error
func (*InMemoryStore) AddRelation ¶
func (s *InMemoryStore) AddRelation(ctx context.Context, r Relation) error
func (*InMemoryStore) RelationsIn ¶
func (*InMemoryStore) RelationsOut ¶
func (*InMemoryStore) ShortestPath ¶
func (s *InMemoryStore) ShortestPath(ctx context.Context, sourceID, targetID string) ([]string, error)
ShortestPath returns the shortest path of entity IDs from source to target using unweighted edges. Returns nil path and nil error if no path exists. Returns an error if source or target vertex does not exist. Uses dominikbraun/graph's BFS-based path.
type InMemoryStoreOption ¶
type InMemoryStoreOption func(*InMemoryStore)
InMemoryStoreOption configures the in-memory store.
func WithPersistenceDir ¶
func WithPersistenceDir(dir string) InMemoryStoreOption
WithPersistenceDir sets the directory for the snapshot file (e.g. ~/.genie/<agent>). When set, load is called from NewInMemoryStore and save is called on Close and after writes.
type Neighbor ¶
type Neighbor struct {
Entity Entity `json:"entity"`
Predicate string `json:"predicate"`
Outgoing bool `json:"outgoing"` // true = from subject to this entity, false = from this entity to object
}
Neighbor is an entity reachable from a given node (1-hop) with the predicate that connects them. Used for graph_query tool results.
type Relation ¶
type Relation struct {
SubjectID string `json:"subject_id"`
Predicate string `json:"predicate"`
ObjectID string `json:"object_id"`
}
Relation represents a directed edge: subject --[predicate]--> object. Example: ("person-1", "WORKED_ON", "issue-2").
type Subgraph ¶ added in v0.1.7
type Subgraph struct {
// Root is the central entity that was explored.
Root Entity `json:"root"`
// Entities are all other entities discovered during traversal (excludes root).
Entities []Entity `json:"entities"`
// Relations are all directed edges (subject→object) involving the root.
Relations []Relation `json:"relations"`
// Neighbors provides the same data as action=neighbors would, with
// predicate and direction info for each connected entity.
Neighbors []Neighbor `json:"neighbors"`
}
Subgraph is an ego-graph (local subgraph) centered on a root entity. Contains the root, all connected entities within the traversal depth, the directed relations (edges) between them, and neighbor metadata. This structure is inspired by GraphRAG's local-search subgraph extraction pattern — returning a rich context in a single query.
type ToolProvider ¶
type ToolProvider struct {
// contains filtered or unexported fields
}
ToolProvider wraps an IStore and satisfies the tools.ToolProviders interface so graph tools can be passed to tools.NewRegistry. Pass a non-nil store only when the graph is enabled; when store is nil, GetTools returns no tools (callers should not register the provider when graph is disabled).
func NewToolProvider ¶
func NewToolProvider(store IStore) *ToolProvider
NewToolProvider creates a ToolProvider for the graph tools. store must be non-nil when the graph is enabled; otherwise do not register this provider.
func (*ToolProvider) GetTools ¶
func (p *ToolProvider) GetTools() []tool.Tool
GetTools returns graph_store and graph_query when store is non-nil. Returns nil when store is nil so that disabled graph does not add tools.
type VectorBackedStore ¶ added in v0.1.7
type VectorBackedStore struct {
// contains filtered or unexported fields
}
VectorBackedStore implements IStore on top of the existing vector.IStore (Qdrant or in-memory). Entities and relations are stored as vector documents with metadata discriminators, enabling the knowledge graph to reuse the same scalable storage backend as memory_search/memory_store.
Trade-offs:
- ShortestPath uses iterative BFS built on Neighbors — acceptable for typical agent-sized graphs (hundreds to low thousands of nodes).
- Embeddings for graph docs use the same embedder. The text stored is a JSON representation of the entity/relation, enabling semantic search over entities directly from memory_search.
func NewVectorBackedStore ¶ added in v0.1.7
func NewVectorBackedStore(vs vector.IStore) (*VectorBackedStore, error)
NewVectorBackedStore creates a graph IStore that delegates to the given vector.IStore. The vector store must already be initialised and is owned by the caller (Close on VectorBackedStore is a no-op).
func (*VectorBackedStore) AddEntities ¶ added in v0.1.7
func (s *VectorBackedStore) AddEntities(ctx context.Context, entities []Entity) error
AddEntities stores multiple entities in a single batch, reducing the number of embedding API calls by passing them all to a single Upsert which in turn generates embeddings concurrently via errgroup. Use this instead of calling AddEntity in a loop when storing entities discovered in bulk (e.g. infra discovery, batch graph ingestion).
func (*VectorBackedStore) AddEntity ¶ added in v0.1.7
func (s *VectorBackedStore) AddEntity(ctx context.Context, e Entity) error
AddEntity stores an entity. Upserts so that overwrites work correctly.
func (*VectorBackedStore) AddRelation ¶ added in v0.1.7
func (s *VectorBackedStore) AddRelation(ctx context.Context, r Relation) error
AddRelation stores a directed relation. Upserts, so the same triple is idempotent.
func (*VectorBackedStore) Close ¶ added in v0.1.7
func (s *VectorBackedStore) Close(_ context.Context) error
Close is a no-op for the vector-backed store because the vector store lifecycle is managed by the caller (app.go closes vectorStore separately).
func (*VectorBackedStore) GetEntity ¶ added in v0.1.7
GetEntity looks up an entity by ID using metadata filter.
func (*VectorBackedStore) Neighbors ¶ added in v0.1.7
func (s *VectorBackedStore) Neighbors(ctx context.Context, id string, limit int) ([]Neighbor, error)
Neighbors returns entities reachable in one hop from id (outgoing and incoming).
func (*VectorBackedStore) RelationsIn ¶ added in v0.1.7
RelationsIn returns relations where object_id equals id (incoming edges).
func (*VectorBackedStore) RelationsOut ¶ added in v0.1.7
RelationsOut returns relations where subject_id equals id (outgoing edges).
func (*VectorBackedStore) ShortestPath ¶ added in v0.1.7
func (s *VectorBackedStore) ShortestPath(ctx context.Context, sourceID, targetID string) ([]string, error)
ShortestPath finds the shortest path between source and target using BFS over the Neighbors operation. Returns nil path and nil error if no path exists. Returns an error if source or target does not exist.