kglite 0.16.4

Pure-Rust embedded Cypher knowledge graph engine with in-memory, mmap, and disk storage, and agent-facing schema introspection
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
//! kglite — pure-Rust knowledge graph engine.
//!
//! Cypher pipeline, snapshot/working CoW transactions, columnar /
//! mmap / disk storage backends, and optional format loaders (RDF,
//! OKF). Pre-packaged domain dataset loaders live in the separate
//! kglite-datasets project. The Python wheel (`pip install kglite`)
//! is built by the sibling `kglite-py` crate; the Bolt and MCP
//! protocol servers are separate workspace binaries.
//!
//! ## Public API
//!
//! Downstream Rust consumers (the Python wheel, the bolt and
//! mcp server binaries, future Go/TypeScript/JVM bindings)
//! should depend on the curated [`api`] module — those items
//! get semver guarantees. Anything else is an implementation
//! detail.
//!
//! See `docs/rust/embedding.md` for the embedder guide.

pub mod datatypes;
pub mod error;
// Engine internals — sealed behind the curated `api` facade (roadmap Piece 4).
// `pub(crate)` so no downstream crate can reach `kglite::graph::*` directly;
// the `api` re-exports below still resolve (re-exporting a `pub` item out of a
// `pub(crate)` module is legal). A CI grep (`scripts/check_api_chokepoint.sh`)
// keeps the wrapper crates honest.
pub(crate) mod graph;
pub mod graphgen;
#[cfg(feature = "okf")]
pub mod okf;
pub mod param;
pub(crate) mod serde_codec;

