Skip to main content
Everything on this page is part of Khora’s stable public API, pinned by __all__ in khora/__init__.py. Additive changes land in minor releases; breaking changes require a major bump. Private imports (khora.engines.*, khora.query.engine, khora.pipelines.flows) are not stable.

Khora

The primary facade. Delegates to a pluggable engine (default vectorcypher).
Pass a PostgreSQL URL or a full KhoraConfig, or nothing, to read KHORA_DATABASE_URL / KHORA_NEO4J_URL. run_migrations=True runs Alembic under an advisory lock on connect. Credential fields are pydantic.SecretStr (rendered as '**********', call .get_secret_value() to read).
await Khora.shared() returns a process-wide, already-connected singleton (created and connected on first call, reused after). Callers must not disconnect() it: its lifetime is the process. await Khora.shared.clear() drops the cached instance and is test-only.

Namespaces

create_namespace is keyword-only, no positional name. Use ns.namespace_id (the stable public id) everywhere below, not the row-level ns.id. See Namespaces & isolation. delete_namespace is the inverse: it cascade-removes the namespace’s documents, chunks, entities, relationships, graph nodes, and vector rows from every backend, then drops the namespace row(s) and frees the storage. namespace can be the stable namespace_id or any version’s row id, and all versions under that stable id go together, whether the namespace is active or deactivated. Afterwards it no longer lists or resolves, and recall / stats / forget on it raise ValueError. A cross-backend delete can partially fail (say the graph backend is unreachable). Rather than raise, the failing backend is recorded as a degradation on the result and the rest still run, so check result.partial_failure to catch a half-deleted namespace. It is safe to re-run.

Writing

remember

Ingests through the three-phase pipeline (see Ingestion). metadata and the provenance kwargs (source_name, source_type, …) are denormalized onto chunks and become filterable at recall (see Recall filters). session_id propagates to the document and its chunks for session-scoped recall and forget_session.

remember_batch

Concurrent ingestion with cross-document dedup. Each dict accepts the same per-document fields as remember(), and per-doc values override the top-level kwargs.

submit_batch

Deferred ingestion: stages every doc as PENDING, returns immediately. Requires kb.start_pending_processor() (after connect()) or it raises. await handle.wait() blocks until every document’s on_result has fired.

Reading

recall

filter= is a deterministic RecallFilter (or its dict form) applied as a hard predicate alongside the ranking. start_time / end_time are deprecated in favor of filter={"occurred_at": {...}} and cannot be combined with it. Fusion weights, reranking, HyDE, and recency are global (KhoraConfig.query / KHORA_QUERY_*). There’s no config= kwarg. See Retrieval and Recall filters.

context_text

Renders a RecallResult into a flat LLM-context string (chunks grouped by document title, then --- Entities --- and --- Relationships --- sections).

Entity & document reads

namespace is required on these (accepts str | UUID). Cross-namespace ids resolve to None / empty rather than leaking the foreign row. The isolation contract holds at every layer (see Namespaces & isolation). search_entities ranks entities by embedding similarity to query. On the reads that take include_sources, pass True to populate each entity’s source-document metadata (an extra lookup, off by default).

Community reads

Both return list[CommunityNode] (a summary string plus member_ids), the community summaries the dream phase materializes into the graph. get_communities lists a namespace’s communities; get_entity_communities returns the ones a given set of entities belong to. Read-only, and empty on a stack without a graph backend or without materialized communities.

Deleting

forget_session cascade-deletes every document tagged with session_id (chunks via FK cascade, graph cleanup via the engine). For TTL cleanup, the opt-in helper khora.gc.expire_sessions(*, kb, before, namespace_id=None) calls forget_session for each session whose newest document predates before. Khora runs no scheduler. Call it from your own loop.

Background processing & health

submit_batch needs the pending processor running: call start_pending_processor() after connect() on services that write documents (read-only services skip it), and stop_pending_processor() to cancel the background worker (it can be restarted). health_check() returns a per-component health dict, or {"status": "disconnected"} before connect().

Result types

All result types are frozen, slotted dataclasses. RememberResult: document_id, namespace_id, chunks_created, entities_extracted, relationships_created, relationships_skipped (un-remappable edges the ingest pipeline dropped, always 0 outside the shared pipeline), metadata, llm_usage. BatchResult: total / processed / skipped / failed, chunks / entities / relationships, metadata, llm_usage, per_document (one entry per submitted document in input order, mapping each back to its stored document_id; populated by VectorCypher, may be empty on other engines). BatchHandle: batch_id, total, the read-only properties completed / failed / is_done, and await handle.wait(). DocumentResult (per-doc on_result payload): document_id, namespace_id, success, error, per-doc counts, llm_usage, skipped, external_id (the caller’s Document.external_id, for mapping a result back to its source row). Stats: documents / chunks / entities / relationships, last_activity_at. NamespaceDeletionResult (from delete_namespace): namespace_id, removed_row_ids, the removed counts namespaces_removed / documents_removed / chunks_removed / vector_rows_removed / graph_nodes_removed, degradations, and the partial_failure property (True when any backend purge failed). CommunityNode (from get_communities / get_entity_communities): a materialized dream community: id, namespace_id, summary, member_ids, summary_depth, and an optional embedding. UsageSummary (from khora import UsageSummary): an aggregate over a list of LLMUsage. Carries total_prompt_tokens, total_completion_tokens, total_tokens, total_cost_usd, total_latency_ms, and the by_operation / by_model breakdowns. Build one with UsageSummary.from_usage(result.llm_usage).

RecallResult

Producer invariant: every chunks[i].document_id and every id in entities[i].source_document_ids / relationships[i].source_document_ids appears in documents[]. RecallChunk carries id, document_id, content, score, created_at, occurred_at, connected_entity_ids, chunker_info.

engine_info["filter"]

On a recall(filter=...), engine_info["filter"] carries a FilterPushdownReport: an honest account of how the filter was handled. Both it and the per-channel FilterChannelReport are public (exported from khora). pushed_keys, post_filtered_keys, and unenforced_keys form a total, disjoint partition of the filter’s constraint leaves. See Recall filters for the full filter grammar.

SearchMode

Engines

vectorcypher is the default and the engine these docs cover. Prefer the engine= argument to Khora(...) over create_engine directly. Custom engines must implement the full MemoryEngineProtocol. See VectorCypher.

Expertise

ExpertiseConfig (a stable public API) defines a domain ontology, with entity/relationship types plus a system prompt, correlation rules, and inference rules. See Expertise & ontologies for the full guide:

Hooks & errors

kb.subscribe(event_type, callback, filter=None) / kb.unsubscribe(id) / kb.hooks. For delivery that survives a restart, kb.subscribe_persistent(event_type, delivery, *, filter=None, namespace_id=None) / kb.unsubscribe_persistent(id) record a webhook or queue target to PostgreSQL. See Semantic hooks. All domain errors subclass KhoraError. Catch it at system boundaries.

Ingestion

What remember() / remember_batch() / submit_batch() do under the hood.

Retrieval

What recall() does and how to read a RecallResult.