whatsapp-formatter 0.2.0
Transform LLM output to WhatsApp Business API JSON format
Requires Python: >=3.10
Keywords: chatbot, formatter, llm, messaging, whatsapp, whatsapp-api, whatsapp-business

# WhatsApp Formatter

Transform LLM output to WhatsApp Business API JSON format.

This library uses an LLM to parse free-form text (with options, headers, footers) and automatically converts it to valid WhatsApp message formats (plain text, quick reply buttons, or interactive lists).

## Installation

```bash
pip install whatsapp-formatter
```

## Quick Start

```python
import os
from whatsapp_formatter import format_for_whatsapp

# Configure LLM (works with any OpenAI-compatible API)
os.environ["LLM_API_KEY"] = "your-api-key"
os.environ["LLM_BASE_URL"] = "https://openrouter.ai/api/v1"  # Optional
os.environ["LLM_MODEL"] = "qwen/qwen3-30b-a3b-instruct-2507"  # Optional

# Format LLM output for WhatsApp
raw_text = """
I can help with your account. What would you like to do?

1. Check your balance
2. Transfer funds
3. View recent transactions
"""

result = format_for_whatsapp(raw_text)
print(result.whatsapp_json)
```

Output:
```json
{
  "type": "interactive",
  "interactive": {
    "type": "button",
    "body": { "text": "I can help with your account. What would you like to do?" },
    "action": {
      "buttons": [
        { "type": "reply", "reply": { "id": "check_your_balance", "title": "Check your balance" } },
        { "type": "reply", "reply": { "id": "transfer_funds", "title": "Transfer funds" } },
        { "type": "reply", "reply": { "id": "view_recent_transac", "title": "View Transactions" } }
      ]
    }
  }
}
```

## Features

- **Automatic format selection** based on option count:
  - 0 options → Plain text message
  - 1-3 options → Quick reply buttons
  - 4-10 options → Interactive list
  - 11+ options → Plain text fallback with numbered list

- **Smart truncation** to fit WhatsApp constraints:
  - Button titles: max 20 characters
  - List row titles: max 24 characters
  - Body text: max 1024 characters (interactive) or 1600 (plain text)

- **Automatic deduplication** of option titles with suffixes

- **Header/footer extraction** from structured messages

- **Multiple option formats** supported:
  - Numbered lists (`1. Option`)
  - Bullet points (`- Option`, `• Option`)
  - Inline choices (`Yes, No, or Cancel`)

## API Reference

### `format_for_whatsapp(text: str) -> TransformResult`

The main entry point. Parses text and returns WhatsApp-ready JSON.

```python
from whatsapp_formatter import format_for_whatsapp

result = format_for_whatsapp("Your balance is $500.")
print(result.format_used)      # FormatType.PLAIN_TEXT
print(result.whatsapp_json)    # {"type": "text", "text": {"body": "..."}}
print(result.fallback_used)    # False
print(result.modifications)    # List of any truncations applied
```

### `transform(llm_output: LLMOutput) -> TransformResult`

Transform pre-parsed data to WhatsApp format (skip LLM parsing step).

```python
from whatsapp_formatter import transform, LLMOutput, Option

llm_output = LLMOutput(
    body="Choose an option:",
    options=[
        Option(title="Yes"),
        Option(title="No"),
    ]
)
result = transform(llm_output)
```

### `parse(text: str) -> LLMOutput`

Parse text using LLM (requires API key).

```python
from whatsapp_formatter import parse

llm_output = parse("Select: 1. Apple 2. Banana 3. Cherry")
print(llm_output.body)     # "Select:"
print(llm_output.options)  # [Option(title="Apple"), ...]
```

## Data Types

### `LLMOutput`
```python
class LLMOutput:
    body: str                    # Main message text (required)
    options: list[Option] | None # Flat list of options
    sections: list[Section] | None # Grouped options (for list format)
    header: str | None           # Optional header text
    footer: str | None           # Optional footer text
```

