> ## Documentation Index
> Fetch the complete documentation index at: https://docs.heylua.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# PreProcessor

> Filter and route messages before they reach your AI agent

## Overview

`PreProcessor` allows you to intercept and process user messages before they reach your AI agent. Use preprocessors to filter spam, route messages, validate inputs, or modify messages before the agent sees them.

```typescript theme={null}
import { PreProcessor } from 'lua-cli';

const profanityFilter = new PreProcessor({
  name: 'profanity-filter',
  description: 'Filter inappropriate content',
  priority: 10,
  execute: async (user, messages, channel) => {
    const hasProfanity = messages.some(msg => 
      msg.type === 'text' && msg.text.includes('badword')
    );
    
    if (hasProfanity) {
      // Block message and respond
      return {
        action: 'block',
        response: "Please keep the conversation respectful."
      };
    }
    
    // Allow messages to proceed
    return { action: 'proceed' };
  }
});

export default profanityFilter;
```

<Note>
  Message preprocessing for filtering, routing, and validation. Use with LuaAgent.
</Note>

## Use Cases

<CardGroup cols={2}>
  <Card title="Content Filtering" icon="filter">
    Block spam, profanity, or inappropriate content
  </Card>

  <Card title="Message Routing" icon="route">
    Route messages to different agents or handlers
  </Card>

  <Card title="Input Validation" icon="check">
    Validate message format or required information
  </Card>

  <Card title="Analytics" icon="chart-line">
    Track message metrics before processing
  </Card>
</CardGroup>

## Constructor

### new PreProcessor(config)

Creates a new message preprocessor.

<ParamField path="config" type="PreProcessorConfig" required>
  Preprocessor configuration object
</ParamField>

## Configuration Parameters

### Required Fields

<ParamField path="description" type="string" required>
  Preprocessor description for documentation
</ParamField>