/// Curated stable Rust API. Downstream consumers should depend on
/// items here, not on the underlying module structure (which may
/// move between minor releases).
pub mod api {
    // ── Root prelude ──────────────────────────────────────────────────────
    // The root holds only the cross-cutting *data model* (the types every
    // binding speaks) + a couple of standalone top-level capabilities
    // (`graphgen`, `explore_markdown`). Everything else is clustered into a
    // submodule by concern: `param`, `mutation`, `fluent`, `algorithms`,
    // `timeseries`, `introspection`, `io`, `blueprint`,
    // `cypher`, `session`. Per-cluster items live in exactly one
    // place (no root↔submodule duplication).
    pub use crate::datatypes::values::{NodeValue, PathValue, RelValue};
    pub use crate::datatypes::Value;
    pub use crate::error::{KgError, KgErrorCode};
    pub use crate::graph::dir_graph::DirGraph;
    /// The old→new node-index mapping `DirGraph::vacuum` returns.
    pub use crate::graph::dir_graph::NodeRemap;
    #[cfg(feature = "fastembed")]
    pub use crate::graph::embedder::fastembed::FastEmbedAdapter;
    pub use crate::graph::embedder::Embedder;
    pub use crate::graph::explore::{explore_markdown, ExploreOptions};
    /// Streaming synthetic-graph generator — `generate_to_dir(&config, dir)`
    /// streams the benchmark/demo graph as CSVs + a manifest in bounded memory.
    /// Surfaced through the wheel as `kglite.graphgen(...)`.
    pub use crate::graphgen::{generate_to_dir as graphgen, GraphGenConfig, GraphGenStats};
    /// The petgraph types this API's own signatures already speak.
    ///
    /// `NodeIndex` and `EdgeIndex` are the slot handles every `GraphRead` /
    /// `GraphWrite` call takes and returns; `Direction` is the in/out argument
    /// on every adjacency call (`edges_directed`, `count_edges_filtered`,
    /// `fluent::filter_by_connection`). A consumer cannot call those without
    /// naming them, so until now the curated surface required reaching around
    /// itself for a direct `petgraph` dependency — and pinning *the same major*
    /// as the engine links, since a mismatch there is a type error at the call
    /// site rather than a version warning. Re-exported so the version coupling
    /// is the engine's to carry.
    pub use petgraph::graph::{EdgeIndex, NodeIndex};
    pub use petgraph::Direction;
    // Thin pure-Rust graph handle for embedders + the free function
    // backing it. The wheel crate (`kglite-py`) defines its own,
    // Python-flavored `KnowledgeGraph` separately — same name,
    // different audience (`pip install kglite` users), polars-style.
    //
    // `infer_selection_node_type` infers the node type of a selection's
    // current level; it takes `&CowSelection`, so it landed here in
    // Piece 3b alongside the Selection api-type lift (Piece 3a).
    //
    // (The code-tree handle helpers `resolve_code_entity` / `CODE_TYPES` /
    // `source_location` live in `api::code_entities`.)
    pub use crate::graph::handle::{
        discover_property_keys_excluding, discover_property_keys_from_data,
        infer_selection_node_type, is_canonical_node_column, KnowledgeGraph,
        CANONICAL_NODE_COLUMNS,
    };
    /// Core schema data types — the node and edge records (`NodeData` /
    /// `EdgeData`), the projected `NodeInfo`, geo/temporal validity configs
    /// (`SpatialConfig` / `TemporalConfig`), and the declarative
    /// schema-definition + validation types. Generic across bindings;
    /// lifted in roadmap Piece 3 cleanup. `EdgeData` joined in 0.15.11:
    /// `GraphWrite::add_edge` and `DiskGraph::from_stable_digraph` name it
    /// in public signatures, so it must be publicly nameable too.
    pub use crate::graph::schema::{
        parse_spatial_column_types_from_pairs, parse_temporal_column_types_from_pairs,
        ConnectionSchemaDefinition, EdgeData, NodeData, NodeInfo, NodeSchemaDefinition,
        SchemaDefinition, SchemaInstall, SpatialConfig, TemporalConfig, ValidationError,
    };
    /// The fluent **selection** data model — the cursor state threaded
    /// through the fluent query chain (and through Selection-scoped
    /// capabilities like `algorithms::vector_search`, `mutation`
    /// set-ops/subgraph, and the spatial predicates). `CowSelection` is
    /// the Arc copy-on-write wrapper a binding holds as its cursor;
    /// `CurrentSelection` is the underlying level/plan state; `PlanStep`
    /// is an `explain()` plan entry. Pure core types (petgraph node
    /// indices and hash maps), no binding coupling. Lifted in roadmap
    /// Piece 3a as the foundation for the fluent api surface. The
    /// high-level fluent chain operations are consolidated into core and
    /// exposed in Piece 3c; the fine-grained `core::*` primitives stay
    /// internal.
    pub use crate::graph::schema::{
        CowSelection, CurrentSelection, PlanStep, SelectionLevel, SelectionOperation,
    };
    /// The single external schema **dialect** — the `{"nodes": {...},
    /// "connections": {...}}` shape users write — and its parser. Every
    /// binding's `define_schema` routes through here rather than hand-walking
    /// its own dict: the Python wheel converts its dict to a [`Value`] and
    /// calls `schema_from_value`, the C ABI's `kglite_define_schema` parses
    /// JSON with `schema_from_json`. Keeping one grammar matters most for the
    /// C ABI, where a published signature can never change within a major, so
    /// a second dialect would be permanent. `SchemaParseErrorKind` lets a
    /// binding raise its own conventional exception class.
    pub use crate::graph::schema_json::{
        schema_from_json, schema_from_value, SchemaParseError, SchemaParseErrorKind,
    };
    /// Arena guard for direct `GraphRead` traversals on disk-backed graphs.
    /// Acquire via [`DirGraph::begin_read_pass`] and keep alive while
    /// borrowed node/edge weights live; `None` on memory/mapped backends.
    pub use crate::graph::storage::disk::graph::DiskQueryGuard;
    /// Interned property-/type-key handle (`InternedKey`, a transparent
    /// `u64` newtype) + the `StringInterner` that mints them.
    /// `InternedKey::from_str(..)` computes the hash **without registering
    /// the name** — safe for lookups and removals, but a key that reaches a
    /// *write* unregistered reads back in-session and then breaks
    /// enumeration and persistence (the name cannot be resolved). For
    /// writes, use `DirGraph::set_node_property` (registers for you) or
    /// register via `StringInterner::try_get_or_intern` first.
    pub use crate::graph::storage::interner::{InternedKey, InternerCollision, StringInterner};
    /// The canonical graph read trait — node/edge/property accessors
    /// shared by every storage backend. Non-object-safe (GATs on the
    /// iterator-returning methods), so consumers take `&impl GraphRead`,
    /// never `&dyn`. Lifted for cross-binding read access (roadmap Piece 1).
    pub use crate::graph::storage::GraphRead;
    /// The canonical graph write trait (`GraphWrite: GraphRead`) —
    /// storage-variant-routed mutation, including `set_node_property` and
    /// its siblings, the documented replacements for the `NodeData` mutators
    /// removed in 0.15.9. Non-object-safe like `GraphRead`: consumers take
    /// `&mut impl GraphWrite`, never `&mut dyn`. Implemented by the storage
    /// backends — reach it as `graph.graph.set_node_property(..)` on a
    /// `DirGraph` (the `graph` field is public), the same call the Cypher
    /// `SET` executor makes. Bridge string keys via
    /// `StringInterner::try_get_or_intern` (`DirGraph::interner` is public).
    pub use crate::graph::storage::GraphWrite;
    /// The authoritative read handle for one node's properties.
    ///
    /// A `&NodeData` is one *replica* of a columnar type's column store;
    /// `NodeView` is the store the storage backend answers with, and its
    /// enumeration methods are complete for every storage variant (the
    /// removed `NodeData::property_iter` yielded nothing for columnar rows,
    /// which is why it is gone).
    /// Obtain one from `GraphRead::node_view` / `DirGraph::node_view`; do not
    /// hold it across a `Python::attach` boundary — resolve to owned values
    /// first. See `crates/kglite/src/graph/storage/node_view.rs`.
    pub use crate::graph::storage::NodeView;
    /// The temporal query context (`At` / `During` / `Today` / `All`) — the
    /// as-of filter a binding's cursor carries for temporal-validity
    /// auto-filtering. Lifted in roadmap Piece 4.
    pub use crate::graph::TemporalContext;
    // `Arc<DirGraph>` → `&mut DirGraph` + version bump (lifted in 0.10.1).
    pub use crate::graph::handle::make_dir_graph_mut;
    // (Mutation reports → `api::mutation`; schema introspection /
    // `SchemaOverview` / detail enums → `api::introspection`; `.kgl`
    // load/save → `api::io`; `SourceLocation`/`SourceLookup` →
    // `api::code_entities`.)

