"""Subject Lines Generator Tool Specialized tool for email subject line generation with psychological angle testing. Generates 15-25 subject lines grouped by psychological mechanism. """ from typing import Optional from pydantic import Field from config import TEMPERATURE_HIGHLY_CREATIVE from systemprompts import SUBJECTLINES_PROMPT from tools.models import ToolModelCategory from tools.shared.base_models import ToolRequest from tools.simple.base import SimpleTool class SubjectLinesRequest(ToolRequest): """Request model for Subject Lines Generator""" email_topic: str = Field( ..., description="The main topic or content of the email. Can be a brief description or the full email content.", ) target_audience: str = Field( ..., description="Who will receive this email. Be specific about their role, interests, or pain points (e.g., 'HVAC technicians', 'B2B SaaS founders', 'technical content marketers').", ) angles_to_test: Optional[list[str]] = Field( default=None, description="Psychological angles to explore: 'curiosity', 'contrarian', 'knowledge_gap', 'urgency', 'insider', 'problem_solution', 'social_proof', 'transformation', 'fomo', 'specificity'. Leave empty for comprehensive testing.", ) subject_count: int = Field( default=15, ge=10, le=25, description="Number of subject lines to generate (10-25). Default is 15.", ) include_emoji: bool = Field( default=True, description="Include emoji suggestions for each subject line. Default is True.", ) preview_text: bool = Field( default=False, description="Also generate preview text (first line shown after subject in inbox) to complement each subject line.", ) constraints: Optional[str] = Field( default=None, description="Additional constraints: brand voice, prohibited words, industry terminology requirements, tone preferences.", ) class SubjectLinesTool(SimpleTool): """Generate email subject lines with psychological angle testing""" def get_name(self) -> str: return "subjectlines" def get_description(self) -> str: return ( "Generate 15-25 email subject lines testing different psychological angles. " "Groups lines by mechanism (curiosity, contrarian, urgency, etc.) with A/B testing rationale. " "Includes character counts, emoji suggestions, and preview text options. " "Optimized for email marketing and newsletter campaigns." ) def get_system_prompt(self) -> str: return SUBJECTLINES_PROMPT def get_default_temperature(self) -> float: return TEMPERATURE_HIGHLY_CREATIVE def get_model_category(self) -> ToolModelCategory: return ToolModelCategory.FAST_RESPONSE def get_request_model(self): return SubjectLinesRequest def get_tool_fields(self) -> dict: """Tool-specific field definitions for SubjectLines""" return { "email_topic": { "type": "string", "description": "The main topic or content of the email", }, "target_audience": { "type": "string", "description": "Who will receive this email", }, } async def prepare_prompt(self, request: SubjectLinesRequest) -> str: """Prepare the subject lines prompt""" prompt_parts = [ f"Generate {request.subject_count} email subject lines for this topic:" ] prompt_parts.append(f"\n**Email Topic:**\n{request.email_topic}") prompt_parts.append(f"\n**Target Audience:** {request.target_audience}") if request.angles_to_test: prompt_parts.append( f"\n**Focus on These Psychological Angles:** {', '.join(request.angles_to_test)}" ) else: prompt_parts.append( "\n**Test Comprehensive Range of Angles:** Include curiosity, contrarian, urgency, knowledge gap, and other effective psychological mechanisms" ) if request.constraints: prompt_parts.append(f"\n**Brand/Style Constraints:** {request.constraints}") prompt_parts.append( f"\n**Emoji Suggestions:** {'Include' if request.include_emoji else 'Skip'}" ) if request.preview_text: prompt_parts.append( "\n**Preview Text:** Also generate complementary preview text (40-100 chars) for each subject line" ) prompt_parts.append( "\n\nGroup by psychological angle, include character counts, A/B testing rationale, and make each line genuinely different." ) # Return the complete prompt return "\n".join(prompt_parts) def get_input_schema(self) -> dict: """Return the JSON schema for this tool's input""" return { "type": "object", "properties": { "email_topic": { "type": "string", "description": "The main topic or content of the email", }, "target_audience": { "type": "string", "description": "Who will receive this email (be specific about role, interests, or pain points)", }, "angles_to_test": { "type": "array", "items": {"type": "string"}, "description": "Angles: 'curiosity', 'contrarian', 'knowledge_gap', 'urgency', 'insider', 'problem_solution', 'social_proof', 'transformation', 'fomo', 'specificity'", }, "subject_count": { "type": "integer", "description": "Number of subject lines (10-25, default 15)", "minimum": 10, "maximum": 25, "default": 15, }, "include_emoji": { "type": "boolean", "description": "Include emoji suggestions (default true)", "default": True, }, "preview_text": { "type": "boolean", "description": "Generate preview text for each subject line", "default": False, }, "constraints": { "type": "string", "description": "Brand voice, prohibited words, tone preferences", }, "files": { "type": "array", "items": {"type": "string"}, "description": "Optional brand guidelines or previous email examples", }, "images": { "type": "array", "items": {"type": "string"}, "description": "Optional visual assets for context", }, "continuation_id": { "type": "string", "description": "Thread ID to continue previous conversation", }, "model": { "type": "string", "description": "AI model to use (leave empty for default fast model)", }, "temperature": { "type": "number", "description": "Creativity level 0.0-1.0 (default 0.8 for high variation)", "minimum": 0.0, "maximum": 1.0, }, "thinking_mode": { "type": "string", "description": "Thinking depth: minimal, low, medium, high, max", "enum": ["minimal", "low", "medium", "high", "max"], }, "use_websearch": { "type": "boolean", "description": "Enable web search for current email marketing best practices", "default": False, }, }, "required": ["email_topic", "target_audience"], }