# PostProcessor

The `PostProcessor` class allows you to modify, format, or enrich agent responses before they reach the user. Use postprocessors for response formatting, branding, translation, and content enhancement.

## Import

```typescript
import { PostProcessor } from 'lua-cli';
```

## Overview

PostProcessors enable:
- Format and style agent responses
- Add branding and signatures
- Translate responses to user's language
- Inject dynamic content (links, images, CTAs)
- Apply channel-specific formatting
- Sanitize or filter responses
- Add disclaimers and legal notices
- Track and log responses

## Class Definition

```typescript
class PostProcessor {
  constructor(config: PostProcessorConfig);
  getName(): string;
  getDescription(): string;
  execute(user: UserDataInstance, message: string, response: string, channel: string): Promise<PostProcessorResponse>;
}
```

## Configuration

```typescript
interface PostProcessorConfig {
  /** PostProcessor name (optional, defaults to 'unnamed-postprocessor') */
  name?: string;

  /** Short description of the postprocessor */
  description: string;

  /**
   * Function that processes the agent's response before sending to user.
   * MUST return { modifiedResponse: string } - the formatted response text.
   */
  execute: (
    user: UserDataInstance,
    message: string,      // Original user message
    response: string,     // Agent's response
    channel: string       // Channel (whatsapp, web, etc.)
  ) => Promise<PostProcessorResponse>;
}
```

## Response Type

PostProcessors must return:

```typescript
interface PostProcessorResponse {
  modifiedResponse: string;  // The formatted response text
}
```

## Complete Examples

### Branding Footer

```typescript
import { PostProcessor } from 'lua-cli';

export const brandingFooter = new PostProcessor({
  name: 'branding-footer',
  description: 'Adds company branding to responses',

  execute: async (user, message, response, channel) => {
    const footer = '\n\n---\n💼 Powered by Acme Corp\n🌐 www.acme.com | 📧 support@acme.com';

    return {
      modifiedResponse: response + footer
    };
  }
});
```

### Markdown Formatter

```typescript
import { PostProcessor } from 'lua-cli';

export const markdownFormatter = new PostProcessor({
  name: 'markdown-formatter',
  description: 'Formats responses with markdown',

  execute: async (user, message, response, channel) => {
    // Only apply to web channel
    if (channel !== 'web') {
      return { modifiedResponse: response };
    }

    // Apply markdown formatting
    let formatted = response;

    // Bold for **text**
    formatted = formatted.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');

    // Italic for *text*
    formatted = formatted.replace(/\*(.*?)\*/g, '<em>$1</em>');

    // Code blocks for ```code```
    formatted = formatted.replace(/```(.*?)```/gs, '<pre><code>$1</code></pre>');

    // Line breaks
    formatted = formatted.replace(/\n/g, '<br>');

    return { modifiedResponse: formatted };
  }
});
```

### WhatsApp Formatter

```typescript
import { PostProcessor } from 'lua-cli';

export const whatsappFormatter = new PostProcessor({
  name: 'whatsapp-formatter',
  description: 'Formats responses for WhatsApp',

  execute: async (user, message, response, channel) => {
    if (channel !== 'whatsapp') {
      return { modifiedResponse: response };
    }

    let formatted = response;

    // Apply WhatsApp-specific formatting
    // Bold: *text*
    formatted = formatted.replace(/\*\*(.*?)\*\*/g, '*$1*');

    // Italic: _text_
    formatted = formatted.replace(/_(.*?)_/g, '_$1_');

    // Strikethrough: ~text~
    formatted = formatted.replace(/~~(.*?)~~/g, '~$1~');

    // Monospace: ```text```
    formatted = formatted.replace(/`(.*?)`/g, '```$1```');

    // Add emojis based on content
    if (formatted.includes('error') || formatted.includes('failed')) {
      formatted = '⚠️ ' + formatted;
    } else if (formatted.includes('success') || formatted.includes('completed')) {
      formatted = '✅ ' + formatted;
    }

    return { modifiedResponse: formatted };
  }
});
```

### Response Translator

```typescript
import { PostProcessor, AI } from 'lua-cli';

export const responseTranslator = new PostProcessor({
  name: 'response-translator',
  description: 'Translates responses to user language',

  execute: async (user, message, response, channel) => {
    const userData = await user.get();
    const userLanguage = userData.data?.language || 'en';

    // Skip if already in user's language (English)
    if (userLanguage === 'en') {
      return { modifiedResponse: response };
    }

    // Translate response
    const languageNames: Record<string, string> = {
      es: 'Spanish',
      fr: 'French',
      de: 'German',
      it: 'Italian',
      pt: 'Portuguese',
      ja: 'Japanese',
      ko: 'Korean',
      zh: 'Chinese'
    };

    const targetLanguage = languageNames[userLanguage] || userLanguage;

    const translatedResponse = await AI.generate(
      `Translate this text to ${targetLanguage}. Return only the translation, preserving formatting and structure.`,
      [{ type: 'text', text: response }]
    );

    return { modifiedResponse: translatedResponse };
  }
});
```