    /// Parameter-shape helpers for bindings — wire-shaped values
    /// (JSON / protobuf-map / etc.) ↔ `kglite::api::Value`. Future
    /// REST / gRPC bindings shouldn't re-implement the JSON dispatch
    /// each time; these re-exports hand them the canonical converters
    /// for both directions: `json_value_to_kglite_value` (inbound
    /// params) and `kglite_value_to_json` (outbound result cells, in
    /// natural untagged JSON).
    pub mod param {
        pub use crate::param::{
            json_object_to_value_map, json_value_to_kglite_value, kglite_value_to_json,
        };
    }

    /// Bulk graph construction + maintenance. `add_edges_from_specs` is
    /// the DataFrame-free edge-ingest path that non-Python bindings use
    /// (the C ABI's `create_edges_batch` wraps it); the DataFrame-based
    /// `add_nodes` / `add_connections` / `replace_connections` are the
    /// Rust-side bulk-ingest path (`DataFrame` in, operation report out).
    /// That `DataFrame` is kglite's own columnar container
    /// (`crate::datatypes::values::DataFrame`, built on `Value`) — kglite
    /// does not depend on polars. `update_node_properties`,
    /// `purge_provisional_nodes`, and
    /// `extend_graph` (merge one graph into another) round out the
    /// generic, non-Selection mutation surface. Lifted in roadmap Piece 2.
    /// `create_connections` (edge-create between the two ends of a
    /// selection) lifted in Piece 3b once `CurrentSelection` reached api.
    pub mod mutation {
        /// Structured mutation reports — what a write touched (nodes/edges
        /// created/updated/deleted, per operation). Returned by the mutation
        /// functions above; every binding surfaces them after a mutating call.
        pub use crate::graph::introspection::reporting::{
            ConnectionOperationReport, NodeOperationReport, OperationReport, OperationReports,
        };
        pub use crate::graph::mutation::add_properties::{add_properties, PropertySpec};
        pub use crate::graph::mutation::extend::{extend_graph, ExtendReport};
        // `AddPropertiesReport` is deliberately not re-exported: it was not part
        // of the public surface before this module was split out, and the API
        // baseline pins that surface.
        pub use crate::graph::mutation::maintain::{
            add_connections, add_edges_from_specs, add_nodes, create_connections,
            purge_provisional_nodes, replace_connections, update_node_properties, EdgeSpec,
            EdgeSpecReport,
        };
        /// Validate a graph against a `SchemaDefinition` (Piece 3 cleanup).
        pub use crate::graph::mutation::validation::validate_graph;
    }

