> ## 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.

# PostProcessors

> Transform and enhance AI responses before sending to users

## What are PostProcessors?

**PostProcessors** modify or enhance AI-generated responses before they're sent to users. They allow you to add disclaimers, apply formatting, inject dynamic content, or transform the output in any way.

<Card title="Think of it as:" icon="paper-plane">
  A final editor - reviews and enhances responses before they go to users
</Card>

<Note>
  Response postprocessing for consistent formatting, branding, and enhancement.
</Note>

## Why PostProcessors?

<CardGroup cols={2}>
  <Card title="Consistency" icon="check-double">
    Apply consistent formatting and style to all responses
  </Card>

  <Card title="Branding" icon="palette">
    Add company branding, signatures, or footers automatically
  </Card>

  <Card title="Legal Protection" icon="gavel">
    Add disclaimers to medical, financial, or legal advice
  </Card>

  <Card title="Personalization" icon="user">
    Inject user-specific information or context
  </Card>
</CardGroup>

## How PostProcessors Work

<Steps>
  <Step title="AI Generates Response">
    Your agent creates a response using its tools and knowledge
  </Step>

  <Step title="PostProcessors Run">
    Each postprocessor modifies the response in priority order
  </Step>

  <Step title="Transformation">
    PostProcessors can:

    * Add content (disclaimers, footers)
    * Format text (markdown, styling)
    * Replace placeholders
    * Translate or enhance
  </Step>

  <Step title="Final Response">
    Modified response is sent to the user
  </Step>
</Steps>

## Simple Example

```typescript theme={null}
import { PostProcessor, UserDataInstance } from 'lua-cli';

const addDisclaimer = new PostProcessor({
  name: 'add-disclaimer',
  description: 'Add legal disclaimer to responses',
  
  execute: async (user: UserDataInstance, message: string, response: string, channel: string) => {
    return {
      modifiedResponse: response + 
        "\n\n_Disclaimer: This is AI-generated content for informational purposes only._"
    };
  }
});
```

<Note>
  **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](/api/lua) for details.
</Note>

## Common Use Cases

<Tabs>
  <Tab title="Disclaimers">
    **Add legal or informational disclaimers**

    ```typescript theme={null}
    execute: async (user, message, response, channel) => {
      if (containsMedicalAdvice(response)) {
        return {
          modifiedResponse: response + 
            "\n\n⚠️ **Medical Disclaimer:** Please consult a healthcare professional."
        };
      }
      return { modifiedResponse: response };
    }
    ```
  </Tab>

  <Tab title="Branding">
    **Add company branding and signatures**

    ```typescript theme={null}
    execute: async (user, message, response, channel) => {
      const footer = "\n\n---\n" +
        "_Powered by Acme Corp_\n" +
        "Need help? support@acme.com";
      
      return {
        modifiedResponse: response + footer
      };
    }
    ```
  </Tab>

  <Tab title="Formatting">
    **Apply consistent text formatting**

    ```typescript theme={null}
    execute: async (user, message, response, channel) => {
      // Capitalize sentences
      let formatted = capitalizeFirst(response);
      
      // Add spacing
      formatted = addProperSpacing(formatted);
      
      // Highlight important terms
      formatted = highlightKeywords(formatted);
      
      return { modifiedResponse: formatted };
    }
    ```
  </Tab>

  <Tab title="Personalization">
    **Add user-specific context**

    ```typescript theme={null}
    execute: async (user, message, response, channel) => {
      const greeting = `Hi ${user.name}! `;
      const userData = await user.data;
      
      if (userData.vipStatus) {
        return {
          modifiedResponse: greeting + response + 
            "\n\n✨ VIP members get priority support!"
        };
      }
      
      return {
        modifiedResponse: greeting + response
      };
    }
    ```
  </Tab>
</Tabs>

## Execution Order

PostProcessors run in the order they are defined in the `postProcessors` array of your `LuaAgent` configuration. Each postprocessor receives the output of the previous one in the chain.

```
AI Agent Response
    ↓
PostProcessor 1 → Runs first
    ↓
PostProcessor 2 → Runs second
    ↓
PostProcessor 3 → Runs last
    ↓
Final Response to User
```

