pub struct Engine { /* private fields */ }Expand description
The Keel kernel, Tier 1 scope. One per process; &self-concurrent.
A journal and/or discovery store are optional attachments: the engine is
fully functional without either, and neither can change a call’s outcome —
their I/O failures degrade to a warn! (resilience first, honest reporting).
Implementations§
Source§impl Engine
impl Engine
pub fn new() -> Self
Sourcepub fn attach_journal(&mut self, journal: impl Journal + 'static) -> &mut Self
pub fn attach_journal(&mut self, journal: impl Journal + 'static) -> &mut Self
Attach a journal at construction time, enabling the persistent cache
scope. Optional; set at setup, before the engine is shared for
concurrent execute. A later configure whose
policy carries a journal key replaces this attachment — the effective
policy is authoritative (spec §4.2).
Sourcepub fn journal(&self) -> Option<Arc<dyn Journal>>
pub fn journal(&self) -> Option<Arc<dyn Journal>>
The attached journal, if any — shared (Arc) so a FlowManager can run
its Tier 2 steps over the same store the engine caches through. None
for an in-memory engine (Tier 2 requires a durable journal). Live: a
configure whose policy selects a journal location changes what this
returns, so Tier 2 wiring should read it after the engine is configured.
Sourcepub fn attach_discovery(
&mut self,
discovery: impl DiscoveryRecorder + 'static,
) -> &mut Self
pub fn attach_discovery( &mut self, discovery: impl DiscoveryRecorder + 'static, ) -> &mut Self
Attach a discovery store; each execute then records one observation.
Optional and failure-isolated — recording never affects an outcome.
Sourcepub fn attach_events(&mut self, sink: EventSink) -> &mut Self
pub fn attach_events(&mut self, sink: EventSink) -> &mut Self
Attach (or replace) a live event sink. Engine::new already resolves
one from the environment (see crate::events); this override exists
for tests and embedders that place the feed elsewhere.
Sourcepub fn events(&self) -> Option<&EventSink>
pub fn events(&self) -> Option<&EventSink>
The active event sink, if any — its run id anchors this engine’s trace
refs, and flush() makes the feed current for a same-process reader.
Sourcepub fn configure(&self, policy_json: &Value) -> Result<(), KeelError>
pub fn configure(&self, policy_json: &Value) -> Result<(), KeelError>
Apply a policy document (keel.toml as JSON, per
contracts/policy.schema.json), replacing the previous one atomically.
Rejections are KEEL-E001 with the exact offending field path;
a valid policy naming a journal backend this build cannot provide is
KEEL-E005 (and the previous policy stays in force).
Sourcepub fn telemetry_otlp_endpoint(&self) -> Option<String>
pub fn telemetry_otlp_endpoint(&self) -> Option<String>
The effective telemetry.otlp_endpoint (None if [telemetry] is
absent or the key unset), read live so a reconfigure is honored. Native
front ends built with the otel feature read this after configure and
pass it to [crate::otel::export_enabled] / [crate::otel::resolve_endpoint]
to decide whether/where to export (env vars still take precedence).
Sourcepub fn idempotency_header(&self, target: &str) -> Option<String>
pub fn idempotency_header(&self, target: &str) -> Option<String>
The target’s resolved idempotency = { header } knob, read live so a
reconfigure is honored. This is the engine surface of the injection
contract (contracts/adapter-pack.md “Idempotency-key injection”):
adapters/bindings consult it to mint and inject an idempotency key on
unsafe-method calls — the engine itself never injects, and the flipped
judgment reaches it as Request.idempotent = true.
Sourcepub fn resolve_target(
&self,
method: &str,
host: &str,
scheme: Option<&str>,
port: Option<u16>,
path: Option<&str>,
) -> String
pub fn resolve_target( &self, method: &str, host: &str, scheme: Option<&str>, port: Option<u16>, path: Option<&str>, ) -> String
Resolve the policy target key for one outbound request — the engine
surface of Policy::resolve_target (SP-1: LLM host map, exact
bare-host [target] key, most-specific pattern match, then the bare
host). Read live so a reconfigure is honored.
Sourcepub fn layer(&self, target: &str, key: &str) -> Value
pub fn layer(&self, target: &str, key: &str) -> Value
One resolved layer value as JSON (null when unset) — the front ends’
backend.layer(target, key) reader. Walks the RAW configured JSON
(never a typed re-serialization) with the same precedence as
Policy::resolve/the stubs’ _layer: an exact [target] entry for
target, else (for llm:* targets) defaults.llm, else
defaults.outbound, else null. Resolving over the raw document
(rather than serializing the typed ResolvedPolicy) preserves the
original config literal verbatim — e.g. "120s", not a re-serialized
DurationMs number — which is what callers actually need; see
DurationMs’s Deserialize-only impl (policy.rs).
Sourcepub fn known_llm_hosts() -> Vec<(&'static str, &'static str)>
pub fn known_llm_hosts() -> Vec<(&'static str, &'static str)>
Every host the LLM host map knows about, as (host, provider) pairs —
the engine surface of Policy::known_llm_hosts. Not read from
self: the map is a hardcoded constant, identical regardless of
which policy is configured, so this needs no live lock (issue #49).
Sourcepub fn nondeterminism_response(&self) -> NondeterminismResponse
pub fn nondeterminism_response(&self) -> NondeterminismResponse
The configured Tier 2 flows.on_nondeterminism response (default
NondeterminismResponse::Fail), read live so a reconfigure is honored.
The flow manager consults this when a replay (seq, step_key) diverges.
Sourcepub async fn execute<F>(&self, request: &Request, effect: F) -> Outcome
pub async fn execute<F>(&self, request: &Request, effect: F) -> Outcome
Run one intercepted call through the target’s layer chain, then record it
for discovery. effect performs a single attempt (1-based attempt
numbers). Always returns an Outcome — policy failures are outcomes, not
panics, and neither journal nor discovery I/O can change what’s returned.