    /// Selection-scoped operations — selection set algebra
    /// (`union`/`intersection`/`difference`/`symmetric_difference`) and
    /// subgraph extract / expand / stats. These take `&CurrentSelection`
    /// (now an api type, roadmap Piece 3a) and are the building blocks the
    /// fluent chain composes.
    ///
    /// The bulk of this module (Piece 3c) is the **shared selection-based
    /// query-primitive layer** — `core::graph::core::*`, which CLAUDE.md
    /// describes as "pattern matching, filtering, traversal … used by both
    /// Cypher and the fluent API." Each op takes `(&DirGraph, &mut
    /// CurrentSelection, …already-marshalled params)` and mutates the
    /// selection in place; a binding building a fluent surface composes
    /// these directly (the wheel's `kg_fluent` / `kg_introspection` PyO3
    /// methods marshal Python args, then call straight into here). The
    /// primitives stay *defined* in `core::graph::core`; this is their
    /// curated, stable re-export surface. (A future refinement could hoist
    /// the small amount of per-method branching — `select`'s
    /// include-secondary / temporal logic, `traverse`'s temporal precedence
    /// — into higher-level ops, but the primitives below are already the
    /// correctly-grained shared operations, not glue to hide.)
    pub mod fluent {
        // Selection set algebra + subgraph (Piece 3b).
        pub use crate::graph::mutation::set_ops::{
            difference_selections, intersection_selections, symmetric_difference_selections,
            union_selections,
        };
        pub use crate::graph::mutation::subgraph::{
            expand_selection, extract_subgraph, get_subgraph_stats, SubgraphStats,
        };
        // Filtering / sorting / pagination over a selection.
        pub use crate::graph::core::filtering::{
            filter_by_connection, filter_nodes, filter_nodes_any, filter_nodes_by_label,
            filter_orphan_nodes, limit_nodes_per_group, offset_nodes, sort_nodes,
        };
        // Traversal (parent→child level expansion) + its config/filter types.
        pub use crate::graph::core::traversal::{
            format_for_dictionary, format_for_storage, get_children_properties,
            make_comparison_traversal, make_traversal, MethodConfig, TemporalEdgeFilter,
        };
        // Per-level calculations / equation evaluation / counts.
        pub use crate::graph::core::calculations::{
            count_nodes_by_parent, count_nodes_in_level, process_equation, store_count_results,
            EvaluationResult, StatResult,
        };
        // Node/connection/property retrieval from a selection + result types.
        pub use crate::graph::core::data_retrieval::{
            format_unique_values_for_storage, get_connections, get_node_degrees, get_nodes,
            get_property_values, get_unique_values, LevelConnections, LevelNodes, LevelValues,
            UniqueValues,
        };
        // Aggregate statistics over selected nodes.
        pub use crate::graph::core::statistics::{
            calculate_grouped_property_stats, calculate_property_stats, collect_selected_nodes,
            get_parent_child_pairs, GroupedPropertyStats, PropertyStats,
        };
        // Pattern-match execution (shared with Cypher MATCH).
        pub use crate::graph::core::pattern_matching::{
            parse_pattern, MatchBinding, PatternExecutor, PatternMatch,
        };
        // Compact value formatting for fluent result shaping.
        pub use crate::graph::core::value_operations::format_value_compact;
        // Spatial predicates over a selection (geo filters / centroids /
        // bounds). Selection-scoped — lifted in Piece 3 cleanup now that
        // CurrentSelection is an api type.
        pub use crate::graph::features::spatial::{
            calculate_centroid, contains_point, get_bounds, intersects_geometry, near_point,
            near_point_m, within_bounds, wkt_centroid,
        };
        // Temporal validity predicates (per NodeData + TemporalConfig).
        pub use crate::graph::features::temporal::{
            node_is_temporally_valid, node_overlaps_range, node_passes_context,
        };
    }