### Link Injector

```typescript
import { PostProcessor } from 'lua-cli';

export const linkInjector = new PostProcessor({
  name: 'link-injector',
  description: 'Adds helpful links to responses',

  execute: async (user, message, response, channel) => {
    // Detect topics and add relevant links
    const links: Record<string, string> = {
      pricing: 'View pricing: https://example.com/pricing',
      support: 'Get support: https://example.com/support',
      documentation: 'Read docs: https://example.com/docs',
      account: 'Manage account: https://example.com/account'
    };

    let modified = response;
    const lowerResponse = response.toLowerCase();

    // Add relevant links based on content
    const relevantLinks: string[] = [];
    for (const [keyword, link] of Object.entries(links)) {
      if (lowerResponse.includes(keyword)) {
        relevantLinks.push(link);
      }
    }

    if (relevantLinks.length > 0) {
      modified += '\n\n📎 Helpful Links:\n' + relevantLinks.join('\n');
    }

    return { modifiedResponse: modified };
  }
});
```

### Sentiment Analyzer

```typescript
import { PostProcessor, Data, AI } from 'lua-cli';

export const sentimentAnalyzer = new PostProcessor({
  name: 'sentiment-analyzer',
  description: 'Analyzes and logs response sentiment',

  execute: async (user, message, response, channel) => {
    // Analyze sentiment of agent's response
    const analysis = await AI.generate(
      'Analyze the sentiment of this response. Reply with JSON: { "sentiment": "positive|negative|neutral", "score": 0-1 }',
      [{ type: 'text', text: response }]
    );

    try {
      const result = JSON.parse(analysis);

      // Log sentiment data
      await Data.create('response_sentiment', {
        userId: user.id,
        userMessage: message,
        agentResponse: response,
        sentiment: result.sentiment,
        score: result.score,
        channel,
        timestamp: new Date().toISOString()
      });

      // Add positive emoji for positive sentiments
      if (result.sentiment === 'positive' && result.score > 0.7) {
        return { modifiedResponse: response + ' 😊' };
      }
    } catch (error) {
      console.error('Sentiment analysis error:', error);
    }

    return { modifiedResponse: response };
  }
});
```

### Legal Disclaimer

```typescript
import { PostProcessor } from 'lua-cli';

export const legalDisclaimer = new PostProcessor({
  name: 'legal-disclaimer',
  description: 'Adds legal disclaimers to financial advice',

  execute: async (user, message, response, channel) => {
    const financialKeywords = [
      'invest', 'stock', 'trading', 'financial', 'money',
      'portfolio', 'crypto', 'bitcoin', 'tax', 'retirement'
    ];

    const lowerResponse = response.toLowerCase();
    const containsFinancialContent = financialKeywords.some(
      keyword => lowerResponse.includes(keyword)
    );

    if (containsFinancialContent) {
      const disclaimer = '\n\n⚠️ Disclaimer: This information is for educational purposes only and should not be considered financial advice. Please consult with a licensed financial advisor before making investment decisions.';

      return { modifiedResponse: response + disclaimer };
    }

    return { modifiedResponse: response };
  }
});
```

### Response Summarizer

```typescript
import { PostProcessor, AI } from 'lua-cli';

export const responseSummarizer = new PostProcessor({
  name: 'response-summarizer',
  description: 'Adds TL;DR for long responses',

  execute: async (user, message, response, channel) => {
    // Only summarize long responses
    if (response.length < 500) {
      return { modifiedResponse: response };
    }

    // Generate summary
    const summary = await AI.generate(
      'Create a 1-sentence summary (TL;DR) of this response.',
      [{ type: 'text', text: response }]
    );

    const modified = `**TL;DR:** ${summary}\n\n---\n\n${response}`;

    return { modifiedResponse: modified };
  }
});
```

### CTA Injector

