feat: Initial implementation of Marketing Assistant AI for Adriana James

- Set up FastAPI backend with modular structure:

  - main.py for API routing

  - copywriter.py for AI-powered content generation using Cohere

  - embeddings.py for generating and reranking content embeddings

  - vector_store.py for FAISS-based similarity search

  - brand_style.py for managing brand tone, taboo words, and preferred terms

  - config.py for managing environment and application settings

- Configured RESTful API endpoints: /generate-copy, /brand-style, /training-data, /improve-content, /analyze-content

- Created frontend with vanilla HTML, CSS, and JS (index.html, styles.css, app.js)

- Integrated brand style management for tone, voice, taboo words, and terminology

- Implemented vector search for referencing similar historical content

- Enabled training data input to improve future AI output

- Added environment variable support for API keys and model configs

- Structured data storage with local JSON and DB files

- Added developer documentation, API reference, and project setup instructions

This commit provides the foundation for a full-stack, AI-driven content creation platform that ensures brand consistency, speeds up marketing workflows, and supports iterative improvement over time.
This commit is contained in:
Michael Ikehi
2025-04-17 08:50:12 +01:00
commit 6fd7213076
21 changed files with 4497 additions and 0 deletions
+175
View File
@@ -0,0 +1,175 @@
"""
Brand style module for the Marketing Assistant AI.
Ensures generated content aligns with Adriana James' brand voice and tone.
"""
import json
import os
from typing import Dict, List, Any, Optional
from pathlib import Path
from loguru import logger
import config
class BrandStyleManager:
"""Manages brand style guidelines and ensures content consistency."""
def __init__(self):
"""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()
logger.info("BrandStyleManager initialized successfully")
def _load_or_create_style(self) -> Dict[str, Any]:
"""Load existing style guidelines or create new ones with defaults."""
try:
if self.style_path.exists():
with open(self.style_path, 'r') as f:
style = json.load(f)
logger.info("Loaded existing brand style guidelines")
return style
else:
# Create directory if it doesn't exist
self.style_path.parent.mkdir(exist_ok=True)
# Use default style guidelines
style = config.DEFAULT_BRAND_STYLE
# Save default style
with open(self.style_path, 'w') as f:
json.dump(style, f, indent=2)
logger.info("Created default brand style guidelines")
return style
except Exception as e:
logger.error(f"Error loading or creating style guidelines: {str(e)}")
# Fall back to default style
return config.DEFAULT_BRAND_STYLE
def get_style_guidelines(self) -> Dict[str, Any]:
"""
Get current brand style guidelines.
Returns:
Dictionary of style guidelines
"""
return self.style_guidelines
def update_style_guidelines(self, new_style: Dict[str, Any]) -> Dict[str, Any]:
"""
Update brand style guidelines.
Args:
new_style: Dictionary with new style guidelines
Returns:
Updated style guidelines dictionary
"""
try:
# Merge new style with existing
for key, value in new_style.items():
self.style_guidelines[key] = value
# Ensure brand name is preserved
self.style_guidelines['brand_name'] = config.BRAND_NAME
# Save updated style
with open(self.style_path, 'w') as f:
json.dump(self.style_guidelines, f, indent=2)
logger.info("Updated brand style guidelines")
return self.style_guidelines
except Exception as e:
logger.error(f"Error updating style guidelines: {str(e)}")
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.
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', []))}",
]
# 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
def check_content_alignment(self, content: str) -> Dict[str, Any]:
"""
Check if generated content aligns with brand style guidelines.
Args:
content: Generated marketing content
Returns:
Dictionary with alignment metrics and suggestions
"""
style = self.style_guidelines
taboo_words = style.get('taboo_words', [])
preferred_terms = style.get('preferred_terms', {})
# Check for taboo words
found_taboo_words = []
for word in taboo_words:
if word.lower() in content.lower():
found_taboo_words.append(word)
# Check for preferred terminology
terminology_issues = []
for avoid, use in preferred_terms.items():
if avoid.lower() in content.lower():
terminology_issues.append(f"Found '{avoid}', should use '{use}' instead")
# Calculate an overall alignment score (simple implementation)
issues_count = len(found_taboo_words) + len(terminology_issues)
alignment_score = max(0, 100 - (issues_count * 10)) # Reduce score for each issue
return {
'alignment_score': alignment_score,
'taboo_words_found': found_taboo_words,
'terminology_issues': terminology_issues,
'aligned': alignment_score >= 80 # Consider aligned if score is 80% or higher
}
# Create a singleton instance
brand_style_manager = BrandStyleManager()