    /// Embedding ingest + vector-index construction — how a binding gets
    /// vectors *into* a graph. `set_embeddings` replaces a store,
    /// `add_embeddings` upserts into one, `build_vector_index` builds the
    /// HNSW index that accelerates whole-corpus top-k, and `store_key` is the
    /// one place the `"{text_column}_emb"` store key is derived. Each ingest
    /// call validates every id and dimension before it touches a store and
    /// bumps the graph version on a non-empty write, so it is all-or-nothing
    /// under a plain `&mut DirGraph`.
    ///
    /// Querying by vector needs no surface here: `vector_score` and
    /// `text_score` both take a caller-supplied query vector through
    /// `cypher_query`.
    pub mod embeddings {
        pub use crate::graph::embeddings::{
            add_embeddings, build_vector_index, list_embeddings, set_embeddings, store_key,
            EmbeddingIngestReport, EmbeddingStoreInfo, VectorIndexReport,
        };
    }

    /// Graph algorithms — pathfinding, components, centrality, community
    /// detection (the typed, direct-call surface). Every binding that
    /// exposes a typed `shortest_path()` / `pagerank()` / `louvain()`
    /// method reaches these; they all take `&DirGraph` + plain params and
    /// return the result structs below. (Per-query algorithm access is
    /// also available through Cypher procedures; this is the typed-result
    /// path for bindings that want structs, not result rows.) Lifted in
    /// api-sealing roadmap Piece 2 (`vector_search` + `VectorSearchResult`
    /// added in Piece 3b once `CurrentSelection` was lifted to api — vector
    /// search is scoped to a selection).
    pub mod algorithms {
        pub use crate::graph::algorithms::graph_algorithms::{
            all_paths, are_connected, betweenness_centrality, closeness_centrality,
            connected_components, degree_centrality, get_node_info, get_path_connections,
            label_propagation, leiden_communities, louvain_communities, node_degree, pagerank,
            shortest_path, shortest_path_cost, shortest_path_cost_batch,
            shortest_path_cost_weighted, shortest_path_weighted, weakly_connected_components,
            AllPathsOptions, CentralityOptions, CentralityResult, CommunityOptions,
            CommunityResult, DegreeCentralityOptions, LabelPropagationOptions, PagerankOptions,
            PathNodeInfo, PathOptions, PathResult,
        };
        pub use crate::graph::algorithms::hnsw::HnswParams;
        pub use crate::graph::algorithms::vector::{
            vector_search, DistanceMetric, VectorSearchOptions, VectorSearchResult,
        };
        pub use crate::graph::algorithms::Interrupt;
    }

    /// Timeseries date/query helpers — the pure date-parsing and
    /// range-finding utilities behind inline timeseries support.
    /// `parse_date_query` ("2013" / "2010..2015" → `NaiveDate` +
    /// `DatePrecision`), `expand_end`, `date_from_ymd`, `find_range`, and
    /// the validators are plain functions every binding's date handling
    /// reaches; `TimeseriesConfig` / `NodeTimeseries` are the config/data
    /// types. Lifted in roadmap Piece 2. (The KG-construction-level
    /// `InlineTimeseriesConfig` / `TimeSpec` live in the api root.)
    pub mod timeseries {
        pub use crate::graph::features::timeseries::{
            date_from_ymd, expand_end, find_range, parse_date_query, validate_channel_length,
            validate_keys_sorted, validate_resolution, DatePrecision, InlineTimeseriesConfig,
            NodeTimeseries, TimeSpec, TimeseriesConfig,
        };
    }

    /// Schema/graph introspection — the compute primitives behind
    /// `describe()` / schema overview (connectivity, per-type stats,
    /// neighbor schema) + the detail-level enums + a bug-report writer.
    /// The typed schema-discovery surface every binding builds its
    /// agent-facing schema from. Lifted in roadmap Piece 3 cleanup.
    pub mod introspection {
        pub use crate::graph::introspection::bug_report::write_bug_report;
        /// Debug-string helpers (schema / selection dumps) for diagnostics.
        pub use crate::graph::introspection::debugging;
        pub use crate::graph::introspection::describe::{compute_description, mcp_quickstart};
        pub use crate::graph::introspection::schema_overview::{
            compute_connection_type_stats, compute_neighbors_schema, compute_property_stats,
            compute_schema,
        };
        pub use crate::graph::introspection::{
            compute_type_connectivity, derive_edge_counts_from_triples, schema_overview_to_json,
            ConnectionDetail, ConnectionTypeStats, CypherDetail, FluentDetail, SchemaOverview,
            EXACT_PROPERTY_STATS_MAX_NODES,
        };
    }

