pub struct KhiveRuntime { /* private fields */ }Expand description
Composable runtime handle used by the MCP server.
Wraps a StorageBackend and provides namespace-scoped accessor methods
for each storage capability, plus a lazily-loaded embedder.
Implementations§
Source§impl KhiveRuntime
impl KhiveRuntime
Sourcepub async fn update_entity(
&self,
token: &NamespaceToken,
id: Uuid,
patch: EntityPatch,
) -> RuntimeResult<Entity>
pub async fn update_entity( &self, token: &NamespaceToken, id: Uuid, patch: EntityPatch, ) -> RuntimeResult<Entity>
Patch-style entity update.
Only fields set to Some(_) are changed. Re-indexes FTS5 (and vectors if configured)
when name or description changes; skips re-indexing for property/tag-only patches.
Returns RuntimeError::NotFound if the entity does not exist or belongs to a different
namespace. Namespace isolation is enforced at the runtime layer.
Sourcepub async fn merge_entity(
&self,
token: &NamespaceToken,
into_id: Uuid,
from_id: Uuid,
strategy: EntityDedupMergePolicy,
dry_run: bool,
) -> RuntimeResult<MergeSummary>
pub async fn merge_entity( &self, token: &NamespaceToken, into_id: Uuid, from_id: Uuid, strategy: EntityDedupMergePolicy, dry_run: bool, ) -> RuntimeResult<MergeSummary>
Merge from_id into into_id.
All edges incident to from_id are rewired to into_id. Self-loops that would
result from the rewire are dropped. Properties and tags are merged per strategy.
from_id is tombstoned with merge provenance and removed from indexes. Returns a summary.
If dry_run is true, computes and returns the planned summary without mutating any rows.
Atomic: all SQL (entity reads/writes, edge rewires, FTS updates, vec-index delete)
runs on a single pool connection inside one BEGIN IMMEDIATE transaction via
merge_entity_sql. If embedding vectors are configured, the vector re-insert for
into_id is performed after the transaction (requires async embedding computation).
Sourcepub async fn update_note(
&self,
token: &NamespaceToken,
id: Uuid,
patch: NotePatch,
) -> RuntimeResult<Note>
pub async fn update_note( &self, token: &NamespaceToken, id: Uuid, patch: NotePatch, ) -> RuntimeResult<Note>
Patch-style note update.
Sourcepub async fn merge_note(
&self,
token: &NamespaceToken,
into_id: Uuid,
from_id: Uuid,
strategy: EntityDedupMergePolicy,
content_strategy: ContentMergeStrategy,
dry_run: bool,
) -> RuntimeResult<MergeSummary>
pub async fn merge_note( &self, token: &NamespaceToken, into_id: Uuid, from_id: Uuid, strategy: EntityDedupMergePolicy, content_strategy: ContentMergeStrategy, dry_run: bool, ) -> RuntimeResult<MergeSummary>
Merge from_id note into into_id note.
Both notes must exist in the namespace and have the same kind. Content is merged
per content_strategy. Properties are merged per strategy. from_id is
tombstoned (status=‘deleted’, deleted_at set). Returns a summary.
If dry_run is true, computes and returns the planned summary without mutating
any rows, edges, or indexes.
Source§impl KhiveRuntime
impl KhiveRuntime
Sourcepub async fn hybrid_search_with_strategy(
&self,
token: &NamespaceToken,
query_text: &str,
query_vector: Option<Vec<f32>>,
strategy: FusionStrategy,
limit: u32,
) -> RuntimeResult<Vec<SearchHit>>
pub async fn hybrid_search_with_strategy( &self, token: &NamespaceToken, query_text: &str, query_vector: Option<Vec<f32>>, strategy: FusionStrategy, limit: u32, ) -> RuntimeResult<Vec<SearchHit>>
Hybrid search with a caller-supplied fusion strategy.
Source§impl KhiveRuntime
impl KhiveRuntime
Sourcepub async fn bfs_traverse(
&self,
token: &NamespaceToken,
start: Uuid,
options: TraversalOptions,
) -> RuntimeResult<Vec<PathNode>>
pub async fn bfs_traverse( &self, token: &NamespaceToken, start: Uuid, options: TraversalOptions, ) -> RuntimeResult<Vec<PathNode>>
BFS traversal from start, returning nodes in level order.
The first element is always the start node (via_edge = None, depth = 0).
Nodes already visited are skipped so the result set is deduplicated.
DB round-trips are O(max_depth): one batch_neighbors call and one
get_edges call per BFS level, rather than one call per node/edge.
Sourcepub async fn shortest_path(
&self,
token: &NamespaceToken,
from: Uuid,
to: Uuid,
max_depth: usize,
) -> RuntimeResult<Option<Vec<PathNode>>>
pub async fn shortest_path( &self, token: &NamespaceToken, from: Uuid, to: Uuid, max_depth: usize, ) -> RuntimeResult<Option<Vec<PathNode>>>
Bidirectional BFS shortest path from from to to.
Returns Some(path) where path[0] is from and path.last() is to,
or None if no path exists within max_depth hops.
For from == to returns Some with a single-node path immediately.
DB round-trips are O(max_depth): one batch_neighbors per frontier
expansion level, plus one get_edges call during path reconstruction.
Source§impl KhiveRuntime
impl KhiveRuntime
Sourcepub async fn create_entity(
&self,
token: &NamespaceToken,
kind: &str,
entity_type: Option<&str>,
name: &str,
description: Option<&str>,
properties: Option<Value>,
tags: Vec<String>,
) -> RuntimeResult<Entity>
pub async fn create_entity( &self, token: &NamespaceToken, kind: &str, entity_type: Option<&str>, name: &str, description: Option<&str>, properties: Option<Value>, tags: Vec<String>, ) -> RuntimeResult<Entity>
Create and persist a new entity.
Sourcepub async fn get_entity(
&self,
token: &NamespaceToken,
id: Uuid,
) -> RuntimeResult<Entity>
pub async fn get_entity( &self, token: &NamespaceToken, id: Uuid, ) -> RuntimeResult<Entity>
Retrieve an entity by ID.
UUID v4 is globally unique — no namespace filter on by-ID ops (ADR-007 rule 2).
Sourcepub async fn get_entity_including_deleted(
&self,
token: &NamespaceToken,
id: Uuid,
) -> RuntimeResult<Option<Entity>>
pub async fn get_entity_including_deleted( &self, token: &NamespaceToken, id: Uuid, ) -> RuntimeResult<Option<Entity>>
Retrieve an entity by ID including soft-deleted rows.
UUID v4 is globally unique — no namespace filter on by-ID ops (ADR-007 rule 2).
Sourcepub async fn get_note_including_deleted(
&self,
token: &NamespaceToken,
id: Uuid,
) -> RuntimeResult<Option<Note>>
pub async fn get_note_including_deleted( &self, token: &NamespaceToken, id: Uuid, ) -> RuntimeResult<Option<Note>>
Retrieve a note by ID including soft-deleted rows.
UUID v4 is globally unique — no namespace filter on by-ID ops (ADR-007 rule 2).
Sourcepub async fn get_entities_by_ids(
&self,
token: &NamespaceToken,
ids: &[Uuid],
) -> RuntimeResult<Vec<Entity>>
pub async fn get_entities_by_ids( &self, token: &NamespaceToken, ids: &[Uuid], ) -> RuntimeResult<Vec<Entity>>
Fetch multiple entities by ID, returning only those that exist in the caller’s namespace. Missing or namespace-mismatched IDs are silently omitted so that batch lookups don’t abort on a single stale reference.
Sourcepub async fn list_entities(
&self,
token: &NamespaceToken,
kind: Option<&str>,
entity_type: Option<&str>,
limit: u32,
offset: u32,
) -> RuntimeResult<Vec<Entity>>
pub async fn list_entities( &self, token: &NamespaceToken, kind: Option<&str>, entity_type: Option<&str>, limit: u32, offset: u32, ) -> RuntimeResult<Vec<Entity>>
List entities visible to the token, optionally filtered by kind and entity_type.
When the token carries a multi-namespace visible set, entities from all
visible namespaces are returned. When the visible set is [primary]
(the default) this behaves identically to the pre-visibility behaviour.
Sourcepub async fn list_entities_tagged(
&self,
token: &NamespaceToken,
kind: Option<&str>,
domain_tag: Option<&str>,
limit: u32,
offset: u32,
) -> RuntimeResult<Vec<Entity>>
pub async fn list_entities_tagged( &self, token: &NamespaceToken, kind: Option<&str>, domain_tag: Option<&str>, limit: u32, offset: u32, ) -> RuntimeResult<Vec<Entity>>
List entities filtered by kind, optional domain tag, limit, and offset.
When domain_tag is Some, the query is restricted at the storage layer via
EntityFilter::tags_any so the page result already reflects the domain
constraint. This avoids the silent truncation that occurs when filtering
post-page (K-3). Multi-namespace visibility from the token is applied.
Sourcepub async fn count_entities_tagged(
&self,
token: &NamespaceToken,
kind: Option<&str>,
domain_tag: Option<&str>,
) -> RuntimeResult<u64>
pub async fn count_entities_tagged( &self, token: &NamespaceToken, kind: Option<&str>, domain_tag: Option<&str>, ) -> RuntimeResult<u64>
Count entities filtered by kind and optional domain tag.
Used to report a meaningful total alongside a paginated listing (K-6).
Multi-namespace visibility from the token is applied.
Sourcepub async fn list_events(
&self,
token: &NamespaceToken,
filter: EventFilter,
page: PageRequest,
) -> RuntimeResult<Page<Event>>
pub async fn list_events( &self, token: &NamespaceToken, filter: EventFilter, page: PageRequest, ) -> RuntimeResult<Page<Event>>
List events in the namespace proven by the caller token.
Sourcepub async fn validate_link_endpoints(
&self,
token: &NamespaceToken,
source_id: Uuid,
target_id: Uuid,
relation: EdgeRelation,
) -> RuntimeResult<()>
pub async fn validate_link_endpoints( &self, token: &NamespaceToken, source_id: Uuid, target_id: Uuid, relation: EdgeRelation, ) -> RuntimeResult<()>
Public delegator for cross-backend link validation (ADR-029 D3).
Exposes validate_edge_relation_endpoints for the SubstrateCoordinator
so it can validate the relation before writing the edge on the source backend.
Sourcepub fn validate_link_endpoints_by_resolved(
&self,
source_id: Uuid,
target_id: Uuid,
relation: EdgeRelation,
src: Option<&Resolved>,
tgt: Option<&Resolved>,
) -> RuntimeResult<()>
pub fn validate_link_endpoints_by_resolved( &self, source_id: Uuid, target_id: Uuid, relation: EdgeRelation, src: Option<&Resolved>, tgt: Option<&Resolved>, ) -> RuntimeResult<()>
Validate an edge relation using pre-fetched endpoint records (ADR-029 D3).
For cross-backend links the source and target live on different backends — the source runtime cannot resolve the target. The coordinator fetches each endpoint from its own backend, then calls this method to enforce ADR-002 kind-pairing rules without a second DB round-trip.
src and tgt are the resolve_primary results from each backend. The
token supplies the pack edge rules installed on this (source) runtime;
no DB access is performed.
Sourcepub async fn link(
&self,
token: &NamespaceToken,
source_id: Uuid,
target_id: Uuid,
relation: EdgeRelation,
weight: f64,
metadata: Option<Value>,
) -> RuntimeResult<Edge>
pub async fn link( &self, token: &NamespaceToken, source_id: Uuid, target_id: Uuid, relation: EdgeRelation, weight: f64, metadata: Option<Value>, ) -> RuntimeResult<Edge>
Create a directed edge between two substrates.
Enforces the three-case relation contract via
validate_edge_relation_endpoints. See that method for the full contract.
For symmetric relations (competes_with, composed_with) the endpoint
pair is canonicalised to source_uuid < target_uuid so that A→B and B→A
deduplicate to one row (F012).
metadata is validated against governed keys; dependency_kind is
inferred for depends_on edges when absent (F013).
target_backend is always None for locally-routed edges written through
this path. Both endpoints must exist in the local namespace, so setting
target_backend = None is the only valid choice (F161).
A record that exists but belongs to a different namespace is treated as not found (fail-closed; no cross-namespace existence leak).
Sourcepub async fn link_with_target_backend(
&self,
token: &NamespaceToken,
source_id: Uuid,
target_id: Uuid,
relation: EdgeRelation,
weight: f64,
metadata: Option<Value>,
target_backend: Option<String>,
) -> RuntimeResult<Edge>
pub async fn link_with_target_backend( &self, token: &NamespaceToken, source_id: Uuid, target_id: Uuid, relation: EdgeRelation, weight: f64, metadata: Option<Value>, target_backend: Option<String>, ) -> RuntimeResult<Edge>
Write an edge with an explicit target_backend stamp (ADR-029 D3).
Called by the SubstrateCoordinator when source and target are on
different backends. The coordinator validates endpoints before calling
this method via Self::validate_link_endpoints, so endpoint validation is
skipped here. The edge is written on the source backend only.
Sourcepub async fn neighbors(
&self,
token: &NamespaceToken,
node_id: Uuid,
direction: Direction,
limit: Option<u32>,
relations: Option<Vec<EdgeRelation>>,
) -> RuntimeResult<Vec<NeighborHit>>
pub async fn neighbors( &self, token: &NamespaceToken, node_id: Uuid, direction: Direction, limit: Option<u32>, relations: Option<Vec<EdgeRelation>>, ) -> RuntimeResult<Vec<NeighborHit>>
Get immediate neighbors of a node, optionally filtered by relation type.
Pass relations: Some(vec![EdgeRelation::Annotates]) to retrieve only
annotation edges, enabling cross-substrate navigation.
Symmetric relations (competes_with, composed_with) are stored
with the canonical source as the lower UUID. Direction normalization is
applied in neighbors_with_query so both callers see correct results.
Sourcepub async fn neighbors_with_query(
&self,
token: &NamespaceToken,
node_id: Uuid,
query: NeighborQuery,
) -> RuntimeResult<Vec<NeighborHit>>
pub async fn neighbors_with_query( &self, token: &NamespaceToken, node_id: Uuid, query: NeighborQuery, ) -> RuntimeResult<Vec<NeighborHit>>
Get neighbors with full query control (includes min_weight).
Applies symmetric-relation direction normalization: if the
relations filter contains only symmetric relations the direction is
overridden to Both so edges stored in canonical order are always found.
Soft-deleted entity nodes are excluded from results unless the caller
explicitly requested them (future: include_deleted flag; currently
always false per Fix 2).
Sourcepub async fn traverse(
&self,
token: &NamespaceToken,
request: TraversalRequest,
) -> RuntimeResult<Vec<GraphPath>>
pub async fn traverse( &self, token: &NamespaceToken, request: TraversalRequest, ) -> RuntimeResult<Vec<GraphPath>>
Traverse the graph from a set of root nodes.
Roots in a foreign namespace are silently filtered before storage expansion. Soft-deleted entity nodes are excluded from results (Fix 2).
Sourcepub async fn create_note(
&self,
token: &NamespaceToken,
kind: &str,
name: Option<&str>,
content: &str,
salience: Option<f64>,
properties: Option<Value>,
annotates: Vec<Uuid>,
) -> RuntimeResult<Note>
pub async fn create_note( &self, token: &NamespaceToken, kind: &str, name: Option<&str>, content: &str, salience: Option<f64>, properties: Option<Value>, annotates: Vec<Uuid>, ) -> RuntimeResult<Note>
Create and persist a note, optionally with properties and annotation targets.
After creating the note:
- Always indexes into FTS5 at the
notes_<namespace>key. - If an embedding model is configured, indexes into the vector store with
SubstrateKind::Note. - For each UUID in
annotates, creates anEdgeRelation::Annotatesedge from the note to that target.
Sourcepub async fn create_note_with_decay(
&self,
token: &NamespaceToken,
kind: &str,
name: Option<&str>,
content: &str,
salience: Option<f64>,
decay_factor: f64,
properties: Option<Value>,
annotates: Vec<Uuid>,
) -> RuntimeResult<Note>
pub async fn create_note_with_decay( &self, token: &NamespaceToken, kind: &str, name: Option<&str>, content: &str, salience: Option<f64>, decay_factor: f64, properties: Option<Value>, annotates: Vec<Uuid>, ) -> RuntimeResult<Note>
Like Self::create_note but also sets a non-zero decay factor on the note.
Sourcepub async fn create_note_with_decay_for_embedding_model(
&self,
token: &NamespaceToken,
kind: &str,
name: Option<&str>,
content: &str,
salience: Option<f64>,
decay_factor: f64,
properties: Option<Value>,
annotates: Vec<Uuid>,
embedding_model: Option<&str>,
) -> RuntimeResult<Note>
pub async fn create_note_with_decay_for_embedding_model( &self, token: &NamespaceToken, kind: &str, name: Option<&str>, content: &str, salience: Option<f64>, decay_factor: f64, properties: Option<Value>, annotates: Vec<Uuid>, embedding_model: Option<&str>, ) -> RuntimeResult<Note>
Like Self::create_note_with_decay but targets a specific embedding model.
Sourcepub async fn try_create_note(
&self,
token: &NamespaceToken,
kind: &str,
name: Option<&str>,
content: &str,
properties: Option<Value>,
) -> RuntimeResult<Option<Note>>
pub async fn try_create_note( &self, token: &NamespaceToken, kind: &str, name: Option<&str>, content: &str, properties: Option<Value>, ) -> RuntimeResult<Option<Note>>
Insert a note using INSERT OR IGNORE semantics for atomic deduplication.
Returns Ok(Some(note)) when the note was newly written. Returns
Ok(None) when a unique constraint (e.g. the external_id partial
index on comm message notes) was already satisfied by an existing row,
making this call a no-op. FTS indexing and vector embedding are
attempted on success but treated as best-effort: failures are logged
and do not abort the write.
This method is intentionally narrower than create_note: it skips
salience/decay, annotates edges, and embedding-model selection, which
are not needed for channel-ingest paths.
Sourcepub async fn list_notes(
&self,
token: &NamespaceToken,
kind: Option<&str>,
limit: u32,
offset: u32,
) -> RuntimeResult<Vec<Note>>
pub async fn list_notes( &self, token: &NamespaceToken, kind: Option<&str>, limit: u32, offset: u32, ) -> RuntimeResult<Vec<Note>>
List notes visible to the token, optionally filtered by kind.
When the token carries a multi-namespace visible set, notes from all
visible namespaces are returned. When the visible set is [primary]
(the default) this behaves identically to the pre-visibility behaviour.
Sourcepub async fn search_notes(
&self,
token: &NamespaceToken,
query_text: &str,
query_vector: Option<Vec<f32>>,
limit: u32,
note_kind: Option<&str>,
include_superseded: bool,
tags_any: &[String],
properties_filter: Option<&Value>,
) -> RuntimeResult<Vec<NoteSearchHit>>
pub async fn search_notes( &self, token: &NamespaceToken, query_text: &str, query_vector: Option<Vec<f32>>, limit: u32, note_kind: Option<&str>, include_superseded: bool, tags_any: &[String], properties_filter: Option<&Value>, ) -> RuntimeResult<Vec<NoteSearchHit>>
Search notes using a hybrid FTS5 + vector pipeline with salience weighting.
Pipeline:
- FTS5 query against
notes_<namespace>. - If embedding model is configured: vector search filtered to
kind="note". - RRF fusion (k=60).
- Salience-weighted rerank:
score *= (0.5 + 0.5 * note.salience). - Filter soft-deleted notes, apply optional kind / tag / properties predicates.
Tags and properties are pushed into the per-note fetch loop BEFORE truncation
so that matching notes ranked beyond
limitin the raw fusion are not silently dropped (fix for issue #225). - Truncate to
limit.
tags_any: when non-empty, only notes that have at least one of these tags
(stored in properties["tags"], case-insensitive match) are retained. The
check happens inside the alive-note loop, before hits.truncate(limit).
properties_filter: when Some, only notes whose properties JSON object is
a superset of the given filter object are retained. Also applied before truncation.
Sourcepub async fn resolve_prefix(
&self,
token: &NamespaceToken,
prefix: &str,
) -> RuntimeResult<Option<Uuid>>
pub async fn resolve_prefix( &self, token: &NamespaceToken, prefix: &str, ) -> RuntimeResult<Option<Uuid>>
Resolve a short UUID prefix (8+ hex chars) to a full UUID.
Searches entities, notes, and edges tables for a UUID starting with the
given prefix, scoped to the caller’s namespace. Returns Ok(Some(uuid))
if exactly one match is found, Ok(None) if no matches, or an error if
ambiguous (multiple matches).
pub async fn resolve_prefix_including_deleted( &self, token: &NamespaceToken, prefix: &str, ) -> RuntimeResult<Option<Uuid>>
Sourcepub async fn resolve_by_id(
&self,
token: &NamespaceToken,
id: Uuid,
) -> RuntimeResult<Option<Resolved>>
pub async fn resolve_by_id( &self, token: &NamespaceToken, id: Uuid, ) -> RuntimeResult<Option<Resolved>>
Resolve a UUID to its substrate kind with NO namespace filter.
By-ID contract (ADR-007): UUID v4 is globally unique — by-ID substrate
inference must return the record regardless of caller namespace. Used by
the public update and delete verb handlers when no explicit kind is
supplied (PR-A1 / codex r2).
Does NOT consult the visible set or the primary-namespace check. The token is still required to route to the correct backend pool but its namespace value is not used as a filter.
Sourcepub async fn resolve_by_id_including_deleted(
&self,
token: &NamespaceToken,
id: Uuid,
) -> RuntimeResult<Option<Resolved>>
pub async fn resolve_by_id_including_deleted( &self, token: &NamespaceToken, id: Uuid, ) -> RuntimeResult<Option<Resolved>>
Resolve a UUID to its substrate kind with NO namespace filter, including soft-deleted rows.
Used by the hard-delete path when no explicit kind is supplied, so
already-soft-deleted records can still be located by UUID alone.
Sourcepub async fn resolve(
&self,
token: &NamespaceToken,
id: Uuid,
) -> RuntimeResult<Option<Resolved>>
pub async fn resolve( &self, token: &NamespaceToken, id: Uuid, ) -> RuntimeResult<Option<Resolved>>
Resolve a UUID to its substrate kind by trying entity, then note, then event stores.
Returns None if the UUID is not found in any substrate.
Cost: at most 3 store lookups per call (cheap for v0.1).
Sourcepub async fn resolve_primary(
&self,
token: &NamespaceToken,
id: Uuid,
) -> RuntimeResult<Option<Resolved>>
pub async fn resolve_primary( &self, token: &NamespaceToken, id: Uuid, ) -> RuntimeResult<Option<Resolved>>
Resolve a UUID to its substrate kind using primary-namespace-only enforcement.
Unlike resolve, never consults the visible set. Use from mutation validation
paths (link, annotate, build_edge) where strict primary ownership is required.
Sourcepub async fn resolve_including_deleted(
&self,
token: &NamespaceToken,
id: Uuid,
) -> RuntimeResult<Option<Resolved>>
pub async fn resolve_including_deleted( &self, token: &NamespaceToken, id: Uuid, ) -> RuntimeResult<Option<Resolved>>
Resolve a UUID to its substrate kind, including soft-deleted rows.
Used exclusively by the hard-delete path to locate records that have already been soft-deleted. Namespace isolation is still enforced.
Sourcepub async fn delete_note(
&self,
token: &NamespaceToken,
id: Uuid,
hard: bool,
) -> RuntimeResult<bool>
pub async fn delete_note( &self, token: &NamespaceToken, id: Uuid, hard: bool, ) -> RuntimeResult<bool>
Delete a note by ID, enforcing namespace isolation.
On hard delete, cascades to remove all incident edges (both inbound and
outbound) and cleans up FTS and vector indexes, preventing dangling
references for annotates edges that target this note.
Soft delete also cleans FTS and vector indexes; edges are left in place.
Returns Ok(false) if the note does not exist or belongs to a different
namespace (wrong-namespace is indistinguishable from absent).
Soft-delete or hard-delete a note by ID.
PR-A1: UUID v4 is globally unique — no namespace filter on by-ID ops (ADR-007 rule 2). Cascade and index cleanup target the RECORD’s stored namespace, not the caller token’s.
Source§impl KhiveRuntime
impl KhiveRuntime
Sourcepub async fn query(
&self,
token: &NamespaceToken,
query: &str,
) -> RuntimeResult<Vec<SqlRow>>
pub async fn query( &self, token: &NamespaceToken, query: &str, ) -> RuntimeResult<Vec<SqlRow>>
Execute a GQL or SPARQL query string, returning raw SQL rows.
The query is compiled to SQL with the namespace scope applied.
GQL syntax: MATCH (a:concept)-[e:extends]->(b) RETURN a, b LIMIT 10
SPARQL syntax: SELECT ?a WHERE { ?a :kind "concept" . }
Sourcepub async fn query_with_metadata(
&self,
token: &NamespaceToken,
query: &str,
opts: CompileOptions,
) -> RuntimeResult<QueryResult>
pub async fn query_with_metadata( &self, token: &NamespaceToken, query: &str, opts: CompileOptions, ) -> RuntimeResult<QueryResult>
Execute a GQL/SPARQL query, returning rows and any validation warnings.
Sourcepub async fn delete_entity(
&self,
token: &NamespaceToken,
id: Uuid,
hard: bool,
) -> RuntimeResult<bool>
pub async fn delete_entity( &self, token: &NamespaceToken, id: Uuid, hard: bool, ) -> RuntimeResult<bool>
Delete an entity by ID (soft delete by default).
On hard delete, cascades to remove all incident edges (both inbound and outbound) to prevent dangling references. Soft delete also cleans FTS and vector indexes; edges are left in place.
Soft-delete or hard-delete an entity by ID.
UUID v4 is globally unique — no namespace filter on by-ID ops (ADR-007 rule 2).
Sourcepub async fn count_entities(
&self,
token: &NamespaceToken,
kind: Option<&str>,
) -> RuntimeResult<u64>
pub async fn count_entities( &self, token: &NamespaceToken, kind: Option<&str>, ) -> RuntimeResult<u64>
Count entities in a namespace, optionally filtered.
Sourcepub async fn get_edge(
&self,
_token: &NamespaceToken,
edge_id: Uuid,
) -> RuntimeResult<Option<Edge>>
pub async fn get_edge( &self, _token: &NamespaceToken, edge_id: Uuid, ) -> RuntimeResult<Option<Edge>>
Fetch a single edge by id.
PR-A1: UUID v4 is globally unique — returns the edge regardless of which
namespace the token carries. Ok(None) means the edge does not exist at all.
Sourcepub async fn get_edge_visible(
&self,
token: &NamespaceToken,
edge_id: Uuid,
) -> RuntimeResult<Option<Edge>>
pub async fn get_edge_visible( &self, token: &NamespaceToken, edge_id: Uuid, ) -> RuntimeResult<Option<Edge>>
Fetch a single edge by id.
PR-A1: delegates to get_edge — visible-set check removed. By-ID ops are
namespace-agnostic; UUID v4 is globally unique (ADR-007 rule 2).
Sourcepub async fn get_edge_including_deleted(
&self,
_token: &NamespaceToken,
edge_id: Uuid,
) -> RuntimeResult<Option<Edge>>
pub async fn get_edge_including_deleted( &self, _token: &NamespaceToken, edge_id: Uuid, ) -> RuntimeResult<Option<Edge>>
Fetch an edge by UUID including soft-deleted rows.
PR-A1: returns the edge regardless of which namespace the token carries — UUID v4 is globally unique. Used by the hard-delete path so that a soft-deleted edge can still be purged via its edge ID.
Sourcepub async fn list_edges(
&self,
token: &NamespaceToken,
filter: EdgeListFilter,
limit: u32,
) -> RuntimeResult<Vec<Edge>>
pub async fn list_edges( &self, token: &NamespaceToken, filter: EdgeListFilter, limit: u32, ) -> RuntimeResult<Vec<Edge>>
List edges matching filter. limit is capped at 1000; defaults to 100.
Sourcepub async fn update_edge(
&self,
token: &NamespaceToken,
edge_id: Uuid,
patch: EdgePatch,
) -> RuntimeResult<Edge>
pub async fn update_edge( &self, token: &NamespaceToken, edge_id: Uuid, patch: EdgePatch, ) -> RuntimeResult<Edge>
Patch-style edge update. Only Some(_) fields are applied.
When relation is Some(new_rel), validates that the edge’s existing endpoints
are legal for new_rel before persisting. Weight-only updates (relation = None)
skip validation. Returns InvalidInput if the new relation would violate the
three-case endpoint contract; the edge is NOT mutated on error.
For symmetric relations (competes_with, composed_with), endpoint order is
canonicalised to source_uuid < target_uuid after validation. If a canonical
row already exists at the target triple, the non-canonical edge is deleted and
the existing canonical row is refreshed (DELETE + UPDATE pattern, mirroring
merge_entity_sql).
Sourcepub async fn delete_edge(
&self,
token: &NamespaceToken,
edge_id: Uuid,
hard: bool,
) -> RuntimeResult<bool>
pub async fn delete_edge( &self, token: &NamespaceToken, edge_id: Uuid, hard: bool, ) -> RuntimeResult<bool>
Hard-delete an edge by id.
Cascades to remove any annotates edges whose target is the deleted edge
(annotates is note → anything; deleting an edge target leaves annotation
edges dangling if not cleaned up). Returns true if the primary
edge was removed.
If edge_id does not refer to an edge (e.g. the caller passes an entity or
note UUID by mistake), this method returns Ok(false) immediately with no
side effects — it does not cascade inbound edges of the non-edge record.
Sourcepub async fn count_edges(
&self,
token: &NamespaceToken,
filter: EdgeListFilter,
) -> RuntimeResult<u64>
pub async fn count_edges( &self, token: &NamespaceToken, filter: EdgeListFilter, ) -> RuntimeResult<u64>
Count edges matching filter.
Sourcepub async fn build_edge(
&self,
token: &NamespaceToken,
spec: &LinkSpec,
) -> RuntimeResult<Edge>
pub async fn build_edge( &self, token: &NamespaceToken, spec: &LinkSpec, ) -> RuntimeResult<Edge>
Validate and construct an edge from a LinkSpec without writing to storage.
Applies the full edge contract (endpoint validation, symmetric
canonicalization, dependency_kind inference and metadata validation).
Returns the constructed Edge on success; the caller is responsible for
persisting it (e.g. via upsert_edge or link_many).
The token must be a pre-authorized namespace token from the dispatch
layer. If spec.namespace is set it must match token.namespace();
a mismatch returns RuntimeError::InvalidInput.
Sourcepub async fn link_many(
&self,
token: &NamespaceToken,
specs: Vec<LinkSpec>,
) -> RuntimeResult<Vec<Edge>>
pub async fn link_many( &self, token: &NamespaceToken, specs: Vec<LinkSpec>, ) -> RuntimeResult<Vec<Edge>>
Validate and atomically upsert a batch of edges.
All edges are validated and constructed with build_edge before any
write. If validation fails for any entry the entire batch is rejected
(no writes occur). On success, all edges are persisted in a single
atomic transaction via upsert_edges.
After the bulk upsert, each edge is read back by its natural key
(namespace, source_id, target_id, relation) so that the returned IDs
are always the persisted row IDs, not the locally-generated UUIDs that
may have been displaced by an ON CONFLICT DO UPDATE. This mirrors the
H1 fix applied to singleton link() and prevents phantom-ID exposure
when callers upsert overlapping triples with verbose=true.
All specs must share the same namespace; the namespace is taken from
token (or validated against it if spec.namespace is set).
Sourcepub async fn create_many(
&self,
token: &NamespaceToken,
specs: Vec<EntityCreateSpec>,
) -> RuntimeResult<Vec<Entity>>
pub async fn create_many( &self, token: &NamespaceToken, specs: Vec<EntityCreateSpec>, ) -> RuntimeResult<Vec<Entity>>
Create a batch of entities atomically.
All specs are validated before any write. If ANY spec fails validation (unknown kind, empty name, secret-gate violation), the method returns that error and no entities are written.
Storage writes are issued as ONE upsert_entities call followed by ONE
upsert_documents call — the same primitives that the single-entity path
uses, but batched. Embedding is intentionally skipped: bulk structural
ingest is the expected use-case, and dense vectors are backfilled later
via a reindex call. Callers that need immediate vector search
immediately after creation should use per-entity create_entity instead.
On FTS failure, every newly written entity row is hard-deleted to maintain
consistency (mirrors the single-entity rollback in create_entity).
Source§impl KhiveRuntime
impl KhiveRuntime
Sourcepub async fn export_kg(
&self,
token: &NamespaceToken,
) -> RuntimeResult<KgArchive>
pub async fn export_kg( &self, token: &NamespaceToken, ) -> RuntimeResult<KgArchive>
Export all entities and edges in a namespace to a portable JSON archive.
Edge collection: all entity IDs in the namespace are gathered first;
query_edges is then called with those IDs as source_ids. This
captures every edge whose source entity belongs to the namespace.
Sourcepub async fn export_kg_json(
&self,
token: &NamespaceToken,
) -> RuntimeResult<String>
pub async fn export_kg_json( &self, token: &NamespaceToken, ) -> RuntimeResult<String>
Export to a JSON string (convenience wrapper around export_kg).
Sourcepub async fn import_kg(
&self,
archive: &KgArchive,
token: &NamespaceToken,
) -> RuntimeResult<ImportSummary>
pub async fn import_kg( &self, archive: &KgArchive, token: &NamespaceToken, ) -> RuntimeResult<ImportSummary>
Import an archive into target_namespace.
If target_namespace is None, the archive’s own namespace is used.
- Entities: upserted by ID; existing records are overwritten.
- Edges: upserted; existing records are overwritten.
- Validation:
format != "khive-kg"or unsupported version →InvalidInput. Invalid edge relations are caught at JSON deserialization time.
Sourcepub async fn import_kg_json(
&self,
json: &str,
token: &NamespaceToken,
) -> RuntimeResult<ImportSummary>
pub async fn import_kg_json( &self, json: &str, token: &NamespaceToken, ) -> RuntimeResult<ImportSummary>
Import from a JSON string (convenience wrapper around import_kg).
Source§impl KhiveRuntime
impl KhiveRuntime
Sourcepub async fn embed(&self, text: &str) -> RuntimeResult<Vec<f32>>
pub async fn embed(&self, text: &str) -> RuntimeResult<Vec<f32>>
Generate an embedding vector for text using the configured default model.
First call lazily loads model weights (cold start cost). Subsequent calls reuse them.
Returns Unconfigured("embedding_model") if no model is configured.
Sourcepub async fn embed_with_model(
&self,
model_name: &str,
text: &str,
) -> RuntimeResult<Vec<f32>>
pub async fn embed_with_model( &self, model_name: &str, text: &str, ) -> RuntimeResult<Vec<f32>>
Generate an embedding vector for text using the named model.
Accepts both built-in lattice model names/aliases and custom provider
names registered via KhiveRuntime::register_embedder. For lattice
models the resolved EmbeddingModel enum is forwarded to embed_one
so the service can select the correct model variant. For custom
providers, embed_one is called with EmbeddingModel::default()
because custom services are expected to ignore the enum argument (they
own a single model implicitly).
Applies no instruction prefix (generic role). Use
Self::embed_document_with_model / Self::embed_query_with_model for
instruction-tuned models where the asymmetric prefix matters.
Returns UnknownModel if model_name is not in the embedder registry.
Sourcepub async fn embed_document_with_model(
&self,
model_name: &str,
text: &str,
) -> RuntimeResult<Vec<f32>>
pub async fn embed_document_with_model( &self, model_name: &str, text: &str, ) -> RuntimeResult<Vec<f32>>
Embed a document/passage for indexing using the named model.
Applies EmbeddingService::embed_passage, which prepends the model’s
document_instruction() prefix when defined (e.g. "passage: " for
multilingual-e5). For models with no document prefix (MiniLM, BGE) this
is identical to Self::embed_with_model.
Use this for all index/store/backfill paths so that instruction-tuned models produce passage-side vectors.
Reindex caveat: switching from an unprefixed model (or a model with no
document_instruction) to an instruction-tuned model changes the vector
representation. Vectors stored under the old scheme are not comparable to
newly prefixed vectors. Operators must trigger a full reindex
(knowledge.index(rebuild_ann=true) / kkernel reindex) after changing
the embedding model config.
Returns UnknownModel if model_name is not registered.
Sourcepub async fn embed_query_with_model(
&self,
model_name: &str,
text: &str,
) -> RuntimeResult<Vec<f32>>
pub async fn embed_query_with_model( &self, model_name: &str, text: &str, ) -> RuntimeResult<Vec<f32>>
Embed a query string for retrieval using the named model.
Applies EmbeddingService::embed_query, which prepends the model’s
query_instruction() prefix when defined (e.g. "query: " for
multilingual-e5). For models with no query prefix (MiniLM, BGE) this
is identical to Self::embed_with_model.
Use this for all search/recall/suggest query embedding paths so that instruction-tuned models land in the correct side of their retrieval space.
Returns UnknownModel if model_name is not registered.
Sourcepub async fn embed_document(&self, text: &str) -> RuntimeResult<Vec<f32>>
pub async fn embed_document(&self, text: &str) -> RuntimeResult<Vec<f32>>
Embed a document for indexing using the configured default model.
Delegates to Self::embed_document_with_model. Use for entity/note
create and reindex paths.
Returns Unconfigured("embedding_model") if no model is configured.
Sourcepub async fn embed_query(&self, text: &str) -> RuntimeResult<Vec<f32>>
pub async fn embed_query(&self, text: &str) -> RuntimeResult<Vec<f32>>
Embed a query for retrieval using the configured default model.
Delegates to Self::embed_query_with_model. Use for vector search and
hybrid search query paths.
Returns Unconfigured("embedding_model") if no model is configured.
Sourcepub async fn embed_batch(
&self,
texts: &[String],
) -> RuntimeResult<Vec<Vec<f32>>>
pub async fn embed_batch( &self, texts: &[String], ) -> RuntimeResult<Vec<Vec<f32>>>
Generate embeddings for multiple texts in one call using the configured default model.
Delegates to the cached EmbeddingService::embed, so repeated texts within
and across calls benefit from the runtime-level LRU cache.
Returns an empty vec for empty input without hitting the embedding service.
Returns Unconfigured("embedding_model") if no model is configured.
Sourcepub async fn embed_batch_with_model(
&self,
model_name: &str,
texts: &[String],
) -> RuntimeResult<Vec<Vec<f32>>>
pub async fn embed_batch_with_model( &self, model_name: &str, texts: &[String], ) -> RuntimeResult<Vec<Vec<f32>>>
Generate embeddings for multiple texts using the named model.
Accepts lattice model names/aliases and custom provider names.
Returns UnknownModel if model_name is not in the embedder registry.
Sourcepub async fn embed_document_batch_with_model(
&self,
model_name: &str,
texts: &[String],
) -> RuntimeResult<Vec<Vec<f32>>>
pub async fn embed_document_batch_with_model( &self, model_name: &str, texts: &[String], ) -> RuntimeResult<Vec<Vec<f32>>>
Embed a batch of documents for indexing using the named model.
Applies EmbeddingService::embed_passage. Use for all bulk
index/backfill/reindex operations to apply the passage-side prefix.
Reindex caveat: see Self::embed_document_with_model — the same
incomparability applies to batch-indexed vectors when switching models.
Returns UnknownModel if model_name is not registered.
Sourcepub async fn embed_document_batch(
&self,
texts: &[String],
) -> RuntimeResult<Vec<Vec<f32>>>
pub async fn embed_document_batch( &self, texts: &[String], ) -> RuntimeResult<Vec<Vec<f32>>>
Embed a batch of documents for indexing using the configured default model.
Convenience delegate to Self::embed_document_batch_with_model. Use for
bulk knowledge-atom and section indexing paths.
Returns Unconfigured("embedding_model") if no model is configured.
Sourcepub async fn embed_query_batch_with_model(
&self,
model_name: &str,
texts: &[String],
) -> RuntimeResult<Vec<Vec<f32>>>
pub async fn embed_query_batch_with_model( &self, model_name: &str, texts: &[String], ) -> RuntimeResult<Vec<Vec<f32>>>
Embed a batch of queries for retrieval using the named model.
Applies EmbeddingService::embed_query. Use for bulk query-side
operations where multiple queries need instruction-tuned prefixing.
Returns UnknownModel if model_name is not registered.
Sourcepub async fn vector_search(
&self,
token: &NamespaceToken,
query_embedding: Option<Vec<f32>>,
query_text: Option<&str>,
top_k: u32,
kind: Option<SubstrateKind>,
) -> RuntimeResult<Vec<VectorSearchHit>>
pub async fn vector_search( &self, token: &NamespaceToken, query_embedding: Option<Vec<f32>>, query_text: Option<&str>, top_k: u32, kind: Option<SubstrateKind>, ) -> RuntimeResult<Vec<VectorSearchHit>>
Search vectors using either a caller-provided embedding or query text.
Existing callers pass query_embedding: Some(vec) to avoid re-embedding.
Text callers pass query_embedding: None, query_text: Some(...) and the
runtime embeds internally.
Sourcepub async fn hybrid_search(
&self,
token: &NamespaceToken,
query_text: &str,
query_vector: Option<Vec<f32>>,
limit: u32,
entity_kind: Option<&str>,
entity_type: Option<&str>,
tags_any: &[String],
properties_filter: Option<&Value>,
) -> RuntimeResult<Vec<SearchHit>>
pub async fn hybrid_search( &self, token: &NamespaceToken, query_text: &str, query_vector: Option<Vec<f32>>, limit: u32, entity_kind: Option<&str>, entity_type: Option<&str>, tags_any: &[String], properties_filter: Option<&Value>, ) -> RuntimeResult<Vec<SearchHit>>
Hybrid search: text (FTS5) + vector retrieval fused via Reciprocal Rank Fusion.
- Always performs text search over
query_text. - If
query_vectorisSome, also performs vector search and fuses both lists. - If
None, returns text-only results — no vector store needed. - If
entity_kindisSome, the alive-set query filters to that kind. The text/vector candidate pools are unfiltered up front; the kind filter applies at the alive-check stage where we already fetch each candidate to confirm it isn’t soft-deleted. tags_any: when non-empty, only entities that have at least one of these tags (case-insensitive) survive the alive-set intersection. Applied BEFORE truncation so matches ranked beyondlimitin the raw fusion are not lost.properties_filter: whenSome, only entities whose properties are a superset of the given JSON object survive. Applied BEFORE truncation.
limit caps the final returned list; internally pulls limit * 4 candidates per path.
The fused candidate set is kept untruncated until after the alive + kind + tag +
properties filter so that matching hits ranked below limit in the raw fusion
still surface when higher-ranked candidates are excluded by any filter.
§Cross-namespace visibility (entity search — primary namespace only; deferred)
Both the FTS leg and the vector/ANN leg of entity search (hybrid_search)
are restricted to the primary namespace only.
Rationale: each namespace owns a separate FTS table (fts_entities_{ns})
and a separate ANN index instance. Cross-namespace entity-search fanout
requires iterating over every visible namespace’s store, issuing parallel
search requests, and fusing the results — this is deferred (entity-search
cross-namespace fanout is the outstanding follow-up).
Note: this is distinct from memory recall’s cross-namespace fanout, which
ships in ADR-007 Rev 4 (memory.recall iterates visible_namespaces across
both the FTS and vector legs). Entity search fanout is the remaining deferred
piece; memory recall fanout is not deferred.
The visible_ns list is forwarded in the TextFilter.namespaces field,
which limits results to those namespaces within the primary store. Because
entities from extra namespaces live in their own FTS tables, this filter has
no cross-namespace effect today.
Callers with a multi-namespace visible set can READ cross-namespace entities
directly via get_entity / resolve, but hybrid_search returns only
primary-namespace hits until entity-search cross-namespace fanout ships.
Sourcepub async fn knn(
&self,
token: &NamespaceToken,
query_vector: Vec<f32>,
top_k: u32,
) -> RuntimeResult<Vec<VectorSearchHit>>
pub async fn knn( &self, token: &NamespaceToken, query_vector: Vec<f32>, top_k: u32, ) -> RuntimeResult<Vec<VectorSearchHit>>
Exact KNN over the full namespace’s vector store.
sqlite-vec uses brute-force cosine — results are exact, not approximate. Cost is O(N · D) per query. For small-to-medium namespaces (~hundreds of thousands of vectors) this is well within latency budgets.
Sourcepub async fn rerank(
&self,
token: &NamespaceToken,
query_vector: &[f32],
candidate_ids: &[Uuid],
top_k: u32,
) -> RuntimeResult<Vec<VectorSearchHit>>
pub async fn rerank( &self, token: &NamespaceToken, query_vector: &[f32], candidate_ids: &[Uuid], top_k: u32, ) -> RuntimeResult<Vec<VectorSearchHit>>
Exact KNN restricted to a candidate set.
Useful for reranking the top-N results from hybrid_search (or any other
retrieval path) with exact cosine similarity against a query vector.
Returns hits sorted by similarity (highest first), truncated to top_k.
Sourcepub async fn backfill_missing_embeddings(
&self,
token: &NamespaceToken,
) -> RuntimeResult<u64>
pub async fn backfill_missing_embeddings( &self, token: &NamespaceToken, ) -> RuntimeResult<u64>
Backfill vector and FTS index entries for entities and notes that are missing them.
Intended to run once at startup as a background task (warm-up sequence steps 2–4). Queries the SQL substrate for entity descriptions and note contents that have no corresponding entry in the vector store for any registered embedding model, then embeds and inserts them. FTS entries missing for notes are also repopulated.
The operation is best-effort: individual embed/insert failures are logged and skipped rather than aborting the whole backfill. If no embedding models are registered, returns immediately with 0.
Returns the total number of records backfilled across all models.
Sourcepub async fn sweep_orphan_vectors(
&self,
token: &NamespaceToken,
max_delete_per_model: u32,
dry_run: bool,
) -> RuntimeResult<u64>
pub async fn sweep_orphan_vectors( &self, token: &NamespaceToken, max_delete_per_model: u32, dry_run: bool, ) -> RuntimeResult<u64>
Sweep orphaned vector entries for all registered embedding models.
A vector entry is orphaned when its subject_id no longer exists as a
live row in the entity or note tables (i.e. either the row is absent or
has deleted_at IS NOT NULL). Orphaned entries accumulate after
hard-deletes because the vector store and SQL substrate are decoupled.
Iterates over every registered embedding model and calls
khive_storage::VectorStore::orphan_sweep for the token’s namespace. Models whose
backend returns khive_storage::StorageError::Unsupported are skipped without error —
this preserves forward-compat when a newly registered model does not yet
implement sweep.
Returns the total number of vector rows deleted across all models.
Source§impl KhiveRuntime
impl KhiveRuntime
Sourcepub fn new(config: RuntimeConfig) -> RuntimeResult<Self>
pub fn new(config: RuntimeConfig) -> RuntimeResult<Self>
Create a new runtime with the given config.
The config’s db_path is used to open or create the SQLite backend.
For the preferred boot path in multi-backend deployments, use
from_backend instead.
Sourcepub fn new_readonly(config: RuntimeConfig) -> RuntimeResult<Self>
pub fn new_readonly(config: RuntimeConfig) -> RuntimeResult<Self>
Open a runtime for read-only inspection (no model registration, no DB creation).
Runs migrations (idempotent) but skips register_configured_embedding_models,
so engine list / engine status cannot mutate the registry as a side effect.
Returns None when db_path is None and the default DB does not exist.
Sourcepub fn from_backend(backend: Arc<StorageBackend>, config: RuntimeConfig) -> Self
pub fn from_backend(backend: Arc<StorageBackend>, config: RuntimeConfig) -> Self
Construct a runtime from an already-opened backend.
This is the preferred constructor for multi-backend deployments. The caller
(boot path in kkernel or khive-mcp) opens each backend from khive.toml,
then constructs a KhiveRuntime per pack using this method.
The returned runtime has db_path = None and embedding_model = None; all
storage access is through the provided backend. Set backend_id and
default_namespace via the config builder pattern if non-defaults are needed.
Sourcepub fn with_core_backend(self, core: Arc<StorageBackend>) -> Self
pub fn with_core_backend(self, core: Arc<StorageBackend>) -> Self
Wire this runtime as a secondary-backend runtime pointing at core.
After this call, self.core() returns a handle to core rather than
cloning self. The caller (the boot path, not pack code) is responsible
for passing the correct main backend.
Panics in debug builds if self.config.backend_id == BackendId::MAIN,
because the main runtime does not need a core pointer.
Sourcepub fn core(&self) -> KhiveRuntime
pub fn core(&self) -> KhiveRuntime
Return a runtime handle bound to the main (shared-graph) backend.
When self is already the main runtime (core_backend is None),
this returns a clone of self — no new backend reference is acquired.
When self is a secondary-backend runtime (core_backend is Some),
this returns a new KhiveRuntime backed by the main
Arc<StorageBackend> and sharing all registry state (embedder_registry,
edge_rules, valid_entity_kinds, valid_note_kinds,
entity_type_validator) with self.
No database I/O occurs; no embedding models are reloaded.
Use core() for notes and entities that must reside in the shared graph
so that memory.recall, cross-pack search, and annotates edges work.
Use self (or self.sql()) for pack-auxiliary bulk tables.
Handlers that call core() more than once per request or loop should bind
let core = self.core(); once and reuse it, since each call clones
RuntimeConfig (a heap-allocated struct containing Vec<String> fields).
Sourcepub fn memory() -> RuntimeResult<Self>
pub fn memory() -> RuntimeResult<Self>
Create an in-memory runtime (for tests and ephemeral use).
Sourcepub fn backend_id(&self) -> &BackendId
pub fn backend_id(&self) -> &BackendId
Return the BackendId for this runtime’s backend.
Used by SubstrateCoordinator in kkernel
to identify which backend owns a given node, and to detect cross-backend merges.
Sourcepub fn visible_namespaces(&self) -> &[Namespace]
pub fn visible_namespaces(&self) -> &[Namespace]
Return the extra-visible namespaces assembled at config load.
OSS dispatch uses this set to widen the DEFAULT multi-record read scope
to ['local'] ∪ visible_namespaces (ADR-007 Rev 4 Rule 3b). Writes are
unchanged — always pinned to 'local'. This set is also available as
gate/cloud policy input.
Sourcepub fn config(&self) -> &RuntimeConfig
pub fn config(&self) -> &RuntimeConfig
Return a reference to the runtime config.
Sourcepub fn backend(&self) -> &StorageBackend
pub fn backend(&self) -> &StorageBackend
Return a reference to the underlying storage backend.
Sourcepub fn backend_data_dir(&self) -> Option<PathBuf>
pub fn backend_data_dir(&self) -> Option<PathBuf>
Return the directory containing the backend’s database file, or None
for an in-memory backend.
Sourcepub fn entities(
&self,
token: &NamespaceToken,
) -> RuntimeResult<Arc<dyn EntityStore>>
pub fn entities( &self, token: &NamespaceToken, ) -> RuntimeResult<Arc<dyn EntityStore>>
Get an EntityStore scoped to the token’s namespace.
Sourcepub fn graph(
&self,
token: &NamespaceToken,
) -> RuntimeResult<Arc<dyn GraphStore>>
pub fn graph( &self, token: &NamespaceToken, ) -> RuntimeResult<Arc<dyn GraphStore>>
Get a GraphStore scoped to the token’s namespace.
Sourcepub fn notes(&self, token: &NamespaceToken) -> RuntimeResult<Arc<dyn NoteStore>>
pub fn notes(&self, token: &NamespaceToken) -> RuntimeResult<Arc<dyn NoteStore>>
Get a NoteStore scoped to the token’s namespace.
Sourcepub fn events(
&self,
token: &NamespaceToken,
) -> RuntimeResult<Arc<dyn EventStore>>
pub fn events( &self, token: &NamespaceToken, ) -> RuntimeResult<Arc<dyn EventStore>>
Get an EventStore scoped to the token’s namespace.
Sourcepub fn sql(&self) -> Arc<dyn SqlAccess> ⓘ
pub fn sql(&self) -> Arc<dyn SqlAccess> ⓘ
Get the raw SQL access capability (for ad-hoc queries).
Sourcepub fn vectors(
&self,
token: &NamespaceToken,
) -> RuntimeResult<Arc<dyn VectorStore>>
pub fn vectors( &self, token: &NamespaceToken, ) -> RuntimeResult<Arc<dyn VectorStore>>
Get a VectorStore for the configured embedding model, scoped to the token’s namespace.
Returns Unconfigured("embedding_model") if no model is set.
Sourcepub fn vectors_for_model(
&self,
token: &NamespaceToken,
model_name: &str,
) -> RuntimeResult<Arc<dyn VectorStore>>
pub fn vectors_for_model( &self, token: &NamespaceToken, model_name: &str, ) -> RuntimeResult<Arc<dyn VectorStore>>
Get a VectorStore for a specific named embedding model, scoped to the token’s namespace.
Accepts both built-in lattice model names/aliases and custom provider names
registered via register_embedder. Lattice names
are routed through the enum-backed path; custom provider names use the
provider’s declared dimensions() directly so that the vector store key
is consistent with how vectors were written during remember/recall.
Sourcepub fn text(&self, token: &NamespaceToken) -> RuntimeResult<Arc<dyn TextSearch>>
pub fn text(&self, token: &NamespaceToken) -> RuntimeResult<Arc<dyn TextSearch>>
Get a TextSearch index for the entity corpus (single shared table).
Sourcepub fn text_for_notes(
&self,
token: &NamespaceToken,
) -> RuntimeResult<Arc<dyn TextSearch>>
pub fn text_for_notes( &self, token: &NamespaceToken, ) -> RuntimeResult<Arc<dyn TextSearch>>
Get a TextSearch index for the notes corpus (single shared table).
Mint an authorization token for the given namespace.
Consults the configured crate::Gate before minting. With the default
AllowAllGate this always succeeds. When a real policy-backed gate is
installed, this method enforces it and returns PermissionDenied on
denial.
The returned token’s read visibility set defaults to [ns] — identical
to the pre-visibility-set behaviour. Use Self::authorize_with_visibility
to mint a token that can read additional namespaces.
When actor_id is configured in RuntimeConfig, the token carries that
actor label so that comm.inbox filters by to_actor (ADR-057). When
unconfigured, the token carries ActorRef::anonymous() and inbox falls
back to party-line behavior.
Mint an authorization token with an explicit read-visibility set.
primary is the write namespace — all records created via the
returned token land there. extra_visible lists additional namespaces
the token may read. The primary is always included in the visible set
regardless of extra_visible.
Usage (lambda:leo reading both leo and khive namespaces):
let tok = rt.authorize_with_visibility(
Namespace::parse("lambda:leo").unwrap(),
vec![Namespace::parse("lambda:khive").unwrap()],
)?;Sourcepub fn install_edge_rules(&self, rules: Vec<EdgeEndpointRule>)
pub fn install_edge_rules(&self, rules: Vec<EdgeEndpointRule>)
Install the pack-aggregated edge endpoint rules.
Called by the transport layer after the VerbRegistry is built so
that runtime-layer edge validation can consult pack rules. Idempotent:
later calls overwrite the previous rule set.
Sourcepub fn install_kind_registry(
&self,
entity_kinds: Vec<String>,
note_kinds: Vec<String>,
)
pub fn install_kind_registry( &self, entity_kinds: Vec<String>, note_kinds: Vec<String>, )
Install the pack-aggregated valid entity and note kinds.
Called by the transport layer after the VerbRegistry is built so that
runtime-layer entity/note creation and import validate kind strings against
the merged pack vocabulary. Idempotent: later calls overwrite previous sets.
When no kinds are installed (empty lists), kind validation is skipped at the runtime layer. The pack handler layer remains the primary enforcement point; this provides defense-in-depth for direct Rust callers and import.
Sourcepub fn install_entity_type_validator(&self, f: EntityTypeValidatorFn)
pub fn install_entity_type_validator(&self, f: EntityTypeValidatorFn)
Install a pack-supplied entity-type validator (ADR-004 §runtime-layer validation).
Called by the KgPack during registration so that create_many can validate
entity_type values at the runtime layer, closing the hole where direct Rust
callers bypass the handler-layer validate_entity_type check.
The callback receives (kind, entity_type) and returns the normalised type
string, or RuntimeError::InvalidInput if the type is not registered for that
kind. Passing entity_type = None must return Ok(None).
Sourcepub fn default_embedder_name(&self) -> &str
pub fn default_embedder_name(&self) -> &str
Return the name of the default embedding model (empty string if none configured).
Sourcepub fn resolve_embedding_model(
&self,
name: Option<&str>,
) -> RuntimeResult<EmbeddingModel>
pub fn resolve_embedding_model( &self, name: Option<&str>, ) -> RuntimeResult<EmbeddingModel>
Resolve a model name (or None for the default) to an EmbeddingModel.
Returns UnknownModel if the name is not in the registry, or
Unconfigured if None is passed and no default model is set.
Sourcepub fn registered_embedding_model_names(&self) -> Vec<String>
pub fn registered_embedding_model_names(&self) -> Vec<String>
Names of all registered embedding models in this runtime.
Includes both built-in lattice models and any custom embedders
registered by packs via register_embedder.
Useful for operations that must touch every model’s storage (e.g.,
scoped vector deletion on note delete — codex High 2 (PR #407)).
The default model is included.
Sourcepub async fn embedder(
&self,
name: &str,
) -> RuntimeResult<Arc<dyn EmbeddingService>>
pub async fn embedder( &self, name: &str, ) -> RuntimeResult<Arc<dyn EmbeddingService>>
Get the lazily-initialized embedding service for the named model.
Accepts both built-in lattice model names (e.g. "all-minilm-l6-v2",
"paraphrase") and custom provider names registered via
register_embedder.
For lattice model names, aliases (e.g. "paraphrase") are resolved to
their canonical key before looking up the registry. For custom providers
the name must match exactly as supplied during registration.
First call for any name loads the underlying service (cold start cost);
subsequent calls are cheap (registry caches the Arc).
Sourcepub fn register_embedder(&self, provider: impl EmbedderProvider + 'static)
pub fn register_embedder(&self, provider: impl EmbedderProvider + 'static)
Register a custom embedding provider with this runtime.
The provider is added to the shared EmbedderRegistry so all clones
of this runtime see the new provider immediately. If a provider with the
same name already exists it is replaced (last-writer wins — see
crate::EmbedderRegistry::register for the rationale).
Packs should call this from crate::PackRuntime::register_embedders (the
hook is invoked by the transport during pack initialisation, before the
first verb dispatch).
Sourcepub async fn list_embedding_models(
&self,
engine_filter: Option<&str>,
) -> RuntimeResult<Vec<EmbeddingModelRegistryRecord>>
pub async fn list_embedding_models( &self, engine_filter: Option<&str>, ) -> RuntimeResult<Vec<EmbeddingModelRegistryRecord>>
List registered embedding models via SqlAccess, routing through the
existing connection pool rather than opening a fresh Connection per call.
Optionally filter by engine_name. Returns an empty vec when the
_embedding_models table does not yet exist (e.g. no migrations have run
or no models have been registered). All other SQL errors are propagated.
Trait Implementations§
Source§impl Clone for KhiveRuntime
impl Clone for KhiveRuntime
Source§fn clone(&self) -> KhiveRuntime
fn clone(&self) -> KhiveRuntime
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more