EviCortex

API guide

This guide covers the public embedded-library API implemented in evicortex 0.3.0a0. The project is pre-alpha; pin a commit and review changes before upgrading an integration.

Imports

The common deterministic API is available from the package root:

from evicortex import ClaimInput, EviCortex, MemoryCapsule, MemoryRecord

Optional local Transformers support is imported separately so importing evicortex never loads a model framework:

from evicortex.providers import TransformersEmbedder

Open and close a store

EviCortex.open(
    path=":memory:",
    *,
    embedder=None,
    extractor=None,
    auto_index=True,
    semantic_scan_cap=1024,
    minimum_semantic_score=0.05,
    reranker=None,
    rerank_limit=16,
    reranker_weight=1.25,
) -> EviCortex

path accepts :memory:, a string path, or a pathlib.Path. Prefer a context manager so the SQLite connection closes deterministically:

with EviCortex.open("memory.db") as memory:
    ...

close() is idempotent. Operations after close raise StorageClosedError from evicortex.errors.

Provider identities and compatibility metadata are snapshotted at open time. Mutating a configured provider’s identity, dimensions, or normalization behavior causes validation to fail instead of silently corrupting an index.

Store exact evidence

memory.remember(
    *,
    namespace: str,
    content: str,
    kind: str = "episode",
    source: str = "",
    occurred_at: datetime | str | None = None,
    recorded_at: datetime | str | None = None,
    metadata: Mapping[str, Any] | None = None,
    idempotency_key: str | None = None,
    claims: Iterable[ClaimInput] = (),
) -> MemoryRecord

The evidence and explicit claims are validated and written atomically. content must be a non-empty UTF-8 string; metadata must encode a JSON object. Timestamps are normalized to UTC.

recorded_at normally defaults to ingestion time. Supply it only for a trusted replay or import where preserving the original recording time is intentional.

idempotency_key is unique per namespace. An identical replay returns the existing record; a different request with the same key raises IdempotencyConflictError.

ClaimInput

ClaimInput(
    subject: str,
    predicate: str,
    value: str,
    valid_from: datetime | str | None = None,
    valid_to: datetime | str | None = None,
    confidence: float = 1.0,
    authority: str = "application",
    supersedes: str | None = None,
)

confidence must be between 0 and 1. valid_to must be later than valid_from when both are supplied. supersedes is a prior claim ID in the same namespace; it creates a temporal revision without deleting the earlier claim or evidence.

MemoryRecord.claims contains the stored ClaimRecord values, including generated claim IDs and projected status.

Extract claims before storing

memory.remember_extracted(
    *,
    namespace: str,
    content: str,
    kind: str = "episode",
    source: str = "",
    occurred_at: datetime | str | None = None,
    recorded_at: datetime | str | None = None,
    metadata: Mapping[str, Any] | None = None,
    idempotency_key: str | None = None,
    claim_ids_by_source: Mapping[str, str] | None = None,
    extractor: ClaimExtractor | None = None,
) -> MemoryRecord

A per-call extractor overrides the one configured in open(). Extraction and complete span/provenance validation happen before the evidence transaction. Invalid provider output therefore cannot leave a partial memory.

Use claim_ids_by_source when an extractor grammar explicitly names the source of a claim to supersede. EviCortex never guesses that mapping.

Retrieve the persisted audit record with:

memory.get_extraction(namespace: str, memory_id: str) -> ExtractionRecord

The built-in diagnostic extractor can be exercised as follows:

from evicortex import CanonicalClaimExtractor, EviCortex

text = (
    "Ada selected dark mode. "
    "Canonical claim: subject=user:ada; predicate=prefers_theme; value=dark; "
    "valid_from=2026-04-18T09:30:00Z; confidence=1.000; authority=application."
)

with EviCortex.open(":memory:", extractor=CanonicalClaimExtractor()) as memory:
    record = memory.remember_extracted(namespace="demo/ada", content=text)
    audit = memory.get_extraction("demo/ada", record.id)

CanonicalClaimExtractor understands only this exact grammar. Implement the ClaimExtractor protocol for another provider and return ClaimExtraction with one exact ClaimProvenance span per claim plus any ExtractionDiagnostic values.

Recall and expand

memory.recall(
    *,
    namespace: str,
    query: str,
    budget_tokens: int = 512,
    as_of: datetime | str | None = None,
    limit: int = 20,
) -> MemoryCapsule

budget_tokens is a hard limit under the built-in UTF-8-bytes/3 estimator. limit bounds the returned candidate count; it does not override the budget. as_of selects valid-time state and is not a transaction-time known_at filter.

Important MemoryCapsule members are:

