diff --git a/.gitignore b/.gitignore
index aa2ff0d..1984e10 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,6 +38,11 @@ data/past_campaigns/*
data/user_queries/*
!data/past_campaigns/.gitkeep
!data/user_queries/.gitkeep
+backend/data/vector_store/*
+!backend/data/vector_store/.gitkeep
+
+# Logs
+logs/*
# OS specific
.DS_Store
diff --git a/backend/brand_style.py b/backend/brand_style.py
index 6232d35..3104434 100644
--- a/backend/brand_style.py
+++ b/backend/brand_style.py
@@ -18,6 +18,104 @@ class BrandStyleManager:
"""Initialize the BrandStyleManager with default or stored style guidelines."""
self.style_path = Path(config.DATA_DIR) / "style_guidelines" / "brand_style.json"
self.style_guidelines = self._load_or_create_style()
+ self.content_formats = {
+ "website_copy": """
+ Generate engaging website copy for a brand or business.
+ - Start with a strong headline and supporting subheadline
+ - Write in a clear, benefit-driven tone
+ - Use SEO-friendly keywords naturally
+ - Structure content with short paragraphs and bullet points
+ - Include a clear call-to-action at the end
+ """,
+ "email": """
+ Create a marketing or sales email for a target audience.
+ - Start with a compelling subject line
+ - Use a warm, conversational tone
+ - Keep the message focused and value-driven
+ - Personalize where possible (name, context)
+ - End with a clear and persuasive CTA
+ """,
+ "social_media": """
+ Write social media content tailored to a specific platform.
+ - Hook the reader within the first sentence
+ - Keep the message concise and engaging
+ - Use platform-appropriate tone and emojis (if applicable)
+ - Add relevant hashtags and tag accounts when needed
+ - Include a prompt or CTA to drive interaction
+ """,
+ "blog_post": """
+ Generate a blog article on a given topic or keyword.
+ - Begin with a strong hook or introduction
+ - Organize content with subheadings and logical flow
+ - Use examples, data, and storytelling
+ - Optimize for SEO with keywords and meta description
+ - Conclude with a summary or actionable insight
+ """,
+ "sales_copy": """
+ Write persuasive sales copy for a product or service.
+ - Lead with a strong value proposition
+ - Address specific pain points and offer solutions
+ - Highlight features, benefits, and outcomes
+ - Include social proof (testimonials, stats, etc.)
+ - End with a direct and compelling CTA
+ """,
+ "ad_copy": """
+ Create short, punchy ad copy for digital or print campaigns.
+ - Capture attention in the first line
+ - Use emotional or benefit-driven language
+ - Keep it brief and persuasive
+ - Align copy with the target audience
+ - Include a CTA or promotional message
+ """,
+ "video_script": """
+ Generate a short video script for a marketing video.
+ - Hook the viewer in the first few seconds
+ - Introduce the problem and present the solution
+ - Keep the tone conversational and natural
+ - Include visual cues and on-screen text ideas
+ - Wrap up with a strong CTA
+ """,
+ "case_study": """
+ Write a case study that highlights a customer success story.
+ - Start with a quick summary of the results
+ - Describe the client and their initial problem
+ - Explain how the product/service helped
+ - Include measurable outcomes or metrics
+ - End with a quote and a CTA to learn more
+ """,
+ "product_description": """
+ Generate a product description that drives interest and conversions.
+ - Begin with the most attractive benefit
+ - Mention key features and what makes the product unique
+ - Use sensory and persuasive language
+ - Include important specs or FAQs
+ - End with a micro-CTA (e.g., "Shop now", "View details")
+ """,
+ "landing_page": """
+ Write copy for a focused landing page.
+ - Use a bold, attention-grabbing headline
+ - Describe the offer clearly and simply
+ - Include supporting details that reinforce value
+ - Remove distractions and focus on a single goal
+ - Add a CTA above the fold and at the end
+ """,
+ "press_release": """
+ Create a professional press release for an announcement.
+ - Begin with a headline that summarizes the news
+ - Use a journalistic tone and structure
+ - Provide key facts in the first paragraph
+ - Add quotes from relevant leaders or stakeholders
+ - End with boilerplate company info and contact details
+ """,
+ "newsletter": """
+ Write a newsletter update for subscribers.
+ - Start with a warm greeting or short intro
+ - Highlight the most important news or offer first
+ - Use engaging sub-sections or article teasers
+ - Maintain consistent tone with the brand
+ - Include CTAs to drive clicks or traffic
+ """
+ }
logger.info("BrandStyleManager initialized successfully")
def _load_or_create_style(self) -> Dict[str, Any]:
@@ -84,55 +182,29 @@ class BrandStyleManager:
raise
def format_prompt_with_brand_style(self, user_prompt: str, content_type: Optional[str] = None) -> str:
- """
- Format user prompt with brand style guidelines for the LLM.
+ """Format user prompt to match the established writing style."""
- Args:
- user_prompt: Original user prompt
- content_type: Type of content being generated
-
- Returns:
- Formatted prompt with brand style instructions
- """
- style = self.style_guidelines
-
- # Create a formatted prompt with brand style instructions
- prompt_parts = [
- f"Generate marketing content for {style['brand_name']} based on the following request:",
- f"\"{user_prompt}\"",
- "\nFollow these brand style guidelines:",
- f"- Brand Name: {style['brand_name']}",
- f"- Tone: {', '.join(style.get('tone', []))}",
- f"- Voice Characteristics: {', '.join(style.get('voice_characteristics', []))}",
+ style_instructions = [
+ "Follow these writing style guidelines:",
+ "- Use direct commands that empower the reader",
+ "- Address the reader directly using 'you' and 'your'",
+ "- Create rhythmic, repetitive patterns in key messages",
+ "- Maintain a clear, confident, and authoritative tone",
+ "- Use simple, practical language without jargon",
+ "- Acknowledge challenges while focusing on solutions",
+ "- Include empowering phrases that emphasize reader's control and choice"
]
+
+ # Content type specific formatting
+ content_format = self._get_content_format(content_type) if content_type else ""
- # Add taboo words if any
- if 'taboo_words' in style and style['taboo_words']:
- prompt_parts.append(f"- Avoid these words: {', '.join(style['taboo_words'])}")
-
- # Add preferred terms if any
- if 'preferred_terms' in style and style['preferred_terms']:
- terms = [f"use '{value}' instead of '{key}'" for key, value in style['preferred_terms'].items()]
- prompt_parts.append(f"- Preferred terminology: {'; '.join(terms)}")
-
- # Add content type specific instructions
- if content_type:
- if content_type == "email_campaign":
- prompt_parts.append("- Format as a professional email with subject line, greeting, body, and signature")
- elif content_type == "social_media":
- prompt_parts.append("- Format as a concise social media post with appropriate hashtags")
- elif content_type == "blog_post":
- prompt_parts.append("- Format as a blog post with title, introduction, body with subheadings, and conclusion")
- elif content_type == "website_copy":
- prompt_parts.append("- Format as website copy with clear headings and concise paragraphs")
- elif content_type == "ad_copy":
- prompt_parts.append("- Format as advertising copy with headline, body, and clear call to action")
-
- # Combine all parts
- formatted_prompt = "\n".join(prompt_parts)
-
- logger.debug("Created formatted prompt with brand style")
- return formatted_prompt
+ return "\n".join([
+ f"Generate content based on this request:",
+ f"\"{user_prompt}\"",
+ "",
+ "\n".join(style_instructions),
+ content_format
+ ])
def check_content_alignment(self, content: str) -> Dict[str, Any]:
"""
@@ -171,5 +243,23 @@ class BrandStyleManager:
'aligned': alignment_score >= 80 # Consider aligned if score is 80% or higher
}
+ def _get_content_format(self, content_type: str) -> str:
+ """
+ Get formatting instructions for specific content type.
+
+ Args:
+ content_type: Type of content to generate
+
+ Returns:
+ Formatting instructions as string
+ """
+ if not content_type:
+ return ""
+
+ format_instructions = self.content_formats.get(content_type, "")
+ if format_instructions:
+ return f"\nContent type specific instructions:\n{format_instructions.strip()}"
+ return ""
+
# Create a singleton instance
-brand_style_manager = BrandStyleManager()
\ No newline at end of file
+brand_style_manager = BrandStyleManager()
diff --git a/backend/config.py b/backend/config.py
index 214da1a..a1ef94f 100644
--- a/backend/config.py
+++ b/backend/config.py
@@ -38,26 +38,26 @@ BRAND_NAME = os.getenv("BRAND_NAME", "Adriana James")
# Content types
CONTENT_TYPES = [
- "email_campaign",
+ "website_copy",
+ "email",
"social_media",
"blog_post",
- "website_copy",
+ "sales_copy",
"ad_copy",
- "funnel_page",
+ "video_script",
+ "case_study",
"product_description",
- "press_release"
+ "landing_page",
+ "press_release",
+ "newsletter"
]
-# Tone options
+# Tone options - simplified to match the core style
TONE_OPTIONS = [
- "professional",
- "friendly",
- "excited",
- "authoritative",
- "casual",
- "inspirational",
- "empathetic",
- "humorous"
+ "direct",
+ "empowering",
+ "confident",
+ "practical"
]
# Content length options
@@ -69,13 +69,17 @@ LENGTH_OPTIONS = [
# Default brand style guidelines
DEFAULT_BRAND_STYLE = {
- "brand_name": BRAND_NAME,
- "tone": ["professional", "friendly", "inspirational"],
- "voice_characteristics": ["clear", "direct", "empowering"],
- "taboo_words": ["cheap", "discount", "bargain"],
+ "tone": ["direct", "empowering", "confident", "practical"],
+ "voice_characteristics": ["clear", "authoritative", "steady", "rhythmic"],
+ "writing_patterns": ["direct commands", "personal pronouns", "repetitive rhythms"],
+ "taboo_words": ["cheap", "discount", "bargain", "failure", "impossible", "difficult"],
"preferred_terms": {
- "customers": "clients",
- "products": "solutions"
+ "problems": "challenges",
+ "try": "take action",
+ "difficult": "ready for growth",
+ "failure": "learning opportunity",
+ "hope": "know",
+ "maybe": "will"
}
}
@@ -84,4 +88,4 @@ LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
LOG_FILE = os.getenv("LOG_FILE", str(BASE_DIR / "logs" / "app.log"))
# Create logs directory if it doesn't exist
-(BASE_DIR / "logs").mkdir(exist_ok=True)
\ No newline at end of file
+(BASE_DIR / "logs").mkdir(exist_ok=True)
diff --git a/backend/copywriter.py b/backend/copywriter.py
index ede1bbc..4014c87 100644
--- a/backend/copywriter.py
+++ b/backend/copywriter.py
@@ -28,7 +28,6 @@ class Copywriter:
self,
prompt: str,
content_type: Optional[str] = None,
- tone: Optional[str] = None,
length: Optional[str] = None,
include_cta: bool = False,
reference_similar_content: bool = True,
@@ -36,18 +35,7 @@ class Copywriter:
) -> Dict[str, Any]:
"""
Generate marketing copy based on the user prompt and parameters.
-
- Args:
- prompt: User prompt for content generation
- content_type: Type of content to generate
- tone: Desired tone of the content
- length: Desired length of the content
- include_cta: Whether to include a call to action
- reference_similar_content: Whether to fetch and reference similar content
- max_tokens: Maximum tokens for the generated response
-
- Returns:
- Dictionary with generated content and metadata
+ Note: Removed tone parameter as we always use the established style
"""
try:
# Step 1: Format prompt with brand style guidelines
@@ -60,35 +48,19 @@ class Copywriter:
if search_results:
reference_content = [result['text'] for result in search_results]
- # Step 3: Add additional instructions based on parameters
- full_prompt = branded_prompt
-
- if tone:
- full_prompt += f"\n- Use a {tone} tone"
-
+ # Step 3: Add length and CTA instructions if needed
if length:
- length_instructions = {
- "short": "Keep the content brief and to the point (under 100 words).",
- "medium": "Write a moderate amount of content (100-300 words).",
- "long": "Create comprehensive content with depth (over 300 words)."
- }
- full_prompt += f"\n- {length_instructions.get(length, '')}"
-
+ branded_prompt += f"\n- Generate {length} content"
if include_cta:
- full_prompt += "\n- Include a strong call to action at the end"
+ branded_prompt += "\n- Include a direct, empowering call to action"
# Step 4: Add reference content if available
if reference_content:
- full_prompt += "\n\nFor reference, here are some similar pieces of content that have performed well in the past:"
- for i, content in enumerate(reference_content, 1):
- # Truncate reference content if it's too long
- preview = content[:300] + "..." if len(content) > 300 else content
- full_prompt += f"\n\nReference {i}:\n{preview}"
-
- full_prompt += "\n\nUse these references for inspiration, but create original content."
+ branded_prompt += "\n\nReference these successful examples for tone and style:\n"
+ branded_prompt += "\n---\n".join(reference_content)
# Step 5: Generate content using the LLM
- generated_content = await self._call_llm_api(full_prompt, max_tokens)
+ generated_content = await self._call_llm_api(branded_prompt, max_tokens)
# Step 6: Check content alignment with brand style
alignment_check = brand_style_manager.check_content_alignment(generated_content)
@@ -102,7 +74,7 @@ class Copywriter:
"suggestions": headline_suggestions,
"metadata": {
"content_type": content_type,
- "tone": tone,
+ "tone": None, # Removed tone parameter
"alignment_score": alignment_check['alignment_score'],
"generated_at": None # Will be added by the API
}
@@ -174,20 +146,51 @@ class Copywriter:
Args:
original_prompt: The original user prompt
generated_content: The generated marketing content
-
+
Returns:
List of headline suggestions
"""
try:
- # This would call the LLM to generate headlines
- # Simplified mock response for demonstration
- return [
- "Alternative Headline 1: Discover the Power of Adriana James' Solutions",
- "Alternative Headline 2: Transform Your Results with Adriana James",
- "Alternative Headline 3: The Adriana James Approach: Excellence Redefined"
+ # Create a prompt for headline generation
+ headline_prompt = f"""
+ Generate 3 alternative marketing headlines for the following content.
+ Make headlines compelling, concise, and aligned with the content's message.
+ Each headline should be unique and capture attention.
+
+ ORIGINAL PROMPT:
+ {original_prompt}
+
+ CONTENT:
+ {generated_content}
+
+ Generate exactly 3 headlines, one per line, without numbering or prefixes.
+ """
+
+ # Call LLM to generate headlines
+ response = await self._call_llm_api(
+ prompt=headline_prompt,
+ max_tokens=100 # Shorter limit for headlines
+ )
+
+ # Process the response into a list of headlines
+ headlines = [
+ headline.strip()
+ for headline in response.split('\n')
+ if headline.strip() and not headline.lower().startswith(('headline', 'title', '-', '*', '•'))
]
+
+ # Ensure we have exactly 3 headlines
+ if len(headlines) > 3:
+ headlines = headlines[:3]
+ while len(headlines) < 3:
+ headlines.append(f"Headline Option {len(headlines) + 1}")
+
+ logger.info(f"Generated {len(headlines)} headline suggestions")
+ return headlines
+
except Exception as e:
logger.error(f"Error generating headline suggestions: {str(e)}")
+ # Return empty list instead of mock response on error
return []
async def improve_copy(self, content: str, feedback: str) -> str:
@@ -275,4 +278,4 @@ class Copywriter:
raise
# Create a singleton instance
-copywriter = Copywriter()
\ No newline at end of file
+copywriter = Copywriter()
diff --git a/backend/data/vector_store/.gitkeep b/backend/data/vector_store/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/backend/data/vector_store/faiss_index.bin b/backend/data/vector_store/faiss_index.bin
index 969a818..c9f990d 100644
Binary files a/backend/data/vector_store/faiss_index.bin and b/backend/data/vector_store/faiss_index.bin differ
diff --git a/backend/data/vector_store/metadata.pkl b/backend/data/vector_store/metadata.pkl
index 6d6ae08..7e07f3f 100644
Binary files a/backend/data/vector_store/metadata.pkl and b/backend/data/vector_store/metadata.pkl differ
diff --git a/backend/main.py b/backend/main.py
index 673aca0..57be807 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -43,7 +43,6 @@ app.add_middleware(
class GenerateCopyRequest(BaseModel):
prompt: str = Field(..., description="The main instruction for generating content")
content_type: Optional[str] = Field(None, description="Type of content to generate")
- tone: Optional[str] = Field(None, description="Desired tone of the content")
length: Optional[str] = Field(None, description="Desired length of the content")
include_cta: Optional[bool] = Field(False, description="Whether to include a call to action")
reference_similar_content: Optional[bool] = Field(True, description="Whether to reference similar content")
@@ -88,31 +87,10 @@ async def generate_copy(request: GenerateCopyRequest):
}
)
- # Validate tone if provided
- if request.tone and request.tone not in config.TONE_OPTIONS:
- return JSONResponse(
- status_code=status.HTTP_400_BAD_REQUEST,
- content={
- "status": "error",
- "message": f"Invalid tone. Must be one of: {', '.join(config.TONE_OPTIONS)}"
- }
- )
-
- # Validate length if provided
- if request.length and request.length not in config.LENGTH_OPTIONS:
- return JSONResponse(
- status_code=status.HTTP_400_BAD_REQUEST,
- content={
- "status": "error",
- "message": f"Invalid length. Must be one of: {', '.join(config.LENGTH_OPTIONS)}"
- }
- )
-
# Generate copy
result = await copywriter.generate_copy(
prompt=request.prompt,
content_type=request.content_type,
- tone=request.tone,
length=request.length,
include_cta=request.include_cta,
reference_similar_content=request.reference_similar_content,
@@ -126,7 +104,6 @@ async def generate_copy(request: GenerateCopyRequest):
if result["content"]:
metadata = {
"content_type": request.content_type,
- "tone": request.tone,
"prompt": request.prompt,
"generated": True
}
@@ -139,7 +116,6 @@ async def generate_copy(request: GenerateCopyRequest):
"prompt": request.prompt,
"parameters": {
"content_type": request.content_type,
- "tone": request.tone,
"length": request.length,
"include_cta": request.include_cta
},
diff --git a/data/style_guidelines/brand_style.json b/data/style_guidelines/brand_style.json
index 43e5c62..129d514 100644
--- a/data/style_guidelines/brand_style.json
+++ b/data/style_guidelines/brand_style.json
@@ -1,12 +1,9 @@
{
- "brand_name": "Adriana James",
"tone": [
"professional",
"friendly",
"inspirational",
- "empowering",
- "excited",
- "authoritative"
+ "empowering"
],
"voice_characteristics": [
"clear",
@@ -14,9 +11,13 @@
"empowering",
"confident",
"authentic",
- "innovative",
- "visionary",
- "approachable"
+ "innovative"
+ ],
+ "writing_patterns": [
+ "direct commands",
+ "personal pronouns",
+ "repetitive rhythms",
+ "empowering phrases"
],
"taboo_words": [
"cheap",
@@ -32,5 +33,6 @@
"problems": "challenges",
"services": "experiences",
"training": "transformation"
- }
+ },
+ "brand_name": "Adriana James"
}
\ No newline at end of file
diff --git a/docs/API_DOCUMENTATION.md b/docs/API_DOCUMENTATION.md
index e9dbdf1..7868a6e 100644
--- a/docs/API_DOCUMENTATION.md
+++ b/docs/API_DOCUMENTATION.md
@@ -15,7 +15,6 @@ Generates marketing copy based on the provided prompt and optional parameters.
{
"prompt": "Write a social media post for our new product launch",
"content_type": "social_media",
- "tone": "excited",
"length": "medium",
"include_cta": true
}
@@ -24,7 +23,6 @@ Generates marketing copy based on the provided prompt and optional parameters.
**Parameters**:
- `prompt` (string, required): The main instruction for generating content
- `content_type` (string, optional): Type of content to generate (social_media, email, blog, website, etc.)
-- `tone` (string, optional): Desired tone (excited, professional, casual, etc.)
- `length` (string, optional): Content length (short, medium, long)
- `include_cta` (boolean, optional): Whether to include a call to action
@@ -184,4 +182,4 @@ Error response format:
"message": "Detailed error message",
"error_code": "ERROR_CODE"
}
-```
\ No newline at end of file
+```
diff --git a/frontend/app.js b/frontend/app.js
index 2d8bf8c..99cc6fe 100644
--- a/frontend/app.js
+++ b/frontend/app.js
@@ -8,7 +8,6 @@ document.addEventListener('DOMContentLoaded', function() {
const generateBtn = document.getElementById('generate-btn');
const promptInput = document.getElementById('prompt');
const contentTypeSelect = document.getElementById('content-type');
- const toneSelect = document.getElementById('tone');
const lengthSelect = document.getElementById('length');
const includeCTACheckbox = document.getElementById('include-cta');
const referenceSimilarCheckbox = document.getElementById('reference-similar');
@@ -86,7 +85,6 @@ document.addEventListener('DOMContentLoaded', function() {
const requestData = {
prompt: promptInput.value,
content_type: contentTypeSelect.value || null,
- tone: toneSelect.value || null,
length: lengthSelect.value || null,
include_cta: includeCTACheckbox.checked,
reference_similar_content: referenceSimilarCheckbox.checked
@@ -510,4 +508,4 @@ document.addEventListener('DOMContentLoaded', function() {
// For demonstration purposes, let's create a mocked pre-filled content
// In a real implementation, this would be loaded from the backend
document.getElementById('prompt').value = 'Write a social media post about our new coaching program';
-});
\ No newline at end of file
+});
diff --git a/frontend/index.html b/frontend/index.html
index 2d8977b..67b7a8e 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -56,30 +56,21 @@
-