## Channel Compatibility

PostProcessors are now supported on **all channels**, including streaming endpoints.

| Channel                | PostProcessors Supported | Notes                                 |
| ---------------------- | ------------------------ | ------------------------------------- |
| Email                  | Yes                      | Uses generate (non-streaming)         |
| HTTP API (`/generate`) | Yes                      | Non-streaming endpoint                |
| HTTP API (`/stream`)   | Yes                      | Post-processes after stream completes |
| WhatsApp               | Yes                      | Post-processes after stream completes |
| LuaPop (Web Widget)    | Yes                      | Post-processes after stream completes |
| Slack                  | Yes                      | Post-processes after stream completes |
| Facebook Messenger     | Yes                      | Post-processes after stream completes |
| Instagram              | Yes                      | Post-processes after stream completes |

### Streaming Behavior

For streaming channels, post-processing works as follows:

1. **Real-time streaming**: Users see the response as it's generated (original, unprocessed text)
2. **Post-processing**: After the stream completes, post-processors run on the full response
3. **Final event**: A `postprocess-complete` event is emitted with the modified response
4. **Storage**: The post-processed response is saved to the conversation history

<Note>
  **UX Consideration:** With streaming, users briefly see the original response before post-processing completes. For use cases requiring immediate post-processed content (e.g., strict compliance), consider using the non-streaming `/generate` endpoint instead.
</Note>

### Handling the `postprocess-complete` Event

If you're building a custom client, you can listen for the `postprocess-complete` event to update the displayed response:

```typescript theme={null}
// Example: Handling postprocess-complete in a streaming client
stream.on('chunk', (chunk) => {
  if (chunk.type === 'text-delta') {
    appendToDisplay(chunk.textDelta);
  } else if (chunk.type === 'postprocess-complete') {
    // Replace displayed content with post-processed version
    replaceDisplay(chunk.modifiedResponse);
  }
});
```

## Adding to Your Agent

```typescript theme={null}
import { LuaAgent } from 'lua-cli';
import addDisclaimer from './postprocessors/disclaimer';
import addBranding from './postprocessors/branding';

export const agent = new LuaAgent({
  name: "my-agent",
  persona: "...",
  skills: [...],
  
  // Add postprocessors
  postProcessors: [
    addDisclaimer,  // Runs first
    addBranding     // Runs second
  ]
});
```

## Best Practices

<AccordionGroup>
  <Accordion title="✅ Preserve Original Meaning">
    Don't alter the core message

    ```typescript theme={null}
    // ✅ Add to response
    modifiedResponse: response + "\n\nAdditional info..."

    // ❌ Don't completely replace
    modifiedResponse: "Something different"
    ```
  </Accordion>

  <Accordion title="✅ Chain Thoughtfully">
    Order matters - structure flows logically

    ```typescript theme={null}
    priority: 1   // Translation (first)
    priority: 10  // Formatting
    priority: 50  // Content injection
    priority: 100 // Disclaimer (last)
    ```
  </Accordion>

  <Accordion title="✅ Handle Errors Gracefully">
    Fall back to original response on errors

    ```typescript theme={null}
    execute: async (user, message, response, channel) => {
      try {
        return { modifiedResponse: process(response) };
      } catch (error) {
        return { modifiedResponse: response };
      }
    }
    ```
  </Accordion>

  <Accordion title="✅ Keep It Fast">
    PostProcessors should be quick (\< 50ms)

    Avoid heavy computations or external API calls
  </Accordion>
</AccordionGroup>

## Testing PostProcessors

```bash theme={null}
lua test
# Select: PostProcessor → add-disclaimer
# Provide test response
# See transformed output
```

## Next Steps

<CardGroup cols={2}>
  <Card title="PostProcessor API" icon="book" href="/api/postprocessor">
    Complete API documentation
  </Card>

  <Card title="PreProcessor Concept" icon="filter" href="/overview/preprocessors">
    Learn about message filtering
  </Card>

  <Card title="Agent Concept" icon="robot" href="/overview/agent">
    Learn about LuaAgent
  </Card>

  <Card title="Workflows Guide" icon="diagram-project" href="/concepts/workflows">
    Development best practices
  </Card>
</CardGroup>
