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
pip install whatsapp-formatter
Quick Start
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:
{
"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)
- Numbered lists (
API Reference
format_for_whatsapp(text: str) -> TransformResult
The main entry point. Parses text and returns WhatsApp-ready JSON.
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).
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).
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
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
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
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:
# 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)
result = format_for_whatsapp("Your balance is $500.")
# → {"type": "text", "text": {"body": "Your balance is $500."}}
Quick Reply (1-3 options)
result = format_for_whatsapp("""
Was this transaction authorized?
1. Yes
2. No
""")
# → {"type": "interactive", "interactive": {"type": "button", ...}}
List (4-10 options)
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
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
# 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
Release files for whatsapp-formatter 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| whatsapp_formatter-0.2.0.tar.gz | 12.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| whatsapp_formatter-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 31.3 kB
Release files / whatsapp_formatter-0.2.0.tar.gz
| Download URL | whatsapp_formatter-0.2.0.tar.gz |
|---|---|
| Size | 12.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9f917fba232907beee047122665e394efa72ca10877fd7c53b03554c997e8d17
|
|
BLAKE2b-256 checksum How to use checksums |
56157a744eff23956b9e7c1c828c98192ecd43cb3394d7aa5d4ebf1ca4bc09ae
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.7
|
Release files / whatsapp_formatter-0.2.0-py3-none-any.whl
| Download URL | whatsapp_formatter-0.2.0-py3-none-any.whl |
|---|---|
| Size | 18.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
31c52d7cb154cafb5a7092d07e9d1467fbf71e19dbc4ef995367e46c768fd158
|
|
BLAKE2b-256 checksum How to use checksums |
9ac43ef26787f4904b65d5342028bd1a837019fb0712f58ad7906ff91e26955e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.7
|