    /// Graph I/O: `.kgl` load/save, format exporters (GraphML / GEXF /
    /// D3-JSON / CSV), the N-Triples (RDF) streaming loader + progress
    /// callbacks, embedding-vector file export/import, and streaming
    /// disk subset export.
    pub mod io {
        pub use crate::graph::io::export::{
            to_csv, to_csv_dir, to_d3_json, to_gexf, to_graphml, to_text,
        };
        /// Dependency-free relational exit: a deterministic SQLite-dialect SQL
        /// script (`sqlite3 out.db < dump.sql`). Node types become tables,
        /// connection types become link tables.
        pub use crate::graph::io::export_sql::to_sqlite_dump;
        /// Everything a `.kgl` write needs done to the graph before its bytes
        /// exist: metadata stamp plus the column-consolidation pass whose row
        /// order *is* the file's node binding. A binding that wants the bytes
        /// rather than a file calls this and then `write_kgl_to`; `save_graph`
        /// runs it internally.
        pub use crate::graph::io::file::prepare_kgl_write;
        /// Embedding-vector file export / import.
        pub use crate::graph::io::file::{
            export_embeddings_to_file, import_embeddings_from_file, EmbeddingExportFilter,
            ImportStats,
        };
        /// `.kgl` load / save (the canonical persistence format). `save_graph`
        /// and `save_graph_with` are the single save dispatch and report
        /// `SaveError`, whose `Refused` variant is a save declined *before*
        /// the path was touched — a write-ahead sidecar beside the target
        /// holds commits this checkpoint would strand. Bindings map that to
        /// their own class for a bad request, not to an I/O failure.
        pub use crate::graph::io::file::{
            load_file, load_kgl_bytes, prepare_save, save_graph, save_graph_with, write_kgl,
            write_kgl_to, write_kgl_with, SaveError,
        };
        pub use crate::graph::io::ntriples::{
            load_ntriples, Cancelled, NTriplesConfig, ProgressEvent, ProgressSink, ProgressValue,
        };
        /// `open_or_create_graph` treats its mode as a *creation default* and
        /// never touches an existing graph's own mode;
        /// `open_or_create_graph_in_mode` treats it as the caller's explicit
        /// request and converts (or refuses) on an existing graph too. A
        /// binding that took the mode from a user wants the latter.
        pub use crate::graph::io::open::{
            open_or_create_graph, open_or_create_graph_in_mode, GraphFileIdentity,
            GraphWriterLease, LeaseHolder, LeaseRefusal, OpenDisposition, OpenGraphResult,
        };
        /// General-purpose RDF loader (Turtle / N-Triples / N-Quads /
        /// TriG). Gated behind the `rdf` Cargo feature.
        #[cfg(feature = "rdf")]
        pub use crate::graph::io::rdf::{load_rdf, RdfConfig, RdfStats};
        /// Streaming disk subset export (bounded-memory subgraph save).
        pub use crate::graph::mutation::subgraph_streaming::{
            pass_a_scan, pass_a_scan_to_file, save_subset, save_subset_streaming_disk, RankIndex,
            SubsetSpec,
        };
        /// The persisted-format version numbers this build reads and writes:
        /// [`KGL_FORMAT_VERSION`] is the `.kgl` snapshot format stamped into new
        /// saves, [`WAL_FORMAT_VERSION`] the write-ahead-log frame format, and
        /// [`MIN_READABLE_WAL_FORMAT_VERSION`] the oldest WAL frame format this
        /// build can replay. All three are distinct from the engine SemVer a
        /// binding reads via the ABI version probe — they describe the on-disk
        /// format lifecycle, not the library version. Exposed so a binding can
        /// report the storage format it operates against.
        pub use crate::graph::schema::KGL_FORMAT_VERSION;
        pub use crate::graph::wal::{MIN_READABLE_WAL_FORMAT_VERSION, WAL_FORMAT_VERSION};
    }