### `TransformResult`
```python
class TransformResult:
    success: bool                # Always True unless error
    whatsapp_json: dict          # Ready-to-send WhatsApp payload
    format_used: FormatType      # PLAIN_TEXT, QUICK_REPLY, or LIST
    fallback_used: bool          # True if format was downgraded
    fallback_reason: str | None  # Reason for fallback
    modifications: list[str]     # Truncations/deduplications applied
```

### `FormatType`
```python
class FormatType(Enum):
    PLAIN_TEXT = "plain_text"
    QUICK_REPLY = "quick_reply"
    LIST = "list"
```

## Configuration

### Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `LLM_API_KEY` | API key | Required |
| `LLM_BASE_URL` | API endpoint | `https://api.openai.com/v1` |
| `LLM_MODEL` | Model name | `gpt-4o-mini` |

### Supported Providers

Works with any OpenAI-compatible API:

```bash
# OpenAI (default)
export LLM_API_KEY="sk-..."
export LLM_MODEL="gpt-4o-mini"

# OpenRouter
export LLM_API_KEY="sk-or-..."
export LLM_BASE_URL="https://openrouter.ai/api/v1"
export LLM_MODEL="qwen/qwen3-30b-a3b-instruct-2507"

# vLLM (self-hosted)
export LLM_API_KEY="token"
export LLM_BASE_URL="http://localhost:8000/v1"
export LLM_MODEL="meta-llama/Llama-3-8b-instruct"

# Ollama
export LLM_API_KEY="ollama"
export LLM_BASE_URL="http://localhost:11434/v1"
export LLM_MODEL="llama3"

# Together AI
export LLM_API_KEY="..."
export LLM_BASE_URL="https://api.together.xyz/v1"
export LLM_MODEL="meta-llama/Llama-3-70b-chat-hf"

# Groq
export LLM_API_KEY="..."
export LLM_BASE_URL="https://api.groq.com/openai/v1"
export LLM_MODEL="llama3-70b-8192"
```

### Recommended Models

For best results with structured JSON extraction:
- `qwen/qwen3-30b-a3b-instruct-2507` (91%+ accuracy)
- `gpt-4o-mini` (with JSON mode)

## WhatsApp Constraints

The library automatically handles these WhatsApp Business API limits:

| Element | Max Length |
|---------|------------|
| Quick reply button title | 20 chars |
| Quick reply buttons | 3 max |
| List row title | 24 chars |
| List row description | 72 chars |
| List sections | 10 max |
| List rows total | 10 max |
| Interactive body | 1024 chars |
| Plain text body | 1600 chars |
| Header | 60 chars |
| Footer | 60 chars |

## Examples

### Plain Text (no options)
```python
result = format_for_whatsapp("Your balance is $500.")
# → {"type": "text", "text": {"body": "Your balance is $500."}}
```

### Quick Reply (1-3 options)
```python
result = format_for_whatsapp("""
Was this transaction authorized?
1. Yes
2. No
""")
# → {"type": "interactive", "interactive": {"type": "button", ...}}
```

### List (4-10 options)
```python
result = format_for_whatsapp("""
Select a service:
1. Balance
2. Transfer
3. Cards
4. Loans
5. Support
""")
# → {"type": "interactive", "interactive": {"type": "list", ...}}
```

### With Header and Footer
```python
result = format_for_whatsapp("""
⚠️ Security Alert

We detected a login from a new device. Was this you?

1. Yes, it was me
2. No, secure my account

Reply within 24 hours.
""")
# Extracts header: "Security Alert"
# Extracts footer: "Reply within 24 hours."
```

## Development

```bash
# Clone the repository
git clone https://github.com/dnivra26/whatsapp-formatter.git
cd whatsapp-formatter

# Install with dev dependencies
pip install -e ".[dev]"

# Run tests
pytest
```

## License

MIT License