Guardrail extensions
Input and output checks are separate public extension points in
Netresearch\. Both inherit Guardrail:
get is a stable logical policy name and
is declares whether configurations may omit it.
The registry resolves mandatory status per identifier and refuses mismatched
input/output declarations. Renaming an identifier changes stored policy.
InputGuardrail Interface:: check Input (string $text): Guardrail Result - Runs on outgoing message text before provider dispatch. Its
nr_autoconfiguration tag registers the check.llm. input_ guardrail GuardrailInterface:: check Output (Completion Response $response): Guardrail Result - Runs on buffered provider output through
nr_. Output-only checks cannot inspect the original prompt through pipeline context.llm. guardrail
Minimal application policy
Use separate input and output services: the interfaces have different
TAG_ constants. Their shared identifier links the policy.
This example blocks an explicit application marker. It illustrates registration
and return types; it is not a detector for all secret or harmful content.
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\Guardrail;
use Netresearch\NrLlm\Domain\Model\CompletionResponse;
use Netresearch\NrLlm\Domain\ValueObject\GuardrailResult;
use Netresearch\NrLlm\Service\Guardrail\GuardrailInterface;
use Netresearch\NrLlm\Service\Guardrail\InputGuardrailInterface;
final class PublicationInputMarker implements InputGuardrailInterface
{
public function getIdentifier(): string
{
return 'myext-publication-marker';
}
public function isMandatory(): bool
{
return false;
}
public function checkInput(string $text): GuardrailResult
{
return str_contains($text, 'DO_NOT_SEND')
? GuardrailResult::deny('Application marker present.')
: GuardrailResult::allow();
}
}
final class PublicationOutputMarker implements GuardrailInterface
{
public function getIdentifier(): string
{
return 'myext-publication-marker';
}
public function isMandatory(): bool
{
return false;
}
public function checkOutput(CompletionResponse $response): GuardrailResult
{
return str_contains($response->content, 'DO_NOT_PUBLISH')
? GuardrailResult::deny('Application marker present.')
: GuardrailResult::allow();
}
}
Verdicts and limits
Guardrail provides allow, `redact` and
require. Input redaction replaces message text;
input retry has no useful pre-provider action and is ignored. Output checks
can request a bounded retry through the runtime policy.
Deny and approval-required verdicts become typed guardrail policy exceptions.
They do not by themselves create a resumable human-approval workflow. In a
persisted agent run that path settles to FAILED with the policy reason;
tool approvals are the separate Persisted agent runtime lifecycle.
A paid output can therefore be withheld after the provider call succeeded.
For configuration/site-sensitive output policy implement
Contextual
and holds. A held stream is screened as a whole before
release; do not assume every visible live token has passed final output checks.
Streaming redaction has its separate Stream contract.
Text screening does not inspect every image byte, remote-tool side effect or value ultimately written by a tool. Tool authorization, approval, input validation and contextual output escaping remain separate responsibilities. See Effective policy and Tools, languages and workspaces.