Layers and the handshake¶
Built and verified
All three layers and the memory backend are built: the actor models and the
memory backend (memory), Embedder with local_hash and
random_projection (embedding), Structure with flat and
segment_tree (structure), Strategy with default (strategy) and
the ContinuityEngine (engine).
coniti has three layers. The memory backend sits underneath all of them.
flowchart LR
subgraph Actor["Actor layer"]
N["Note, TimeRange, Query"]
end
subgraph Broker["Broker layer"]
EN["ContinuityEngine"]
ST["Strategy"]
end
subgraph System["System layer"]
EM["Embedder"]
SC["Structure"]
end
MB["MemoryBackend"]
N <--> EN
EN --> EM
EN --> ST
ST --> SC
EN --> SC
EN <--> MB
| Layer | Role |
|---|---|
| Actor | Notes, time ranges and questions. No storage, no vectors to handle. Built. |
| System | Embedder gives each message a vector. Structure organises vectors over time. |
| Broker | Strategy decides what to admit and how to answer. The engine translates between actor objects and system inputs, and back. |
The actor layer¶
The actor is whoever works with the library: a human, an agent, or any other party. A human is one kind of actor.
Built and verified
Import from coniti (they are defined in coniti.actor). These models hold no storage: storing notes is the
memory backend’s job (memory).
from datetime import datetime, timezone
from coniti import Note, Query, TimeRange
note = Note.create("met Ana about the budget", project="q3") # random id, time = now
dated = Note.create("kick-off", timestamp=datetime(2026, 1, 5, tzinfo=timezone.utc))
window = TimeRange(start=dated.timestamp, end=None) # [start, forever)
query = Query(text="budget", time_range=window, k=5)
Note(id, text, timestamp, metadata) is frozen. id is a non-empty string.
metadata must be JSON-serialisable and finite, and is deep-copied on the way in,
so later edits to your dict do not change the note. Note.create(text,
timestamp=None, **metadata) gives a random id and defaults the time to now.
Timestamp is seconds since the Unix epoch as a finite float. A
timezone-aware datetime is accepted and converted. These are rejected: naive
datetimes (their meaning depends on the machine’s timezone), booleans, NaN and
infinity.
TimeRange(start, end) is half-open, [start, end). Either side may be
None (unbounded). Bounds must be finite and start <= end. contains(t) tests
membership and is_empty is true when start == end.
Query(text, time_range, near, k) needs at least one of text, time_range
or near. near is the id of a note to recall around and must be non-empty (the
engine will pass it to structures as anchor_id). k is the number of results,
between 1 and 10000 (default 10).
The handshake¶
Built and verified
The handshake below is what the ContinuityEngine does; see engine.
- Ingest:
Note→ embed → strategy decides to admit → insert into every structure → write to memory last. If a structure fails, the engine rebuilds the structures from memory (memory was not written yet), so ingest is atomic. - Ask:
Query→ embed the text if any → strategy queries the structures → hits are resolved to notes →Answer. - Remove: delete from memory, then rebuild every structure from memory. Structures are derived data.
There is no session object. All state is the memory backend plus the structures derived from it.