Class UserInputRole

java.lang.Object
com.tnsai.roles.Role
com.tnsai.integration.scop.examples.UserInputRole

@RoleSpec(name="UserInputAgent", description="Validates and sanitises user-supplied text before it reaches the LLM or downstream services.", version="0.1.0", llm=@LLMSpec(provider=OPENAI,model="gpt-4o-mini",temperature=0.2f)) @InputGuardrail(maxLength=16384, blockPatterns={"(?i)ignore\\s++previous\\s++instructions","(?i)disregard\\s++the\\s++system\\s++prompt","<script\\b"}, onFailure=REJECT) public class UserInputRole extends com.tnsai.roles.Role
Reference example: declarative @InputGuardrail + @OutputGuardrail composition (issue #171, Phase 1).

Demonstrates the four documented dispositions for hostile input and over-budget output, all enforced by the framework's InputGuardrailEnforcer / OutputGuardrailEnforcer inside ActionExecutor.executeInternal — no per-action imperative validation in the Role:

  1. REJECT on input — anything matching a denylist regex throws GuardrailViolationException (wrapped as ActionExecutionException(VALIDATION) at the dispatcher's edge). Use for prompt-injection defence, length DoS, schema enforcement.
  2. WARN on input — log but proceed; useful when telemetry is more important than blocking (e.g. tracking borderline values in a staging tenant).
  3. SANITIZE on input — strip C0 control chars, truncate to maxLength; the action body sees a cleaned value rather than failing.
  4. TRUNCATE / FALLBACK on output — clip oversized responses to maxChars, or substitute a safe fallback string. Symmetric pair to SANITIZE on the output side.

Class-level default + method-level override

The class-level InputGuardrail below sets a baseline (16KB max, no obvious prompt-injection markers) that every action inherits. Per-method annotations override the class default entirely — there is no merging. summarize keeps the class default; formatJson narrows to a JSON-only allowlist; adminCommand escalates to REJECT on top of length bounds.

How SCOP routes these

Every @ActionSpec on this role is dispatched through ActionExecutor.executeInternal, which calls InputGuardrailEnforcer.enforce immediately after preconditions and before the action body, and OutputGuardrailEnforcer.enforce immediately after the action body returns and before postconditions. Violations surface as ActionExecutionException of category VALIDATION — the same surface consumers already handle for ActionValidator.validateParameters failures.

Phase 2+ deferred

The @InputGuardrail.validators / .sanitizers / .jsonSchema / .detectInjection / .moderateContent fields and @OutputGuardrail's .maskPII / .checkHallucination / .format fields are not exercised in Phase 1 (they need SPI design or external services — tracked separately on #171).
Since:
0.5.4 (issue #171, Phase 1 — guardrail cookbook)
  • Constructor Summary

    Constructors
    Constructor
    Description
     
  • Method Summary

    Modifier and Type
    Method
    Description
    Hardest-locked path: short bound, strict allowlist (only lowercase commands), REVIEW disposition routes any violation to a human-approval queue rather than simply blocking it.
    Method-level override: tighter length, allowlist limited to what looks like JSON.
    com.tnsai.models.role.RoleIdentity
     
    Inherits the class-level guardrail defaults.

    Methods inherited from class com.tnsai.roles.Role

    create, getAction, getActionCount, getActions, getDomain, getGoal, getInputGuardrailConfig, getLLM, getMinimalPrompt, getName, getOutputGuardrailConfig, getResilienceConfig, getSpec, getSystemPrompt, hasAction, hasLLM, identity, setLLM, toString

    Methods inherited from class java.lang.Object

    equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
  • Constructor Details

    • UserInputRole

      public UserInputRole()
  • Method Details

    • getIdentity

      public com.tnsai.models.role.RoleIdentity getIdentity()
      Specified by:
      getIdentity in class com.tnsai.roles.Role
    • summarize

      @ActionSpec(type=LOCAL, description="Summarize user-provided text in plain language.") @OutputGuardrail(maxChars=2000, onFailure=TRUNCATE) public String summarize(String text)
      Inherits the class-level guardrail defaults. The action body sees a value that's already passed length + denylist checks. Output is bounded to a sensible summary length.
    • formatJson

      @ActionSpec(type=LOCAL, description="Format a JSON-shaped payload (object or array).") @InputGuardrail(minLength=2, maxLength=4096, allowPatterns="^\\s*[\\{\\[]", onFailure=SANITIZE) @OutputGuardrail(maxChars=4096, onFailure=TRUNCATE) public String formatJson(String json)
      Method-level override: tighter length, allowlist limited to what looks like JSON. SANITIZE on a borderline payload strips control characters and truncates rather than failing.
    • adminCommand

      @ActionSpec(type=LOCAL, description="Issue a privileged admin command.") @InputGuardrail(maxLength=64, allowPatterns="^[a-z][a-z0-9_-]{0,62}$", onFailure=REVIEW, errorMessage="Admin commands must be lowercase identifiers; routing to human review.") @OutputGuardrail(maxChars=256, onFailure=FALLBACK, fallback="(redacted: admin output exceeded budget)") public String adminCommand(String command)
      Hardest-locked path: short bound, strict allowlist (only lowercase commands), REVIEW disposition routes any violation to a human-approval queue rather than simply blocking it. FALLBACK on output substitutes a safe placeholder if the action body produces too much.