feat(8.0): brain.graph.subgraph() + native-provider routing

Introduces the brain.graph namespace and its first op, subgraph() — bounded
multi-hop neighborhood extraction returning a hydrated GraphView (nodes with hop
depth + type/subtype, edges as Relations, a truncated flag). The one-call answer
to 'show me everything around this node, N hops out' the graph-viz path needs.

- brain.graph.subgraph(seeds, { depth, direction, type, subtype, includeInternal,
  includeSystem, maxNodes, maxEdges, hydrateNodes }). seeds: id or id[].
- Feature-detect routing: when a GraphAccelerationProvider is registered
  (isGraphAccelerationProvider on the 'graphAcceleration' provider) it routes to
  native traverse() and hydrates the columnar Subgraph (node ints↔ids paired with
  depth position-preserving, edge verb ints → Relations); otherwise a pure-TS BFS
  fallback expands each frontier through the O(degree) related() adjacency.
- Both paths produce the identical GraphView, so CI exercises the shape via the
  fallback; the native path is cross-layer-tested against cor's provider.
- New public types: GraphView, GraphNode, SubgraphOptions, GraphApi (the surface
  grows: export/rank/path/communities follow). isGraphAccelerationProvider guard
  added to plugin.ts.

Test: graph-subgraph.test.ts — depth tracking, direction, type filter, default-
hides-internal, node hydration on/off, maxNodes truncation, multi-seed.
This commit is contained in:
David Snelling 2026-06-21 09:23:33 -07:00
parent d4de48d004
commit 8c2b57a488
4 changed files with 417 additions and 1 deletions

View file

@ -763,6 +763,88 @@ export interface RelatedParams {
service?: string
}
// ============= Graph views (brain.graph.*) =============
/**
* A node in a {@link GraphView} a lightweight reference (id + classification +
* hop depth), NOT a full {@link Entity}. Graph reads are structure-first; resolve
* a node to its full entity with `get(id)` when you need its `data` / metadata.
*/
export interface GraphNode {
/** Entity id. */
id: string
/** Entity type (present when the view hydrates node classification). */
type?: NounType
/** Per-product subtype (present when hydrated). */
subtype?: string
/** Hop distance from the nearest seed (0 = a seed); present on subgraph results. */
depth?: number
}
/**
* A hydrated view of a region of the graph the discovered nodes plus the edges
* among them, in id form (the public face of the provider's columnar int form).
*/
export interface GraphView<T = any> {
/** The nodes in this region (seeds + everything reached). */
nodes: GraphNode[]
/** The edges among `nodes`, as full {@link Relation} objects. */
edges: Relation<T>[]
/** `true` when a `maxNodes` / `maxEdges` cap truncated the result. */
truncated?: boolean
}
/**
* Options for {@link GraphApi.subgraph}.
*/
export interface SubgraphOptions {
/** Max hop distance from any seed (default `1`). */
depth?: number
/** Edge direction to follow (default `'both'`). */
direction?: 'in' | 'out' | 'both'
/** Restrict traversed edges to these relationship type(s). */
type?: VerbType | VerbType[]
/** Restrict traversed edges to these subtype(s). */
subtype?: string | string[]
/** Also traverse through `internal`-visibility edges/nodes (hidden by default). */
includeInternal?: boolean
/** Also traverse through `system`-visibility edges/nodes (hidden by default). */
includeSystem?: boolean
/** Cap on returned nodes (sets `GraphView.truncated`). */
maxNodes?: number
/** Cap on returned edges. */
maxEdges?: number
/**
* Hydrate each node's `type` / `subtype` (one batch read). Default `true`;
* set `false` to skip it when you only need graph structure (ids + edges).
*/
hydrateNodes?: boolean
}
/**
* The `brain.graph` namespace graph-shaped reads over the knowledge graph.
* Routes to a native {@link import('../plugin.js').GraphAccelerationProvider}
* when one is registered, otherwise serves the same results from Brainy's
* pure-TS adjacency (correct at small/medium scale).
*
* (Grows with the engine: `subgraph` ships first; `export` / `rank` / `path` /
* `communities` follow.)
*/
export interface GraphApi<T = any> {
/**
* @description Extract the subgraph reachable from one or more seed entities
* the bounded multi-hop neighborhood, as nodes + edges. The one-call answer to
* "show me everything around this node, N hops out".
* @param seeds - A seed entity id, or an array of them.
* @param options - Depth, direction, edge/visibility filters, and caps.
* @returns The hydrated {@link GraphView}.
* @example
* const view = await brain.graph.subgraph(personId, { depth: 2 })
* // view.nodes = the 2-hop neighborhood; view.edges = the relations among them
*/
subgraph(seeds: string | string[], options?: SubgraphOptions): Promise<GraphView<T>>
}
// ============= Batch Operations =============
/**