    /// Storage backend configuration — the in-memory / mmap / disk backends
    /// (`GraphBackend` + `DiskGraph` / `MappedGraph` constructors), the
    /// per-type lookup, and the embedding store. CLAUDE.md designates
    /// storage-backend configuration a direct-api concern; these let a
    /// binding open / inspect a graph in a specific storage mode and manage
    /// embeddings. Lifted in roadmap Piece 4 (the hard-seal gateway).
    pub mod storage {
        pub use crate::graph::schema::EmbeddingStore;
        pub use crate::graph::storage::backend::GraphBackend;
        pub use crate::graph::storage::disk::graph::DiskGraph;
        pub use crate::graph::storage::lookups::TypeLookup;
        /// The cross-binding create-in-mode builder: resolve a mode string to
        /// a [`StorageMode`] and build a fresh graph in that backend. Shared by
        /// the wheel (`storage='mapped'/'disk'`), the bolt/mcp servers
        /// (`--storage`), and the C ABI (`kglite_graph_new_in_mode`).
        /// `live_storage_mode` answers "which mode is this graph actually in?"
        /// — the classification every binding needs after an open — and
        /// `convert_dir_graph_to_mode` is the explicit switch between the two
        /// portable backends, refusing the disk directions structurally.
        pub use crate::graph::storage::mode::{
            convert_dir_graph_to_mode, live_storage_mode, new_dir_graph_in_mode, StorageMode,
        };
        pub use crate::graph::storage::MappedGraph;
    }

    /// Change data capture — the opt-in in-process change stream a binding
    /// exposes through `db.cdc.*`, plus the commit-boundary drain any owner of
    /// a bare `DirGraph` must call for its commits to be published (the same
    /// obligation the durable paths carry for `flush_wal`). Cypher-first: a
    /// binding needs none of this to *read* the stream, only to say where its
    /// commit boundaries are.
    pub mod cdc {
        pub use crate::graph::cdc::{
            disable, drain_at_commit, enable, publish_drained, read, status, CdcChange, CdcEvent,
            CdcEventKind, CdcHandle, CdcLog, CdcStatus, EdgeState, NodeState, DEFAULT_CAPACITY,
            MAX_CAPACITY,
        };
    }

    /// Durable transactions — the write-ahead log (append / recover / replay)
    /// and the write-capture recording layer behind a binding's `durable()`
    /// feature. The in-process WAL mechanism (distinct from the checkpoint
    /// save in `io`). Lifted in roadmap Piece 4.
    pub mod durable {
        /// Binding-agnostic durable-open + checkpoint orchestration: the
        /// recover→replay→wrap→append ordering every owner of a log performs at
        /// open (`open_log`, which also enforces the unconditional
        /// recovery-on-open refusal), and the two halves of the four-step
        /// checkpoint that bracket a binding's own save. `ensure_recovered` is
        /// that same refusal for an opener that attaches no log at all, and is
        /// already applied by `io::open_or_create_graph`.
        pub use crate::graph::durability::{
            checkpoint_epilogue, checkpoint_prologue, ensure_recovered, open_log, DurableOpenError,
        };
        pub use crate::graph::mutation::wal_replay::apply_frames;
        pub use crate::graph::storage::recording::{
            resolve_ops, wrap_for_durability, CaptureOrigin, RawOp, RecordingGraph,
        };
        pub use crate::graph::wal::{recover, wal_path, DurabilityLevel, SyncMode, Wal, WalFrame};
    }

    /// Code-entity read surface — resolve / locate / contextualize entities
    /// (`Type::method` helpers + source-location types) on any graph with
    /// the code schema (Function/Class/… nodes carrying `file_path`/`line`).
    /// Defined on the graph handle, independent of the builder: graphs built
    /// by an external builder (codingest) get the same surface.
    pub mod code_entities {
        pub use crate::graph::handle::{
            code_entity_context, find_code_entities, resolve_code_entity, source_location,
            CodeContextLookup, CodeEntityContext, CodeEntityMatch, CODE_TYPES,
        };
        pub use crate::graph::{SourceLocation, SourceLookup};
    }

