Skip to main content

What are PreProcessors?

PreProcessors intercept user messages before they reach your AI agent. They act as a middleware layer, allowing you to filter spam, validate inputs, route messages, or modify content before the agent processes it.

Think of it as:

A security checkpoint and enrichment station - messages pass through preprocessors before reaching your agent’s brain.
New in v3.0.0: Message preprocessing for content filtering, routing, and validation.

Why PreProcessors?

PreProcessors handle “meta” tasks that shouldn’t consume your agent’s context window or reasoning capabilities.

Content Filtering

Block spam, profanity, or inappropriate content before it costs you tokens.

Input Validation

Ensure messages meet requirements (e.g., “must include an email”) before the agent sees them.

Context Injection

Fetch data from a CRM or database and append it to the user’s message.

Rate Limiting

Prevent abuse by limiting message frequency per user.

PreProcessors vs Skills

FeaturePreProcessorSkill
TimingRuns before the agent thinksRuns when the agent decides to call it
DecisionDeterministic (code-based)Probabilistic (LLM-based)
Use CaseSecurity, formatting, required contextActions, data retrieval, business logic
AccessCan block the agent completelyCannot block, returns data to agent

How PreProcessors Work

1

User Sends Message

User types a message (e.g., “Check my order status”) via WhatsApp, Web, or API.
2

Pipeline Execution

The system runs your active PreProcessors in order of priority (lowest first).
3

Transformation & Decision

Each preprocessor receives the output of the previous one. It can:
  • Proceed: Pass the (potentially modified) message to the next step.
  • Block: Stop the pipeline and return a response immediately.
4

Agent Handover

If no preprocessor blocks, the final message reaches the AI agent.

Simple Example

Here is a simple profanity filter that runs early in the pipeline.
import { PreProcessor } from 'lua-cli';

const profanityFilter = new PreProcessor({
  name: 'profanity-filter',
  description: 'Block inappropriate content',
  priority: 10, // Run early (low number)
  
  execute: async (user, messages, channel) => {
    const text = messages
      .filter(m => m.type === 'text')
      .map(m => m.text.toLowerCase())
      .join(' ');
    
    if (text.includes('badword')) {
      // Block the message
      return {
        action: 'block',
        response: "Please keep the conversation respectful."
      };
    }
    
    // Allow message through
    return { action: 'proceed' };
  }
});
Tip: You can also access the channel via Lua.request.channel, the user via User.get(), and raw webhook data via Lua.request.webhook?.payload. See the Lua API for details.

Advanced Patterns

Context Injection (RAG Lite)

You can use a preprocessor to fetch user data and “inject” it into the message, giving the agent context without it needing to ask tools.
execute: async (user, messages, channel) => {
  // Fetch recent order from your DB
  const recentOrder = await fetchRecentOrder(user.email);
  
  if (recentOrder) {
    // Append order info to the message
    const enrichedMessages = messages.map(msg => {
      if (msg.type === 'text') {
        return {
          ...msg,
          text: `${msg.text}\n\n[System Context: Last Order #${recentOrder.id} is ${recentOrder.status}]`
        };
      }
      return msg;
    });
    
    return { 
      action: 'proceed', 
      modifiedMessage: enrichedMessages 
    };
  }
  
  return { action: 'proceed' };
}

Channel-Specific Logic

You can apply different rules based on where the user is chatting from.
execute: async (user, messages, channel) => {
  if (channel === 'whatsapp' && messages.some(m => m.type === 'file')) {
    return {
      action: 'block',
      response: "Sorry, we don't accept file uploads on WhatsApp yet."
    };
  }
  return { action: 'proceed' };
}

Execution Pipeline

PreProcessors run in a pipeline. The output of one preprocessor becomes the input of the next.
  1. Priority 10: RateLimiter checks if user is spamming.
  2. Priority 20: ProfanityFilter checks for bad words.
  3. Priority 50: ContextInjector adds user account info.
  4. Agent: Receives a clean, verified, and context-rich message.

Adding to Your Agent

Add preprocessors to your LuaAgent configuration.
import { LuaAgent } from 'lua-cli';
import profanityFilter from './preprocessors/profanity-filter';
import rateLimiter from './preprocessors/rate-limiter';

export const agent = new LuaAgent({
  name: "my-agent",
  persona: "...",
  skills: [...],
  
  // Add preprocessors here
  preProcessors: [
    rateLimiter,      // Will run first (assuming priority 5)
    profanityFilter   // Will run second (assuming priority 10)
  ]
});

Testing PreProcessors

You can test preprocessors in isolation using the Lua CLI.
lua test
# Select: PreProcessor → profanity-filter
# Provide test message
# See if it's blocked or allowed

Next Steps