<ParamField path="execute" type="function" required>
  Function that processes incoming messages

  **Signature:** `(user: UserDataInstance, messages: ChatMessage[], channel: string) => Promise<PreProcessorBlockResponse | PreProcessorProceedResponse>`

  **Arguments:**

  * `user`: The [UserDataInstance](#userdata-object) representing the current user.
  * `messages`: Array of [ChatMessage](#message-structure) objects sent by the user.
  * `channel`: The channel identifier (e.g., `'whatsapp'`, `'web'`, `'api'`).
</ParamField>

<Note>
  **Recommended:** Use the [Lua Runtime API](/api/lua) instead of the function parameters:

  * **User:** `User.get()` to retrieve the current user
  * **Channel:** `Lua.request.channel` for the current channel
  * **Webhook:** `Lua.request.webhook?.payload` for raw webhook data (WhatsApp, Slack, Teams, etc.)

  The function parameters may be removed in a future version.
</Note>

### Optional Fields

<ParamField path="name" type="string">
  Unique preprocessor name. Defaults to `'unnamed-preprocessor'` if not provided.

  **Examples**: `'profanity-filter'`, `'message-router'`
</ParamField>

<ParamField path="priority" type="number">
  Execution priority (lower runs first). Default: `100`
</ParamField>

<ParamField path="async" type="boolean">
  Run this preprocessor asynchronously on the server. When `true`, the preprocessor executes in a non-blocking mode. Default: `false`
</ParamField>

## UserData Object

The `user` argument passed to `execute` provides access to the current user's data and methods.

```typescript theme={null}
interface UserDataInstance {
  // Direct access to user data properties
  uid: string;
  name?: string;
  email?: string;
  [key: string]: any;

  // Helper methods
  getChatHistory(): Promise<ChatHistoryMessage[]>;
  // ... other user management methods
}
```

## Message Structure

The `messages` array passed to your execute function contains `ChatMessage` objects:

```typescript theme={null}
type ChatMessage = TextMessage | ImageMessage | FileMessage;

interface TextMessage {
  type: 'text';
  text: string;
}

interface ImageMessage {
  type: 'image';
  image: string;      // URL or base64
  mimeType: string;   // e.g. "image/png", "image/jpeg"
}

interface FileMessage {
  type: 'file';
  data: string;       // URL or base64
  mimeType: string;   // e.g. "application/pdf"
}
```

## Return Type

Your execute function must return a `PreProcessorBlockResponse` or `PreProcessorProceedResponse` object indicating whether to proceed or block.

### 1. Proceed

To allow the message to continue to the next preprocessor or the agent:

```typescript theme={null}
// Proceed with original message
return { action: 'proceed' };

// OR proceed with a modified message
return {
  action: 'proceed',
  modifiedMessage: [{ type: 'text', text: 'Modified content' }],
  metadata: { reason: 'message-modified' }  // optional metadata
};
```

### 2. Block

To stop processing immediately and respond to the user:

```typescript theme={null}
return {
  action: 'block',
  response: "This message was blocked.",
  metadata: { reason: 'profanity' }  // optional metadata
};
```

## Complete Examples

### Profanity Filter

```typescript theme={null}
import { PreProcessor } from 'lua-cli';

const profanityWords = ['badword1', 'badword2', 'badword3'];

const profanityFilter = new PreProcessor({
  name: 'profanity-filter',
  description: 'Block messages containing inappropriate language',
  priority: 10, // Run early
  
  execute: async (user, messages, channel) => {
    // Check all text messages
    const hasProfanity = messages.some(msg => {
      if (msg.type === 'text') {
        return profanityWords.some(word => msg.text.toLowerCase().includes(word));
      }
      return false;
    });
    
    if (hasProfanity) {
      console.log(`Blocked profanity from user ${user.uid}`);
      return {
        action: 'block',
        response: "Please keep the conversation respectful. Inappropriate language is not allowed."
      };
    }
    
    return { action: 'proceed' };
  }
});

export default profanityFilter;
```

### Business Hours Filter

```typescript theme={null}
import { PreProcessor } from 'lua-cli';

const businessHoursFilter = new PreProcessor({
  name: 'business-hours-filter',
  description: 'Block messages outside business hours',
  
  execute: async (user, messages, channel) => {
    const now = new Date();
    const hour = now.getHours();
    const day = now.getDay();
    
    // Business hours: Mon-Fri, 9 AM - 5 PM
    const isWeekday = day >= 1 && day <= 5;
    const isBusinessHours = hour >= 9 && hour < 17;
    
    if (!isWeekday || !isBusinessHours) {
      return {
        action: 'block',
        response: "Thank you for contacting us. Our business hours are Monday-Friday, 9 AM - 5 PM. We'll respond to your message during our next business day."
      };
    }
    
    return { action: 'proceed' };
  }
});

export default businessHoursFilter;
```

### Message Enrichment

```typescript theme={null}
import { PreProcessor } from 'lua-cli';

const messageEnricher = new PreProcessor({
  name: 'message-enricher',
  description: 'Add context to messages before processing',
  priority: 50,
  
  execute: async (user, messages, channel) => {
    // Enrich message with user info
    const enrichedMessages = messages.map(msg => {
      if (msg.type === 'text') {
        return {
          ...msg,
          text: `${msg.text}\n\n[Context: User ID ${user.uid}]`
        };
      }
      return msg;
    });
    
    return {
      action: 'proceed',
      modifiedMessage: enrichedMessages
    };
  }
});

export default messageEnricher;
```

## Execution Flow

Preprocessors are executed in a **pipeline**:

1. **Sequential Execution**: Preprocessors run one after another, sorted by `priority`.
2. **Modifications**: If a preprocessor modifies a message, the *next* preprocessor receives the modified version.
3. **Blocking**: If any preprocessor returns `action: 'block'`, execution stops immediately, and the response is sent to the user. The agent is not called.

## Testing Preprocessors

```bash theme={null}
lua test
# Select: PreProcessor → your-preprocessor-name
# Provide test message
```

## See Also

* [PostProcessor](/api/postprocessor) - Processing responses
* [LuaAgent](/api/luaagent) - Adding preprocessors to your agent
