A medium determines the shape of thought inside the circle. Implement this behaviour when conversation, code, and bash do not fit the natural surface of the work.
Behaviour for a circle medium.
A medium owns the "inside" of a circle: how capabilities are presented to the LLM, how an utterance is executed, and how medium-local state is captured for persistence or fork.
The runtime decides when an entity takes a turn; mediums decide what an LLM utterance means inside that turn. Code, bash, and conversation can therefore keep different execution semantics without hiding control flow inside the entity process.
Summary
Callbacks
Execute one model utterance inside the medium.
Return the LLM-facing presentation for this medium in the given circle.
Restore medium state from a snapshot.
Capture enough medium state to fork or persist an entity.
Types
@type circle() :: Cantrip.Circle.t()
@type execution_result() :: {:ok, medium_state(), [map()], term(), boolean()} | {:error, medium_state(), [map()]}
@type medium_state() :: map()
@type runtime() :: map()
Callbacks
@callback execute(term(), medium_state(), runtime()) :: execution_result()
Execute one model utterance inside the medium.
The returned boolean is the medium-level termination signal for the current episode. Gate failures should be represented as observations rather than process crashes when they are expected operational failures.
@callback present(circle(), medium_state()) :: presentation()
Return the LLM-facing presentation for this medium in the given circle.
Implementations should keep this pure. It is used to build the model request, not to execute host effects.
@callback restore(term()) :: medium_state()
Restore medium state from a snapshot.
@callback snapshot(medium_state()) :: term()
Capture enough medium state to fork or persist an entity.