Skip to content

Strategy

Built and verified

Strategy, its contracts, EngineContext (defined in coniti.broker; Strategy and EngineContext are also exported from coniti) and the default strategy (in coniti.impl.strategy) are built and tested, and the Container has a strategy slot. The engine that calls the strategy is described in engine.

Strategy

A Strategy decides how the system layer is used.

from coniti import EngineContext, Strategy
from coniti.broker import AdmitInput, AdmitOutput, RetrieveInput, RetrieveOutput

A strategy subclasses Strategy and implements one abstract method, _retrieve. Callers use the public methods, which check the contracts around your code:

Method Behaviour
admit(AdmitInput) Returns an AdmitOutput. Calls _admit, which admits everything unless you override it.
retrieve(RetrieveInput) Returns a RetrieveOutput from _retrieve.

Inside a strategy, self._context(inp) returns the input’s EngineContext as a typed value.

Contract Fields
AdmitInput note, vector, context
AdmitOutput admit (default True)
RetrieveInput query, query_vector (None when the query has no text), context
RetrieveOutput hits, groups, raw (each structure’s output, by name)
EngineContext embedder, memory, structures: how a strategy reaches the system

Like every contract, they may be extended by a subclass, never replaced (contracts). Strategies reach memory only through the context’s MemoryBackend.

The context is runtime-only

context holds live objects (the embedder, the memory, the structures), so it cannot be serialised. It is excluded from to_dict() and to_json() and from the JSON Schema. The consequence:

  • An input rebuilt with from_dict / from_json has context set to None.
  • Calling a strategy with such an input raises ContractError (“needs an EngineContext”), instead of failing later with an obscure error.
  • Set input.context = EngineContext(...) on the deserialised input before passing it on.

The default strategy

Kind default, class DefaultStrategy. It has no tuning params.

  • admit: admits every note.
  • retrieve: calls each structure in order. The input it builds is the structure’s own declared query_input, filled from the query (vector from query_vector, the time_range, k, and anchor_id from query.near). Each structure’s output is stored in raw under "<index>:<ClassName>" (for example "0:FlatStructure"). hits are the first non-empty hit list, and groups are collected from all structures. An error from a structure propagates.
from coniti import (
    Container, ContinuityEngine, Embedder, EngineContext, MemoryBackend, Note,
    Query, Strategy, Structure,
)
from coniti.broker import RetrieveInput

c = Container(search_cwd=False)       # defaults: in_memory, local_hash, [flat], default
ContinuityEngine(container=c).ingest_many([
    Note(id="n1", text="october product launch", timestamp=1.0),
    Note(id="n2", text="october launch plan", timestamp=2.0),
    Note(id="n3", text="apple pie recipe", timestamp=3.0),
])
embedder, memory = c.get(Embedder), c.get(MemoryBackend)
structures = c.get_all(Structure)

ctx = EngineContext(embedder=embedder, memory=memory, structures=structures)
q = Query(text="october launch", k=2)
out = c.get(Strategy).retrieve(
    RetrieveInput(query=q, query_vector=embedder.vector(q.text), context=ctx)
)
[(h.id, round(h.score, 3)) for h in out.hits]   # [('n1', 0.816), ('n2', 0.816)]
list(out.raw)                                   # ['0:FlatStructure']

The Container provides the strategy like the other pieces; set it in coniti.toml with [strategy] and kind = "default" (see container).

Engine

The ContinuityEngine that uses the strategy (ingest, ask, Answer) is built: see engine.