AI Backlinker, Google Ads Generator, Letter Writer - WIP

This commit is contained in:
ajaysi
2025-05-06 22:27:43 +05:30
parent 26b02b9719
commit 5f7d319859
38 changed files with 14572 additions and 302 deletions

View File

@@ -7,6 +7,7 @@ well-researched FAQs from various content sources with customizable options.
import sys
import json
import re
from typing import Dict, List, Optional, Union
from pathlib import Path
from enum import Enum
@@ -15,12 +16,12 @@ from loguru import logger
from lib.gpt_providers.text_generation.main_text_generation import llm_text_gen
from lib.ai_web_researcher.google_serp_search import google_search
from lib.ai_web_researcher.tavily_ai_search import tavily_search
from lib.ai_web_researcher.tavily_ai_search import do_tavily_ai_search
from lib.ai_web_researcher.metaphor_basic_neural_web_search import metaphor_search_articles
logger.remove()
logger.add(sys.stdout,
colorize=True,
colorize=True,
format="<level>{level}</level>|<green>{file}:{line}:{function}</green>| {message}")
class TargetAudience(Enum):
@@ -51,6 +52,7 @@ class FAQConfig:
time_range: str = "last_6_months"
exclude_domains: List[str] = None
language: str = "English"
selected_search_queries: List[str] = None
@dataclass
class FAQItem:
@@ -71,26 +73,77 @@ class FAQGenerator:
self.config = config or FAQConfig()
self.faqs: List[FAQItem] = []
self.research_results = {}
self.search_queries = []
async def generate_faqs(self, content: str, content_type: str = "general") -> List[FAQItem]:
def generate_search_queries(self, content: str) -> List[str]:
"""Generate search queries based on the content."""
try:
prompt = f"""Based on the following content, generate 5 specific search queries that would help create comprehensive FAQs.
Content: {content}
Guidelines for search queries:
1. Focus on key concepts and terms
2. Include common questions users might have
3. Cover technical aspects that need clarification
4. Include best practices and recommendations
5. Make queries specific and focused
Please provide exactly 5 search queries, one per line.
Do not include numbers or bullet points in the queries.
"""
response = llm_text_gen(prompt)
# Clean up the queries by removing numbers and extra spaces
queries = []
for line in response.split('\n'):
# Remove any leading numbers, dots, or spaces
cleaned = re.sub(r'^\d+\.\s*', '', line.strip())
if cleaned:
queries.append(cleaned)
self.search_queries = queries[:5] # Ensure we only get 5 queries
return self.search_queries
except Exception as err:
logger.error(f"Failed to generate search queries: {err}")
return []
def _clean_search_query(self, query: str) -> str:
"""Clean up a search query by removing numbers and extra formatting."""
# Remove any leading numbers, dots, or spaces
cleaned = re.sub(r'^\d+\.\s*', '', query.strip())
# Remove any quotes
cleaned = cleaned.replace('"', '').replace("'", '')
# Remove any extra spaces
cleaned = ' '.join(cleaned.split())
return cleaned
def generate_faqs(self, content: str, content_type: str = "general") -> List[FAQItem]:
"""Generate FAQs from the given content with research integration."""
try:
# Step 1: Research the topic
research_results = await self._conduct_research(content)
if not self.config.selected_search_queries:
raise ValueError("No search queries selected. Please select queries to proceed.")
# Clean up selected queries
cleaned_queries = [self._clean_search_query(q) for q in self.config.selected_search_queries]
self.config.selected_search_queries = cleaned_queries
# Step 1: Research the topic using selected queries
research_results = self._conduct_research(content)
# Step 2: Generate initial FAQs
initial_faqs = await self._generate_initial_faqs(content, research_results)
initial_faqs = self._generate_initial_faqs(content, research_results)
# Step 3: Enhance FAQs with research
enhanced_faqs = await self._enhance_faqs_with_research(initial_faqs, research_results)
enhanced_faqs = self._enhance_faqs_with_research(initial_faqs, research_results)
# Step 4: Add code examples if requested
if self.config.include_code_examples:
enhanced_faqs = await self._add_code_examples(enhanced_faqs)
enhanced_faqs = self._add_code_examples(enhanced_faqs)
# Step 5: Add references if requested
if self.config.include_references:
enhanced_faqs = await self._add_references(enhanced_faqs, research_results)
enhanced_faqs = self._add_references(enhanced_faqs, research_results)
self.faqs = enhanced_faqs
return enhanced_faqs
@@ -99,38 +152,34 @@ class FAQGenerator:
logger.error(f"Failed to generate FAQs: {err}")
raise
async def _conduct_research(self, content: str) -> Dict:
"""Conduct online research based on the content."""
def _conduct_research(self, content: str) -> Dict:
"""Conduct online research based on the selected search queries."""
try:
research_prompt = f"""Based on the following content, identify key topics and questions for research:
{content}
Please provide a list of research topics and questions that would help create comprehensive FAQs.
Focus on:
1. Key concepts and terms
2. Common questions users might have
3. Technical aspects that need clarification
4. Best practices and recommendations
"""
research_topics = await llm_text_gen(research_prompt)
# Conduct research for each topic
research_results = {}
for topic in research_topics.split('\n'):
if topic.strip():
for query in self.config.selected_search_queries:
try:
# Clean the query before searching
cleaned_query = self._clean_search_query(query)
logger.info(f"Researching query: {cleaned_query}")
# Select search function based on search depth
if self.config.search_depth == SearchDepth.BASIC:
results = await google_search(topic.strip())
results = google_search(cleaned_query)
elif self.config.search_depth == SearchDepth.COMPREHENSIVE:
results = await tavily_search(topic.strip())
results = do_tavily_ai_search(cleaned_query)
elif self.config.search_depth == SearchDepth.EXPERT:
results = await metaphor_search_articles(topic.strip())
results = metaphor_search_articles(cleaned_query)
else:
logger.warning(f"Unknown search depth: {self.config.search_depth}, defaulting to Google search")
results = await google_search(topic.strip())
results = google_search(cleaned_query)
research_results[topic.strip()] = results
research_results[query] = results
logger.info(f"Research completed for query: {query}")
except Exception as err:
logger.error(f"Failed to research query '{query}': {err}")
continue
return research_results
@@ -138,7 +187,7 @@ class FAQGenerator:
logger.error(f"Failed to conduct research: {err}")
return {}
async def _generate_initial_faqs(self, content: str, research_results: Dict) -> List[FAQItem]:
def _generate_initial_faqs(self, content: str, research_results: Dict) -> List[FAQItem]:
"""Generate initial FAQs using LLM."""
try:
system_prompt = f"""You are an expert FAQ generator with deep knowledge in content creation and technical writing.
@@ -159,6 +208,13 @@ class FAQGenerator:
- Based on the provided research
- Relevant to the target audience
- Written in the specified style
Format each FAQ exactly as follows:
Q: [Your question here]
A: [Your detailed answer here]
Category: [Category name]
Confidence: [Score between 0 and 1]
---
"""
prompt = f"""Content to generate FAQs from:
@@ -168,22 +224,26 @@ class FAQGenerator:
{json.dumps(research_results, indent=2)}
Please generate {self.config.num_faqs} FAQs following the guidelines above.
Format each FAQ with:
- Question
- Detailed answer
- Category
- Confidence score (0-1)
Each FAQ must be separated by '---' and include all required fields.
"""
response = await llm_text_gen(prompt, system_prompt=system_prompt)
response = llm_text_gen(prompt, system_prompt=system_prompt)
logger.info(f"LLM Response: {response}")
# Parse the response into FAQItem objects
faqs = []
current_faq = None
for line in response.split('\n'):
line = line.strip()
if not line or line == '---':
if current_faq and current_faq.question and current_faq.answer:
faqs.append(current_faq)
current_faq = None
continue
if line.startswith('Q:'):
if current_faq:
if current_faq and current_faq.question and current_faq.answer:
faqs.append(current_faq)
current_faq = FAQItem(question=line[2:].strip(), answer="", category="")
elif line.startswith('A:'):
@@ -194,18 +254,23 @@ class FAQGenerator:
current_faq.category = line[9:].strip()
elif line.startswith('Confidence:'):
if current_faq:
current_faq.confidence_score = float(line[11:].strip())
try:
current_faq.confidence_score = float(line[11:].strip())
except ValueError:
current_faq.confidence_score = 0.5
if current_faq:
# Add the last FAQ if it exists and is complete
if current_faq and current_faq.question and current_faq.answer:
faqs.append(current_faq)
logger.info(f"Generated {len(faqs)} FAQs")
return faqs
except Exception as err:
logger.error(f"Failed to generate initial FAQs: {err}")
raise
async def _enhance_faqs_with_research(self, faqs: List[FAQItem], research_results: Dict) -> List[FAQItem]:
def _enhance_faqs_with_research(self, faqs: List[FAQItem], research_results: Dict) -> List[FAQItem]:
"""Enhance FAQs with research findings."""
try:
enhanced_faqs = []
@@ -231,7 +296,7 @@ class FAQGenerator:
4. Keeping the answer concise and clear
"""
enhanced_answer = await llm_text_gen(enhancement_prompt)
enhanced_answer = llm_text_gen(enhancement_prompt)
faq.answer = enhanced_answer
enhanced_faqs.append(faq)
@@ -242,24 +307,20 @@ class FAQGenerator:
logger.error(f"Failed to enhance FAQs with research: {err}")
return faqs
async def _add_code_examples(self, faqs: List[FAQItem]) -> List[FAQItem]:
def _add_code_examples(self, faqs: List[FAQItem]) -> List[FAQItem]:
"""Add code examples to FAQs where applicable."""
try:
for faq in faqs:
if self._is_technical_question(faq.question):
code_prompt = f"""Generate a code example for the following FAQ:
Question: {faq.question}
Answer: {faq.answer}
Please provide a relevant code example that:
1. Illustrates the answer clearly
2. Includes comments and explanations
3. Follows best practices
4. Is easy to understand
Please provide a relevant code example that demonstrates the concept.
Include comments and explanations where necessary.
"""
code_example = await llm_text_gen(code_prompt)
code_example = llm_text_gen(code_prompt)
faq.code_example = code_example
return faqs
@@ -268,21 +329,19 @@ class FAQGenerator:
logger.error(f"Failed to add code examples: {err}")
return faqs
async def _add_references(self, faqs: List[FAQItem], research_results: Dict) -> List[FAQItem]:
"""Add references to FAQs."""
def _add_references(self, faqs: List[FAQItem], research_results: Dict) -> List[FAQItem]:
"""Add references to FAQs based on research results."""
try:
for faq in faqs:
relevant_research = self._find_relevant_research(faq, research_results)
if relevant_research:
faq.references = [
{
"title": ref.get("title", ""),
"url": ref.get("url", ""),
"source": ref.get("source", ""),
"date": ref.get("date", "")
}
for ref in relevant_research.get("references", [])
]
references = []
for source, content in relevant_research.items():
references.append({
"source": source,
"content": content
})
faq.references = references
return faqs
@@ -291,8 +350,7 @@ class FAQGenerator:
return faqs
def _find_relevant_research(self, faq: FAQItem, research_results: Dict) -> Dict:
"""Find research relevant to a specific FAQ."""
# Simple keyword matching for now - can be enhanced with semantic search
"""Find research results relevant to a specific FAQ."""
relevant_research = {}
for topic, results in research_results.items():
if any(keyword in faq.question.lower() for keyword in topic.lower().split()):
@@ -308,8 +366,8 @@ class FAQGenerator:
"""Convert FAQs to markdown format."""
markdown = "# Frequently Asked Questions\n\n"
for i, faq in enumerate(self.faqs, 1):
markdown += f"## {i}. {faq.question}\n\n"
for faq in self.faqs:
markdown += f"## {faq.question}\n\n"
markdown += f"{faq.answer}\n\n"
if faq.code_example:
@@ -320,7 +378,7 @@ class FAQGenerator:
if faq.references:
markdown += "### References\n"
for ref in faq.references:
markdown += f"- [{ref['title']}]({ref['url']}) - {ref['source']} ({ref['date']})\n"
markdown += f"- {ref['source']}\n"
markdown += "\n"
return markdown
@@ -333,52 +391,52 @@ class FAQGenerator:
<head>
<title>Frequently Asked Questions</title>
<style>
.faq-container { max-width: 800px; margin: 0 auto; }
.faq-item { margin-bottom: 2em; }
.question { font-weight: bold; font-size: 1.2em; }
.answer { margin: 1em 0; }
.code-example { background: #f5f5f5; padding: 1em; }
.references { margin-top: 1em; font-size: 0.9em; }
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
.faq { margin-bottom: 30px; }
.question { font-weight: bold; font-size: 1.2em; color: #2c3e50; }
.answer { margin: 10px 0; }
.code-example { background: #f8f9fa; padding: 15px; border-radius: 4px; }
.references { margin-top: 15px; font-size: 0.9em; }
</style>
</head>
<body>
<div class="faq-container">
<h1>Frequently Asked Questions</h1>
<h1>Frequently Asked Questions</h1>
"""
for i, faq in enumerate(self.faqs, 1):
for faq in self.faqs:
html += f"""
<div class="faq-item">
<div class="question">{i}. {faq.question}</div>
<div class="answer">{faq.answer}</div>
<div class="faq">
<div class="question">{faq.question}</div>
<div class="answer">{faq.answer}</div>
"""
if faq.code_example:
html += f"""
<pre class="code-example">{faq.code_example}</pre>
<div class="code-example">
<pre><code>{faq.code_example}</code></pre>
</div>
"""
if faq.references:
html += """
<div class="references">
<h3>References</h3>
<ul>
<div class="references">
<h3>References</h3>
<ul>
"""
for ref in faq.references:
html += f"""
<li><a href="{ref['url']}">{ref['title']}</a> - {ref['source']} ({ref['date']})</li>
<li>{ref['source']}</li>
"""
html += """
</ul>
</div>
</ul>
</div>
"""
html += """
</div>
</div>
"""
html += """
</div>
</body>
</html>
"""

View File