```typescript
import { PostProcessor } from 'lua-cli';

export const ctaInjector = new PostProcessor({
  name: 'cta-injector',
  description: 'Adds call-to-action buttons',

  execute: async (user, message, response, channel) => {
    // Only for web channel
    if (channel !== 'web') {
      return { modifiedResponse: response };
    }

    const userData = await user.get();
    const hasPremium = userData.data?.plan === 'premium';

    // Add appropriate CTA based on user status
    let cta = '';

    if (!hasPremium) {
      cta = '\n\n[Upgrade to Premium](https://example.com/upgrade) for advanced features! 🚀';
    } else {
      cta = '\n\n[Explore More Features](https://example.com/features) | [Get Help](https://example.com/support)';
    }

    return { modifiedResponse: response + cta };
  }
});
```

### Profanity Filter

```typescript
import { PostProcessor } from 'lua-cli';

export const profanityFilter = new PostProcessor({
  name: 'profanity-filter',
  description: 'Filters inappropriate content from responses',

  execute: async (user, message, response, channel) => {
    // List of words to filter (abbreviated for example)
    const blockedWords = ['damn', 'hell', 'crap'];

    let filtered = response;

    // Replace blocked words with asterisks
    for (const word of blockedWords) {
      const regex = new RegExp(`\\b${word}\\b`, 'gi');
      const replacement = '*'.repeat(word.length);
      filtered = filtered.replace(regex, replacement);
    }

    return { modifiedResponse: filtered };
  }
});
```

### Response Logger

```typescript
import { PostProcessor, Data } from 'lua-cli';

export const responseLogger = new PostProcessor({
  name: 'response-logger',
  description: 'Logs all agent responses',

  execute: async (user, message, response, channel) => {
    // Log response for analytics
    await Data.create('response_logs', {
      userId: user.id,
      userMessage: message,
      agentResponse: response,
      responseLength: response.length,
      channel,
      timestamp: new Date().toISOString()
    });

    // Return response unchanged
    return { modifiedResponse: response };
  }
});
```

## Channel-Specific Formatting

```typescript
export const channelFormatter = new PostProcessor({
  name: 'channel-formatter',
  description: 'Formats responses for different channels',

  execute: async (user, message, response, channel) => {
    switch (channel) {
      case 'whatsapp':
        // WhatsApp: Use emojis, short paragraphs
        return { modifiedResponse: formatForWhatsApp(response) };

      case 'sms':
        // SMS: Keep short, no formatting
        return { modifiedResponse: truncate(response, 160) };

      case 'web':
        // Web: Use HTML, rich formatting
        return { modifiedResponse: formatForWeb(response) };

      case 'slack':
        // Slack: Use slack markdown
        return { modifiedResponse: formatForSlack(response) };

      default:
        return { modifiedResponse: response };
    }
  }
});
```

## Best Practices

### ✅ DO

- Keep processing fast (< 100ms when possible)
- Return the full response (don't truncate accidentally)
- Test with different response lengths
- Handle edge cases (empty responses, special characters)
- Use channel detection for platform-specific formatting
- Log important transformations
- Preserve response meaning
- Add value to the user experience

### ❌ DON'T

- Don't remove important information
- Don't add excessive fluff
- Don't perform slow operations synchronously
- Don't forget to handle errors
- Don't modify responses without clear benefit
- Don't add confusing formatting
- Don't break response structure
- Don't ignore channel context

## Error Handling

```typescript
export const safePostProcessor = new PostProcessor({
  name: 'safe-postprocessor',
  description: 'PostProcessor with error handling',

  execute: async (user, message, response, channel) => {
    try {
      // Your post-processing logic
      const modified = await processResponse(response);

      return { modifiedResponse: modified };
    } catch (error) {
      console.error('PostProcessor error:', error);

      // Log error
      await Data.create('postprocessor_errors', {
        error: error.message,
        userId: user.id,
        originalResponse: response,
        timestamp: new Date().toISOString()
      });

      // Return original response on error
      return { modifiedResponse: response };
    }
  }
});
```

## Common Patterns

### Conditional Formatting

```typescript
if (condition) {
  return { modifiedResponse: format(response) };
}
return { modifiedResponse: response };
```

### String Manipulation

```typescript
const modified = response
  .replace(/old/g, 'new')
  .trim()
  .toUpperCase();

return { modifiedResponse: modified };
```

### Appending Content

```typescript
const footer = '\n\n---\nAdditional info';
return { modifiedResponse: response + footer };
```

### Prepending Content

```typescript
const header = 'Important Notice:\n\n';
return { modifiedResponse: header + response };
```

## Related APIs

- [PreProcessor](./PreProcessor.md) - Process user messages
- [User API](./User.md) - Access user preferences
- [Data API](./Data.md) - Log responses
- [AI API](./AI.md) - Enhance responses with AI

## See Also

- [Response Formatting Guide](../guides/ResponseFormatting.md)
- [Channel Integration](../guides/Channels.md)
- [Localization Guide](../guides/Localization.md)
