NeMo Guardrailsnemoguardrails 0.24.1 · Python 3.10+
Dashboard
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
37 small wins to finish your pathNext lesson →

config.py on disk

config.py is the Python file in a config folder that NeMo imports when it loads the folder; its init(app) function registers actions and providers, so the folder alone is the whole guarded assistant.

Last updated: 30 Sep, 2026 · NeMo Guardrails 0.24.1

Every action lesson called register_action in the application. The video's repository keeps the actions in guardrail_actions.py and registers them by hand. config.py moves that registration into the folder.

Syntax:

python
def init(app):                                   # called with the LLMRails object
    app.register_action(detect_pii_in_input)
    app.register_embedding_search_provider("every_example", EveryExample)
Project files used on this pageThis lesson builds on a project from earlier lessons. The code below imports these files. Click a file to see its code, or follow the link to the lesson that wrote it. To run the code yourself, keep them in the same folder.
View the code here
rails.co
define user ask off topic
  "tell me a joke"
  "what is the capital of france"
  "write me a poem"
  "what is 2 plus 2"
  "what should I eat for dinner"
  "who won the game yesterday"
  "recommend a movie"
  "what is the weather like"

define bot refuse off topic
  "I'm an Enterprise IT Assistant focused on Kubernetes, Intel hardware, and networking. I can't help with that — but ask me anything technical!"

define flow handle off topic
  user ask off topic
  bot refuse off topic
  stop

define bot ask to remove pii
  "I noticed your message may contain sensitive information (email, phone, API key, etc.). Please remove any personal or secret data before sending — I don't store sensitive details!"

define flow check input for pii
  $pii_found = execute detect_pii_in_input
  if $pii_found
    bot ask to remove pii
    stop
actions.py
import re
from typing import Optional

from nemoguardrails.actions import action


@action(is_system_action=True)
async def detect_pii_in_input(context: Optional[dict] = None):
    """Returns list of PII types found, or empty list (falsy) if clean."""
    user_message = context.get("user_message", "") if context else ""

    patterns = {
        "email":       r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b",
        "phone":       r"\b(\+\d{1,2}\s?)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}\b",
        "ssn":         r"\b\d{3}-\d{2}-\d{4}\b",
        "api_key":     r"(api[_\s-]?key|token|secret)[:\s]+[A-Za-z0-9_\-]{10,}",
        "credit_card": r"\b\d{4}[\s-]\d{4}[\s-]\d{4}[\s-]\d{4}\b",
    }
    found = [ptype for ptype, pat in patterns.items()
             if re.search(pat, user_message, re.IGNORECASE)]
    return found  # empty list = no PII = falsy
config.yml
models:
  - type: main
    engine: openai
    model: openai/gpt-oss-20b
    api_key_env_var: GROQ_API_KEY
    parameters:
      base_url: https://api.groq.com/openai/v1
      temperature: 0

instructions:
  - type: general
    content: |
      You are an Enterprise IT Assistant specialising in Kubernetes,
      Intel hardware, and enterprise networking.
      Only answer questions about these topics.
      Answer in one or two short sentences.

core:
  embedding_search_provider:
    name: every_example

rails:
  input:
    flows:
      - check input for pii
prompts.yml
prompts:
  - task: generate_user_intent
    content: |-
      """
      {{ general_instructions }}
      """

      # This is how a conversation between a user and the bot can go:
      {{ sample_conversation | verbose_v1 }}

      # This is how the user talks:
      {{ examples | verbose_v1 }}

      # This is the current conversation between the user and the bot:
      {{ sample_conversation | first_turns(2) | verbose_v1 }}
      {{ history | colang | verbose_v1 }}

      Do not answer the user. Reply with one line: the user intent of the last message.
      Use an intent from the examples when one fits, otherwise write a new short intent.
    output_parser: verbose_v1

config.py with the PII action

The search provider from define user and define bot and the PII action from the actions part, registered in one init. This replaces config.py.

python
from nemoguardrails.embeddings.index import EmbeddingsIndex


class EveryExample(EmbeddingsIndex):
    """Hands the model every example instead of the closest few."""

    def __init__(self, **kwargs):
        self.items = []

    async def add_items(self, items):
        self.items.extend(items)

    async def build(self):
        pass

    async def search(self, text, max_results=5, threshold=None):
        return self.items

from actions import detect_pii_in_input


def init(app):
    app.register_embedding_search_provider("every_example", EveryExample)
    app.register_action(detect_pii_in_input)
yaml
rails:
  input:
    flows:
      - check input for pii

The folder on its own

The runs on this page use openai/gpt-oss-20b, the smaller gpt-oss model on the same free Groq key, in the model line of config.yml. This config makes several model calls per message, and the smaller model spends fewer of the key's daily tokens. Put openai/gpt-oss-120b back in that line to use the course's main model.

ExampleAPI key
from nemoguardrails import LLMRails, RailsConfig

rails = LLMRails(RailsConfig.from_path("."))


def chat(message):
    reply = rails.generate(messages=[{"role": "user", "content": message}])
    print("User:", message)
    print("Bot :", reply["content"])

chat("My email is john.doe@company.com, help me set up Kubernetes RBAC")
chat("Tell me a joke")

What the folder did

  • The application is the same few lines as every lesson: load the folder, generate. It registers nothing.
  • The email was refused by the PII action that init registered.
  • The joke was refused by the topic guard in rails.co.

Registering in the app vs in config.py

In the applicationIn config.py
Where the action is attachedEvery script that loads the folderOnce, in the folder
The NeMo server and nemoguardrails chatDo not see itLoad it

Where it matters

  • Running the folder with nemoguardrails chat or the server, which only read the folder.
  • Sharing a config with another team.
Watch out. from actions import ... in config.py needs actions.py next to it. The folder is imported as its own module, so keep every file the actions need inside it.
Try it yourself
  • Delete init and send the email message.
  • Run nemoguardrails chat --config=. in the folder and send a greeting.

This is what real progress feels like.