ALwrity Version 0.5.1 (Fastapi + React)
This commit is contained in:
557
ToBeMigrated/ai_writers/ai_outline_writer/README.md
Normal file
557
ToBeMigrated/ai_writers/ai_outline_writer/README.md
Normal 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
|
||||
317
ToBeMigrated/ai_writers/ai_outline_writer/get_blog_outline.py
Normal file
317
ToBeMigrated/ai_writers/ai_outline_writer/get_blog_outline.py
Normal file
@@ -0,0 +1,317 @@
|
||||
"""
|
||||
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
|
||||
import json
|
||||
|
||||
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 = {}
|
||||
|
||||
def generate_outline(self, topic: str) -> Dict[str, List[str]]:
|
||||
"""Generate a blog outline based on the topic and configuration."""
|
||||
try:
|
||||
# Create a focused prompt for outline generation
|
||||
prompt = f"""Generate a blog outline for topic: {topic}
|
||||
|
||||
Content Type: {self.config.content_type.value}
|
||||
Target Audience: {self.config.target_audience}
|
||||
Content Depth: {self.config.content_depth.value}
|
||||
Style: {self.config.outline_style.value}
|
||||
Word Count Target: {self.config.target_word_count}
|
||||
Main Sections: {self.config.num_main_sections}
|
||||
Subsections per Section: {self.config.num_subsections_per_section}
|
||||
|
||||
Requirements:
|
||||
- Create exactly {self.config.num_main_sections} main sections
|
||||
- Each section should have exactly {self.config.num_subsections_per_section} subsections
|
||||
- Focus on {self.config.content_type.value} content style
|
||||
- Target {self.config.target_audience} audience
|
||||
- Maintain {self.config.content_depth.value} depth
|
||||
- Follow {self.config.outline_style.value} style
|
||||
- Optimize for {self.config.target_word_count} words total
|
||||
|
||||
IMPORTANT: You must return a valid JSON object with main sections as keys and lists of subsections as values.
|
||||
Example format: {{"Section 1": ["Subsection 1.1", "Subsection 1.2"], "Section 2": ["Subsection 2.1", "Subsection 2.2"]}}
|
||||
Do not include any additional text or explanations, only the JSON object."""
|
||||
|
||||
# Get outline from LLM
|
||||
outline_json = llm_text_gen(prompt)
|
||||
|
||||
# Clean the response to ensure it's valid JSON
|
||||
outline_json = outline_json.strip()
|
||||
if not outline_json.startswith('{'):
|
||||
outline_json = outline_json[outline_json.find('{'):]
|
||||
if not outline_json.endswith('}'):
|
||||
outline_json = outline_json[:outline_json.rfind('}')+1]
|
||||
|
||||
# Parse the outline
|
||||
try:
|
||||
outline = json.loads(outline_json)
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"JSON parsing error: {str(e)}")
|
||||
logger.error(f"Raw response: {outline_json}")
|
||||
# Fallback to a basic outline structure
|
||||
outline = {
|
||||
f"Section {i+1}": [f"Subsection {i+1}.{j+1}" for j in range(self.config.num_subsections_per_section)]
|
||||
for i in range(self.config.num_main_sections)
|
||||
}
|
||||
|
||||
# Add introduction and conclusion if configured
|
||||
if self.config.include_introduction:
|
||||
outline = {"Introduction": ["Overview", "Importance", "What to Expect"]} | outline
|
||||
|
||||
if self.config.include_conclusion:
|
||||
outline["Conclusion"] = ["Summary", "Key Takeaways", "Next Steps"]
|
||||
|
||||
# Add FAQs if configured
|
||||
if self.config.include_faqs:
|
||||
# Generate topic-specific FAQs
|
||||
faq_prompt = f"""Generate 3 specific and relevant FAQ questions for a blog post about: {topic}
|
||||
|
||||
Content Type: {self.config.content_type.value}
|
||||
Target Audience: {self.config.target_audience}
|
||||
Content Depth: {self.config.content_depth.value}
|
||||
|
||||
Requirements:
|
||||
- Questions should be specific to the topic
|
||||
- Cover common concerns and important aspects
|
||||
- Be relevant to the target audience
|
||||
- Include both basic and advanced questions
|
||||
|
||||
Format: Return only a JSON array of 3 questions.
|
||||
Example format: ["Question 1?", "Question 2?", "Question 3?"]"""
|
||||
|
||||
try:
|
||||
faq_json = llm_text_gen(faq_prompt)
|
||||
faq_json = faq_json.strip()
|
||||
if not faq_json.startswith('['):
|
||||
faq_json = faq_json[faq_json.find('['):]
|
||||
if not faq_json.endswith(']'):
|
||||
faq_json = faq_json[:faq_json.rfind(']')+1]
|
||||
|
||||
faqs = json.loads(faq_json)
|
||||
outline["Frequently Asked Questions"] = faqs
|
||||
except Exception as e:
|
||||
logger.error(f"Error generating FAQs: {str(e)}")
|
||||
outline["Frequently Asked Questions"] = [
|
||||
f"Common Question about {topic} 1",
|
||||
f"Common Question about {topic} 2",
|
||||
f"Common Question about {topic} 3"
|
||||
]
|
||||
|
||||
# Add resources if configured
|
||||
if self.config.include_resources:
|
||||
outline["Additional Resources"] = [
|
||||
"Further Reading",
|
||||
"Tools and References",
|
||||
"Related Topics"
|
||||
]
|
||||
|
||||
return outline
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error generating outline: {str(e)}")
|
||||
return {}
|
||||
|
||||
def generate_section_content(self, section: str, subsections: List[str]) -> Optional[SectionContent]:
|
||||
"""Generate content for a section."""
|
||||
try:
|
||||
# Create a focused prompt for content generation
|
||||
prompt = f"""Generate content for section: {section}
|
||||
|
||||
Subsections: {', '.join(subsections)}
|
||||
Content Type: {self.config.content_type.value}
|
||||
Target Audience: {self.config.target_audience}
|
||||
Content Depth: {self.config.content_depth.value}
|
||||
Style: {self.config.outline_style.value}
|
||||
Word Count Target: {self.config.target_word_count // self.config.num_main_sections}
|
||||
|
||||
Requirements:
|
||||
- Write content for each subsection
|
||||
- Maintain {self.config.content_depth.value} depth
|
||||
- Target {self.config.target_audience} audience
|
||||
- Follow {self.config.outline_style.value} style
|
||||
- Optimize for {self.config.target_word_count // self.config.num_main_sections} words
|
||||
- Include relevant examples and data points
|
||||
- Use clear, engaging language
|
||||
|
||||
Format: Return only a JSON object with 'content' and 'image_prompt' fields.
|
||||
Example format: {{"content": "Section content here...", "image_prompt": "Image description here..."}}"""
|
||||
|
||||
# Get content from LLM
|
||||
content_json = llm_text_gen(prompt)
|
||||
content_data = json.loads(content_json)
|
||||
|
||||
# Generate image if configured
|
||||
image_path = None
|
||||
if self.config.include_images:
|
||||
image_path = self.generate_section_image(section)
|
||||
|
||||
return SectionContent(
|
||||
title=section,
|
||||
content=content_data["content"],
|
||||
image_prompt=content_data.get("image_prompt"),
|
||||
image_path=image_path
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error generating content for section {section}: {str(e)}")
|
||||
return None
|
||||
|
||||
def generate_section_image(self, section: str) -> Optional[str]:
|
||||
"""Generate an image for a section."""
|
||||
try:
|
||||
# Create a focused prompt for image generation
|
||||
prompt = f"""Generate an image prompt for section: {section}
|
||||
|
||||
Style: {self.config.image_style}
|
||||
Engine: {self.config.image_engine}
|
||||
Content Type: {self.config.content_type.value}
|
||||
Target Audience: {self.config.target_audience}
|
||||
|
||||
Requirements:
|
||||
- Create a {self.config.image_style} style image
|
||||
- Optimize for {self.config.image_engine} engine
|
||||
- Match {self.config.content_type.value} content type
|
||||
- Appeal to {self.config.target_audience} audience
|
||||
- Be visually engaging and relevant
|
||||
|
||||
Format: Return only a JSON object with an 'image_prompt' field.
|
||||
Example format: {{"image_prompt": "Detailed image description here..."}}"""
|
||||
|
||||
# Get image prompt from LLM
|
||||
prompt_json = llm_text_gen(prompt)
|
||||
prompt_data = json.loads(prompt_json)
|
||||
|
||||
# Generate image using the specified engine
|
||||
if self.config.image_engine == "Gemini-AI":
|
||||
image_path = generate_gemini_image(prompt_data["image_prompt"])
|
||||
elif self.config.image_engine == "Dalle3":
|
||||
image_path = generate_dalle_image(prompt_data["image_prompt"])
|
||||
else: # Stability-AI
|
||||
image_path = generate_stability_image(prompt_data["image_prompt"])
|
||||
|
||||
return image_path
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error generating image for section {section}: {str(e)}")
|
||||
return None
|
||||
|
||||
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"\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
|
||||
739
ToBeMigrated/ai_writers/ai_outline_writer/outline_ui.py
Normal file
739
ToBeMigrated/ai_writers/ai_outline_writer/outline_ui.py
Normal file
@@ -0,0 +1,739 @@
|
||||
"""
|
||||
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;
|
||||
width: 100%;
|
||||
}
|
||||
.stButton>button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
/* Add specific styling for the generate outline button */
|
||||
.generate-outline-button {
|
||||
width: 100%;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.generate-outline-button > button {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
.section-card {
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
margin-bottom: 20px;
|
||||
width: 100%;
|
||||
}
|
||||
.content-preview {
|
||||
background-color: #f8f9fa;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
margin: 10px 0;
|
||||
width: 100%;
|
||||
}
|
||||
.image-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 20px 0;
|
||||
width: 100%;
|
||||
}
|
||||
.stats-card {
|
||||
background-color: #e8f5e9;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
margin: 10px 0;
|
||||
width: 100%;
|
||||
}
|
||||
.edit-section {
|
||||
background-color: #e3f2fd;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
margin: 10px 0;
|
||||
width: 100%;
|
||||
}
|
||||
.subsection-list {
|
||||
margin-left: 20px;
|
||||
width: 100%;
|
||||
}
|
||||
/* Main container width */
|
||||
.main .block-container {
|
||||
max-width: 100%;
|
||||
padding: 2rem;
|
||||
}
|
||||
/* Full width for the outline display */
|
||||
.outline-container {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
/* Section styling */
|
||||
.section-header {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 2px solid #e0e0e0;
|
||||
}
|
||||
.subsection-item {
|
||||
font-size: 1.1rem;
|
||||
color: #34495e;
|
||||
margin: 0.5rem 0;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
/* Content area styling */
|
||||
.content-area {
|
||||
background-color: white;
|
||||
padding: 2rem;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
margin: 1rem 0;
|
||||
}
|
||||
/* Make sure all Streamlit elements use full width */
|
||||
.stMarkdown, .stText, .stTextArea, .stSelectbox, .stSlider {
|
||||
width: 100% !important;
|
||||
}
|
||||
/* Full width for code blocks */
|
||||
.stCodeBlock {
|
||||
width: 100% !important;
|
||||
}
|
||||
/* Full width for the main content */
|
||||
.main .block-container {
|
||||
padding-left: 2rem;
|
||||
padding-right: 2rem;
|
||||
max-width: 100%;
|
||||
}
|
||||
/* Adjust the main content area */
|
||||
.main .block-container > div {
|
||||
max-width: 100%;
|
||||
}
|
||||
/* Make sure the outline content uses full width */
|
||||
.outline-content {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
/* Adjust the preview section */
|
||||
.preview-section {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
padding: 1rem;
|
||||
}
|
||||
</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">
|
||||
<div class="section-header">{section}</div>
|
||||
""", 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)
|
||||
|
||||
# Image generation and display - Always show if images are enabled
|
||||
if generator and generator.config.include_images:
|
||||
st.markdown("### Image Generation")
|
||||
col1, col2, col3 = st.columns([2, 2, 1])
|
||||
|
||||
with col1:
|
||||
image_style = st.selectbox(
|
||||
"Image Style",
|
||||
["realistic", "illustration", "minimalist", "photographic", "artistic"],
|
||||
index=["realistic", "illustration", "minimalist", "photographic", "artistic"].index(generator.config.image_style),
|
||||
key=f"img_style_{section}"
|
||||
)
|
||||
|
||||
with col2:
|
||||
image_engine = st.selectbox(
|
||||
"Image Engine",
|
||||
["Gemini-AI", "Dalle3", "Stability-AI"],
|
||||
index=["Gemini-AI", "Dalle3", "Stability-AI"].index(generator.config.image_engine),
|
||||
key=f"img_engine_{section}"
|
||||
)
|
||||
|
||||
with col3:
|
||||
if st.button("Generate Image", key=f"gen_img_{section}"):
|
||||
with st.spinner(f"Generating image for {section}..."):
|
||||
# Update config with selected options
|
||||
generator.config.image_style = image_style
|
||||
generator.config.image_engine = image_engine
|
||||
image_path = generator.generate_section_image(section)
|
||||
if image_path:
|
||||
st.success("Image generated successfully!")
|
||||
st.experimental_rerun()
|
||||
else:
|
||||
st.error("Failed to generate image")
|
||||
|
||||
# Display existing 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:
|
||||
st.markdown("### Edit Content")
|
||||
# Edit content
|
||||
edited_content = edit_section_content(section, content.content)
|
||||
if edited_content != content.content:
|
||||
content.content = edited_content
|
||||
st.experimental_rerun()
|
||||
|
||||
st.markdown("### Edit Subsections")
|
||||
# Edit subsections
|
||||
edited_subsections = edit_subsections(section, subsections)
|
||||
if edited_subsections != subsections:
|
||||
subsections[:] = edited_subsections
|
||||
st.experimental_rerun()
|
||||
|
||||
st.markdown("### Edit Metadata")
|
||||
# Edit metadata
|
||||
if generator:
|
||||
edit_section_metadata(section, generator)
|
||||
else:
|
||||
# Display subsections in view mode
|
||||
st.markdown("### Subsections")
|
||||
st.markdown('<div class="subsection-list">', unsafe_allow_html=True)
|
||||
for subsection in subsections:
|
||||
st.markdown(f'<div class="subsection-item">• {subsection}</div>', unsafe_allow_html=True)
|
||||
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():
|
||||
# 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.
|
||||
""")
|
||||
|
||||
# Main content area with full width
|
||||
st.markdown('<div class="outline-container">', unsafe_allow_html=True)
|
||||
|
||||
# Move topic input to main area and make it more prominent
|
||||
st.markdown("### Enter Your Blog Topic")
|
||||
topic = st.text_input("", placeholder="Enter your blog topic here for creating outline...", key="blog_topic")
|
||||
|
||||
st.markdown("---") # Add a separator
|
||||
st.markdown("### Configuration Options")
|
||||
|
||||
# Create tabs for different configuration sections
|
||||
tab1, tab2, tab3, tab4 = st.tabs([
|
||||
"📝 Content Type & Target",
|
||||
"📊 Content Structure",
|
||||
"🎨 Style & Sections",
|
||||
"🖼️ Image & Optimization"
|
||||
])
|
||||
|
||||
with tab1:
|
||||
st.markdown("#### Content Type & Target")
|
||||
col1, col2, col3 = st.columns(3)
|
||||
|
||||
with col1:
|
||||
content_type = st.selectbox(
|
||||
"Content Type",
|
||||
[type.value for type in ContentType],
|
||||
index=[type.value for type in ContentType].index(ContentType.GUIDE.value),
|
||||
help="Select the type of content you want to generate"
|
||||
)
|
||||
|
||||
with col2:
|
||||
target_audience = st.selectbox(
|
||||
"Target Audience",
|
||||
["General", "Technical", "Professional", "Academic", "Business", "Students", "Developers"],
|
||||
index=0,
|
||||
help="Select your target audience"
|
||||
)
|
||||
|
||||
with col3:
|
||||
language = st.selectbox(
|
||||
"Language",
|
||||
["English", "Spanish", "French", "German", "Italian", "Portuguese", "Chinese", "Japanese", "Korean"],
|
||||
index=0,
|
||||
help="Select the language for your content"
|
||||
)
|
||||
|
||||
with tab2:
|
||||
st.markdown("#### Content Structure")
|
||||
col1, col2 = st.columns(2)
|
||||
|
||||
with col1:
|
||||
num_main_sections = st.slider(
|
||||
"Number of Main Sections",
|
||||
min_value=3,
|
||||
max_value=10,
|
||||
value=5,
|
||||
step=1,
|
||||
help="Choose how many main sections your outline should have"
|
||||
)
|
||||
|
||||
num_subsections = st.slider(
|
||||
"Subsections per Section",
|
||||
min_value=2,
|
||||
max_value=5,
|
||||
value=3,
|
||||
step=1,
|
||||
help="Choose how many subsections each main section should have"
|
||||
)
|
||||
|
||||
with col2:
|
||||
target_word_count = st.slider(
|
||||
"Target Word Count",
|
||||
min_value=500,
|
||||
max_value=5000,
|
||||
value=2000,
|
||||
step=100,
|
||||
help="Set your target word count for the entire blog post"
|
||||
)
|
||||
|
||||
# Display content statistics
|
||||
st.markdown("##### Content Statistics")
|
||||
st.markdown(f"""
|
||||
- Estimated Sections: {num_main_sections}
|
||||
- Total Subsections: {num_main_sections * num_subsections}
|
||||
- Target Word Count: {target_word_count}
|
||||
- Average Words per Section: {target_word_count // num_main_sections}
|
||||
""")
|
||||
|
||||
with tab3:
|
||||
st.markdown("#### Style & Sections")
|
||||
col1, col2 = st.columns(2)
|
||||
|
||||
with col1:
|
||||
content_depth = st.selectbox(
|
||||
"Content Depth",
|
||||
[depth.value for depth in ContentDepth],
|
||||
index=[depth.value for depth in ContentDepth].index(ContentDepth.INTERMEDIATE.value),
|
||||
help="Select the depth of content coverage"
|
||||
)
|
||||
|
||||
outline_style = st.selectbox(
|
||||
"Outline Style",
|
||||
[style.value for style in OutlineStyle],
|
||||
index=[style.value for style in OutlineStyle].index(OutlineStyle.MODERN.value),
|
||||
help="Select the style of your outline"
|
||||
)
|
||||
|
||||
with col2:
|
||||
st.markdown("##### Additional Sections")
|
||||
include_intro = st.checkbox("Include Introduction", value=True, help="Add an introduction section")
|
||||
include_conclusion = st.checkbox("Include Conclusion", value=True, help="Add a conclusion section")
|
||||
include_faqs = st.checkbox("Include FAQs", value=True, help="Add a FAQ section")
|
||||
include_resources = st.checkbox("Include Resources", value=True, help="Add a resources section")
|
||||
|
||||
with tab4:
|
||||
st.markdown("#### Image & Optimization")
|
||||
col1, col2 = st.columns(2)
|
||||
|
||||
with col1:
|
||||
st.markdown("##### Image Settings")
|
||||
include_images = st.checkbox("Enable Image Generation", value=True, help="Enable AI image generation for sections")
|
||||
|
||||
if include_images:
|
||||
image_style = st.selectbox(
|
||||
"Image Style",
|
||||
["realistic", "illustration", "minimalist", "photographic", "artistic"],
|
||||
index=0,
|
||||
help="Select the style for generated images"
|
||||
)
|
||||
|
||||
image_engine = st.selectbox(
|
||||
"Image Engine",
|
||||
["Gemini-AI", "Dalle3", "Stability-AI"],
|
||||
index=0,
|
||||
help="Select the AI engine for image generation"
|
||||
)
|
||||
|
||||
with col2:
|
||||
st.markdown("##### Content Optimization")
|
||||
keywords = st.text_area(
|
||||
"Keywords (comma-separated)",
|
||||
help="Enter keywords for SEO optimization, separated by commas"
|
||||
)
|
||||
|
||||
exclude_topics = st.text_area(
|
||||
"Topics to Exclude (comma-separated)",
|
||||
help="Enter topics you want to exclude from the content"
|
||||
)
|
||||
|
||||
st.markdown("---") # Add a separator before the generate button
|
||||
|
||||
# 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)
|
||||
|
||||
# Store the generated outline in session state
|
||||
if 'outline' not in st.session_state:
|
||||
st.session_state.outline = None
|
||||
if 'section_contents' not in st.session_state:
|
||||
st.session_state.section_contents = {}
|
||||
|
||||
# Generate outline button with full width
|
||||
st.markdown('<div class="generate-outline-button">', unsafe_allow_html=True)
|
||||
if not topic:
|
||||
st.warning("Please enter a blog topic to generate the outline.")
|
||||
if st.button("Generate Outline", type="primary", use_container_width=True, disabled=not topic):
|
||||
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 = generator.generate_outline(topic)
|
||||
st.session_state.outline = outline
|
||||
st.session_state.section_contents = generator.section_contents
|
||||
|
||||
# Display results
|
||||
st.success("Outline generated successfully!")
|
||||
|
||||
# Add copy button and display outline in full width
|
||||
st.markdown('<div class="outline-content">', unsafe_allow_html=True)
|
||||
outline_text = json.dumps(outline, indent=2)
|
||||
st.code(outline_text, language="json")
|
||||
st.button("Copy Outline", key="copy_outline",
|
||||
help="Copy the outline to clipboard",
|
||||
on_click=lambda: st.write(f'<script>navigator.clipboard.writeText(`{outline_text}`)</script>',
|
||||
unsafe_allow_html=True))
|
||||
st.markdown('</div>', unsafe_allow_html=True)
|
||||
|
||||
# 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
|
||||
st.markdown('<div class="preview-section">', unsafe_allow_html=True)
|
||||
for section, subsections in outline.items():
|
||||
content = generator.section_contents.get(section)
|
||||
display_section(section, subsections, content, generator)
|
||||
st.markdown('</div>', unsafe_allow_html=True)
|
||||
|
||||
elif output_format == "Markdown":
|
||||
markdown_output = generator.to_markdown()
|
||||
st.markdown('<div class="outline-content">', unsafe_allow_html=True)
|
||||
st.code(markdown_output, language="markdown")
|
||||
st.download_button(
|
||||
"Download Markdown",
|
||||
markdown_output,
|
||||
file_name="blog_outline.md",
|
||||
mime="text/markdown"
|
||||
)
|
||||
st.markdown('</div>', unsafe_allow_html=True)
|
||||
|
||||
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.markdown('<div class="outline-content">', unsafe_allow_html=True)
|
||||
st.code(json_output, language="json")
|
||||
st.download_button(
|
||||
"Download JSON",
|
||||
json_output,
|
||||
file_name="blog_outline.json",
|
||||
mime="application/json"
|
||||
)
|
||||
st.markdown('</div>', unsafe_allow_html=True)
|
||||
|
||||
elif output_format == "HTML":
|
||||
html_output = f"""
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{topic} - Blog Outline</title>
|
||||
<style>
|
||||
body {{ font-family: Arial, sans-serif; max-width: 100%; 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.markdown('<div class="outline-content">', unsafe_allow_html=True)
|
||||
st.code(html_output, language="html")
|
||||
st.download_button(
|
||||
"Download HTML",
|
||||
html_output,
|
||||
file_name="blog_outline.html",
|
||||
mime="text/html"
|
||||
)
|
||||
st.markdown('</div>', unsafe_allow_html=True)
|
||||
|
||||
except Exception as e:
|
||||
st.error(f"Error generating outline: {str(e)}")
|
||||
st.markdown('</div>', unsafe_allow_html=True)
|
||||
|
||||
# Display the outline if it exists in session state
|
||||
if st.session_state.outline:
|
||||
st.markdown('<div class="preview-section">', unsafe_allow_html=True)
|
||||
for section, subsections in st.session_state.outline.items():
|
||||
content = st.session_state.section_contents.get(section)
|
||||
display_section(section, subsections, content, generator)
|
||||
st.markdown('</div>', unsafe_allow_html=True)
|
||||
|
||||
st.markdown('</div>', unsafe_allow_html=True) # Close the outline container
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user