    /// Blueprint loader + builder — declarative graph construction
    /// from a YAML/JSON spec + a directory of CSVs. The wheel's
    /// `from_blueprint` is a thin ergonomics wrapper around
    /// [`load_blueprint_file`] + [`build`]; future bindings (Go,
    /// JS, JVM, …) call these directly.
    pub mod blueprint {
        pub use crate::graph::blueprint::build::{build, BuildReport, FlatSpec};
        pub use crate::graph::blueprint::json_records::{from_records, RecordsReport};
        pub use crate::graph::blueprint::schema::{
            load_blueprint_file, AggregateEdge, Blueprint, CalendarLink, ComputeOp, Connections,
            FkEdge, JunctionEdge, NodeSpec, Settings, TimeKey, TimeseriesSpec,
        };
    }

    /// Cypher parser + planner + executor primitives. Downstream
    /// consumers can build their own custom Cypher pipelines using
    /// these items; for the canonical pipeline see [`session`].
    pub mod cypher {
        pub use crate::graph::languages::cypher::ast::{
            CypherQuery, Expression, OutputFormat, ReturnItem,
        };
        /// Bind label / relationship-type positions written as parameters
        /// (`MATCH (n:$label)`) before validation and planning. `session`
        /// runs this for every statement it prepares; a binding that drives
        /// the parse → optimize → execute steps itself must call it too, or
        /// a parameterised label silently matches nothing.
        pub use crate::graph::languages::cypher::dynamic_labels;
        pub use crate::graph::languages::cypher::executor::write::execute_mutable;
        pub use crate::graph::languages::cypher::executor::CypherExecutor;
        pub use crate::graph::languages::cypher::generate_explain_result;
        pub use crate::graph::languages::cypher::is_mutation_query;
        pub use crate::graph::languages::cypher::parameter_names;
        pub use crate::graph::languages::cypher::parse_with_mutation_check;
        pub use crate::graph::languages::cypher::parser::parse_cypher;
        pub use crate::graph::languages::cypher::planner;
        pub use crate::graph::languages::cypher::planner::mark_lazy_eligibility;
        pub use crate::graph::languages::cypher::planner::schema_check::validate_schema;
        pub use crate::graph::languages::cypher::planner::simplification::rewrite_text_score;
        pub use crate::graph::languages::cypher::query_features;
        pub use crate::graph::languages::cypher::result::{
            materialise_lazy, materialise_lazy_range, materialise_lazy_row, CypherResult,
            LazyResultDescriptor,
        };
        /// Operator-declared value codecs — position-scoped, bidirectional
        /// literal conversions (`'Q42'` ↔ `42`) bound to a property. Bindings
        /// build a `Vec<ValueCodec>` (e.g. from a YAML manifest) and pass it via
        /// `session::ExecuteOptions::value_codecs`. See `value_codec` module
        /// docs for the safety model.
        pub use crate::graph::languages::cypher::value_codec::{CodecKind, StoredType, ValueCodec};
        pub use crate::graph::languages::cypher::QueryFeatures;
        // Specific Cypher-pipeline items a binding implementing a native
        // `cypher()` method (the wheel) reaches. Exposed INDIVIDUALLY — not as
        // whole `ast`/`executor`/`parser`/`result` submodules — so the rest of
        // the executor/parser internals stay un-exported and the optimizer can
        // keep inlining the per-query hot path. (Re-exporting the whole
        // executor module measurably regressed cypher micro-query latency by
        // ~60% on tiny graphs — roadmap Piece 4 perf follow-up.)
        pub use crate::graph::languages::cypher::executor::helpers::{
            resolve_edge_property, resolve_node_property,
        };
        pub use crate::graph::languages::cypher::optimize;
        pub use crate::graph::languages::cypher::planner::schema_check::collect_unknown_pattern_warnings;
        pub use crate::graph::languages::cypher::result::{
            ClauseStats, EdgeBinding, MutationStats, QueryDiagnostics, ResultRow,
        };
    }

    /// Canonical query + transaction surface — single source of
    /// truth for the Cypher pipeline + snapshot/working CoW
    /// transaction model. See `docs/rust/session.md`.
    pub mod session {
        /// `LOAD CSV` filesystem capability. Every binding decides what its
        /// callers get; see `ExecuteOptions::csv_import`.
        pub use crate::graph::languages::cypher::executor::load_csv::CsvImportPolicy;
        pub use crate::graph::session::{
            execute_mut, execute_read, resolve_noderefs, CommitOutcome, ExecuteOptions,
            ExecuteOutcome, Session, Transaction, QUERY_THREAD_STACK_SIZE,
        };
    }
}