fix cross-repo path deps; remove el compiler from neuron-lang/
- neuron-runtime, neuron-store, neuron-migrate: fix path deps (products/engram/ → foundation/engram/engrams/, products/el/ → foundation/el/engrams/) - neuron-rs workspace: fix axon-events/axon-protocol paths (../../../platform/ → ../../platform/, paths were off by one level) - neuron-lang/compiler/ removed (el self-hosting compiler now lives in foundation/el/el-compiler/)
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
// graph.el — Graph operations subsystem.
|
||||
//
|
||||
// Neuron's storage backend is Engram — a graph database where every entity
|
||||
// (Memory, Knowledge, BacklogItem, etc.) is a node and every relationship
|
||||
// is a typed edge. This subsystem exposes the raw graph API for operations
|
||||
// that span entity types or need structural graph information.
|
||||
//
|
||||
// WHY expose graph ops directly?
|
||||
// High-level tools like search_memories or retrieve_knowledge hide the graph
|
||||
// structure. But some questions are inherently graph-shaped:
|
||||
// "What entities are connected to this decision?"
|
||||
// "How deep is the dependency chain for this backlog item?"
|
||||
// "Are these two memories causally related?"
|
||||
//
|
||||
// Graph ops answer these questions without the overhead of entity-typed
|
||||
// queries. They're the escape hatch when you need to see the whole picture.
|
||||
//
|
||||
// link_entities is a @manager because it mutates the graph topology.
|
||||
// All reads are @accessor.
|
||||
|
||||
from types import {
|
||||
GraphNode,
|
||||
SearchResult,
|
||||
NeuronError,
|
||||
}
|
||||
|
||||
// ── Public API ────────────────────────────────────────────────────────────────
|
||||
|
||||
// inspect_graph — get a node and its immediate connections.
|
||||
//
|
||||
// entity_type: "Memory" | "Knowledge" | "BacklogItem" | "Context" | "Artifact" | ...
|
||||
// entity_id: the UUID or string ID of the entity.
|
||||
//
|
||||
// Returns the node and its edges formatted as "relation:target_id" strings.
|
||||
// Use this to understand what a node is connected to before traversing deeper.
|
||||
@accessor
|
||||
fn inspect_graph(entity_type: String, entity_id: String) -> Result<GraphNode, NeuronError> {
|
||||
let node = native_graph_inspect(entity_type, entity_id)?
|
||||
Ok(node)
|
||||
}
|
||||
|
||||
// traverse_graph — walk the graph from a starting node up to depth hops.
|
||||
//
|
||||
// depth=1 returns immediate neighbors only (equivalent to inspect_graph).
|
||||
// depth=2 returns neighbors of neighbors, etc.
|
||||
// Default depth is 2 — deep enough for most dependency chains.
|
||||
//
|
||||
// Returns all visited nodes, not just leaves. This gives the caller the
|
||||
// full subgraph around the starting node.
|
||||
@accessor
|
||||
fn traverse_graph(entity_id: String, depth: Int) -> Result<[GraphNode], NeuronError> {
|
||||
let effective_depth = if depth == 0 { 2 } else { depth }
|
||||
let nodes = native_graph_traverse(entity_id, effective_depth)?
|
||||
Ok(nodes)
|
||||
}
|
||||
|
||||
// link_entities — create a typed edge between two graph nodes.
|
||||
//
|
||||
// relation examples: "supersedes", "depends_on", "caused_by", "related_to",
|
||||
// "implements", "references", "blocks"
|
||||
//
|
||||
// Edges are directional: source --relation--> target.
|
||||
// Use causal links for reasoning chains, dependency links for work items,
|
||||
// supersedes links for knowledge evolution.
|
||||
@manager
|
||||
fn link_entities(
|
||||
source_id: String,
|
||||
target_id: String,
|
||||
relation: String,
|
||||
) -> Result<GraphNode, NeuronError> {
|
||||
native_graph_link(source_id, target_id, relation)?
|
||||
native_emit("graph.linked", {"source": source_id, "target": target_id, "relation": relation})
|
||||
// Return the source node with its updated edges.
|
||||
let source_node = native_graph_inspect("", source_id)?
|
||||
Ok(source_node)
|
||||
}
|
||||
|
||||
// search_graph — semantic search across all node types in the graph.
|
||||
//
|
||||
// Unlike type-specific search (search_memories, search_knowledge), this
|
||||
// searches the entire graph regardless of entity type. Useful for
|
||||
// cross-cutting queries: "what's related to authentication across all
|
||||
// memories, knowledge, and backlog items?"
|
||||
@accessor
|
||||
fn search_graph(query: String) -> Result<[SearchResult], NeuronError> {
|
||||
let results = native_semantic_search(query, "", 20)?
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
// rebuild_graph — trigger a full graph index rebuild.
|
||||
//
|
||||
// Used after bulk imports or when the semantic index drifts out of sync.
|
||||
// This is an expensive operation — avoid in normal sessions.
|
||||
// It's sealed because it modifies the graph infrastructure, not just data.
|
||||
@manager
|
||||
fn rebuild_graph() -> Result<String, NeuronError> {
|
||||
sealed {
|
||||
native_emit("graph.rebuild_requested", {})
|
||||
Ok("graph rebuild initiated")
|
||||
}
|
||||
}
|
||||
|
||||
// pin_node — mark a graph node as pinned (exempt from compaction).
|
||||
//
|
||||
// Pinned nodes survive graph compaction even when their activation scores
|
||||
// drop below the compaction threshold. Use for foundational knowledge nodes
|
||||
// that must never be evicted.
|
||||
@manager
|
||||
fn pin_node(entity_id: String, entity_type: String) -> Result<GraphNode, NeuronError> {
|
||||
sealed {
|
||||
// Pinning is implemented as a special self-referential "pinned" edge.
|
||||
native_graph_link(entity_id, entity_id, "pinned")?
|
||||
native_emit("graph.node_pinned", {"id": entity_id, "type": entity_type})
|
||||
let node = native_graph_inspect(entity_type, entity_id)?
|
||||
Ok(node)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user