@@ -5,15 +5,27 @@ This module provides a user-friendly interface for generating FAQs from various
"""
import streamlit as st
import asyncio
from pathlib import Path
from typing import Optional
import json
import requests
from bs4 import BeautifulSoup
import logging
import pyperclip
from .faqs_generator_blog import FAQGenerator, FAQConfig, TargetAudience, FAQStyle, SearchDepth
# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def copy_to_clipboard(text: str) -> None:
"""Copy text to clipboard and show success message."""
try:
pyperclip.copy(text)
st.success("Copied to clipboard!")
except Exception as e:
st.error(f"Failed to copy to clipboard: {str(e)}")
def fetch_url_content(url):
"""Fetch and extract content from a URL."""
@@ -42,15 +54,27 @@ def fetch_url_content(url):
return None
def main():
st.set_page_config(
page_title="FAQ Generator",
page_icon="",
layout="wide"
)
st.title("FAQ Generator")
st.markdown("Generate comprehensive FAQs from your content with research integration.")
# Initialize session state variables if they don't exist
if 'search_queries' not in st.session_state:
st.session_state.search_queries = []
if 'selected_queries' not in st.session_state:
st.session_state.selected_queries = []
if 'research_completed' not in st.session_state:
st.session_state.research_completed = False
if 'research_results' not in st.session_state:
st.session_state.research_results = {}
if 'faq_config' not in st.session_state:
st.session_state.faq_config = None
if 'generator' not in st.session_state:
st.session_state.generator = FAQGenerator()
if 'generated_faqs' not in st.session_state:
st.session_state.generated_faqs = None
if 'output_format' not in st.session_state:
st.session_state.output_format = "Preview"
# Sidebar for configuration
with st.sidebar:
st.header("Configuration")
@@ -99,40 +123,137 @@ def main():
if content:
st.text_area("Extracted Content", content, height=300)
# Generate button
if st.button("Generate FAQs") and content:
try:
# Create config
config = FAQConfig(
num_faqs=num_faqs,
target_audience=TargetAudience(target_audience),
faq_style=FAQStyle(faq_style),
include_emojis=include_emojis,
include_code_examples=include_code_examples,
include_references=include_references,
search_depth=SearchDepth(search_depth),
time_range=time_range,
language=language
)
# Initialize generator
generator = FAQGenerator(config)
# Generate FAQs
with st.spinner("Generating FAQs..."):
faqs = asyncio.run(generator.generate_faqs(content))
# Display results
st.success("FAQs generated successfully!")
# Step 1: Generate search queries
if content and not st.session_state.search_queries:
if st.button("Generate Search Queries"):
with st.spinner("Generating search queries..."):
search_queries = st.session_state.generator.generate_search_queries(content)
if search_queries:
st.session_state.search_queries = search_queries
st.session_state.selected_queries = [] # Reset selected queries
st.session_state.research_completed = False # Reset research status
st.session_state.research_results = {} # Reset research results
st.session_state.faq_config = None # Reset config
st.session_state.generated_faqs = None # Reset generated FAQs
st.success("Search queries generated successfully!")
# Step 2: Display and select search queries
if st.session_state.search_queries:
st.subheader("Select Search Queries")
st.info("Select the queries you want to use for web research. You can select multiple queries.")
# Create checkboxes for each search query
selected_queries = []
for query in st.session_state.search_queries:
if st.checkbox(query, key=f"query_{query}", value=query in st.session_state.selected_queries):
selected_queries.append(query)
# Update selected queries in session state
st.session_state.selected_queries = selected_queries
# Step 3: Do web research
if st.session_state.selected_queries and not st.session_state.research_completed:
if st.button("Do Web Research"):
try:
# Create config with selected queries
config = FAQConfig(
num_faqs=num_faqs,
target_audience=TargetAudience(target_audience),
faq_style=FAQStyle(faq_style),
include_emojis=include_emojis,
include_code_examples=include_code_examples,
include_references=include_references,
search_depth=SearchDepth(search_depth),
time_range=time_range,
language=language,
selected_search_queries=selected_queries
)
# Store config in session state
st.session_state.faq_config = config
# Update generator with config
st.session_state.generator.config = config
# Do research
with st.spinner("Conducting web research..."):
research_results = st.session_state.generator._conduct_research(content)
st.session_state.research_completed = True
st.session_state.research_results = research_results
st.success("Web research completed successfully!")
# Display research results
st.subheader("Research Results")
for query, results in research_results.items():
with st.expander(f"Results for: {query}"):
if isinstance(results, dict):
st.json(results)
else:
st.text(results)
except Exception as e:
st.error(f"Error during web research: {str(e)}")
st.error("Please try again with different search queries or adjust the search depth.")
# Step 4: Generate FAQs
if st.session_state.research_completed and st.session_state.research_results and st.session_state.faq_config:
if st.button("Generate FAQs"):
try:
# Update generator with stored config
st.session_state.generator.config = st.session_state.faq_config
# Generate FAQs
with st.spinner("Generating FAQs..."):
logger.info("Starting FAQ generation...")
faqs = st.session_state.generator.generate_faqs(content)
logger.info(f"Generated {len(faqs) if faqs else 0} FAQs")
if not faqs:
st.error("No FAQs were generated. Please try again.")
return
st.session_state.generated_faqs = faqs
st.success("FAQs generated successfully!")
except Exception as e:
logger.error(f"Error generating FAQs: {str(e)}")
st.error(f"Error generating FAQs: {str(e)}")
st.error("Please try again or adjust your settings.")
# Display generated FAQs if they exist
if st.session_state.generated_faqs:
st.subheader("Generated FAQs")
# Output format selection
output_format = st.radio(
"Output Format",
["Preview", "Markdown", "HTML", "JSON"]
["Preview", "Markdown", "HTML", "JSON"],
key="output_format"
)
# Create columns for copy and download buttons
col1, col2 = st.columns(2)
if output_format == "Preview":
for i, faq in enumerate(faqs, 1):
# Create a formatted text for copying
preview_text = ""
for i, faq in enumerate(st.session_state.generated_faqs, 1):
preview_text += f"{i}. {faq.question}\n"
preview_text += f"{faq.answer}\n\n"
if faq.code_example:
preview_text += f"Code Example:\n{faq.code_example}\n\n"
if faq.references:
preview_text += "References:\n"
for ref in faq.references:
preview_text += f"- {ref['source']}\n"
preview_text += "\n"
with col1:
if st.button("Copy to Clipboard", key="copy_preview"):
copy_to_clipboard(preview_text)
# Display the FAQs
for i, faq in enumerate(st.session_state.generated_faqs, 1):
with st.expander(f"{i}. {faq.question}"):
st.markdown(faq.answer)
if faq.code_example:
@@ -140,38 +261,52 @@ def main():
if faq.references:
st.markdown("**References:**")
for ref in faq.references:
st.markdown(f"- [{ref['title']}]({ref['url']}) - {ref['source']} ({ref['date']})")
st.markdown(f"- {ref['source']}")
elif output_format == "Markdown":
st.code(generator.to_markdown(), language="markdown")
st.download_button(
"Download Markdown",
generator.to_markdown(),
file_name="faqs.md",
mime="text/markdown"
)
markdown_output = st.session_state.generator.to_markdown()
st.code(markdown_output, language="markdown")
with col1:
if st.button("Copy to Clipboard", key="copy_markdown"):
copy_to_clipboard(markdown_output)
with col2:
st.download_button(
"Download Markdown",
markdown_output,
file_name="faqs.md",
mime="text/markdown"
)
elif output_format == "HTML":
st.code(generator.to_html(), language="html")
st.download_button(
"Download HTML",
generator.to_html(),
file_name="faqs.html",
mime="text/html"
)
html_output = st.session_state.generator.to_html()
st.code(html_output, language="html")
with col1:
if st.button("Copy to Clipboard", key="copy_html"):
copy_to_clipboard(html_output)
with col2:
st.download_button(
"Download HTML",
html_output,
file_name="faqs.html",
mime="text/html"
)
elif output_format == "JSON":
json_output = json.dumps([faq.__dict__ for faq in faqs], indent=2)
json_output = json.dumps([faq.__dict__ for faq in st.session_state.generated_faqs], indent=2)
st.code(json_output, language="json")
st.download_button(
"Download JSON",
json_output,
file_name="faqs.json",
mime="application/json"
)
except Exception as e:
st.error(f"Error generating FAQs: {str(e)}")
with col1:
if st.button("Copy to Clipboard", key="copy_json"):
copy_to_clipboard(json_output)
with col2:
st.download_button(
"Download JSON",
json_output,
file_name="faqs.json",
mime="application/json"
)
if __name__ == "__main__":
main()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,758 @@
"""
Letter Templates Module
This module provides structured templates and guidance for generating
different types and subtypes of letters.
Templates are defined as a nested dictionary containing 'structure' (list of sections)
and 'guidance' (a string) for each letter type and subtype.
"""
from typing import Dict, Any, List
# Define letter templates using a nested dictionary structure for better organization and lookup.
# The structure is {letter_type: {subtype: {template_details}}}
# 'default' subtype is used as a fallback if a specific subtype isn't found for a given type.
TEMPLATES: Dict[str, Dict[str, Dict[str, Any]]] = {
"personal": {
"congratulations": {
"structure": [
"Greeting",
"Express congratulations",
"Acknowledge the achievement",
"Share personal thoughts/memory (optional)",
"Look to the future/well wishes",
"Closing"
],
"guidance": "Be warm, sincere, and specific about the achievement. Express genuine happiness for the recipient. Keep the tone personal and friendly."
},
"thank_you": {
"structure": [
"Greeting",
"Express gratitude clearly",
"Specify what you are thankful for",
"Explain the impact or how you used it (optional)",
"Share a personal thought or memory (optional)",
"Offer reciprocation or look to the future",
"Closing"
],
"guidance": "Be specific about what you're thankful for and how it affected you. Express sincere appreciation. Personalize the message."
},
"sympathy": {
"structure": [
"Greeting",
"Express sympathy for the loss",
"Acknowledge the significance of the person/situation",
"Share a positive memory or quality (optional)",
"Offer specific support (optional)",
"Closing with comforting words"
],
"guidance": "Be gentle, compassionate, and sincere. Avoid clichés. Focus on offering genuine comfort and acknowledging the recipient's feelings."
},
"apology": {
"structure": [
"Greeting",
"Clearly state your apology",
"Acknowledge the specific mistake or action",
"Express understanding of the impact on the other person",
"Explain (briefly, without making excuses) what happened (optional)",
"Offer amends or suggest how to make things right",
"Assure it won't happen again",
"Closing"
],
"guidance": "Be sincere, take full responsibility for your actions, and focus on making things right. Avoid making excuses or blaming others."
},
"invitation": {
"structure": [
"Greeting",
"Clearly state the invitation",
"Provide full event details (What, When, Where)",
"Explain the significance or purpose (optional)",
"Mention who else might be there (optional)",
"Request RSVP (date and contact method)",
"Express anticipation",
"Closing"
],
"guidance": "Be clear and specific about the details (what, when, where, why). Make it easy for the person to respond."
},
"friendship": {
"structure": [
"Greeting",
"Express appreciation for the friendship",
"Share a recent memory or anecdote",
"Acknowledge the value of the relationship",
"Check in on them or share updates",
"Look to the future (getting together, etc.)",
"Closing"
],
"guidance": "Be warm, personal, and specific about what you value in the friendship. Share updates and show genuine interest."
},
"love": {
"structure": [
"Greeting (Terms of endearment)",
"Express depth of feelings",
"Share a cherished memory or moment",
"Describe specific qualities you love and appreciate",
"Reaffirm commitment or future hopes",
"Closing (Terms of endearment)"
],
"guidance": "Be sincere, personal, and specific about your feelings. Use sensory details and emotional language appropriate for your relationship."
},
"encouragement": {
"structure": [
"Greeting",
"Acknowledge the situation or challenge they face",
"Express belief in their abilities/strength",
"Offer specific words of encouragement or support",
"Remind them of past successes (optional)",
"Offer practical help (optional)",
"Look to the future with hope",
"Closing with support"
],
"guidance": "Be positive, supportive, and specific about the person's strengths and abilities. Offer genuine encouragement and belief in them."
},
"farewell": {
"structure": [
"Greeting",
"State the purpose (saying goodbye)",
"Express feelings about their departure (sadness, happiness for them)",
"Share a positive memory or highlight their contribution",
"Express good wishes for their future endeavors",
"Look to staying in touch (optional)",
"Closing"
],
"guidance": "Be warm, reflective, and forward-looking. Focus on positive memories and express genuine good wishes for their next steps."
},
# Default personal letter template if subtype is not found
"default": {
"structure": [
"Greeting",
"Introduction",
"Main content paragraphs",
"Closing thoughts",
"Signature"
],
"guidance": "Be personal, authentic, and appropriate for your relationship with the recipient. The tone is typically informal to semi-formal."
}
},
"formal": {
"application": {
"structure": [
"Sender's contact information",
"Date",
"Recipient's contact information (if known)",
"Subject line (Clear and concise)",
"Salutation (Formal)",
"Introduction (State position applied for and where you saw it)",
"Body paragraphs (Highlight relevant skills and experience)",
"Closing paragraph (Reiterate interest, mention enclosed resume, call to action)",
"Complimentary close (Formal)",
"Signature (Typed name)",
"Enclosures (Mention if attaching resume/portfolio)"
],
"guidance": "Be professional, concise, and specific about your qualifications and genuine interest in the position. Tailor it to the specific job description."
},
"complaint": {
"structure": [
"Sender's contact information",
"Date",
"Recipient's contact information",
"Subject line (Clearly state it's a complaint)",
"Salutation (Formal)",
"Introduction (State the purpose: complaint about X service/product)",
"Problem description (Provide specific details: date, time, location, product details, names if applicable)",
"Impact statement (Explain how the problem affected you)",
"Requested resolution (Clearly state what you want: refund, replacement, action)",
"Closing paragraph (Reference attached documents, state expectation for response)",
"Complimentary close (Formal)",
"Signature (Typed name)"
],
"guidance": "Be clear, factual, and specific about the issue and your desired resolution. Maintain a respectful but firm tone. Include all relevant details."
},
"request": {
"structure": [
"Sender's contact information",
"Date",
"Recipient's contact information",
"Subject line (Clearly state the request)",
"Salutation (Formal)",
"Introduction (State the purpose: making a request)",
"Request details (Clearly explain what you are requesting)",
"Justification (Explain why the request is necessary or beneficial)",
"Provide supporting information (optional)",
"Closing paragraph (Express gratitude for consideration, reiterate call to action)",
"Complimentary close (Formal)",
"Signature (Typed name)"
],
"guidance": "Be clear, specific, and courteous about your request. Explain why it's important or beneficial to the recipient or organization."
},
"recommendation": {
"structure": [
"Sender's contact information",
"Date",
"Recipient's contact information",
"Subject line (Letter of Recommendation for [Name])",
"Salutation (Formal)",
"Introduction (State your name, title, relationship to the recommendee, and for what purpose the letter is written)",
"Body paragraphs (Describe the recommendee's qualifications, skills, and achievements with specific examples)",
"Highlight relevant experiences and contributions",
"Closing recommendation (Summarize endorsement, strongly recommend the person)",
"Complimentary close (Formal)",
"Signature (Typed name and title)"
],
"guidance": "Be specific, positive, and credible. Use concrete examples and anecdotes to support your recommendation. Tailor it to the specific role/opportunity."
},
"resignation": {
"structure": [
"Sender's contact information",
"Date",
"Recipient's contact information (Immediate supervisor/HR)",
"Subject line (Letter of Resignation - [Your Name])",
"Salutation (Formal)",
"Statement of resignation (Clearly state you are resigning)",
"Last day of employment (Specify the date)",
"Gratitude and reflection (Optional: Express thanks for the opportunity/experience)",
"Transition plan/Offer of assistance (Optional: Suggest how to ensure a smooth handover)",
"Closing paragraph (Express good wishes for the company's future)",
"Complimentary close (Formal)",
"Signature (Typed name)"
],
"guidance": "Be professional, positive (if possible), and clear about your departure and last day. Maintain a good relationship."
},
"inquiry": {
"structure": [
"Sender's contact information",
"Date",
"Recipient's contact information",
"Subject line (Clearly state the nature of the inquiry)",
"Salutation (Formal)",
"Introduction (State your purpose for writing - making an inquiry)",
"Inquiry details (Provide necessary context or background)",
"Specific questions (List your questions clearly, perhaps numbered)",
"Closing paragraph (Express gratitude for assistance, indicate when you need a response)",
"Complimentary close (Formal)",
"Signature (Typed name)"
],
"guidance": "Be clear, specific, and courteous about your inquiry. Organize your questions logically for easy answering."
},
"authorization": {
"structure": [
"Sender's contact information (The grantor of authority)",
"Date",
"Recipient's contact information (The person/entity receiving the letter)",
"Subject line (Letter of Authorization)",
"Salutation (Formal)",
"Statement of authorization (Clearly state who is authorized)",
"Authorized person's details (Full name, ID if applicable)",
"Scope of authority (Precisely define what they are authorized to do)",
"Limitations (Specify any restrictions or conditions)",
"Duration of authorization (Start and end dates, if applicable)",
"Closing paragraph (State responsibility, express confidence)",
"Complimentary close (Formal)",
"Signature (Typed name and title)"
],
"guidance": "Be clear, specific, and precise about who is authorized, what they can do, for how long, and under what conditions. This is a legal document."
},
"appeal": {
"structure": [
"Sender's contact information",
"Date",
"Recipient's contact information (Appeals committee/relevant authority)",
"Subject line (Letter of Appeal - [Your Name] - [Subject of Appeal])",
"Salutation (Formal)",
"Introduction (State your name, the decision being appealed, and the date of the decision)",
"Grounds for appeal (Clearly state the reasons why you believe the decision is incorrect)",
"Provide supporting evidence (Reference attached documents: records, photos, etc.)",
"Explain mitigating circumstances (Optional)",
"Requested outcome (Clearly state what resolution you seek)",
"Closing paragraph (Express hope for reconsideration, gratitude for time)",
"Complimentary close (Formal)",
"Signature (Typed name)"
],
"guidance": "Be respectful, factual, and persuasive. Focus on valid grounds for appeal and provide clear, supporting evidence. Maintain a formal tone."
},
"introduction": {
"structure": [
"Sender's contact information",
"Date",
"Recipient's contact information",
"Subject line (Introduction - [Your Name])",
"Salutation (Formal)",
"Introduction (Introduce yourself and the purpose of the letter)",
"Background information (Briefly describe your relevant background or expertise)",
"Reason for reaching out (Explain why you are introducing yourself to this specific person/entity)",
"Potential areas of collaboration or shared interest (Optional)",
"Call to action (Suggest a meeting, call, or further communication)",
"Closing paragraph (Express enthusiasm for potential connection)",
"Complimentary close (Formal)",
"Signature (Typed name)"
],
"guidance": "Be professional, informative, and engaging. Clearly explain who you are, your expertise, and why you're reaching out to them specifically."
},
# Default formal letter template if subtype is not found
"default": {
"structure": [
"Sender's address",
"Date",
"Recipient's address",
"Subject line",
"Salutation",
"Introduction",
"Body paragraphs",
"Closing paragraph",
"Complimentary close",
"Signature"
],
"guidance": "Be professional, clear, and concise. Use formal language and structure. The tone is typically formal."
}
},
"business": {
"sales": {
"structure": [
"Letterhead",
"Date",
"Recipient's address",
"Subject line (Benefit-oriented)",
"Salutation",
"Attention-grabbing opening (Address a pain point or introduce a benefit)",
"Problem statement (Briefly describe the challenge the recipient faces)",
"Solution presentation (Introduce your product/service as the solution)",
"Benefits and features (Explain how your solution helps, focusing on benefits)",
"Social proof (Optional: Testimonials, case studies, data)",
"Call to action (Clearly state what you want them to do next)",
"Closing paragraph (Reiterate benefit, create urgency/incentive)",
"Complimentary close (Professional)",
"Signature (Typed name and title)",
"Enclosures (Optional: Brochure, pricing)"
],
"guidance": "Be persuasive, customer-focused, and clear about the value proposition. Focus on benefits, not just features. Make the call to action obvious."
},
"proposal": {
"structure": [
"Letterhead",
"Date",
"Recipient's address",
"Subject line (Clear and descriptive)",
"Salutation",
"Introduction (State purpose: submitting a proposal)",
"Problem statement/Needs assessment (Demonstrate understanding of client's needs)",
"Proposed solution (Describe your solution in detail)",
"Implementation plan (Outline steps and timeline)",
"Costs and investment (Clearly state pricing and payment terms)",
"Benefits and ROI (Explain the value the client will receive)",
"Call to action (Suggest next steps: meeting, discussion)",
"Closing paragraph (Express enthusiasm, availability for questions)",
"Complimentary close (Professional)",
"Signature (Typed name and title)",
"Enclosures (Proposal document, appendix)"
],
"guidance": "Be clear, specific, and persuasive about your solution. Focus on the client's needs and the value you provide. Structure it logically."
},
"order": {
"structure": [
"Letterhead (Your company)",
"Date",
"Recipient's address (Supplier)",
"Subject line (Purchase Order - [PO Number])",
"Salutation",
"Introduction (Reference quote/agreement, state purpose: placing an order)",
"Order details (Item list with quantities, descriptions, unit prices, total)",
"Delivery requirements (Shipping address, requested delivery date, shipping method)",
"Payment terms (Reference agreed terms)",
"Closing paragraph (Express expectation for timely delivery)",
"Complimentary close (Professional)",
"Signature (Typed name and title)"
],
"guidance": "Be clear, specific, and detailed about what you're ordering, quantities, delivery requirements, and payment terms. Include a purchase order number."
},
"quotation": {
"structure": [
"Letterhead (Your company)",
"Date",
"Recipient's address (Customer)",
"Subject line (Quotation for [Product/Service])",
"Salutation",
"Introduction (Reference inquiry, state purpose: providing a quotation)",
"Quotation details (List items/services, descriptions, unit prices, quantities, line totals)",
"Pricing breakdown (Mention taxes, discounts, fees separately)",
"Terms and conditions (Payment terms, delivery terms, warranty)",
"Validity period (State how long the quote is valid)",
"Next steps (How they can place an order)",
"Closing paragraph (Express hope to do business, offer further assistance)",
"Complimentary close (Professional)",
"Signature (Typed name and title)"
],
"guidance": "Be clear, specific, and transparent about pricing, terms, and what's included or excluded. Make it easy for the customer to understand and accept."
},
"acknowledgment": {
"structure": [
"Letterhead",
"Date",
"Recipient's address",
"Subject line (Acknowledgment of [Received Item/Request])",
"Salutation",
"Acknowledgment statement (Clearly state what you have received or are acknowledging)",
"Details of what's being acknowledged (Reference number, date, brief description)",
"Confirm understanding (Optional: Briefly restate the request/issue to show understanding)",
"Next steps (Outline what will happen next, e.g., processing order, investigating issue)",
"Timeline (Provide an estimated timeframe if possible)",
"Closing paragraph (Express gratitude, offer further assistance)",
"Complimentary close (Professional)",
"Signature (Typed name and title)"
],
"guidance": "Be prompt, clear, and specific about what you're acknowledging. Set clear expectations for next steps and timelines."
},
"collection": {
"structure": [
"Letterhead",
"Date",
"Recipient's address",
"Subject line (Invoice [Invoice Number] - Payment Due)",
"Salutation",
"Introduction (Reference invoice number and due date)",
"Account status (Clearly state the outstanding amount)",
"Payment request (Politely request payment)",
"Payment options (Remind them how to pay)",
"Consequences of non-payment (Optional: Briefly mention late fees or further action, depending on letter stage)",
"Call to action (Request payment by a specific date)",
"Closing paragraph (Express hope for prompt payment, offer to discuss)",
"Complimentary close (Professional)",
"Signature (Typed name and title)"
],
"guidance": "Be firm but professional. Clearly state the amount due, due date, and payment options. The tone may vary depending on how overdue the payment is."
},
"adjustment": {
"structure": [
"Letterhead",
"Date",
"Recipient's address (Customer who made a complaint)",
"Subject line (Response to your inquiry - [Reference Number])",
"Salutation",
"Acknowledgment of complaint (Reference their communication and the issue)",
"Investigation findings (Explain the outcome of your investigation)",
"Adjustment offered (Clearly state the resolution: refund, replacement, credit, etc.)",
"Apology (Optional: Express regret for the inconvenience)",
"Preventive measures (Optional: Explain steps taken to prevent recurrence)",
"Closing paragraph (Express hope for continued business, offer further assistance)",
"Complimentary close (Professional)",
"Signature (Typed name and title)"
],
"guidance": "Be responsive, empathetic, and solution-oriented. Clearly explain the adjustment and any preventive measures taken."
},
"credit": {
"structure": [
"Letterhead",
"Date",
"Recipient's address (Applicant)",
"Subject line (Credit Application Status - [Applicant Name])",
"Salutation",
"Introduction (Reference their credit application and the purpose of the letter)",
"Credit decision (Clearly state if credit is approved or denied)",
"If approved: Credit terms (Credit limit, payment terms, interest rates)",
"If denied: Reason for decision (Provide specific, compliant reasons)",
"Requirements (If approved: any further steps or documents needed)",
"Closing paragraph (If approved: Express welcome; If denied: Offer alternative options or appeals process)",
"Complimentary close (Professional)",
"Signature (Typed name and title)"
],
"guidance": "Be clear, specific, and transparent about the credit decision, terms, limits, or reasons for denial. Ensure compliance with regulations if denying credit."
},
"follow_up": {
"structure": [
"Letterhead",
"Date",
"Recipient's address",
"Subject line (Following up on [Previous Communication/Meeting])",
"Salutation",
"Reference to previous communication (Mention date, topic, or meeting)",
"Purpose of follow-up (Clearly state why you are writing again)",
"Action items/Next steps (Remind of agreed-upon actions or propose next steps)",
"Provide additional information (Optional)",
"Call to action (If applicable, e.g., request a response, schedule a meeting)",
"Closing paragraph (Reiterate interest, express anticipation)",
"Complimentary close (Professional)",
"Signature (Typed name and title)"
],
"guidance": "Be clear, specific, and action-oriented. Reference previous communication and clearly state the purpose of your follow-up and desired outcome."
},
# Default business letter template if subtype is not found
"default": {
"structure": [
"Letterhead",
"Date",
"Recipient's address",
"Subject line",
"Salutation",
"Introduction",
"Body paragraphs",
"Closing paragraph",
"Complimentary close",
"Signature"
],
"guidance": "Be professional, clear, and concise. Focus on the business purpose of your letter. The tone is typically formal to semi-formal."
}
},
"cover": {
"standard": {
"structure": [
"Your contact information",
"Date",
"Hiring Manager contact information (if known)",
"Subject line (Job Application - [Your Name] - [Job Title])",
"Salutation (Formal)",
"Introduction (State the position you are applying for, where you saw the advertisement, and a brief statement of enthusiasm)",
"Body paragraph 1 (Highlight skills and experience directly relevant to the job description - often 1-2 key qualifications)",
"Body paragraph 2 (Provide a specific example or anecdote demonstrating your abilities)",
"Body paragraph 3 (Connect your passion/goals to the company's mission/values - optional but effective)",
"Closing paragraph (Reiterate interest, mention enclosed resume, call to action)",
"Complimentary close (Formal)",
"Signature (Typed name)"
],
"guidance": "Be professional, specific about your most relevant qualifications, and clear about your interest in the position. Tailor every cover letter to the specific job and company."
},
"career_change": {
"structure": [
"Your contact information",
"Date",
"Hiring Manager contact information",
"Subject line (Job Application - [Your Name] - [Job Title])",
"Salutation",
"Introduction (State the position and acknowledge your career transition)",
"Body paragraph 1 (Highlight transferable skills from previous roles)",
"Body paragraph 2 (Explain your motivation for the career change and how your skills apply)",
"Body paragraph 3 (Demonstrate understanding of the new industry/role)",
"Closing paragraph (Reiterate enthusiasm, mention enclosed resume, call to action)",
"Complimentary close",
"Signature"
],
"guidance": "Focus on transferable skills and explain your career transition. Connect your past experience and new skills directly to the requirements of the target role."
},
"entry_level": {
"structure": [
"Your contact information",
"Date",
"Hiring Manager contact information",
"Subject line (Job Application - [Your Name] - [Job Title])",
"Salutation",
"Introduction (State the position and your enthusiasm for the opportunity as a recent graduate/entrant)",
"Body paragraph 1 (Highlight relevant education, coursework, GPA if strong)",
"Body paragraph 2 (Describe relevant internships, projects, or volunteer experience)",
"Body paragraph 3 (Showcase soft skills: teamwork, communication, eagerness to learn)",
"Closing paragraph (Reiterate interest, mention attached resume, express availability for interview)",
"Complimentary close",
"Signature"
],
"guidance": "Emphasize education, relevant internships/projects, and transferable skills gained through academic or extracurricular activities. Show strong potential and enthusiasm."
},
"executive": {
"structure": [
"Your contact information",
"Date",
"Recipient's contact information (Senior Executive/Board Member)",
"Subject line (Executive Application - [Your Name] - [Position])",
"Salutation (Formal)",
"Introduction (State position applying for, brief summary of executive profile)",
"Body paragraph 1 (Highlight strategic leadership experience and key achievements)",
"Body paragraph 2 (Discuss relevant industry expertise and market insights)",
"Body paragraph 3 (Describe experience in driving growth, managing teams, achieving results)",
"Closing paragraph (Reiterate interest, express desire to discuss contribution to the organization)",
"Complimentary close (Formal)",
"Signature"
],
"guidance": "Emphasize strategic leadership experience, significant achievements with measurable results, and industry expertise. Use a confident, authoritative, and forward-looking tone."
},
"creative": {
"structure": [
"Your contact information",
"Date",
"Hiring Manager contact information",
"Subject line (Application - [Your Name] - [Creative Role])",
"Salutation",
"Creative introduction (Engaging hook related to the role or your passion)",
"Body paragraph 1 (Highlight relevant creative experience and skills)",
"Body paragraph 2 (Reference specific portfolio pieces or projects that showcase your style/abilities)",
"Body paragraph 3 (Describe your creative process or approach)",
"Closing paragraph (Reiterate enthusiasm, mention attached resume/portfolio link, call to action)",
"Complimentary close",
"Signature"
],
"guidance": "Use a more engaging and expressive style appropriate for a creative role while maintaining professionalism. Highlight specific creative achievements and link to your portfolio."
},
"technical": {
"structure": [
"Your contact information",
"Date",
"Hiring Manager contact information",
"Subject line (Application - [Your Name] - [Technical Role])",
"Salutation (Formal)",
"Introduction (State position, source, and brief technical interest)",
"Body paragraph 1 (Highlight specific technical skills and proficiencies relevant to the job description)",
"Body paragraph 2 (Describe relevant technical projects or challenges you've solved)",
"Body paragraph 3 (Discuss problem-solving abilities and experience with relevant technologies)",
"Closing paragraph (Reiterate interest, mention attached resume, express availability for technical discussion/interview)",
"Complimentary close (Formal)",
"Signature"
],
"guidance": "Focus on technical skills, relevant projects, and problem-solving abilities. Use appropriate technical terminology accurately."
},
"academic": {
"structure": [
"Your contact information",
"Date",
"Recipient's contact information (Search Committee Chair)",
"Subject line (Application for [Position] - [Your Name])",
"Salutation (Formal)",
"Introduction (State the position, the department, and express your strong interest)",
"Body paragraph 1 (Discuss your research experience, focus on key projects and contributions)",
"Body paragraph 2 (Describe your teaching philosophy and relevant teaching experience)",
"Body paragraph 3 (Mention publications, presentations, grants, and other scholarly contributions)",
"Closing paragraph (Reiterate enthusiasm for joining the faculty, express availability for interview/presentation)",
"Complimentary close (Formal)",
"Signature (Typed name)"
],
"guidance": "Focus on research experience, teaching philosophy, publications, and contributions to the field. Use a scholarly and professional tone suitable for academia."
},
"remote": {
"structure": [
"Your contact information",
"Date",
"Hiring Manager contact information",
"Subject line (Remote Application - [Your Name] - [Job Title])",
"Salutation",
"Introduction (State the remote position, source, and enthusiasm for remote work)",
"Body paragraph 1 (Highlight experience working remotely or independently)",
"Body paragraph 2 (Emphasize self-management, time management, and organizational skills required for remote work)",
"Body paragraph 3 (Describe strong written and verbal communication skills, essential for remote collaboration)",
"Closing paragraph (Reiterate interest in the remote role, mention attached resume, express availability for video interview)",
"Complimentary close",
"Signature"
],
"guidance": "Emphasize self-motivation, excellent communication skills (especially written), time management, and any prior experience working independently or in remote teams."
},
"referral": {
"structure": [
"Your contact information",
"Date",
"Hiring Manager contact information",
"Subject line (Referral Application - [Your Name] - [Job Title] - Referred by [Referrer's Name])",
"Salutation",
"Referral introduction (Immediately state who referred you and for what position)",
"Body paragraph 1 (Briefly explain your connection to the referrer and how you learned about the role)",
"Body paragraph 2 (Highlight key qualifications relevant to the job description)",
"Body paragraph 3 (Express strong interest in the position and the company)",
"Closing paragraph (Reiterate enthusiasm, mention attached resume, express availability for interview)",
"Complimentary close",
"Signature"
],
"guidance": "Mention the referral prominently and early. Explain your connection to the referrer and how it aligns with your interest in the role. Still, ensure you highlight your own qualifications."
},
# Default cover letter template if subtype is not found
"default": {
"structure": [
"Contact information",
"Date",
"Recipient's information",
"Salutation",
"Introduction",
"Body paragraphs",
"Closing paragraph",
"Complimentary close",
"Signature"
],
"guidance": "Be professional, specific about your qualifications, and clear about your interest in the position. Tailor your letter to the specific job and company."
}
},
# Overall default template if letter type is not recognized
"default": {
"structure": [
"Introduction",
"Body",
"Conclusion"
],
"guidance": "Be clear, concise, and appropriate for your audience and purpose. This is a generic structure."
}
}
def get_template_by_type(letter_type: str, subtype: str = "default") -> Dict[str, Any]:
"""
Get a template for a specific letter type and subtype using a dictionary lookup.
Args:
letter_type: Type of letter (e.g., "personal", "formal", "business", "cover").
subtype: Subtype of letter (e.g., "congratulations", "application", "sales").
Defaults to "default" if no subtype is specified.
Returns:
Template dictionary with 'structure' (List[str]) and 'guidance' (str).
Returns the default template if the letter type or subtype is not found,
ensuring the return structure is always consistent.
"""
# Get templates for the specific letter type, or the overall default templates
# .get() method is used for safe dictionary access with a default fallback
type_templates = TEMPLATES.get(letter_type, TEMPLATES["default"])
# Get the template for the specific subtype, or the default for that letter type
# Chain .get() calls to handle cases where subtype or the type's default is missing
template = type_templates.get(subtype, type_templates.get("default", TEMPLATES["default"]))
# Ensure the returned template always has 'structure' (as a list) and 'guidance' (as a string) keys.
# This adds robustness in case a template definition is incomplete.
if "structure" not in template or not isinstance(template["structure"], list):
# Fallback structure if missing or incorrect type
template["structure"] = ["Introduction", "Body", "Conclusion"]
# Update guidance to reflect that the structure was defaulted
template["guidance"] = "Generic template structure applied due to missing or invalid definition."
if "guidance" not in template or not isinstance(template["guidance"], str):
# Fallback guidance if missing or incorrect type
template["guidance"] = "Generic guidance applied due to missing or invalid definition."
return template
# Example usage (for testing purposes)
if __name__ == '__main__':
# Test cases to demonstrate functionality and default handling
print("--- Testing Letter Templates Module ---")
# Test a known personal letter subtype
personal_congrats = get_template_by_type("personal", "congratulations")
print("\nPersonal Congratulations Template:")
print(f"Structure: {personal_congrats['structure']}")
print(f"Guidance: {personal_congrats['guidance']}")
# Test a known formal letter subtype
formal_complaint = get_template_by_type("formal", "complaint")
print("\nFormal Complaint Template:")
print(f"Structure: {formal_complaint['structure']}")
print(f"Guidance: {formal_complaint['guidance']}")
# Test a known business letter subtype
business_sales = get_template_by_type("business", "sales")
print("\nBusiness Sales Template:")
print(f"Structure: {business_sales['structure']}")
print(f"Guidance: {business_sales['guidance']}")
# Test a known cover letter subtype
cover_entry_level = get_template_by_type("cover", "entry_level")
print("\nCover Entry Level Template:")
print(f"Structure: {cover_entry_level['structure']}")
print(f"Guidance: {cover_entry_level['guidance']}")
# Test an unknown letter type (should fallback to overall default)
unknown_type = get_template_by_type("unknown_type", "some_subtype")
print("\nUnknown Type Template (Should be Overall Default):")
print(f"Structure: {unknown_type['structure']}")
print(f"Guidance: {unknown_type['guidance']}")
# Test a known letter type but unknown subtype (should fallback to type's default)
personal_unknown_subtype = get_template_by_type("personal", "unknown_subtype")
print("\nPersonal Unknown Subtype Template (Should be Personal Default):")
print(f"Structure: {personal_unknown_subtype['structure']}")
print(f"Guidance: {personal_unknown_subtype['guidance']}")
# Test with only letter type (should use type's default)
formal_default = get_template_by_type("formal")
print("\nFormal Default Template (No Subtype Specified):")
print(f"Structure: {formal_default['structure']}")
print(f"Guidance: {formal_default['guidance']}")

View File

@@ -0,0 +1,236 @@
"""
AI Letter Writer - Main Module
This module provides a comprehensive interface for generating various types of letters
using AI assistance. It supports multiple letter formats, styles, and use cases.
It uses Streamlit for the user interface.
"""
import streamlit as st
# Assuming these modules exist in a package structure
from .letter_types import (
business_letters,
personal_letters,
formal_letters,
cover_letters,
recommendation_letters,
complaint_letters,
thank_you_letters,
invitation_letters
)
# Assuming these utility functions exist
from .utils.letter_formatter import format_letter
from .utils.letter_analyzer import analyze_letter_tone, check_formality
from .utils.letter_templates import get_template_by_type
# Define the letter types and their properties
LETTER_TYPES_CONFIG = [
{
"id": "business",
"name": "Business Letters",
"icon": "💼",
"description": "Professional correspondence for business contexts.",
"color": "#1E88E5", # Blue 600
"module": business_letters
},
{
"id": "personal",
"name": "Personal Letters",
"icon": "💌",
"description": "Heartfelt messages for friends and family.",
"color": "#43A047", # Green 600
"module": personal_letters
},
{
"id": "formal",
"name": "Formal Letters",
"icon": "📜",
"description": "Official correspondence for institutions and authorities.",
"color": "#5E35B1", # Deep Purple 600
"module": formal_letters
},
{
"id": "cover",
"name": "Cover Letters",
"icon": "📋",
"description": "Job application letters to showcase your qualifications.",
"color": "#FB8C00", # Orange 600
"module": cover_letters
},
{
"id": "recommendation",
"name": "Recommendation Letters",
"icon": "👍",
"description": "Endorse colleagues, students, or employees.",
"color": "#00ACC1", # Cyan 600
"module": recommendation_letters
},
{
"id": "complaint",
"name": "Complaint Letters",
"icon": "⚠️",
"description": "Address issues with products, services, or situations.",
"color": "#E53935", # Red 600
"module": complaint_letters
},
{
"id": "thank_you",
"name": "Thank You Letters",
"icon": "🙏",
"description": "Express gratitude for various occasions.",
"color": "#8E24AA", # Purple 600
"module": thank_you_letters
},
{
"id": "invitation",
"name": "Invitation Letters",
"icon": "🎉",
"description": "Invite people to events, interviews, or gatherings.",
"color": "#FFB300", # Amber 600
"module": invitation_letters
}
]
# Map letter type IDs to their modules for easy access
LETTER_MODULES_MAP = {config["id"]: config["module"] for config in LETTER_TYPES_CONFIG}
def initialize_session_state() -> None:
"""Initializes necessary Streamlit session state variables."""
if "letter_type" not in st.session_state:
st.session_state.letter_type = None
if "letter_subtype" not in st.session_state:
st.session_state.letter_subtype = None # Useful if a letter type has subtypes
if "generated_letter" not in st.session_state:
st.session_state.generated_letter = None
if "letter_metadata" not in st.session_state:
# Store information like sender, recipient, date, subject, tone, etc.
st.session_state.letter_metadata = {}
if "letter_input_data" not in st.session_state:
# Store user inputs for letter generation
st.session_state.letter_input_data = {}
def display_letter_type_selection() -> None:
"""Displays the letter type selection interface using a grid of styled containers with buttons."""
st.markdown("## Select Letter Type")
# Create a grid layout for the cards (3 columns)
cols = st.columns(3)
# Display each letter type as a card with a button below it
for i, letter_type_config in enumerate(LETTER_TYPES_CONFIG):
with cols[i % 3]:
# Use markdown to create a styled container for the card appearance
st.markdown(
f"""
<div style="
background-color: {letter_type_config['color']};
padding: 20px;
border-radius: 10px;
margin-bottom: 10px; /* Space between card content and button */
color: white;
min-height: 180px; /* Ensure consistent minimum height */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
display: flex;
flex-direction: column;
justify-content: space-between; /* Distribute space within the card */
">
<h3 style="margin-top: 0; color: white;">{letter_type_config['icon']} {letter_type_config['name']}</h3>
<p style="color: white;">{letter_type_config['description']}</p>
</div>
""",
unsafe_allow_html=True
)
# Place the Streamlit button below the styled container
# Make the button expand to the width of the column for better alignment with the card
if st.button(
f"Select {letter_type_config['name']}",
key=f"btn_select_{letter_type_config['id']}", # Unique key for each button
use_container_width=True
):
st.session_state.letter_type = letter_type_config['id']
# Clear previous state data when selecting a new type
st.session_state.letter_subtype = None
st.session_state.generated_letter = None
st.session_state.letter_metadata = {}
st.session_state.letter_input_data = {}
st.rerun()
def display_letter_interface(letter_type_id: str) -> None:
"""
Displays the interface for the selected letter type by calling the
appropriate module's write function.
Args:
letter_type_id: The ID string of the selected letter type.
"""
module = LETTER_MODULES_MAP.get(letter_type_id)
if module:
try:
# Call the main function (e.g., write_letter or main) from the selected module
# Assuming the module has a function that renders its UI and handles generation
module.write_letter() # Assuming the function is named 'write_letter'
except AttributeError:
st.error(f"Module for '{letter_type_id}' does not have a 'write_letter' function.")
except Exception as e:
st.error(f"An error occurred while loading the interface for '{letter_type_id}': {e}")
else:
st.error(f"Letter type module '{letter_type_id}' not found in map.")
def write_letter() -> None:
"""Main function for the AI Letter Writer interface."""
# Page title and description
st.title("✉️ AI Letter Writer")
st.markdown("""
Create professional, personalized letters for any occasion. Select a letter type below to get started.
Our AI will help you craft the perfect letter with the right tone, structure, and content.
""")
# Initialize session state on first run
initialize_session_state()
# Back button logic - only show if a letter type is selected
if st.session_state.letter_type is not None:
if st.button("← Back to Letter Types"):
# Reset session state to return to selection
st.session_state.letter_type = None
st.session_state.letter_subtype = None
st.session_state.generated_letter = None
st.session_state.letter_metadata = {}
st.session_state.letter_input_data = {}
st.rerun() # Rerun to show the selection page
# Main navigation logic
if st.session_state.letter_type is None:
# Display letter type selection if no type is selected
display_letter_type_selection()
else:
# Display the interface for the selected letter type
display_letter_interface(st.session_state.letter_type)
# --- Placeholder for displaying generated letter and actions ---
# This part would typically be handled within the specific letter type modules
# after the letter is generated. However, if a common display is needed
# after returning from the module function, it would go here, but this
# requires the module function to somehow signal completion or store
# the generated letter in session state. The current structure expects
# the module's write_letter() to handle its entire lifecycle.
# Example of potentially displaying a generated letter after returning
# (This assumes the module updates st.session_state.generated_letter)
# if st.session_state.generated_letter:
# st.subheader("Generated Letter Preview")
# st.text_area("Your Letter", st.session_state.generated_letter, height=400)
# # Add options like copy, download, analyze, edit, etc.
if __name__ == "__main__":
# Run the main letter writing function when the script is executed
write_letter()

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,493 @@
"""
Letter Analyzer Utility
This module provides functions for analyzing letter content, including tone,
formality, readability, and offering basic suggestions for improvement.
Note: The analysis methods provided here are simplified rule-based and
keyword-based approaches. For more sophisticated analysis in a production
environment, consider using advanced Natural Language Processing (NLP)
libraries and models.
"""
import re
from typing import Dict, Any, Tuple, List
def analyze_letter_tone(content: str) -> Dict[str, float]:
"""
Analyze the tone of a letter based on the presence of specific keywords
and phrases.
Args:
content: The letter content to analyze.
Returns:
Dictionary with tone scores (formal, friendly, assertive, etc.).
Scores are based on the frequency of matching patterns and capped at 1.0.
"""
# This is a simplified version using keyword matching.
# A more sophisticated approach would involve NLP libraries for sentiment and tone analysis.
# Initialize tone scores
# Scores are arbitrary counts normalized in a simple way
tone_scores = {
"formal": 0.0,
"friendly": 0.0,
"assertive": 0.0,
"respectful": 0.0,
"urgent": 0.0,
"apologetic": 0.0
}
# Define patterns for different tones (case-insensitive)
formal_patterns = [
r"\bI am writing to\b",
r"\bI would like to\b",
r"\bplease find\b",
r"\bregarding\b",
r"\bpursuant to\b",
r"\bhereby\b",
r"\bthus\b",
r"\btherefore\b",
r"\bfurthermore\b",
r"\bconsequently\b",
r"\bnevertheless\b",
r"\bmoreover\b",
r"\benclosed\b", # Added common formal word
r"\bherewith\b" # Added common formal word
]
friendly_patterns = [
r"\bhope you're well\b",
r"\bhope this finds you well\b",
r"\bgreat to hear\b",
r"\blooking forward\b",
r"\bthanks\b",
r"\bappreciate\b",
r"!", # Exclamation points often indicate friendly or excited tone
r"\bexcited\b",
r"\bgreat\b", # Common friendly adjective
r"\bnice\b" # Common friendly adjective
]
assertive_patterns = [
r"\brequire\b",
r"\bmust\b",
r"\bneed\b",
r"\bexpect\b",
r"\bdemand\b",
r"\binsist\b",
r"\bimmediately\b",
r"\baction\b", # Often used in assertive contexts
r"\bresolution\b" # Can imply assertion
]
respectful_patterns = [
r"\brespectfully\b",
r"\bhonored\b",
r"\bplease\b",
r"\bkindly\b",
r"\bgrateful\b",
r"\bthank you\b",
r"\bappreciate\b",
r"\bhumbly\b", # Added respectful word
r"\bapologies\b" # Can show respect for impact
]
urgent_patterns = [
r"\burgent\b",
r"\bas soon as possible\b",
r"\bASAP\b",
r"\bimmediately\b",
r"\bpressing\b",
r"\bcritical\b",
r"\bdeadline\b",
r"\bexpedite\b", # Added urgent word
r"\bpromptly\b" # Added urgent word
]
apologetic_patterns = [
r"\bapologize\b",
r"\bsorry\b",
r"\bregret\b",
r"\bmistake\b",
r"\berror\b",
r"\binconvenience\b",
r"\bfault\b", # Added apologetic word
r"\boversight\b" # Added apologetic word
]
# Count pattern matches and update scores (arbitrary weighting)
# A simple count multiplied by a factor acts as a basic indicator
for pattern in formal_patterns:
tone_scores["formal"] += len(re.findall(pattern, content, re.IGNORECASE)) * 0.2
for pattern in friendly_patterns:
tone_scores["friendly"] += len(re.findall(pattern, content, re.IGNORECASE)) * 0.2
for pattern in assertive_patterns:
tone_scores["assertive"] += len(re.findall(pattern, content, re.IGNORECASE)) * 0.2
for pattern in respectful_patterns:
tone_scores["respectful"] += len(re.findall(pattern, content, re.IGNORECASE)) * 0.2
for pattern in urgent_patterns:
tone_scores["urgent"] += len(re.findall(pattern, content, re.IGNORECASE)) * 0.2
for pattern in apologetic_patterns:
tone_scores["apologetic"] += len(re.findall(pattern, content, re.IGNORECASE)) * 0.2
# Cap scores at 1.0 (arbitrary capping)
# A more meaningful score might be relative frequency or use a proper model
for tone in tone_scores:
tone_scores[tone] = min(tone_scores[tone], 1.0)
return tone_scores
def check_formality(content: str) -> float:
"""
Check the formality level of a letter based on the presence of formal
vs. informal indicators and contractions.
Args:
content: The letter content to analyze.
Returns:
Formality score between 0.0 (very informal) and 1.0 (very formal).
Calculated as formal_count / (formal_count + informal_count).
"""
# This is a simplified version based on keyword counting.
# More accurate formality analysis would require advanced NLP techniques.
# Define formal and informal indicators (case-insensitive)
formal_indicators = [
r"\bDear\b",
r"\bSincerely\b",
r"\bRegards\b",
r"\bRespectfully\b",
r"\bI am writing to\b",
r"\bI would like to\b",
r"\bplease find\b",
r"\bregarding\b",
r"\bpursuant to\b",
r"\bhereby\b",
r"\bthus\b",
r"\btherefore\b",
r"\bfurthermore\b",
r"\bconsequently\b",
r"\bnevertheless\b",
r"\bmoreover\b",
r"\benclosed\b",
r"\bherewith\b",
r"\bsincerely yours\b", # Added
r"\bto whom it may concern\b" # Added
]
informal_indicators = [
r"\bHey\b",
r"\bHi\b",
r"\bWhat's up\b",
r"\bCheers\b",
r"\bThanks\b", # 'Thank you' is formal, 'Thanks' is informal
r"\bTake care\b",
r"\bSee you\b",
r"\bLater\b",
r"\bBye\b",
r"\bLove\b", # As a closing
r"\bXO\b",
r"!+", # Multiple exclamation points
r"\bawesome\b",
r"\bcool\b",
r"\bgreat\b",
r"\bnice\b",
r"\bbtw\b", # By the way
r"\bimo\b", # In my opinion
r"\blol\b" # Laugh out loud
]
# Define common contractions (case-insensitive)
contractions = [
r"\bdon't\b", r"\bcan't\b", r"\bwon't\b", r"\bshouldn't\b",
r"\bcouldn't\b", r"\bwouldn't\b", r"\bhasn't\b", r"\bhaven't\b",
r"\bisn't\b", r"\baren't\b", r"\bwasn't\b", r"\bweren't\b",
r"\bi'm\b", r"\byou're\b", r"\bhe's\b", r"\bshe's\b", r"\bit's\b",
r"\bwe're\b", r"\bthey're\b", r"\bi've\b", r"\byou've\b",
r"\bwe've\b", r"\bthey've\b", r"\bi'd\b", r"\byou'd\b",
r"\bhe'd\b", r"\bshe'd\b", r"\bit'd\b", r"\bwe'd\b", r"\bthey'd\b",
r"\bi'll\b", r"\byou'll\b", r"\bhe'll\b", r"\bshe'll\b", r"\bit'll\b",
r"\bwe'll\b", r"\bthey'll\b"
]
formal_count = 0
for pattern in formal_indicators:
formal_count += len(re.findall(pattern, content, re.IGNORECASE))
informal_count = 0
for pattern in informal_indicators:
informal_count += len(re.findall(pattern, content, re.IGNORECASE))
# Count contractions as informal indicators
for pattern in contractions:
informal_count += len(re.findall(pattern, content, re.IGNORECASE))
# Calculate formality score
total_indicators = formal_count + informal_count
if total_indicators == 0:
# If no indicators found, return a neutral score
return 0.5
# Score is the proportion of formal indicators
formality_score = formal_count / total_indicators
return formality_score
def count_syllables_simple(word: str) -> int:
"""
Counts syllables in a word using a simplified heuristic.
This method is not linguistically perfect but provides a basic estimate
for readability formulas.
Args:
word: The word string.
Returns:
Estimated syllable count.
"""
word = word.lower()
if len(word) <= 3:
# Assume short words have one syllable
return 1
# Remove common silent endings like 'e', 'es', 'ed'
if word.endswith(('es', 'ed')):
word = word[:-2]
elif word.endswith('e'):
word = word[:-1]
# Count vowel groups (consecutive vowels count as one syllable)
vowels = 'aeiouy'
count = 0
prev_is_vowel = False
for char in word:
is_vowel = char in vowels
if is_vowel and not prev_is_vowel:
count += 1
prev_is_vowel = is_vowel
# Ensure at least one syllable is counted
return max(1, count)
def get_readability_metrics(content: str) -> Dict[str, Any]:
"""
Calculate readability metrics for a letter using simplified methods
like Flesch Reading Ease.
Args:
content: The letter content to analyze.
Returns:
Dictionary with readability metrics: word_count, sentence_count,
avg_words_per_sentence, flesch_reading_ease, reading_level.
"""
# Split content into words and sentences using simple regex
words = re.findall(r'\b\w+\b', content)
# Split by common sentence terminators, handling potential multiple marks
sentences = re.split(r'[.!?]+\s*', content)
# Filter out empty strings resulting from the split (e.g., trailing punctuation)
sentences = [s for s in sentences if s.strip()]
word_count = len(words)
sentence_count = len(sentences)
syllable_count = sum(count_syllables_simple(word) for word in words)
if word_count == 0 or sentence_count == 0:
return {
"word_count": word_count,
"sentence_count": sentence_count,
"avg_words_per_sentence": 0.0,
"flesch_reading_ease": 0.0,
"reading_level": "N/A"
}
# Calculate average words per sentence
avg_words_per_sentence = word_count / sentence_count
# Calculate Flesch Reading Ease Score
# Formula: 206.835 - (1.015 * AvgWordsPerSentence) - (84.6 * AvgSyllablesPerWord)
# AvgSyllablesPerWord = syllable_count / word_count
avg_syllables_per_word = syllable_count / word_count if word_count > 0 else 0
flesch = 206.835 - (1.015 * avg_words_per_sentence) - (84.6 * avg_syllables_per_word)
# Clamp score between 0 and 100
flesch = max(0.0, min(100.0, flesch))
# Determine reading level based on Flesch score ranges
if flesch >= 90:
reading_level = "Very Easy (5th grade)"
elif flesch >= 80:
reading_level = "Easy (6th grade)"
elif flesch >= 70:
reading_level = "Fairly Easy (7th grade)"
elif flesch >= 60:
reading_level = "Standard (8th-9th grade)"
elif flesch >= 50:
reading_level = "Fairly Difficult (10th-12th grade)"
elif flesch >= 30:
reading_level = "Difficult (College)"
else:
reading_level = "Very Difficult (Graduate)"
return {
"word_count": word_count,
"sentence_count": sentence_count,
"avg_words_per_sentence": round(avg_words_per_sentence, 2), # Rounded for display
"flesch_reading_ease": round(flesch, 2), # Rounded for display
"reading_level": reading_level
}
def suggest_improvements(content: str, letter_type: str) -> List[str]:
"""
Suggest improvements for a letter based on its content, basic analysis,
and target letter type.
Args:
content: The letter content to analyze.
letter_type: The type of letter (e.g., "business", "cover", "personal").
Returns:
List of improvement suggestions strings.
"""
suggestions = []
words = re.findall(r'\b\w+\b', content)
word_count = len(words)
# Basic length check based on letter type
if letter_type in ["business", "formal"]:
if word_count < 100 and word_count > 10: # Avoid suggesting for very short placeholders
suggestions.append("Consider adding more details to make your letter more comprehensive.")
elif word_count > 600: # Increased max length slightly
suggestions.append("Your letter is quite long. Consider condensing it for better readability and focus.")
elif letter_type == "cover":
if word_count < 150 and word_count > 10: # Avoid suggesting for very short placeholders
suggestions.append("Your cover letter may be too brief. Consider highlighting more of your relevant qualifications.")
elif word_count > 500: # Increased max length slightly
suggestions.append("Your cover letter is quite long. Consider focusing on your most relevant qualifications and experiences.")
elif letter_type == "recommendation":
if word_count < 150 and word_count > 10:
suggestions.append("Consider adding more specific examples or anecdotes to strengthen the recommendation.")
elif word_count > 600:
suggestions.append("Your recommendation letter is quite long. Ensure it remains focused and impactful.")
# Check for overuse of "I" (simple count-based heuristic)
# Count "I" as a standalone word
i_count = len(re.findall(r"\bI\b", content))
# Avoid suggestion for very short content or content with few sentences
sentence_count = len(re.split(r'[.!?]+\s*', content.strip()))
if sentence_count > 2 and word_count > 50 and i_count > sentence_count * 1.5: # Suggest if 'I' count is significantly higher than sentence count
suggestions.append("Your letter contains many uses of 'I'. Consider rephrasing some sentences to focus more on the recipient or the subject matter.")
# Check for expression of gratitude (using common phrases)
gratitude_patterns = [r"\bthank you\b", r"\bgrateful\b", r"\bappreciate\b"]
has_gratitude = any(re.search(pattern, content, re.IGNORECASE) for pattern in gratitude_patterns)
# Suggest adding gratitude, but avoid for letter types where it might be less common (e.g., some complaint letters)
if not has_gratitude and letter_type not in ["complaint", "urgent"]:
suggestions.append("Consider expressing gratitude or appreciation somewhere in your letter.")
# Check for clear call to action (using common phrases)
# Phrases indicating desired action or next step
action_phrases = [
"look forward to", "please", "would appreciate", "request",
"hope to", "call me", "email me", "contact me", "schedule",
"arrange", "require action", "next steps"
]
has_call_to_action = any(phrase in content.lower() for phrase in action_phrases)
# Suggest adding a call to action for relevant letter types
if not has_call_to_action and letter_type in ["business", "cover", "complaint", "invitation"]:
suggestions.append("Consider adding a clear call to action or outlining the desired next steps.")
# Check for proper closing (using common phrases)
closing_patterns = [
r"\bSincerely\b", r"\bRegards\b", r"\bThank you\b", r"\bBest regards\b",
r"\bYours sincerely\b", r"\bYours faithfully\b", r"\bRespectfully\b",
r"\bBest wishes\b", r"\bKind regards\b"
]
# Check if any standard closing phrase is present, typically near the end
# A more robust check might look specifically at the last paragraph/lines
has_proper_closing = any(re.search(pattern, content[-200:], re.IGNORECASE) for pattern in closing_patterns) # Check last 200 chars
if not has_proper_closing and word_count > 20: # Avoid suggesting for very short snippets
suggestions.append("Consider adding a proper closing phrase (e.g., Sincerely, Regards) followed by your name.")
return suggestions
# Example usage (for testing purposes, not part of the module's core functionality)
if __name__ == '__main__':
sample_formal_letter = """
Dear Mr. Smith,
I am writing to follow up regarding the project proposal submitted on October 26, 2023.
We believe the proposed solution aligns well with your stated requirements.
Please find the revised budget document attached for your review.
We look forward to your feedback at your earliest convenience.
Sincerely,
Jane Doe
"""
sample_informal_letter = """
Hey John,
Hope you're doing well! Just wanted to quickly touch base about the party next week.
Excited to catch up with everyone! Let me know if you need any help setting up.
Thanks!
Best,
Alex
"""
sample_complaint_letter = """
To Whom It May Concern,
I am writing to complain about the faulty product I received on November 1, 2023 (Order #12345).
The device stopped working after only two days of use. I require a full refund or replacement immediately.
I expect a prompt response regarding this issue.
Sincerely,
Concerned Customer
"""
print("--- Analyzing Formal Letter ---")
tone = analyze_letter_tone(sample_formal_letter)
formality = check_formality(sample_formal_letter)
readability = get_readability_metrics(sample_formal_letter)
suggestions = suggest_improvements(sample_formal_letter, "business")
print(f"Tone: {tone}")
print(f"Formality: {formality:.2f}")
print(f"Readability: {readability}")
print(f"Suggestions: {suggestions}")
print("\n--- Analyzing Informal Letter ---")
tone = analyze_letter_tone(sample_informal_letter)
formality = check_formality(sample_informal_letter)
readability = get_readability_metrics(sample_informal_letter)
suggestions = suggest_improvements(sample_informal_letter, "personal")
print(f"Tone: {tone}")
print(f"Formality: {formality:.2f}")
print(f"Readability: {readability}")
print(f"Suggestions: {suggestions}")
print("\n--- Analyzing Complaint Letter ---")
tone = analyze_letter_tone(sample_complaint_letter)
formality = check_formality(sample_complaint_letter)
readability = get_readability_metrics(sample_complaint_letter)
suggestions = suggest_improvements(sample_complaint_letter, "complaint")
print(f"Tone: {tone}")
print(f"Formality: {formality:.2f}")
print(f"Readability: {readability}")
print(f"Suggestions: {suggestions}")

View File

@@ -0,0 +1,545 @@
"""
Letter Formatter Module
This module provides utilities for formatting letters and generating HTML
previews in different styles (Personal, Formal, Business, Cover).
The formatting functions here are primarily focused on generating HTML
for preview purposes, applying standard layout conventions for each letter type
using inline CSS styles.
"""
import re
from typing import Dict, Any
def format_letter(content: str, metadata: Dict[str, Any], letter_type: str = "personal") -> str:
"""
Format a letter with basic structure (paragraphs).
Args:
content: The raw letter content (string).
metadata: Dictionary containing metadata (currently not used for formatting in this placeholder).
letter_type: Type of letter (personal, formal, business, cover).
Returns:
Formatted letter content (currently just returns the input content).
This is a placeholder and would be expanded to apply specific
formatting rules (e.g., indentation, spacing) based on letter type
and metadata in a full implementation before generating HTML.
For this module, we primarily rely on the HTML generation functions
to handle the visual formatting.
"""
# This is a basic placeholder. In a real implementation, this function
# might process the raw text content to add indentation, adjust line breaks,
# or handle specific markdown-like syntax before it's passed to the
# HTML generation functions.
# For now, we assume the input `content` uses double newlines for paragraphs.
return content
def get_letter_preview_html(content: str, metadata: Dict[str, Any], letter_type: str = "personal") -> str:
"""
Generate HTML for letter preview based on letter type and metadata.
This function acts as a dispatcher to the specific HTML generation functions.
Args:
content: The letter content string.
metadata: Dictionary containing metadata like sender/recipient info, date, etc.
letter_type: Type of letter ("personal", "formal", "business", "cover").
Defaults to "personal".
Returns:
HTML string for letter preview, styled appropriately for the type.
Includes basic styling for a printable letter appearance.
"""
# Dispatch to the appropriate HTML generation function based on letter type
# Pass the content and metadata to the specific functions
if letter_type == "personal":
return get_personal_letter_html(content, metadata)
elif letter_type == "formal":
return get_formal_letter_html(content, metadata)
elif letter_type == "business":
return get_business_letter_html(content, metadata)
elif letter_type == "cover":
return get_cover_letter_html(content, metadata)
else:
# Fallback for unrecognized types, displaying raw content in a styled box
return f"""
<div style="max-width: 800px; margin: 20px auto; padding: 20px; border: 1px solid #ccc; font-family: sans-serif; line-height: 1.6; background-color: #fff8f8; color: #333; border-radius: 8px;">
<h3 style="color: #e53935; margin-top: 0;">Preview Unavailable for Unknown Letter Type</h3>
<p>The letter type '{letter_type}' is not recognized. Displaying raw content:</p>
<pre style="white-space: pre-wrap; word-wrap: break-word; background-color: #f8f8f8; padding: 15px; border: 1px solid #ddd; border-radius: 4px; overflow-x: auto;">{content}</pre>
</div>
"""
def get_personal_letter_html(content: str, metadata: Dict[str, Any]) -> str:
"""
Generate HTML for personal letter preview with basic styling.
Uses a more informal layout and font style.
Args:
content: The letter content string.
metadata: Dictionary containing personal letter metadata (sender_name, date).
Returns:
HTML string for personal letter preview.
"""
# Extract metadata with default empty strings for robustness
sender_name = metadata.get("sender_name", "")
# recipient_name = metadata.get("recipient_name", "") # Less common in personal body, but could be used in greeting
date = metadata.get("date", "")
# Split content into paragraphs based on double newlines
# Use list comprehension to strip whitespace and filter out empty strings
paragraphs = [p.strip() for p in content.split("\n\n") if p.strip()]
# Format paragraphs as HTML <p> tags with bottom margin
formatted_paragraphs = "".join(f"<p style='margin-bottom: 1em;'>{paragraph}</p>" for paragraph in paragraphs)
# Basic HTML structure with inline styles for a personal letter feel
# Styles aim for a warm, readable appearance
html = f"""
<div style="max-width: 700px; margin: 20px auto; padding: 30px; border: 1px solid #e0e0e0; border-radius: 8px; background-color: #ffffff; font-family: 'Georgia', serif; line-height: 1.7; color: #333; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<div style="text-align: right; margin-bottom: 30px; font-size: 0.9em; color: #555;">
{date if date else "[Date]"}
</div>
<div style="margin-bottom: 30px;">
{formatted_paragraphs if formatted_paragraphs else "<p style='color: #888;'>Letter content goes here...</p>"}
</div>
<div style="margin-top: 40px;">
<p style="margin-bottom: 0.5em;">Sincerely,</p>
<p style="font-weight: bold; margin-top: 0;">{sender_name if sender_name else "[Sender Name]"}</p>
</div>
</div>
"""
return html
def get_formal_letter_html(content: str, metadata: Dict[str, Any]) -> str:
"""
Generate HTML for formal letter preview with standard formal structure and styling.
Uses a more professional layout and font style (Arial/sans-serif).
Args:
content: The letter content string.
metadata: Dictionary containing formal letter metadata.
Returns:
HTML string for formal letter preview.
"""
# Extract metadata with default empty strings
sender_name = metadata.get("sender_name", "")
sender_title = metadata.get("sender_title", "")
sender_organization = metadata.get("sender_organization", "")
# Replace newlines in address for HTML display
sender_address = metadata.get("sender_address", "").replace("\n", "<br>")
sender_phone = metadata.get("sender_phone", "")
sender_email = metadata.get("sender_email", "")
recipient_name = metadata.get("recipient_name", "")
recipient_title = metadata.get("recipient_title", "")
recipient_organization = metadata.get("recipient_organization", "")
# Replace newlines in address for HTML display
recipient_address = metadata.get("recipient_address", "").replace("\n", "<br>")
date = metadata.get("date", "")
subject = metadata.get("subject", "") # Added subject line
salutation = metadata.get("salutation", "Dear Sir/Madam,") # Added salutation
complimentary_close = metadata.get("complimentary_close", "Sincerely,") # Added close
# Determine alignment based on letter format (simplified)
# Full Block: All aligned left
# Modified Block: Sender address block, date, closing, and signature are right-aligned
letter_format = metadata.get("letter_format", "Full Block")
sender_address_align = "left"
date_align = "left"
closing_align = "left"
if letter_format == "Modified Block":
sender_address_align = "right"
date_align = "right"
closing_align = "right"
# Split content into paragraphs based on double newlines
paragraphs = [p.strip() for p in content.split("\n\n") if p.strip()]
# Format paragraphs as HTML <p> tags with bottom margin
formatted_paragraphs = "".join(f"<p style='margin-bottom: 1em;'>{paragraph}</p>" for paragraph in paragraphs)
# Basic HTML structure with inline styles for a formal letter
html = f"""
<div style="max-width: 800px; margin: 20px auto; padding: 30px; border: 1px solid #d0d0d0; border-radius: 8px; background-color: #ffffff; font-family: 'Arial', sans-serif; line-height: 1.6; color: #333; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<div style="text-align: {sender_address_align}; margin-bottom: 20px; font-size: 0.9em;">
<p style="margin: 0;">{sender_name if sender_name else "[Sender Name]"}{', ' + sender_title if sender_title else ''}</p>
<p style="margin: 0;">{sender_organization if sender_organization else "[Sender Organization]"}</p>
<p style="margin: 0;">{sender_address if sender_address else "[Sender Address]"}</p>
<p style="margin: 0;">{sender_phone}</p>
<p style="margin: 0;">{sender_email}</p>
</div>
<div style="text-align: {date_align}; margin-bottom: 20px;">
<p style="margin: 0;">{date if date else "[Date]"}</p>
</div>
<div style="margin-bottom: 20px; font-size: 0.9em;">
<p style="margin: 0;">{recipient_name if recipient_name else "[Recipient Name]"}{', ' + recipient_title if recipient_title else ''}</p>
<p style="margin: 0;">{recipient_organization if recipient_organization else "[Recipient Organization]"}</p>
<p style="margin: 0;">{recipient_address if recipient_address else "[Recipient Address]"}</p>
</div>
<div style="margin-bottom: 20px;">
<p style="margin: 0; font-weight: bold;">Subject: {subject if subject else "[Subject Line]"}</p>
</div>
<div style="margin-bottom: 20px;">
<p style="margin: 0;">{salutation}</p>
</div>
<div style="margin-bottom: 20px;">
{formatted_paragraphs if formatted_paragraphs else "<p style='color: #888;'>Letter content goes here...</p>"}
</div>
<div style="margin-top: 40px; text-align: {closing_align};">
<p style="margin-bottom: 0.5em;">{complimentary_close}</p>
<p style="font-weight: bold; margin: 0;">{sender_name}</p>
<p style="margin: 0; font-size: 0.9em;">{sender_title}</p>
<p style="margin: 0; font-size: 0.9em;">{sender_organization}</p>
</div>
</div>
"""
return html
def get_business_letter_html(content: str, metadata: Dict[str, Any]) -> str:
"""
Generate HTML for business letter preview with standard business structure and styling.
Includes optional letterhead.
Args:
content: The letter content string.
metadata: Dictionary containing business letter metadata.
Returns:
HTML string for business letter preview.
"""
# Extract metadata with default empty strings
sender_company = metadata.get("sender_company", "")
sender_name = metadata.get("sender_name", "")
sender_title = metadata.get("sender_title", "")
sender_address = metadata.get("sender_address", "").replace("\n", "<br>")
sender_phone = metadata.get("sender_phone", "")
sender_email = metadata.get("sender_email", "")
sender_website = metadata.get("sender_website", "")
recipient_company = metadata.get("recipient_company", "")
recipient_name = metadata.get("recipient_name", "")
recipient_title = metadata.get("recipient_title", "")
recipient_address = metadata.get("recipient_address", "").replace("\n", "<br>")
date = metadata.get("date", "")
subject = metadata.get("subject", "") # Added subject line
salutation = metadata.get("salutation", "Dear Sir/Madam,") # Added salutation
complimentary_close = metadata.get("complimentary_close", "Sincerely,") # Added close
# Determine alignment based on letter format (simplified)
letter_format = metadata.get("letter_format", "Full Block")
sender_info_align = "left"
date_align = "left"
closing_align = "left"
if letter_format == "Modified Block":
sender_info_align = "right"
date_align = "right"
closing_align = "right"
# Include letterhead logic
include_letterhead = metadata.get("include_letterhead", True)
# Split content into paragraphs based on double newlines
paragraphs = [p.strip() for p in content.split("\n\n") if p.strip()]
# Format paragraphs as HTML <p> tags with bottom margin
formatted_paragraphs = "".join(f"<p style='margin-bottom: 1em;'>{paragraph}</p>" for paragraph in paragraphs)
# Create letterhead HTML if included and company name is provided
letterhead_html = ""
if include_letterhead and sender_company:
letterhead_html = f"""
<div style="padding-bottom: 15px; margin-bottom: 20px; border-bottom: 1px solid #eee;">
<h2 style="margin: 0; color: #333; font-size: 1.5em;">{sender_company}</h2>
<p style="margin: 5px 0 0 0; font-size: 0.9em; color: #555;">
{sender_address.replace('<br>', ', ') if sender_address else ''}
{' | ' + sender_phone if sender_phone else ''}
{' | ' + sender_email if sender_email else ''}
{' | ' + sender_website if sender_website else ''}
</p>
</div>
"""
# Basic HTML structure with inline styles for a business letter
html = f"""
<div style="max-width: 800px; margin: 20px auto; padding: 30px; border: 1px solid #d0d0d0; border-radius: 8px; background-color: #ffffff; font-family: 'Arial', sans-serif; line-height: 1.6; color: #333; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
{letterhead_html}
<div style="text-align: {date_align}; margin-bottom: 20px;">
<p style="margin: 0;">{date if date else "[Date]"}</p>
</div>
<div style="margin-bottom: 20px; font-size: 0.9em;">
<p style="margin: 0;">{recipient_name if recipient_name else "[Recipient Name]"}{', ' + recipient_title if recipient_title else ''}</p>
<p style="margin: 0;">{recipient_company if recipient_company else "[Recipient Company]"}</p>
<p style="margin: 0;">{recipient_address if recipient_address else "[Recipient Address]"}</p>
</div>
<div style="margin-bottom: 20px;">
<p style="margin: 0; font-weight: bold;">Subject: {subject if subject else "[Subject Line]"}</p>
</div>
<div style="margin-bottom: 20px;">
<p style="margin: 0;">{salutation}</p>
</div>
<div style="margin-bottom: 20px;">
{formatted_paragraphs if formatted_paragraphs else "<p style='color: #888;'>Letter content goes here...</p>"}
</div>
<div style="margin-top: 40px; text-align: {closing_align};">
<p style="margin-bottom: 0.5em;">{complimentary_close}</p>
<p style="font-weight: bold; margin: 0;">{sender_name if sender_name else "[Sender Name]"}</p>
<p style="margin: 0; font-size: 0.9em;">{sender_title}</p>
<p style="margin: 0; font-size: 0.9em;">{sender_company}</p>
</div>
</div>
"""
return html
def get_cover_letter_html(content: str, metadata: Dict[str, Any]) -> str:
"""
Generate HTML for cover letter preview with standard cover letter structure and styling.
Includes sender contact block and optional online links.
Args:
content: The letter content string.
metadata: Dictionary containing cover letter metadata.
Returns:
HTML string for cover letter preview.
"""
# Extract metadata with default empty strings
sender_name = metadata.get("sender_name", "")
sender_email = metadata.get("sender_email", "")
sender_phone = metadata.get("sender_phone", "")
sender_location = metadata.get("sender_location", "")
sender_linkedin = metadata.get("sender_linkedin", "")
sender_portfolio = metadata.get("sender_portfolio", "")
recipient_name = metadata.get("recipient_name", "")
recipient_title = metadata.get("recipient_title", "") # Added recipient title
recipient_company = metadata.get("recipient_company", "")
recipient_department = metadata.get("recipient_department", "") # Added department
recipient_address = metadata.get("recipient_address", "").replace("\n", "<br>") # Added recipient address
date = metadata.get("date", "")
job_title = metadata.get("job_title", "") # Added job title for subject
salutation = metadata.get("salutation", "Dear Hiring Manager,") # Added salutation
complimentary_close = metadata.get("complimentary_close", "Sincerely,") # Added close
# Split content into paragraphs based on double newlines
paragraphs = [p.strip() for p in content.split("\n\n") if p.strip()]
# Format paragraphs as HTML <p> tags with bottom margin
formatted_paragraphs = "".join(f"<p style='margin-bottom: 1em;'>{paragraph}</p>" for paragraph in paragraphs)
# Construct sender contact line, only including fields that have values
sender_contact_parts = [sender_location, sender_phone, sender_email]
sender_contact_line = " | ".join(filter(None, sender_contact_parts))
# Construct sender online links line, only including fields that have values
sender_online_parts = []
if sender_linkedin:
# Add basic styling for links
sender_online_parts.append(f'<a href="{sender_linkedin}" style="color: #0077b5; text-decoration: none;">LinkedIn</a>')
if sender_portfolio:
# Add basic styling for links
sender_online_parts.append(f'<a href="{sender_portfolio}" style="color: #0077b5; text-decoration: none;">Portfolio</a>')
sender_online_line = " | ".join(filter(None, sender_online_parts))
# Basic HTML structure with inline styles for a cover letter
# Styles aim for a clean, professional look
html = f"""
<div style="max-width: 800px; margin: 20px auto; padding: 30px; border: 1px solid #d0d0d0; border-radius: 8px; background-color: #ffffff; font-family: 'Arial', sans-serif; line-height: 1.6; color: #333; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<div style="text-align: left; margin-bottom: 30px; padding-bottom: 15px; border-bottom: 1px solid #eee;">
<h2 style="margin: 0; color: #333; font-size: 1.5em;">{sender_name if sender_name else "[Your Name]"}</h2>
{'<p style="margin: 5px 0 0 0; font-size: 0.9em; color: #555;">' + sender_contact_line + '</p>' if sender_contact_line else ''}
{'<p style="margin: 2px 0 0 0; font-size: 0.9em;">' + sender_online_line + '</p>' if sender_online_line else ''}
</div>
<div style="margin-bottom: 20px;">
<p style="margin: 0;">{date if date else "[Date]"}</p>
</div>
<div style="margin-bottom: 20px; font-size: 0.9em;">
<p style="margin: 0;">{recipient_name if recipient_name else "[Recipient Name]"}{', ' + recipient_title if recipient_title else ''}</p>
<p style="margin: 0;">{recipient_department}</p>
<p style="margin: 0;">{recipient_company if recipient_company else "[Recipient Company]"}</p>
<p style="margin: 0;">{recipient_address if recipient_address else "[Recipient Address]"}</p>
</div>
<div style="margin-bottom: 20px;">
<p style="margin: 0; font-weight: bold;">Subject: Application for {job_title if job_title else '[Job Title]'} Position</p>
</div>
<div style="margin-bottom: 20px;">
<p style="margin: 0;">{salutation}</p>
</div>
<div style="margin-bottom: 20px;">
{formatted_paragraphs if formatted_paragraphs else "<p style='color: #888;'>Letter content goes here...</p>"}
</div>
<div style="margin-top: 40px;">
<p style="margin-bottom: 0.5em;">{complimentary_close}</p>
<p style="font-weight: bold; margin: 0;">{sender_name}</p>
</div>
</div>
"""
return html
# Example usage (for testing purposes)
if __name__ == '__main__':
sample_personal_content = """
Hi Sarah,
Hope you're doing well!
Just wanted to send a quick note to say how much I enjoyed catching up last week. It was great hearing about your trip to Italy.
Let's try to do it again soon!
Best,
Emily
"""
sample_personal_metadata = {
"sender_name": "Emily Davis",
"recipient_name": "Sarah Johnson",
"date": "November 5, 2023"
}
sample_formal_content = """
I am writing to formally request a copy of my academic transcript.
I require this document for a graduate school application. The deadline for submission is December 15, 2023.
Please let me know if there are any fees associated with this request or if any further information is needed from my end.
Thank you for your time and assistance.
"""
sample_formal_metadata_full_block = {
"sender_name": "John Smith",
"sender_title": "Student",
"sender_organization": "University of Example",
"sender_address": "123 University Ave\nAnytown, CA 91234",
"sender_phone": "(555) 123-4567",
"sender_email": "john.smith@example.com",
"recipient_name": "Registrar's Office",
"recipient_organization": "University of Example",
"recipient_address": "456 Admin Building\nAnytown, CA 91234",
"date": "November 5, 2023",
"subject": "Request for Academic Transcript",
"salutation": "To the Registrar's Office,",
"complimentary_close": "Sincerely,",
"letter_format": "Full Block"
}
sample_formal_metadata_modified_block = sample_formal_metadata_full_block.copy()
sample_formal_metadata_modified_block["letter_format"] = "Modified Block"
sample_business_content = """
This letter confirms the details of Purchase Order #PO-7890.
We are ordering 50 units of Model X widgets at the agreed-upon price of $100 per unit, totaling $5,000.
Please ensure delivery to our warehouse by November 20, 2023. Payment will be made within 30 days of receipt of invoice.
Thank you for your prompt processing of this order.
"""
sample_business_metadata_full_block = {
"sender_company": "Acme Corp",
"sender_name": "Alice Brown",
"sender_title": "Procurement Manager",
"sender_address": "789 Business Rd\nMetropolis, NY 10001",
"sender_phone": "(555) 987-6543",
"sender_email": "alice.brown@acmecorp.com",
"sender_website": "www.acmecorp.com",
"recipient_company": "Supplier Co.",
"recipient_name": "Sales Department",
"recipient_title": "",
"recipient_address": "101 Vendor Lane\nIndustriatown, TX 75001",
"date": "November 5, 2023",
"subject": "Purchase Order Confirmation - PO-7890",
"salutation": "To the Sales Department,",
"complimentary_close": "Sincerely,",
"letter_format": "Full Block",
"include_letterhead": True
}
sample_business_metadata_modified_block = sample_business_metadata_full_block.copy()
sample_business_metadata_modified_block["letter_format"] = "Modified Block"
sample_business_metadata_no_letterhead = sample_business_metadata_full_block.copy()
sample_business_metadata_no_letterhead["include_letterhead"] = False
sample_cover_letter_content = """
I am writing to express my enthusiastic interest in the Marketing Specialist position advertised on LinkedIn.
With three years of experience in digital marketing and a proven track record in content creation and social media management, I am confident in my ability to contribute to your team. My skills in [Specific Skill 1] and [Specific Skill 2] align perfectly with the requirements outlined in the job description.
In my previous role at [Previous Company], I successfully managed social media campaigns that resulted in a 25% increase in engagement. I am particularly drawn to [Company Name]'s innovative approach to [Industry Trend] and believe my creative problem-solving skills would be a valuable asset.
Thank you for considering my application. I have attached my resume for your review and welcome the opportunity to discuss how my background and skills can benefit [Company Name].
"""
sample_cover_letter_metadata = {
"sender_name": "Jane Doe",
"sender_email": "jane.doe@email.com",
"sender_phone": "(123) 456-7890",
"sender_location": "San Francisco, CA",
"sender_linkedin": "https://linkedin.com/in/janedoe",
"sender_portfolio": "https://janedoeportfolio.com",
"recipient_name": "Hiring Manager",
"recipient_title": "", # Example with no recipient title
"recipient_company": "Innovative Solutions Inc.",
"recipient_department": "Marketing Department",
"recipient_address": "456 Tech Way\nSilicon Valley, CA 95001",
"date": "November 5, 2023",
"job_title": "Marketing Specialist",
"salutation": "Dear Hiring Manager,",
"complimentary_close": "Sincerely,"
}
print("--- Personal Letter HTML Preview ---")
print(get_letter_preview_html(sample_personal_content, sample_personal_metadata, letter_type="personal"))
print("\n--- Formal Letter HTML Preview (Full Block) ---")
print(get_letter_preview_html(sample_formal_content, sample_formal_metadata_full_block, letter_type="formal"))
print("\n--- Formal Letter HTML Preview (Modified Block) ---")
print(get_letter_preview_html(sample_formal_content, sample_formal_metadata_modified_block, letter_type="formal"))
print("\n--- Business Letter HTML Preview (Full Block, with Letterhead) ---")
print(get_letter_preview_html(sample_business_content, sample_business_metadata_full_block, letter_type="business"))
print("\n--- Business Letter HTML Preview (Modified Block, with Letterhead) ---")
print(get_letter_preview_html(sample_business_content, sample_business_metadata_modified_block, letter_type="business"))
print("\n--- Business Letter HTML Preview (Full Block, no Letterhead) ---")
print(get_letter_preview_html(sample_business_content, sample_business_metadata_no_letterhead, letter_type="business"))
print("\n--- Cover Letter HTML Preview ---")
print(get_letter_preview_html(sample_cover_letter_content, sample_cover_letter_metadata, letter_type="cover"))
print("\n--- Unknown Type HTML Preview ---")
print(get_letter_preview_html("Some random content.", {}, letter_type="unknown"))

View File

@@ -0,0 +1,988 @@
"""
Letter Templates Module
This module provides structured templates and guidance for generating
different types and subtypes of letters.
Templates are defined as dictionaries containing a 'structure' (list of sections)
and 'guidance' (a string).
"""
from typing import Dict, Any, List
# Define letter templates using a nested dictionary structure for easier management
TEMPLATES: Dict[str, Dict[str, Dict[str, Any]]] = {
"personal": {
"congratulations": {
"structure": [
"Greeting",
"Express congratulations",
"Acknowledge the achievement",
"Share personal thoughts/memory (optional)",
"Look to the future/well wishes",
"Closing"
],
"guidance": "Be warm, sincere, and specific about the achievement. Express genuine happiness for the recipient. Keep the tone personal and friendly."
},
"thank_you": {
"structure": [
"Greeting",
"Express gratitude clearly",
"Specify what you are thankful for",
"Explain the impact or how you used it (optional)",
"Share a personal thought or memory (optional)",
"Offer reciprocation or look to the future",
"Closing"
],
"guidance": "Be specific about what you're thankful for and how it affected you. Express sincere appreciation. Personalize the message."
},
"sympathy": {
"structure": [
"Greeting",
"Express sympathy for the loss",
"Acknowledge the significance of the person/situation",
"Share a positive memory or quality (optional)",
"Offer specific support (optional)",
"Closing with comforting words"
],
"guidance": "Be gentle, compassionate, and sincere. Avoid clichés. Focus on offering genuine comfort and acknowledging the recipient's feelings."
},
"apology": {
"structure": [
"Greeting",
"Clearly state your apology",
"Acknowledge the specific mistake or action",
"Express understanding of the impact on the other person",
"Explain (briefly, without making excuses) what happened (optional)",
"Offer amends or suggest how to make things right",
"Assure it won't happen again",
"Closing"
],
"guidance": "Be sincere, take full responsibility for your actions, and focus on making things right. Avoid making excuses or blaming others."
},
"invitation": {
"structure": [
"Greeting",
"Clearly state the invitation",
"Provide full event details (What, When, Where)",
"Explain the significance or purpose (optional)",
"Mention who else might be there (optional)",
"Request RSVP (date and contact method)",
"Express anticipation",
"Closing"
],
"guidance": "Be clear and specific about the details (what, when, where, why). Make it easy for the person to respond."
},
"friendship": {
"structure": [
"Greeting",
"Express appreciation for the friendship",
"Share a recent memory or anecdote",
"Acknowledge the value of the relationship",
"Check in on them or share updates",
"Look to the future (getting together, etc.)",
"Closing"
],
"guidance": "Be warm, personal, and specific about what you value in the friendship. Share updates and show genuine interest."
},
"love": {
"structure": [
"Greeting (Terms of endearment)",
"Express depth of feelings",
"Share a cherished memory or moment",
"Describe specific qualities you love and appreciate",
"Reaffirm commitment or future hopes",
"Closing (Terms of endearment)"
],
"guidance": "Be sincere, personal, and specific about your feelings. Use sensory details and emotional language appropriate for your relationship."
},
"encouragement": {
"structure": [
"Greeting",
"Acknowledge the situation or challenge they face",
"Express belief in their abilities/strength",
"Offer specific words of encouragement or support",
"Remind them of past successes (optional)",
"Offer practical help (optional)",
"Look to the future with hope",
"Closing with support"
],
"guidance": "Be positive, supportive, and specific about the person's strengths and abilities. Offer genuine encouragement and belief in them."
},
"farewell": {
"structure": [
"Greeting",
"State the purpose (saying goodbye)",
"Express feelings about their departure (sadness, happiness for them)",
"Share a positive memory or highlight their contribution",
"Express good wishes for their future endeavors",
"Look to staying in touch (optional)",
"Closing"
],
"guidance": "Be warm, reflective, and forward-looking. Focus on positive memories and express genuine good wishes for their next steps."
},
# Default personal letter template if subtype is not found
"default": {
"structure": [
"Greeting",
"Introduction",
"Main content paragraphs",
"Closing thoughts",
"Signature"
],
"guidance": "Be personal, authentic, and appropriate for your relationship with the recipient. The tone is typically informal to semi-formal."
}
},
"formal": {
"application": {
"structure": [
"Sender's contact information",
"Date",
"Recipient's contact information (if known)",
"Subject line (Clear and concise)",
"Salutation (Formal)",
"Introduction (State position applied for and where you saw it)",
"Body paragraphs (Highlight relevant skills and experience)",
"Closing paragraph (Reiterate interest, mention enclosed resume, call to action)",
"Complimentary close (Formal)",
"Signature (Typed name)",
"Enclosures (Mention if attaching resume/portfolio)"
],
"guidance": "Be professional, concise, and specific about your qualifications and genuine interest in the position. Tailor it to the specific job description."
},
"complaint": {
"structure": [
"Sender's contact information",
"Date",
"Recipient's contact information",
"Subject line (Clearly state it's a complaint)",
"Salutation (Formal)",
"Introduction (State the purpose: complaint about X service/product)",
"Problem description (Provide specific details: date, time, location, product details, names if applicable)",
"Impact statement (Explain how the problem affected you)",
"Requested resolution (Clearly state what you want: refund, replacement, action)",
"Closing paragraph (Reference attached documents, state expectation for response)",
"Complimentary close (Formal)",
"Signature (Typed name)"
],
"guidance": "Be clear, factual, and specific about the issue and your desired resolution. Maintain a respectful but firm tone. Include all relevant details."
},
"request": {
"structure": [
"Sender's contact information",
"Date",
"Recipient's contact information",
"Subject line (Clearly state the request)",
"Salutation (Formal)",
"Introduction (State the purpose: making a request)",
"Request details (Clearly explain what you are requesting)",
"Justification (Explain why the request is necessary or beneficial)",
"Provide supporting information (optional)",
"Closing paragraph (Express gratitude for consideration, reiterate call to action)",
"Complimentary close (Formal)",
"Signature (Typed name)"
],
"guidance": "Be clear, specific, and courteous about your request. Explain why it's important or beneficial to the recipient or organization."
},
"recommendation": {
"structure": [
"Sender's contact information",
"Date",
"Recipient's contact information",
"Subject line (Letter of Recommendation for [Name])",
"Salutation (Formal)",
"Introduction (State your name, title, relationship to the recommendee, and for what purpose the letter is written)",
"Body paragraphs (Describe the recommendee's qualifications, skills, and achievements with specific examples)",
"Highlight relevant experiences and contributions",
"Closing recommendation (Summarize endorsement, strongly recommend the person)",
"Complimentary close (Formal)",
"Signature (Typed name and title)"
],
"guidance": "Be specific, positive, and credible. Use concrete examples and anecdotes to support your recommendation. Tailor it to the specific role/opportunity."
},
"resignation": {
"structure": [
"Sender's contact information",
"Date",
"Recipient's contact information (Immediate supervisor/HR)",
"Subject line (Letter of Resignation - [Your Name])",
"Salutation (Formal)",
"Statement of resignation (Clearly state you are resigning)",
"Last day of employment (Specify the date)",
"Gratitude and reflection (Optional: Express thanks for the opportunity/experience)",
"Transition plan/Offer of assistance (Optional: Suggest how to ensure a smooth handover)",
"Closing paragraph (Express good wishes for the company's future)",
"Complimentary close (Formal)",
"Signature (Typed name)"
],
"guidance": "Be professional, positive (if possible), and clear about your departure and last day. Maintain a good relationship."
},
"inquiry": {
"structure": [
"Sender's contact information",
"Date",
"Recipient's contact information",
"Subject line (Clearly state the nature of the inquiry)",
"Salutation (Formal)",
"Introduction (State your purpose for writing - making an inquiry)",
"Inquiry details (Provide necessary context or background)",
"Specific questions (List your questions clearly, perhaps numbered)",
"Closing paragraph (Express gratitude for assistance, indicate when you need a response)",
"Complimentary close (Formal)",
"Signature (Typed name)"
],
"guidance": "Be clear, specific, and courteous about your inquiry. Organize your questions logically for easy answering."
},
"authorization": {
"structure": [
"Sender's contact information (The grantor of authority)",
"Date",
"Recipient's contact information (The person/entity receiving the letter)",
"Subject line (Letter of Authorization)",
"Salutation (Formal)",
"Statement of authorization (Clearly state who is authorized)",
"Authorized person's details (Full name, ID if applicable)",
"Scope of authority (Precisely define what they are authorized to do)",
"Limitations (Specify any restrictions or conditions)",
"Duration of authorization (Start and end dates, if applicable)",
"Closing paragraph (State responsibility, express confidence)",
"Complimentary close (Formal)",
"Signature (Typed name and title)"
],
"guidance": "Be clear, specific, and precise about who is authorized, what they can do, for how long, and under what conditions. This is a legal document."
},
"appeal": {
"structure": [
"Sender's contact information",
"Date",
"Recipient's contact information (Appeals committee/relevant authority)",
"Subject line (Letter of Appeal - [Your Name] - [Subject of Appeal])",
"Salutation (Formal)",
"Introduction (State your name, the decision being appealed, and the date of the decision)",
"Grounds for appeal (Clearly state the reasons why you believe the decision is incorrect)",
"Provide supporting evidence (Reference attached documents: records, photos, etc.)",
"Explain mitigating circumstances (Optional)",
"Requested outcome (Clearly state what resolution you seek)",
"Closing paragraph (Express hope for reconsideration, gratitude for time)",
"Complimentary close (Formal)",
"Signature (Typed name)"
],
"guidance": "Be respectful, factual, and persuasive. Focus on valid grounds for appeal and provide clear, supporting evidence. Maintain a formal tone."
},
"introduction": {
"structure": [
"Sender's contact information",
"Date",
"Recipient's contact information",
"Subject line (Introduction - [Your Name])",
"Salutation (Formal)",
"Introduction (Introduce yourself and the purpose of the letter)",
"Background information (Briefly describe your relevant background or expertise)",
"Reason for reaching out (Explain why you are introducing yourself to this specific person/entity)",
"Potential areas of collaboration or shared interest (Optional)",
"Call to action (Suggest a meeting, call, or further communication)",
"Closing paragraph (Express enthusiasm for potential connection)",
"Complimentary close (Formal)",
"Signature (Typed name)"
],
"guidance": "Be professional, informative, and engaging. Clearly explain who you are, your expertise, and why you're reaching out to them specifically."
},
# Default formal letter template if subtype is not found
"default": {
"structure": [
"Sender's address",
"Date",
"Recipient's address",
"Subject line",
"Salutation",
"Introduction",
"Body paragraphs",
"Closing paragraph",
"Complimentary close",
"Signature"
],
"guidance": "Be professional, clear, and concise. Use formal language and structure. The tone is typically formal."
}
},
"business": {
"sales": {
"structure": [
"Letterhead",
"Date",
"Recipient's address",
"Subject line (Benefit-oriented)",
"Salutation",
"Attention-grabbing opening (Address a pain point or introduce a benefit)",
"Problem statement (Briefly describe the challenge the recipient faces)",
"Solution presentation (Introduce your product/service as the solution)",
"Benefits and features (Explain how your solution helps, focusing on benefits)",
"Social proof (Optional: Testimonials, case studies, data)",
"Call to action (Clearly state what you want them to do next)",
"Closing paragraph (Reiterate benefit, create urgency/incentive)",
"Complimentary close (Professional)",
"Signature (Typed name and title)",
"Enclosures (Optional: Brochure, pricing)"
],
"guidance": "Be persuasive, customer-focused, and clear about the value proposition. Focus on benefits, not just features. Make the call to action obvious."
},
"proposal": {
"structure": [
"Letterhead",
"Date",
"Recipient's address",
"Subject line (Clear and descriptive)",
"Salutation",
"Introduction (State purpose: submitting a proposal)",
"Problem statement/Needs assessment (Demonstrate understanding of client's needs)",
"Proposed solution (Describe your solution in detail)",
"Implementation plan (Outline steps and timeline)",
"Costs and investment (Clearly state pricing and payment terms)",
"Benefits and ROI (Explain the value the client will receive)",
"Call to action (Suggest next steps: meeting, discussion)",
"Closing paragraph (Express enthusiasm, availability for questions)",
"Complimentary close (Professional)",
"Signature (Typed name and title)",
"Enclosures (Proposal document, appendix)"
],
"guidance": "Be clear, specific, and persuasive about your solution. Focus on the client's needs and the value you provide. Structure it logically."
},
"order": {
"structure": [
"Letterhead (Your company)",
"Date",
"Recipient's address (Supplier)",
"Subject line (Purchase Order - [PO Number])",
"Salutation",
"Introduction (Reference quote/agreement, state purpose: placing an order)",
"Order details (Item list with quantities, descriptions, unit prices, total)",
"Delivery requirements (Shipping address, requested delivery date, shipping method)",
"Payment terms (Reference agreed terms)",
"Closing paragraph (Express expectation for timely delivery)",
"Complimentary close (Professional)",
"Signature (Typed name and title)"
],
"guidance": "Be clear, specific, and detailed about what you're ordering, quantities, delivery requirements, and payment terms. Include a purchase order number."
},
"quotation": {
"structure": [
"Letterhead (Your company)",
"Date",
"Recipient's address (Customer)",
"Subject line (Quotation for [Product/Service])",
"Salutation",
"Introduction (Reference inquiry, state purpose: providing a quotation)",
"Quotation details (List items/services, descriptions, unit prices, quantities, line totals)",
"Pricing breakdown (Mention taxes, discounts, fees separately)",
"Terms and conditions (Payment terms, delivery terms, warranty)",
"Validity period (State how long the quote is valid)",
"Next steps (How they can place an order)",
"Closing paragraph (Express hope to do business, offer further assistance)",
"Complimentary close (Professional)",
"Signature (Typed name and title)"
],
"guidance": "Be clear, specific, and transparent about pricing, terms, and what's included or excluded. Make it easy for the customer to understand and accept."
},
"acknowledgment": {
"structure": [
"Letterhead",
"Date",
"Recipient's address",
"Subject line (Acknowledgment of [Received Item/Request])",
"Salutation",
"Acknowledgment statement (Clearly state what you have received or are acknowledging)",
"Details of what's being acknowledged (Reference number, date, brief description)",
"Confirm understanding (Optional: Briefly restate the request/issue to show understanding)",
"Next steps (Outline what will happen next, e.g., processing order, investigating issue)",
"Timeline (Provide an estimated timeframe if possible)",
"Closing paragraph (Express gratitude, offer further assistance)",
"Complimentary close (Professional)",
"Signature (Typed name and title)"
],
"guidance": "Be prompt, clear, and specific about what you're acknowledging. Set clear expectations for next steps and timelines."
},
"collection": {
"structure": [
"Letterhead",
"Date",
"Recipient's address",
"Subject line (Invoice [Invoice Number] - Payment Due)",
"Salutation",
"Introduction (Reference invoice number and due date)",
"Account status (Clearly state the outstanding amount)",
"Payment request (Politely request payment)",
"Payment options (Remind them how to pay)",
"Consequences of non-payment (Optional: Briefly mention late fees or further action, depending on letter stage)",
"Call to action (Request payment by a specific date)",
"Closing paragraph (Express hope for prompt payment, offer to discuss)",
"Complimentary close (Professional)",
"Signature (Typed name and title)"
],
"guidance": "Be firm but professional. Clearly state the amount due, due date, and payment options. The tone may vary depending on how overdue the payment is."
},
"adjustment": {
"structure": [
"Letterhead",
"Date",
"Recipient's address (Customer who made a complaint)",
"Subject line (Response to your inquiry - [Reference Number])",
"Salutation",
"Acknowledgment of complaint (Reference their communication and the issue)",
"Investigation findings (Explain the outcome of your investigation)",
"Adjustment offered (Clearly state the resolution: refund, replacement, credit, etc.)",
"Apology (Optional: Express regret for the inconvenience)",
"Preventive measures (Optional: Explain steps taken to prevent recurrence)",
"Closing paragraph (Express hope for continued business, offer further assistance)",
"Complimentary close (Professional)",
"Signature (Typed name and title)"
],
"guidance": "Be responsive, empathetic, and solution-oriented. Clearly explain the adjustment and any preventive measures taken."
},
"credit": {
"structure": [
"Letterhead",
"Date",
"Recipient's address (Applicant)",
"Subject line (Credit Application Status - [Applicant Name])",
"Salutation",
"Introduction (Reference their credit application and the purpose of the letter)",
"Credit decision (Clearly state if credit is approved or denied)",
"If approved: Credit terms (Credit limit, payment terms, interest rates)",
"If denied: Reason for decision (Provide specific, compliant reasons)",
"Requirements (If approved: any further steps or documents needed)",
"Closing paragraph (If approved: Express welcome; If denied: Offer alternative options or appeals process)",
"Complimentary close (Professional)",
"Signature (Typed name and title)"
],
"guidance": "Be clear, specific, and transparent about the credit decision, terms, limits, or reasons for denial. Ensure compliance with regulations if denying credit."
},
"follow_up": {
"structure": [
"Letterhead",
"Date",
"Recipient's address",
"Subject line (Following up on [Previous Communication/Meeting])",
"Salutation",
"Reference to previous communication (Mention date, topic, or meeting)",
"Purpose of follow-up (Clearly state why you are writing again)",
"Action items/Next steps (Remind of agreed-upon actions or propose next steps)",
"Provide additional information (Optional)",
"Call to action (If applicable, e.g., request a response, schedule a meeting)",
"Closing paragraph (Reiterate interest, express anticipation)",
"Complimentary close (Professional)",
"Signature (Typed name and title)"
],
"guidance": "Be clear, specific, and action-oriented. Reference previous communication and clearly state the purpose of your follow-up and desired outcome."
},
# Default business letter template if subtype is not found
"default": {
"structure": [
"Letterhead",
"Date",
"Recipient's address",
"Subject line",
"Salutation",
"Introduction",
"Body paragraphs",
"Closing paragraph",
"Complimentary close",
"Signature"
],
"guidance": "Be professional, clear, and concise. Focus on the business purpose of your letter. The tone is typically formal to semi-formal."
}
},
"cover": {
"standard": {
"structure": [
"Your contact information",
"Date",
"Hiring Manager contact information (if known)",
"Subject line (Job Application - [Your Name] - [Job Title])",
"Salutation (Formal)",
"Introduction (State the position you are applying for, where you saw the advertisement, and a brief statement of enthusiasm)",
"Body paragraph 1 (Highlight skills and experience directly relevant to the job description - often 1-2 key qualifications)",
"Body paragraph 2 (Provide a specific example or anecdote demonstrating your abilities)",
"Body paragraph 3 (Connect your passion/goals to the company's mission/values - optional but effective)",
"Closing paragraph (Reiterate interest, mention attached resume, express availability for interview)",
"Complimentary close (Formal)",
"Signature (Typed name)"
],
"guidance": "Be professional, specific about your most relevant qualifications, and clear about your interest in the position. Tailor every cover letter to the specific job and company."
},
"career_change": {
"structure": [
"Your contact information",
"Date",
"Hiring Manager contact information",
"Subject line (Job Application - [Your Name] - [Job Title])",
"Salutation",
"Introduction (State the position and acknowledge your career transition)",
"Body paragraph 1 (Highlight transferable skills from previous roles)",
"Body paragraph 2 (Explain your motivation for the career change and how your skills apply)",
"Body paragraph 3 (Demonstrate understanding of the new industry/role)",
"Closing paragraph (Reiterate enthusiasm, mention enclosed resume, call to action)",
"Complimentary close",
"Signature"
],
"guidance": "Focus on transferable skills and explain your career transition. Connect your past experience and new skills directly to the requirements of the target role."
},
"entry_level": {
"structure": [
"Your contact information",
"Date",
"Hiring Manager contact information",
"Subject line (Job Application - [Your Name] - [Job Title])",
"Salutation",
"Introduction (State the position and your enthusiasm for the opportunity as a recent graduate/entrant)",
"Body paragraph 1 (Highlight relevant education, coursework, GPA if strong)",
"Body paragraph 2 (Describe relevant internships, projects, or volunteer experience)",
"Body paragraph 3 (Showcase soft skills: teamwork, communication, eagerness to learn)",
"Closing paragraph (Reiterate interest, mention attached resume, express availability for interview)",
"Complimentary close",
"Signature"
],
"guidance": "Emphasize education, relevant internships/projects, and transferable skills gained through academic or extracurricular activities. Show strong potential and enthusiasm."
},
"executive": {
"structure": [
"Your contact information",
"Date",
"Recipient's contact information (Senior Executive/Board Member)",
"Subject line (Executive Application - [Your Name] - [Position])",
"Salutation (Formal)",
"Introduction (State position applying for, brief summary of executive profile)",
"Body paragraph 1 (Highlight strategic leadership experience and key achievements)",
"Body paragraph 2 (Discuss relevant industry expertise and market insights)",
"Body paragraph 3 (Describe experience in driving growth, managing teams, achieving results)",
"Closing paragraph (Reiterate interest, express desire to discuss contribution to the organization)",
"Complimentary close (Formal)",
"Signature"
],
"guidance": "Emphasize strategic leadership experience, significant achievements with measurable results, and industry expertise. Use a confident, authoritative, and forward-looking tone."
},
"creative": {
"structure": [
"Your contact information",
"Date",
"Hiring Manager contact information",
"Subject line (Application - [Your Name] - [Creative Role])",
"Salutation",
"Creative introduction (Engaging hook related to the role or your passion)",
"Body paragraph 1 (Highlight relevant creative experience and skills)",
"Body paragraph 2 (Reference specific portfolio pieces or projects that showcase your style/abilities)",
"Body paragraph 3 (Describe your creative process or approach)",
"Closing paragraph (Reiterate enthusiasm, mention attached resume/portfolio link, call to action)",
"Complimentary close",
"Signature"
],
"guidance": "Use a more engaging and expressive style appropriate for a creative role while maintaining professionalism. Highlight specific creative achievements and link to your portfolio."
},
"technical": {
"structure": [
"Your contact information",
"Date",
"Hiring Manager contact information",
"Subject line (Application - [Your Name] - [Technical Role])",
"Salutation (Formal)",
"Introduction (State position, source, and brief technical interest)",
"Body paragraph 1 (Highlight specific technical skills and proficiencies relevant to the job description)",
"Body paragraph 2 (Describe relevant technical projects or challenges you've solved)",
"Body paragraph 3 (Discuss problem-solving abilities and experience with relevant technologies)",
"Closing paragraph (Reiterate interest, mention attached resume, express availability for technical discussion/interview)",
"Complimentary close (Formal)",
"Signature"
],
"guidance": "Focus on technical skills, relevant projects, and problem-solving abilities. Use appropriate technical terminology accurately."
},
"academic": {
"structure": [
"Your contact information",
"Date",
"Recipient's contact information (Search Committee Chair)",
"Subject line (Application for [Position] - [Your Name])",
"Salutation (Formal)",
"Introduction (State the position, the department, and express your strong interest)",
"Body paragraph 1 (Discuss your research experience, focus on key projects and contributions)",
"Body paragraph 2 (Describe your teaching philosophy and relevant teaching experience)",
"Body paragraph 3 (Mention publications, presentations, grants, and other scholarly contributions)",
"Closing paragraph (Reiterate enthusiasm for joining the faculty, express availability for interview/presentation)",
"Complimentary close (Formal)",
"Signature (Typed name)"
],
"guidance": "Focus on research experience, teaching philosophy, publications, and contributions to the field. Use a scholarly and professional tone suitable for academia."
},
"remote": {
"structure": [
"Your contact information",
"Date",
"Hiring Manager contact information",
"Subject line (Remote Application - [Your Name] - [Job Title])",
"Salutation",
"Introduction (State the remote position, source, and enthusiasm for remote work)",
"Body paragraph 1 (Highlight experience working remotely or independently)",
"Body paragraph 2 (Emphasize self-management, time management, and organizational skills required for remote work)",
"Body paragraph 3 (Describe strong written and verbal communication skills, essential for remote collaboration)",
"Closing paragraph (Reiterate interest in the remote role, mention attached resume, express availability for video interview)",
"Complimentary close",
"Signature"
],
"guidance": "Emphasize self-motivation, excellent communication skills (especially written), time management, and any prior experience working independently or in remote teams."
},
"referral": {
"structure": [
"Your contact information",
"Date",
"Hiring Manager contact information",
"Subject line (Referral Application - [Your Name] - [Job Title] - Referred by [Referrer's Name])",
"Salutation",
"Referral introduction (Immediately state who referred you and for what position)",
"Body paragraph 1 (Briefly explain your connection to the referrer and how you learned about the role)",
"Body paragraph 2 (Highlight key qualifications relevant to the job description)",
"Body paragraph 3 (Express strong interest in the position and the company)",
"Closing paragraph (Reiterate enthusiasm, mention attached resume, express availability for interview)",
"Complimentary close",
"Signature"
],
"guidance": "Mention the referral prominently and early. Explain your connection to the referrer and how it aligns with your interest in the role. Still, ensure you highlight your own qualifications."
},
# Default cover letter template if subtype is not found
"default": {
"structure": [
"Contact information",
"Date",
"Recipient's information",
"Subject line",
"Salutation",
"Introduction",
"Body paragraphs",
"Closing paragraph",
"Complimentary close",
"Signature"
],
"guidance": "Be professional, specific about your qualifications, and clear about your interest in the position. Tailor your letter to the specific job and company."
}
},
"recommendation": {
# Recommendation letters are often considered a subtype of Formal,
# but can be a top-level type in some systems. Keeping the structure
# consistent with the original request, but noting this potential overlap.
"standard": {
"structure": [
"Sender's contact information",
"Date",
"Recipient's contact information (e.g., Admissions Committee, Hiring Manager)",
"Subject line (Letter of Recommendation for [Name])",
"Salutation (Formal)",
"Introduction (State your name, title, relationship to the recommendee, how long you've known them, and for what opportunity the letter is written)",
"Body paragraph 1 (Describe their relevant skills and qualities, providing specific examples)",
"Body paragraph 2 (Discuss their achievements or contributions, with context and impact)",
"Body paragraph 3 (Optional: Mention character traits, teamwork, or specific anecdotes)",
"Overall Endorsement (Summarize your strong recommendation and why they are a good fit)",
"Closing paragraph (Offer to provide further information)",
"Complimentary close (Formal)",
"Signature (Typed name and title)"
],
"guidance": "Be specific, positive, and credible. Use concrete examples and anecdotes to support your recommendation. Clearly state your relationship with the person and for what opportunity you are recommending them."
},
# Default recommendation letter template if subtype is not found
"default": {
"structure": [
"Sender's contact information",
"Date",
"Recipient's contact information",
"Subject line (Letter of Recommendation for [Name])",
"Salutation",
"Introduction",
"Body paragraphs describing qualifications and experiences",
"Specific examples and anecdotes",
"Overall endorsement and recommendation",
"Closing and offer for further information",
"Complimentary close",
"Signature"
],
"guidance": "Provide a strong, positive, and specific endorsement based on your professional or academic relationship with the individual."
}
},
"complaint": {
# Complaint letters are often considered a subtype of Formal or Business,
# but can be a top-level type. Keeping the structure consistent.
"product": {
"structure": [
"Your contact information",
"Date",
"Company contact information",
"Subject line (Complaint Regarding [Product Name/Model])",
"Salutation (Formal)",
"Introduction (State purpose: complaining about a product, include product name, model, date/place of purchase)",
"Problem description (Explain the specific defect or issue with the product in detail)",
"History of the problem (Mention if you've tried fixing it, contacted support, etc.)",
"Desired resolution (Clearly state if you want a refund, replacement, repair)",
"Call to action (State what you expect the company to do and by when)",
"Closing paragraph (Reference attached documents like receipt, express expectation for resolution)",
"Complimentary close (Formal)",
"Signature"
],
"guidance": "Be clear, factual, and specific about the product issue and your desired resolution. Include all relevant details like model number, date of purchase, and copies of receipts. Maintain a firm but professional tone."
},
"service": {
"structure": [
"Your contact information",
"Date",
"Company/Service Provider contact information",
"Subject line (Complaint Regarding [Service Type/Issue])",
"Salutation (Formal)",
"Introduction (State purpose: complaining about a service received, include date/time/location of service)",
"Problem description (Explain the specific issue with the service provided in detail)",
"Impact of the issue (Explain how this problem affected you)",
"Desired resolution (Clearly state what you want: refund, re-performance of service, compensation)",
"Call to action (State what you expect the company to do and by when)",
"Closing paragraph (Reference any relevant documents, express expectation for resolution)",
"Complimentary close (Formal)",
"Signature"
],
"guidance": "Be clear, factual, and specific about the service issue and your desired resolution. Include details like dates, times, and names of service providers if possible. Maintain a firm but professional tone."
},
"billing": {
"structure": [
"Your contact information",
"Date",
"Company contact information",
"Subject line (Complaint Regarding Billing Error - Account #[Your Account Number])",
"Salutation (Formal)",
"Introduction (State purpose: complaining about a billing error, include account number and invoice number)",
"Problem description (Explain the specific error on the bill: incorrect charge, double billing, etc.)",
"Provide supporting evidence (Reference payments made, attach relevant statements)",
"Desired resolution (Clearly state what you want: correction of bill, refund, credit)",
"Call to action (State what you expect the company to do and by when)",
"Closing paragraph (Reference attached documents, express expectation for resolution)",
"Complimentary close (Formal)",
"Signature"
],
"guidance": "Be clear, factual, and specific about the billing error. Provide supporting documentation like invoices or payment records. Clearly state the desired correction."
},
# Default complaint letter template if subtype is not found
"default": {
"structure": [
"Your contact information",
"Date",
"Recipient's contact information",
"Subject line (Complaint Regarding [Issue Summary])",
"Salutation",
"Introduction (State the purpose of the letter - to complain)",
"Detailed description of the problem",
"Explanation of the impact",
"Desired resolution",
"Call to action",
"Closing",
"Signature"
],
"guidance": "Be clear, factual, and specific about the issue and your desired resolution. Maintain a respectful but firm tone and provide relevant details."
}
},
"thank_you": {
# Thank You letters are often considered a subtype of Personal or Business,
# but can be a top-level type. Keeping the structure consistent.
"personal": {
"structure": [
"Greeting",
"Express gratitude clearly and sincerely",
"Specify what you are thankful for (gift, favor, support)",
"Explain the impact it had on you or how you used it",
"Share a personal thought or memory related to it (optional)",
"Look to the future or express continued appreciation",
"Closing"
],
"guidance": "Be warm, sincere, and specific about what you are thankful for. Personalize the message and explain the impact of their action or gift."
},
"professional": {
"structure": [
"Sender's contact information",
"Date",
"Recipient's contact information",
"Subject line (Thank You - [Your Name])",
"Salutation (Formal/Semi-formal)",
"Express gratitude clearly (e.g., Thank you for the interview, thank you for your help)",
"Specify what you are thankful for (Meeting date/topic, specific assistance)",
"Reiterate interest or connection (e.g., Reiterate interest in the job, mention something discussed)",
"Express appreciation for their time or effort",
"Closing paragraph (Optional: look to future interaction)",
"Complimentary close (Formal/Semi-formal)",
"Signature"
],
"guidance": "Be prompt, professional, and specific. Reiterate your interest or key points discussed. Send within 24 hours for interviews."
},
"after_interview": {
"structure": [
"Your contact information",
"Date",
"Interviewer's contact information",
"Subject line (Thank You - [Your Name] - [Job Title])",
"Salutation (Formal)",
"Express sincere thanks for the interview opportunity",
"Mention the specific position and date of the interview",
"Reiterate your strong interest in the role and the company",
"Reference a specific point discussed during the interview to show engagement",
"Briefly highlight how your skills/experience align with a need discussed",
"Express enthusiasm for next steps",
"Complimentary close (Formal)",
"Signature"
],
"guidance": "Send within 24 hours of the interview. Be specific, professional, and reiterate your key strengths and interest. Proofread carefully."
},
# Default thank you letter template if subtype is not found
"default": {
"structure": [
"Greeting",
"Express thanks",
"Specify reason for thanks",
"Closing"
],
"guidance": "Be sincere and specific about what you are thankful for."
}
},
"invitation": {
# Invitation letters are often considered a subtype of Personal or Formal,
# but can be a top-level type. Keeping the structure consistent.
"event": { # e.g., party, gathering, wedding
"structure": [
"Greeting",
"State the purpose: extending an invitation",
"Event details (Type of event, Host)",
"Date and Time",
"Location (Full address)",
"Purpose/Theme (Optional)",
"Special instructions (Dress code, what to bring, etc. - optional)",
"RSVP information (Date, Contact method)",
"Express anticipation",
"Closing"
],
"guidance": "Be clear about all the event details (What, When, Where). Make it easy for guests to RSVP. Tone can be formal or informal depending on the event."
},
"interview": {
"structure": [
"Company Letterhead",
"Date",
"Candidate's contact information",
"Subject line (Interview Invitation - [Job Title] - [Your Name])",
"Salutation (Formal)",
"State the purpose: inviting them for an interview",
"Specify the position applied for",
"Propose date(s) and time(s) for the interview",
"Provide location details (Address, or link for virtual)",
"Mention who they will meet with (Names and titles)",
"Explain the interview format/duration (Optional)",
"Instructions (What to bring, who to contact with questions)",
"Call to action (Request confirmation or scheduling)",
"Closing paragraph (Express anticipation)",
"Complimentary close (Formal)",
"Signature (Interviewer/HR Contact Name and Title)"
],
"guidance": "Be professional, clear, and provide all necessary details for the candidate. Make the scheduling process straightforward."
},
"meeting": {
"structure": [
"Sender's contact information",
"Date",
"Recipient's contact information",
"Subject line (Invitation to Meeting - [Meeting Topic])",
"Salutation",
"State the purpose: inviting them to a meeting",
"Meeting details (Date, Time, Location/Virtual link)",
"Purpose/Agenda (Clearly state what the meeting is about)",
"Expected duration (Optional)",
"Preparation required (Optional: Documents to review)",
"RSVP information (Optional)",
"Closing paragraph",
"Complimentary close",
"Signature"
],
"guidance": "Be clear about the purpose, date, time, and location. Provide an agenda so attendees can prepare. The tone can be formal or informal depending on the context."
},
# Default invitation letter template if subtype is not found
"default": {
"structure": [
"Greeting",
"Invitation statement",
"Event/Meeting details (What, When, Where)",
"Purpose (Optional)",
"RSVP information",
"Closing"
],
"guidance": "Be clear and specific about the details of the event or meeting."
}
},
# Overall default template if letter type is not recognized
"default": {
"structure": [
"Introduction",
"Body paragraphs",
"Conclusion"
],
"guidance": "Be clear, concise, and appropriate for your audience and purpose. This is a generic structure."
}
}
def get_template_by_type(letter_type: str, subtype: str = "default") -> Dict[str, Any]:
"""
Get a template for a specific letter type and subtype using a dictionary lookup.
Args:
letter_type: Type of letter (e.g., "personal", "formal", "business", "cover").
subtype: Subtype of letter (e.g., "congratulations", "application", "sales").
Defaults to "default" if no subtype is specified.
Returns:
Template dictionary with 'structure' (List[str]) and 'guidance' (str).
Returns the default template if the letter type or subtype is not found.
"""
# Get templates for the specific letter type, or the overall default templates
type_templates = TEMPLATES.get(letter_type, TEMPLATES["default"])
# Get the template for the specific subtype, or the default for that letter type
template = type_templates.get(subtype, type_templates.get("default", TEMPLATES["default"])) # Fallback to overall default
# Ensure the returned template always has 'structure' and 'guidance' keys
# This handles cases where an incomplete template might have been defined (error tolerance)
if "structure" not in template or not isinstance(template["structure"], list):
template["structure"] = ["Introduction", "Body", "Conclusion"]
template["guidance"] = "Generic template: structure or guidance missing."
if "guidance" not in template or not isinstance(template["guidance"], str):
template["guidance"] = "Generic guidance: structure or guidance missing."
return template
# Example usage (for testing purposes)
if __name__ == '__main__':
# Test cases
print("--- Testing Letter Templates ---")
personal_congrats = get_template_by_type("personal", "congratulations")
print("\nPersonal Congratulations Template:")
print(f"Structure: {personal_congrats['structure']}")
print(f"Guidance: {personal_congrats['guidance']}")
formal_complaint = get_template_by_type("formal", "complaint")
print("\nFormal Complaint Template:")
print(f"Structure: {formal_complaint['structure']}")
print(f"Guidance: {formal_complaint['guidance']}")
business_sales = get_template_by_type("business", "sales")
print("\nBusiness Sales Template:")
print(f"Structure: {business_sales['structure']}")
print(f"Guidance: {business_sales['guidance']}")
cover_entry_level = get_template_by_type("cover", "entry_level")
print("\nCover Entry Level Template:")
print(f"Structure: {cover_entry_level['structure']}")
print(f"Guidance: {cover_entry_level['guidance']}")
unknown_type = get_template_by_type("unknown_type", "some_subtype")
print("\nUnknown Type Template (Should be Default):")
print(f"Structure: {unknown_type['structure']}")
print(f"Guidance: {unknown_type['guidance']}")
personal_unknown_subtype = get_template_by_type("personal", "unknown_subtype")
print("\nPersonal Unknown Subtype Template (Should be Personal Default):")
print(f"Structure: {personal_unknown_subtype['structure']}")
print(f"Guidance: {personal_unknown_subtype['guidance']}")

View File

@@ -0,0 +1,557 @@
# Blog Outline Generator
A powerful AI-powered tool for generating comprehensive blog outlines with advanced editing capabilities, content generation, and image integration.
## 🛠 Technical Architecture
### Core Components
- **Backend**: Python-based implementation using Streamlit for UI
- **AI Integration**:
- Text Generation: Integration with multiple LLM providers (Gemini, OpenAI, Anthropic)
- Image Generation: Support for multiple image generation APIs (Gemini-AI, Dalle3, Stability-AI)
- **Data Structures**:
```python
class OutlineConfig:
content_type: ContentType
content_depth: ContentDepth
outline_style: OutlineStyle
target_word_count: int
num_main_sections: int
num_subsections_per_section: int
include_images: bool
image_style: str
image_engine: str
```
### Key Technologies
- **Streamlit**: Web application framework
- **Asyncio**: Asynchronous operations for AI calls
- **Loguru**: Advanced logging system
- **BeautifulSoup**: Web content parsing
- **Pydantic**: Data validation
- **Markdown**: Content formatting
## 🌟 Features with Examples
### 1. Content Generation
- **AI-Powered Content Creation**:
```python
# Example prompt for content generation
prompt = f"""
Generate content for a {content_type} article about {topic}.
Target audience: {target_audience}
Word count: {target_word_count}
Style: {outline_style}
"""
content = await llm_text_gen(prompt)
```
- **Multiple Content Types**:
```python
# Example configuration for different content types
config = OutlineConfig(
content_type=ContentType.TUTORIAL,
content_depth=ContentDepth.INTERMEDIATE,
target_word_count=2000
)
```
### 2. Outline Structure
- **Flexible Section Management**:
```python
# Example section generation
async def generate_sections(self, topic: str) -> List[str]:
sections = []
for i in range(self.config.num_main_sections):
section = await self._generate_section(topic, i)
sections.append(section)
return sections
```
- **Optional Components**:
```python
# Example FAQ generation
async def generate_faqs(self, topic: str) -> List[str]:
prompt = f"""
Generate 5 common questions about {topic}
Content type: {self.config.content_type}
Target audience: {self.config.target_audience}
"""
return await llm_text_gen(prompt)
```
### 3. Advanced Editing Capabilities
- **Section Content Editor**:
```python
# Example content editing interface
def edit_section_content(self, section: str, content: str) -> str:
edited_content = st.text_area(
"Edit Content",
value=content,
height=300,
key=f"content_edit_{section}"
)
return edited_content
```
- **Subsection Management**:
```python
# Example subsection reordering
def reorder_subsections(self, section: str, subsections: List[str]) -> List[str]:
for i, subsection in enumerate(subsections):
if st.button("↑", key=f"move_up_{section}_{i}"):
subsections[i], subsections[i-1] = subsections[i-1], subsections[i]
return subsections
```
### 4. Image Generation
- **AI Image Generation**:
```python
# Example image generation
async def generate_image(self, prompt: str, style: str) -> str:
image_prompt = f"""
Create a {style} image for: {prompt}
Style: {self.config.image_style}
"""
return await generate_image(image_prompt)
```
### 5. Content Optimization
- **SEO Features**:
```python
# Example SEO optimization
def optimize_content(self, content: str, keywords: List[str]) -> str:
for keyword in keywords:
content = self._naturally_insert_keyword(content, keyword)
return content
```
## 📊 Technical Implementation Details
### 1. Content Generation Pipeline
```python
async def generate_content(self, topic: str) -> Dict:
# 1. Generate outline structure
outline = await self.generate_outline(topic)
# 2. Generate content for each section
for section in outline:
content = await self.generate_section_content(section)
outline[section]['content'] = content
# 3. Generate images if enabled
if self.config.include_images:
for section in outline:
image = await self.generate_section_image(section)
outline[section]['image'] = image
return outline
```
### 2. AI Integration
```python
class AIIntegration:
def __init__(self, provider: str):
self.provider = provider
self.model = self._initialize_model()
async def generate_text(self, prompt: str) -> str:
if self.provider == "gemini":
return await gemini_text_response(prompt)
elif self.provider == "openai":
return await openai_chatgpt(prompt)
```
### 3. Image Processing
```python
class ImageProcessor:
def __init__(self, engine: str):
self.engine = engine
async def generate_image(self, prompt: str) -> str:
if self.engine == "Gemini-AI":
return await generate_gemini_image(prompt)
elif self.engine == "Dalle3":
return await generate_dalle3_images(prompt)
```
## 🔧 Configuration Examples
### 1. Basic Configuration
```python
config = OutlineConfig(
content_type=ContentType.GUIDE,
content_depth=ContentDepth.INTERMEDIATE,
target_word_count=2000,
num_main_sections=5,
num_subsections_per_section=3
)
```
### 2. Advanced Configuration
```python
config = OutlineConfig(
content_type=ContentType.TUTORIAL,
content_depth=ContentDepth.ADVANCED,
outline_style=OutlineStyle.MODERN,
target_word_count=3000,
include_images=True,
image_style="realistic",
image_engine="Gemini-AI",
target_audience="developers",
language="English",
keywords=["python", "tutorial", "advanced"]
)
```
## 📝 Usage Examples
### 1. Basic Usage
```python
# Initialize generator
generator = BlogOutlineGenerator()
# Generate outline
outline = await generator.generate_outline("Python Programming Basics")
# Export to markdown
markdown = generator.to_markdown()
```
### 2. Advanced Usage
```python
# Custom configuration
config = OutlineConfig(
content_type=ContentType.TUTORIAL,
content_depth=ContentDepth.ADVANCED,
include_images=True
)
# Initialize with config
generator = BlogOutlineGenerator(config)
# Generate with custom settings
outline = await generator.generate_outline(
"Advanced Python Decorators",
keywords=["python", "decorators", "advanced"]
)
# Export to multiple formats
markdown = generator.to_markdown()
json_output = generator.to_json()
html_output = generator.to_html()
```
## 🔍 Technical Considerations
### 1. Performance Optimization
- Asynchronous operations for AI calls
- Caching of generated content
- Batch processing for images
- Memory management for large documents
### 2. Error Handling
```python
try:
content = await llm_text_gen(prompt)
except Exception as e:
logger.error(f"Content generation failed: {e}")
return None
```
### 3. Data Validation
```python
from pydantic import BaseModel, validator
class SectionContent(BaseModel):
title: str
content: str
image_path: Optional[str]
@validator('content')
def validate_content_length(cls, v):
if len(v.split()) < 100:
raise ValueError("Content too short")
return v
```
## 🌟 Features
### 1. Content Generation
- **AI-Powered Content Creation**: Generate high-quality content for each section using advanced language models
- **Multiple Content Types**: Support for various content formats including:
- How-to guides
- Tutorials
- Listicles
- Comparisons
- Case studies
- Opinion pieces
- News articles
- Reviews
- General guides
- **Customizable Content Depth**:
- Basic: Simple, easy-to-understand content
- Intermediate: Balanced depth with practical examples
- Advanced: Detailed technical content
- Expert: In-depth analysis and advanced concepts
### 2. Outline Structure
- **Flexible Section Management**:
- Customizable number of main sections
- Configurable subsections per section
- Dynamic section reordering
- Easy addition/removal of sections
- **Optional Components**:
- Introduction section
- Conclusion section
- FAQ section
- Additional resources section
### 3. Advanced Editing Capabilities
- **Section Content Editor**:
- Rich text editing interface
- Real-time word count tracking
- Formatting options (Bold, Italic, Lists, Code Blocks, Links)
- AI-powered content enhancement
- **Subsection Management**:
- Drag-and-drop reordering
- Individual subsection editing
- Add/remove subsection functionality
- Bulk editing capabilities
- **Metadata Editing**:
- Section-specific settings
- Content depth adjustment
- Target word count configuration
- Image settings customization
### 4. Image Generation
- **AI Image Generation**:
- Multiple image styles (realistic, illustration, minimalist, photographic, artistic)
- Support for multiple image engines (Gemini-AI, Dalle3, Stability-AI)
- Custom image prompts
- Image regeneration capability
- **Image Integration**:
- Automatic image placement
- Image preview and editing
- Image prompt viewing and editing
- Image style customization
### 5. Content Optimization
- **SEO Features**:
- Keyword integration
- Content structure optimization
- Meta description generation
- SEO-friendly formatting
- **Audience Targeting**:
- Customizable target audience
- Language selection
- Content tone adjustment
- Reading level optimization
### 6. Export Options
- **Multiple Formats**:
- Markdown export
- JSON export
- HTML export
- Custom formatting options
- **Download Capabilities**:
- One-click download
- Format-specific styling
- Custom file naming
- Batch export options
### 7. User Interface
- **Intuitive Design**:
- Clean, modern interface
- Responsive layout
- Easy navigation
- Clear visual hierarchy
- **Interactive Features**:
- Real-time preview
- Drag-and-drop functionality
- Quick edit options
- Contextual help
### 8. Statistics and Analytics
- **Content Metrics**:
- Word count tracking
- Section statistics
- Subsection counts
- Content depth analysis
- **Progress Tracking**:
- Generation progress
- Edit history
- Version comparison
- Performance metrics
## 🚀 Getting Started
### Installation
```bash
pip install -r requirements.txt
```
### Usage
1. Launch the application:
```bash
streamlit run lib/ai_writers/ai_outline_writer/outline_ui.py
```
2. Configure your outline:
- Enter your blog topic
- Select content type and depth
- Choose outline style
- Set target word count
- Configure sections and subsections
3. Generate and edit:
- Click "Generate Outline"
- Review and edit sections
- Customize content and images
- Export in your preferred format
## 🔧 Configuration Options
### Basic Settings
- **Blog Topic**: Main subject of your content
- **Content Type**: Type of content to generate
- **Content Depth**: Level of detail and complexity
- **Outline Style**: Structure and formatting style
### Advanced Settings
- **Target Word Count**: Desired length of the content
- **Number of Sections**: Customize main sections
- **Subsections**: Configure subsections per section
- **Image Settings**: Customize image generation
- **Target Audience**: Define your audience
- **Language**: Select content language
- **Keywords**: Add SEO keywords
- **Excluded Topics**: Specify topics to avoid
## 📊 Output Formats
### 1. Preview Mode
- Interactive preview of the entire outline
- Real-time editing capabilities
- Image preview and management
- Content statistics
### 2. Markdown Export
- Clean markdown formatting
- Proper heading hierarchy
- Image embedding
- Code block formatting
### 3. JSON Export
- Structured data format
- Complete outline information
- Content and image metadata
- Configuration details
### 4. HTML Export
- Styled HTML output
- Responsive design
- Image integration
- Custom CSS support
## 💡 Best Practices
### Content Generation
1. Start with a clear topic and target audience
2. Choose appropriate content type and depth
3. Use relevant keywords for SEO
4. Review and edit generated content
5. Add personal insights and examples
### Outline Structure
1. Maintain logical flow between sections
2. Balance section lengths
3. Include relevant subsections
4. Add appropriate transitions
5. Ensure comprehensive coverage
### Image Usage
1. Choose appropriate image styles
2. Generate relevant images
3. Optimize image placement
4. Review image prompts
5. Consider image licensing
## 🔄 Workflow
1. **Initial Setup**
- Configure basic settings
- Set content parameters
- Define target audience
2. **Generation**
- Generate initial outline
- Review structure
- Generate content
- Create images
3. **Editing**
- Review and edit content
- Adjust structure
- Customize images
- Optimize for SEO
4. **Export**
- Choose export format
- Review final output
- Download content
- Save configuration
## 📝 Tips and Tricks
### Content Generation
- Use specific keywords for better results
- Provide clear context for the AI
- Review and refine generated content
- Add personal expertise
### Structure Optimization
- Maintain consistent section lengths
- Use clear subsection hierarchies
- Include relevant examples
- Add practical applications
### Image Enhancement
- Use descriptive image prompts
- Experiment with different styles
- Consider image placement
- Review image relevance
## 🤝 Contributing
We welcome contributions! Please follow these steps:
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Submit a pull request
## 📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
## 📞 Support
For support, please:
1. Check the documentation
2. Review existing issues
3. Create a new issue if needed
4. Contact the maintainers
## 🔮 Future Enhancements
Planned features:
- Multi-language support
- Advanced AI models
- More export formats
- Enhanced editing tools
- Collaboration features
- Version control integration
- Analytics dashboard
- Custom templates
- API integration
- Mobile optimization

View File

@@ -0,0 +1,336 @@
"""
Enhanced Blog Outline Generator
This module provides a sophisticated outline generation system that creates detailed,
well-structured outlines for blog posts based on user preferences and content requirements.
"""
import sys
from typing import Dict, List, Optional
from enum import Enum
from dataclasses import dataclass
from loguru import logger
from lib.gpt_providers.text_generation.main_text_generation import llm_text_gen
from lib.gpt_providers.text_to_image_generation.main_generate_image_from_prompt import generate_image
logger.remove()
logger.add(sys.stdout,
colorize=True,
format="<level>{level}</level>|<green>{file}:{line}:{function}</green>| {message}")
class ContentType(Enum):
"""Types of content that can be generated."""
HOW_TO = "how-to"
TUTORIAL = "tutorial"
LISTICLE = "listicle"
COMPARISON = "comparison"
CASE_STUDY = "case-study"
OPINION = "opinion"
NEWS = "news"
REVIEW = "review"
GUIDE = "guide"
class ContentDepth(Enum):
"""Depth levels for content coverage."""
BASIC = "basic"
INTERMEDIATE = "intermediate"
ADVANCED = "advanced"
EXPERT = "expert"
class OutlineStyle(Enum):
"""Styles for outline structure."""
TRADITIONAL = "traditional"
MODERN = "modern"
CONVERSATIONAL = "conversational"
ACADEMIC = "academic"
SEO_OPTIMIZED = "seo-optimized"
@dataclass
class OutlineConfig:
"""Configuration for outline generation."""
content_type: ContentType = ContentType.GUIDE
content_depth: ContentDepth = ContentDepth.INTERMEDIATE
outline_style: OutlineStyle = OutlineStyle.MODERN
target_word_count: int = 2000
num_main_sections: int = 5
num_subsections_per_section: int = 3
include_introduction: bool = True
include_conclusion: bool = True
include_faqs: bool = True
include_resources: bool = True
target_audience: str = "general"
language: str = "English"
keywords: List[str] = None
exclude_topics: List[str] = None
include_images: bool = True
image_style: str = "realistic"
image_engine: str = "Gemini-AI"
@dataclass
class SectionContent:
"""Content for a section including text and image."""
title: str
content: str
image_prompt: Optional[str] = None
image_path: Optional[str] = None
class BlogOutlineGenerator:
"""Enhanced blog outline generator with comprehensive controls."""
def __init__(self, config: Optional[OutlineConfig] = None):
"""Initialize the outline generator with optional configuration."""
self.config = config or OutlineConfig()
self.outline = {}
self.section_contents = {}
async def generate_outline(self, topic: str) -> Dict:
"""Generate a comprehensive outline based on the topic and configuration."""
try:
# Step 1: Generate main sections
main_sections = await self._generate_main_sections(topic)
# Step 2: Generate subsections for each main section
detailed_sections = await self._generate_subsections(main_sections)
# Step 3: Add introduction and conclusion if requested
if self.config.include_introduction:
detailed_sections["Introduction"] = await self._generate_introduction(topic)
if self.config.include_conclusion:
detailed_sections["Conclusion"] = await self._generate_conclusion(topic)
# Step 4: Add FAQs if requested
if self.config.include_faqs:
detailed_sections["FAQs"] = await self._generate_faqs(topic)
# Step 5: Add resources if requested
if self.config.include_resources:
detailed_sections["Additional Resources"] = await self._generate_resources(topic)
self.outline = detailed_sections
# Step 6: Generate content for each section
await self._generate_section_contents(topic)
return self.outline
except Exception as err:
logger.error(f"Failed to generate outline: {err}")
raise
async def _generate_main_sections(self, topic: str) -> List[str]:
"""Generate main sections for the outline."""
prompt = f"""Generate {self.config.num_main_sections} main sections for a {self.config.content_type.value}
article about {topic} with the following characteristics:
Content Type: {self.config.content_type.value}
Content Depth: {self.config.content_depth.value}
Target Word Count: {self.config.target_word_count}
Target Audience: {self.config.target_audience}
Style: {self.config.outline_style.value}
Additional Requirements:
- Each section should contribute to the overall word count goal
- Sections should flow logically
- Include key concepts and important points
- Consider SEO optimization
- Keywords to include: {', '.join(self.config.keywords or [])}
- Topics to exclude: {', '.join(self.config.exclude_topics or [])}
Please provide only the section titles, one per line."""
response = await llm_text_gen(prompt)
return [section.strip() for section in response.split('\n') if section.strip()]
async def _generate_subsections(self, main_sections: List[str]) -> Dict[str, List[str]]:
"""Generate subsections for each main section."""
detailed_sections = {}
for section in main_sections:
prompt = f"""Generate {self.config.num_subsections_per_section} subsections for the following section:
{section}
Content Type: {self.config.content_type.value}
Content Depth: {self.config.content_depth.value}
Style: {self.config.outline_style.value}
Each subsection should:
- Be specific and focused
- Support the main section's topic
- Include key points to cover
- Consider SEO optimization
Please provide only the subsection titles, one per line."""
response = await llm_text_gen(prompt)
detailed_sections[section] = [sub.strip() for sub in response.split('\n') if sub.strip()]
return detailed_sections
async def _generate_introduction(self, topic: str) -> List[str]:
"""Generate introduction subsections."""
prompt = f"""Generate introduction subsections for an article about {topic}.
Content Type: {self.config.content_type.value}
Content Depth: {self.config.content_depth.value}
Style: {self.config.outline_style.value}
The introduction should:
- Hook the reader
- Present the main topic
- Outline what's to come
- Set the tone for the article
Please provide only the subsection titles, one per line."""
response = await llm_text_gen(prompt)
return [sub.strip() for sub in response.split('\n') if sub.strip()]
async def _generate_conclusion(self, topic: str) -> List[str]:
"""Generate conclusion subsections."""
prompt = f"""Generate conclusion subsections for an article about {topic}.
Content Type: {self.config.content_type.value}
Content Depth: {self.config.content_depth.value}
Style: {self.config.outline_style.value}
The conclusion should:
- Summarize key points
- Provide final thoughts
- Include a call to action
- Leave a lasting impression
Please provide only the subsection titles, one per line."""
response = await llm_text_gen(prompt)
return [sub.strip() for sub in response.split('\n') if sub.strip()]
async def _generate_faqs(self, topic: str) -> List[str]:
"""Generate FAQ subsections."""
prompt = f"""Generate FAQ subsections for an article about {topic}.
Content Type: {self.config.content_type.value}
Content Depth: {self.config.content_depth.value}
Style: {self.config.outline_style.value}
The FAQs should:
- Address common questions
- Cover important aspects
- Be relevant to the target audience
- Include both basic and advanced questions
Please provide only the FAQ questions, one per line."""
response = await llm_text_gen(prompt)
return [sub.strip() for sub in response.split('\n') if sub.strip()]
async def _generate_resources(self, topic: str) -> List[str]:
"""Generate resource subsections."""
prompt = f"""Generate resource subsections for an article about {topic}.
Content Type: {self.config.content_type.value}
Content Depth: {self.config.content_depth.value}
Style: {self.config.outline_style.value}
The resources should:
- Include relevant links
- Suggest further reading
- Provide tools or references
- Include related materials
Please provide only the resource categories, one per line."""
response = await llm_text_gen(prompt)
return [sub.strip() for sub in response.split('\n') if sub.strip()]
async def _generate_section_contents(self, topic: str):
"""Generate content and images for each section."""
for section, subsections in self.outline.items():
if section not in ["Introduction", "Conclusion", "FAQs", "Additional Resources"]:
# Generate content for the main section
content_prompt = f"""Write a detailed section for a blog post about {topic}.
Section Title: {section}
Content Type: {self.config.content_type.value}
Content Depth: {self.config.content_depth.value}
Style: {self.config.outline_style.value}
Target Word Count: {self.config.target_word_count // self.config.num_main_sections}
Include:
- Clear explanation of the main points
- Examples and illustrations
- Key takeaways
- Relevant data or statistics
"""
content = await llm_text_gen(content_prompt)
# Generate image prompt if images are enabled
image_prompt = None
image_path = None
if self.config.include_images:
image_prompt = f"""Create a detailed image prompt for a blog section about {topic}.
Section: {section}
Content: {content[:200]}...
Style: {self.config.image_style}
"""
image_prompt = await llm_text_gen(image_prompt)
try:
image_path = generate_image(
image_prompt,
title=section,
description=content[:100],
tags=self.config.keywords
)
except Exception as err:
logger.warning(f"Failed to generate image for section {section}: {err}")
self.section_contents[section] = SectionContent(
title=section,
content=content,
image_prompt=image_prompt,
image_path=image_path
)
def to_markdown(self) -> str:
"""Convert outline to markdown format with content and images."""
markdown = f"# {self.outline.get('Introduction', [''])[0]}\n\n"
for section, subsections in self.outline.items():
if section not in ["Introduction", "Conclusion", "FAQs", "Additional Resources"]:
markdown += f"## {section}\n\n"
# Add section content if available
if section in self.section_contents:
content = self.section_contents[section]
markdown += f"{content.content}\n\n"
# Add image if available
if content.image_path:
markdown += f"![{section}]({content.image_path})\n\n"
# Add subsections
for subsection in subsections:
markdown += f"- {subsection}\n"
markdown += "\n"
if "Conclusion" in self.outline:
markdown += "## Conclusion\n\n"
for subsection in self.outline["Conclusion"]:
markdown += f"- {subsection}\n"
markdown += "\n"
if "FAQs" in self.outline:
markdown += "## Frequently Asked Questions\n\n"
for faq in self.outline["FAQs"]:
markdown += f"- {faq}\n"
markdown += "\n"
if "Additional Resources" in self.outline:
markdown += "## Additional Resources\n\n"
for resource in self.outline["Additional Resources"]:
markdown += f"- {resource}\n"
return markdown

View File

@@ -0,0 +1,489 @@
"""
Streamlit UI for Enhanced Blog Outline Generator
This module provides a user-friendly interface for generating comprehensive blog outlines
with AI-powered content and image generation capabilities.
"""
import streamlit as st
import asyncio
from pathlib import Path
from typing import Optional, Dict, List
import json
import time
from datetime import datetime
from .get_blog_outline import (
BlogOutlineGenerator,
OutlineConfig,
ContentType,
ContentDepth,
OutlineStyle
)
# Custom CSS for better styling
st.markdown("""
<style>
.main {
background-color: #f5f5f5;
}
.stButton>button {
background-color: #4CAF50;
color: white;
padding: 10px 24px;
border-radius: 4px;
border: none;
font-weight: bold;
}
.stButton>button:hover {
background-color: #45a049;
}
.section-card {
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.content-preview {
background-color: #f8f9fa;
padding: 15px;
border-radius: 4px;
margin: 10px 0;
}
.image-container {
display: flex;
justify-content: center;
margin: 20px 0;
}
.stats-card {
background-color: #e8f5e9;
padding: 15px;
border-radius: 8px;
margin: 10px 0;
}
.edit-section {
background-color: #e3f2fd;
padding: 15px;
border-radius: 4px;
margin: 10px 0;
}
.subsection-list {
margin-left: 20px;
}
</style>
""", unsafe_allow_html=True)
def edit_section_content(section: str, content: str) -> str:
"""Edit section content with advanced options."""
st.markdown('<div class="edit-section">', unsafe_allow_html=True)
# Content editing
edited_content = st.text_area(
"Edit Content",
value=content,
height=300,
key=f"content_edit_{section}"
)
# Word count and formatting
col1, col2 = st.columns(2)
with col1:
word_count = len(edited_content.split())
st.info(f"Word Count: {word_count}")
with col2:
formatting = st.multiselect(
"Formatting Options",
["Bold", "Italic", "Lists", "Code Blocks", "Links"],
key=f"format_{section}"
)
# AI enhancement options
with st.expander("AI Enhancement Options"):
enhance_options = st.multiselect(
"Select Enhancements",
["Improve Clarity", "Add Examples", "Expand Details", "Add Statistics", "Improve SEO"],
key=f"enhance_{section}"
)
if st.button("Apply Enhancements", key=f"apply_enhance_{section}"):
with st.spinner("Applying enhancements..."):
# TODO: Implement AI enhancement logic
st.success("Enhancements applied!")
st.markdown('</div>', unsafe_allow_html=True)
return edited_content
def edit_subsections(section: str, subsections: List[str]) -> List[str]:
"""Edit subsections with reordering and editing capabilities."""
st.markdown('<div class="edit-section">', unsafe_allow_html=True)
# Reorder subsections
st.markdown("### Reorder Subsections")
for i, subsection in enumerate(subsections):
col1, col2 = st.columns([4, 1])
with col1:
subsections[i] = st.text_input(
f"Subsection {i+1}",
value=subsection,
key=f"subsection_{section}_{i}"
)
with col2:
if st.button("", key=f"move_up_{section}_{i}") and i > 0:
subsections[i], subsections[i-1] = subsections[i-1], subsections[i]
st.experimental_rerun()
if st.button("", key=f"move_down_{section}_{i}") and i < len(subsections)-1:
subsections[i], subsections[i+1] = subsections[i+1], subsections[i]
st.experimental_rerun()
# Add/remove subsections
col1, col2 = st.columns(2)
with col1:
if st.button("Add Subsection", key=f"add_sub_{section}"):
subsections.append("New Subsection")
st.experimental_rerun()
with col2:
if st.button("Remove Last Subsection", key=f"remove_sub_{section}"):
if subsections:
subsections.pop()
st.experimental_rerun()
st.markdown('</div>', unsafe_allow_html=True)
return subsections
def edit_section_metadata(section: str, generator: BlogOutlineGenerator):
"""Edit section metadata and settings."""
st.markdown('<div class="edit-section">', unsafe_allow_html=True)
# Section settings
st.markdown("### Section Settings")
# Image settings
if generator.config.include_images:
col1, col2 = st.columns(2)
with col1:
new_image_style = st.selectbox(
"Image Style",
["realistic", "illustration", "minimalist", "photographic", "artistic"],
key=f"img_style_{section}"
)
with col2:
new_image_engine = st.selectbox(
"Image Engine",
["Gemini-AI", "Dalle3", "Stability-AI"],
key=f"img_engine_{section}"
)
if st.button("Regenerate Image", key=f"regen_img_{section}"):
with st.spinner("Regenerating image..."):
# TODO: Implement image regeneration logic
st.success("Image regenerated!")
# Content settings
st.markdown("### Content Settings")
col1, col2 = st.columns(2)
with col1:
target_word_count = st.number_input(
"Target Word Count",
min_value=100,
max_value=2000,
value=500,
step=100,
key=f"word_count_{section}"
)
with col2:
content_depth = st.selectbox(
"Content Depth",
[depth.value for depth in ContentDepth],
key=f"depth_{section}"
)
st.markdown('</div>', unsafe_allow_html=True)
def display_section(section: str, subsections: List[str], content: Optional[Dict] = None, generator: Optional[BlogOutlineGenerator] = None):
"""Display a section with its content and subsections."""
st.markdown(f"""
<div class="section-card">
<h2>{section}</h2>
""", unsafe_allow_html=True)
# Section editing controls
col1, col2 = st.columns([4, 1])
with col1:
st.markdown(f"### {section}")
with col2:
edit_mode = st.checkbox("Edit Mode", key=f"edit_mode_{section}")
if content:
# Display content with word count
word_count = len(content.content.split())
st.markdown(f"""
<div class="content-preview">
<p><strong>Content Preview</strong> ({word_count} words)</p>
{content.content[:500]}...
</div>
""", unsafe_allow_html=True)
# Display image if available
if content.image_path:
st.markdown('<div class="image-container">', unsafe_allow_html=True)
st.image(content.image_path, caption=section, use_column_width=True)
st.markdown('</div>', unsafe_allow_html=True)
# Display image prompt in expander
if content.image_prompt:
with st.expander("View Image Prompt"):
st.code(content.image_prompt, language="text")
# Edit mode controls
if edit_mode:
# Edit content
edited_content = edit_section_content(section, content.content)
content.content = edited_content
# Edit subsections
edited_subsections = edit_subsections(section, subsections)
subsections[:] = edited_subsections
# Edit metadata
if generator:
edit_section_metadata(section, generator)
# Display subsections
st.markdown("### Subsections")
st.markdown('<div class="subsection-list">', unsafe_allow_html=True)
for subsection in subsections:
st.markdown(f"- {subsection}")
st.markdown('</div>', unsafe_allow_html=True)
st.markdown("</div>", unsafe_allow_html=True)
def display_stats(generator, outline):
"""Display statistics about the generated outline."""
total_sections = len(outline)
total_subsections = sum(len(subsections) for subsections in outline.values())
total_content = sum(len(content.content.split()) for content in generator.section_contents.values())
col1, col2, col3 = st.columns(3)
with col1:
st.markdown(f"""
<div class="stats-card">
<h3>📊 Statistics</h3>
<p>Total Sections: {total_sections}</p>
<p>Total Subsections: {total_subsections}</p>
<p>Estimated Word Count: {total_content}</p>
</div>
""", unsafe_allow_html=True)
with col2:
st.markdown(f"""
<div class="stats-card">
<h3>🎯 Target</h3>
<p>Target Word Count: {generator.config.target_word_count}</p>
<p>Content Depth: {generator.config.content_depth.value}</p>
<p>Style: {generator.config.outline_style.value}</p>
</div>
""", unsafe_allow_html=True)
with col3:
st.markdown(f"""
<div class="stats-card">
<h3>📝 Content Type</h3>
<p>Type: {generator.config.content_type.value}</p>
<p>Audience: {generator.config.target_audience}</p>
<p>Language: {generator.config.language}</p>
</div>
""", unsafe_allow_html=True)
def main():
st.set_page_config(
page_title="Blog Outline Generator",
page_icon="📝",
layout="wide",
initial_sidebar_state="expanded"
)
# Header with description
st.title("Blog Outline Generator")
st.markdown("""
Generate comprehensive blog outlines with AI-powered content and images.
Customize your outline with various options and get detailed content for each section.
""")
# Sidebar for configuration
with st.sidebar:
st.header("Configuration")
# Basic settings
topic = st.text_input("Blog Topic", placeholder="Enter your blog topic")
content_type = st.selectbox(
"Content Type",
[type.value for type in ContentType]
)
content_depth = st.selectbox(
"Content Depth",
[depth.value for depth in ContentDepth]
)
outline_style = st.selectbox(
"Outline Style",
[style.value for style in OutlineStyle]
)
# Content structure
st.subheader("Content Structure")
target_word_count = st.slider("Target Word Count", 500, 5000, 2000, 100)
num_main_sections = st.slider("Number of Main Sections", 3, 10, 5)
num_subsections = st.slider("Subsections per Section", 2, 5, 3)
# Advanced settings
with st.expander("Advanced Settings"):
include_intro = st.checkbox("Include Introduction", value=True)
include_conclusion = st.checkbox("Include Conclusion", value=True)
include_faqs = st.checkbox("Include FAQs", value=True)
include_resources = st.checkbox("Include Resources", value=True)
# Image settings
st.subheader("Image Settings")
include_images = st.checkbox("Include Images", value=True)
if include_images:
image_style = st.selectbox(
"Image Style",
["realistic", "illustration", "minimalist", "photographic", "artistic"]
)
image_engine = st.selectbox(
"Image Engine",
["Gemini-AI", "Dalle3", "Stability-AI"]
)
# Target audience and language
st.subheader("Target Audience")
target_audience = st.text_input("Target Audience", value="general")
language = st.text_input("Language", value="English")
# Keywords and exclusions
st.subheader("Content Optimization")
keywords = st.text_area("Keywords (comma-separated)")
exclude_topics = st.text_area("Topics to Exclude (comma-separated)")
# Main content area
if topic:
# Create configuration
config = OutlineConfig(
content_type=ContentType(content_type),
content_depth=ContentDepth(content_depth),
outline_style=OutlineStyle(outline_style),
target_word_count=target_word_count,
num_main_sections=num_main_sections,
num_subsections_per_section=num_subsections,
include_introduction=include_intro,
include_conclusion=include_conclusion,
include_faqs=include_faqs,
include_resources=include_resources,
include_images=include_images,
image_style=image_style if include_images else "realistic",
image_engine=image_engine if include_images else "Gemini-AI",
target_audience=target_audience,
language=language,
keywords=[k.strip() for k in keywords.split(',')] if keywords else None,
exclude_topics=[t.strip() for t in exclude_topics.split(',')] if exclude_topics else None
)
# Initialize generator
generator = BlogOutlineGenerator(config)
# Generate outline
if st.button("Generate Outline"):
with st.spinner("Generating outline and content..."):
try:
# Add progress bar
progress_bar = st.progress(0)
for i in range(100):
time.sleep(0.01)
progress_bar.progress(i + 1)
outline = asyncio.run(generator.generate_outline(topic))
# Display results
st.success("Outline generated successfully!")
# Display statistics
display_stats(generator, outline)
# Output format selection
output_format = st.radio(
"Output Format",
["Preview", "Markdown", "JSON", "HTML"]
)
if output_format == "Preview":
# Display outline with content and images
for section, subsections in outline.items():
content = generator.section_contents.get(section)
display_section(section, subsections, content)
elif output_format == "Markdown":
st.code(generator.to_markdown(), language="markdown")
st.download_button(
"Download Markdown",
generator.to_markdown(),
file_name="blog_outline.md",
mime="text/markdown"
)
elif output_format == "JSON":
json_output = json.dumps({
"outline": outline,
"contents": {
section: {
"title": content.title,
"content": content.content,
"image_prompt": content.image_prompt,
"image_path": content.image_path
}
for section, content in generator.section_contents.items()
}
}, indent=2)
st.code(json_output, language="json")
st.download_button(
"Download JSON",
json_output,
file_name="blog_outline.json",
mime="application/json"
)
elif output_format == "HTML":
# Add HTML export functionality
html_output = f"""
<!DOCTYPE html>
<html>
<head>
<title>{topic} - Blog Outline</title>
<style>
body {{ font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }}
.section {{ margin-bottom: 30px; }}
.content {{ background: #f8f9fa; padding: 15px; border-radius: 4px; }}
img {{ max-width: 100%; height: auto; }}
</style>
</head>
<body>
<h1>{topic}</h1>
{generator.to_markdown().replace('#', '##')}
</body>
</html>
"""
st.code(html_output, language="html")
st.download_button(
"Download HTML",
html_output,
file_name="blog_outline.html",
mime="text/html"
)
except Exception as e:
st.error(f"Error generating outline: {str(e)}")
else:
st.info("Please enter a blog topic to get started.")
if __name__ == "__main__":
main()

View File

@@ -8,6 +8,7 @@ from lib.ai_writers.linkedin_writer import LinkedInAIWriter
from lib.ai_writers.blog_rewriter_updater.ai_blog_rewriter import write_blog_rewriter
from lib.ai_writers.ai_blog_faqs_writer.faqs_ui import main as faqs_generator
from lib.ai_writers.ai_blog_writer.ai_blog_generator import ai_blog_writer_page
from lib.ai_writers.ai_outline_writer.outline_ui import main as outline_generator
from loguru import logger
def list_ai_writers():
@@ -92,6 +93,14 @@ def list_ai_writers():
"category": "Content Creation",
"function": faqs_generator,
"path": "faqs_generator"
},
{
"name": "Blog Outline Generator",
"icon": "📋",
"description": "Create detailed blog outlines with AI-powered content generation and image integration",
"category": "Content Creation",
"function": outline_generator,
"path": "outline_generator"
}
]