graph

package
v0.1.7 Latest Latest
Warning

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

Go to latest
Published: Mar 10, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

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

View Source
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.

View Source
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

View Source
var ErrInvalidInput = errors.New("invalid input: required fields missing")

ErrInvalidInput is returned when a tool request is missing required fields.

Functions

func DataDirForAgent

func DataDirForAgent(agentName string) string

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

func PendingGraphLearnPath(agentName string) string

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

func (c Config) IsVectorStoreBackend() bool

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) Close

func (s *InMemoryStore) Close(ctx context.Context) error

func (*InMemoryStore) GetEntity

func (s *InMemoryStore) GetEntity(ctx context.Context, id string) (*Entity, error)

func (*InMemoryStore) Neighbors

func (s *InMemoryStore) Neighbors(ctx context.Context, id string, limit int) ([]Neighbor, error)

func (*InMemoryStore) RelationsIn

func (s *InMemoryStore) RelationsIn(ctx context.Context, id string) ([]Relation, error)

func (*InMemoryStore) RelationsOut

func (s *InMemoryStore) RelationsOut(ctx context.Context, id string) ([]Relation, error)

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

func (s *VectorBackedStore) GetEntity(ctx context.Context, id string) (*Entity, error)

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

func (s *VectorBackedStore) RelationsIn(ctx context.Context, id string) ([]Relation, error)

RelationsIn returns relations where object_id equals id (incoming edges).

func (*VectorBackedStore) RelationsOut added in v0.1.7

func (s *VectorBackedStore) RelationsOut(ctx context.Context, id string) ([]Relation, error)

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.

Directories

Path Synopsis
Code generated by counterfeiter.
Code generated by counterfeiter.

Jump to

Keyboard shortcuts

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