Class: Insika::Safety::InputGuardrail
- Inherits:
-
Middleware
- Object
- Middleware
- Insika::Safety::InputGuardrail
- Defined in:
- lib/insika/safety/input_guardrail.rb
Overview
Input guardrail as a Middleware (RFC-0009 ยง3.1). It sits on the ONE seam that
already short-circuits structurally (stage 4: a link that does not call nxt),
and uses the NEW graceful-halt contract: instead of halt_reason (which the
Executor maps to a turn FAILURE), it sets halt_response (a safe reply) +
guardrail_block (audit metadata) and returns without calling nxt. The
Executor completes the turn with that safe reply, never touching the LLM.
Two tiers (D2):
1. deterministic scan (always, cheap, zero-token) โ Detectors#scan_input;
2. LLM moderator (opt-in per agent) โ only when the deterministic tier let
the message through, so the cheap layer short-circuits the expensive one.
Auto-disables per turn by reading guardrails: off the profile (Config): the
link lives ONCE in the global MiddlewareStack โ there is no per-agent stack.
moderator_factory (optional): ->(config) { Moderator | nil }, built by the
Safety::Factory. nil = deterministic only (Fase A/B parity).
Instance Method Summary collapse
- #call(state, &nxt) ⇒ Object
-
#initialize(moderator_factory: nil) ⇒ InputGuardrail
constructor
A new instance of InputGuardrail.
Constructor Details
#initialize(moderator_factory: nil) ⇒ InputGuardrail
Returns a new instance of InputGuardrail.
28 29 30 |
# File 'lib/insika/safety/input_guardrail.rb', line 28 def initialize(moderator_factory: nil) @moderator_factory = moderator_factory end |
Instance Method Details
#call(state, &nxt) ⇒ Object
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/insika/safety/input_guardrail.rb', line 32 def call(state, &nxt) config = Config.from_profile(state.profile) return nxt.call(state) unless config.input hit = Detectors.scan_input(state..to_s, categories: config.input_categories) return block(state, config, category: hit[:category], source: :deterministic, detail: hit[:matched]) if hit if config.moderator? && (mod = build_moderator(config)) verdict = mod.classify(state..to_s) if verdict.block? category = moderator_category(verdict) return block(state, config, category: category, source: :moderator, detail: verdict.reason, action: verdict.action) end end nxt.call(state) end |