Member Purpose
facts Compact structured claims with evidence IDs and score reasons
episodes Exact-evidence excerpts, possibly marked as truncated
conflicts Competing claim values retained for the reader
evidence_ids Sorted unique IDs supporting packed facts and episodes
omitted_count Candidates not included in the hard estimated-token budget
trace Per-candidate score contributions and selection decisions
diagnostics Non-prompt operational facts, including optional-channel state
estimated_tokens Deterministic estimate of to_prompt()
to_prompt() Stable compact JSON for a downstream model

Retrieve an exact supporting record with:

memory.expand(*, namespace: str, memory_id: str) -> MemoryRecord

Missing IDs and namespace mismatches both raise KeyError; the API does not reveal whether another namespace owns the supplied ID.

Semantic projection lifecycle

Configure any object implementing the Embedder protocol:

from evicortex import EviCortex, HashingEmbedder

with EviCortex.open(
    "memory.db",
    embedder=HashingEmbedder(dimensions=256),
) as memory:
    record = memory.remember(namespace="demo/ada", content="Exact evidence")
    status = memory.semantic_status("demo/ada")
    assert status.complete

HashingEmbedder is non-semantic diagnostic feature hashing. It is suitable for testing projection persistence and repair, not for semantic-quality claims.

With auto_index=True, each successful evidence write is followed by semantic projection. Evidence is already committed when that provider call runs. A provider failure raises SemanticProjectionError with memory_id and evidence_committed=True; retry repair with a compatible provider rather than writing the evidence again.

The lifecycle methods are:

memory.index_semantic(record: MemoryRecord) -> EmbeddingUpsertResult
memory.semantic_status(namespace: str) -> SemanticIndexStatus
memory.sync_semantic(namespace: str, batch_size: int = 64) -> SemanticIndexStatus

Set auto_index=False for an explicit indexing workflow, then call index_semantic(record) or batch repairs with sync_semantic(namespace). SemanticIndexStatus reports memory and claim missing/stale IDs plus aggregate complete, registered, missing_count, and stale_count properties.

Exact semantic search is bounded by semantic_scan_cap. Overflow produces a diagnostic rather than silently claiming a complete exact scan. EviCortex does not yet provide an ANN index.

Optional dense embeddings

Install the optional dependencies:

python -m pip install -e ".[local-transformers]"

TransformersEmbedder defaults to offline loading. A remote model name must use a pinned 40-character commit, and the model/tokenizer must already be available in the local cache:

from evicortex import EviCortex
from evicortex.providers import TransformersEmbedder

embedder = TransformersEmbedder(
    "unsloth/bge-small-en-v1.5",
    revision="7382f1122c10708a1faa0bbe548674a14b1ffe7e",
    pooling="cls",
    query_prefix="Represent this sentence for searching relevant passages: ",
    document_prefix="",
    max_length=128,
    overflow_policy="error",
    local_files_only=True,
)

with EviCortex.open("memory.db", embedder=embedder) as memory:
    memory.sync_semantic("demo/ada")

This exact model configuration is the retained Phase C smoke configuration, not a general recommendation or quality guarantee. For a local filesystem model, calculate local_transformers_artifact_sha256(path) and pass the digest as local_artifact_sha256. Inputs longer than max_length fail by default instead of being silently truncated.

Optional reranking

An object implementing Reranker receives at most rerank_limit filtered, temporally valid, deduplicated RerankCandidate values. It returns finite RerankResult scores for known candidate IDs. The provider identity and ordinal contribution appear in retrieval traces.

EviCortex exposes the seam and validation contract but does not currently bundle a production cross-encoder adapter.

Integrity, repair, diagnostics, and purge

memory.verify_integrity(namespace: str | None = None) -> IntegrityReport
memory.rebuild_indexes() -> None
memory.stats(namespace: str | None = None) -> StoreStats
memory.purge_memory(*, namespace: str, memory_id: str) -> bool

Logical purge is not forensic erasure from copied databases, backups, SQLite journals, filesystem snapshots, or storage media. Define retention and backup deletion outside this library.

Errors and validation

Predictable storage exceptions live in evicortex.errors; semantic boundary exceptions most often used by applications are exported from the package root. Common cases include:

Exception Meaning
ValidationError Input violates a storage invariant
IdempotencyConflictError A key was reused for a different request
StorageClosedError The store has already closed
FTS5UnavailableError The SQLite build lacks FTS5
IntegrityViolationError Persisted state violates an invariant
SemanticNotConfiguredError A semantic-only operation has no embedder
SemanticProjectionError Evidence may be committed, but semantic projection failed
EmbeddingProfileNotFoundError A named projection profile is absent
EmbeddingValidationError A provider returned incompatible vectors

Validate and log these failures without logging sensitive evidence content. See FAQ and limitations for security and deployment boundaries.