Spaces:
Runtime error
Runtime error
Upload folder using huggingface_hub
Browse files- .claude/settings.local.json +10 -0
- .github/workflows/update_space.yml +28 -0
- .gitignore +69 -0
- CLAUDE.md +125 -0
- CLAUDE_DEPLOYMENT.md +461 -0
- README.md +3 -9
- api_test_results.json +21 -0
- app.py +472 -0
- articles.txt +304 -0
- requirements.txt +0 -0
- test_apis.py +187 -0
.claude/settings.local.json
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"permissions": {
|
3 |
+
"allow": [
|
4 |
+
"WebFetch(domain:95ec775c1970593485.gradio.live)",
|
5 |
+
"WebFetch(domain:www.gradio.app)",
|
6 |
+
"WebFetch(domain:docs.railway.com)"
|
7 |
+
],
|
8 |
+
"deny": []
|
9 |
+
}
|
10 |
+
}
|
.github/workflows/update_space.yml
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
name: Run Python script
|
2 |
+
|
3 |
+
on:
|
4 |
+
push:
|
5 |
+
branches:
|
6 |
+
- main
|
7 |
+
|
8 |
+
jobs:
|
9 |
+
build:
|
10 |
+
runs-on: ubuntu-latest
|
11 |
+
|
12 |
+
steps:
|
13 |
+
- name: Checkout
|
14 |
+
uses: actions/checkout@v2
|
15 |
+
|
16 |
+
- name: Set up Python
|
17 |
+
uses: actions/setup-python@v2
|
18 |
+
with:
|
19 |
+
python-version: '3.9'
|
20 |
+
|
21 |
+
- name: Install Gradio
|
22 |
+
run: python -m pip install gradio
|
23 |
+
|
24 |
+
- name: Log in to Hugging Face
|
25 |
+
run: python -c 'import huggingface_hub; huggingface_hub.login(token="${{ secrets.hf_token }}")'
|
26 |
+
|
27 |
+
- name: Deploy to Spaces
|
28 |
+
run: gradio deploy
|
.gitignore
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Environment files
|
2 |
+
.env
|
3 |
+
.env.local
|
4 |
+
.env.development.local
|
5 |
+
.env.test.local
|
6 |
+
.env.production.local
|
7 |
+
|
8 |
+
# Python
|
9 |
+
__pycache__/
|
10 |
+
*.py[cod]
|
11 |
+
*$py.class
|
12 |
+
*.so
|
13 |
+
.Python
|
14 |
+
build/
|
15 |
+
develop-eggs/
|
16 |
+
dist/
|
17 |
+
downloads/
|
18 |
+
eggs/
|
19 |
+
.eggs/
|
20 |
+
lib/
|
21 |
+
lib64/
|
22 |
+
parts/
|
23 |
+
sdist/
|
24 |
+
var/
|
25 |
+
wheels/
|
26 |
+
share/python-wheels/
|
27 |
+
*.egg-info/
|
28 |
+
.installed.cfg
|
29 |
+
*.egg
|
30 |
+
MANIFEST
|
31 |
+
|
32 |
+
# Virtual environments
|
33 |
+
.env
|
34 |
+
.venv
|
35 |
+
env/
|
36 |
+
venv/
|
37 |
+
ENV/
|
38 |
+
env.bak/
|
39 |
+
venv.bak/
|
40 |
+
|
41 |
+
# Gradio
|
42 |
+
.gradio/
|
43 |
+
gradio_cached_examples/
|
44 |
+
|
45 |
+
# IDE files
|
46 |
+
.vscode/
|
47 |
+
.idea/
|
48 |
+
*.swp
|
49 |
+
*.swo
|
50 |
+
*~
|
51 |
+
|
52 |
+
# OS files
|
53 |
+
.DS_Store
|
54 |
+
.DS_Store?
|
55 |
+
._*
|
56 |
+
.Spotlight-V100
|
57 |
+
.Trashes
|
58 |
+
ehthumbs.db
|
59 |
+
Thumbs.db
|
60 |
+
|
61 |
+
# Logs
|
62 |
+
*.log
|
63 |
+
logs/
|
64 |
+
|
65 |
+
# Temporary files
|
66 |
+
*.tmp
|
67 |
+
*.temp
|
68 |
+
temp/
|
69 |
+
tmp/
|
CLAUDE.md
ADDED
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# CLAUDE.md
|
2 |
+
|
3 |
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
4 |
+
|
5 |
+
## Project Overview
|
6 |
+
|
7 |
+
This is a **SEO Article Generator** - a Gradio-based web application that generates SEO-optimized articles using OpenAI's GPT models. The application allows users to input context and keywords, then generates articles that match the style and quality of reference articles stored in `articles.txt`.
|
8 |
+
|
9 |
+
## Key Files and Structure
|
10 |
+
|
11 |
+
- **`app.py`** - Main application file containing the entire Gradio interface and business logic
|
12 |
+
- **`articles.txt`** - Reference articles database used to guide writing style and quality
|
13 |
+
- **`.env`** - Environment variables (contains `OPENAI_API_KEY`)
|
14 |
+
|
15 |
+
## Development Commands
|
16 |
+
|
17 |
+
### Running the Application
|
18 |
+
```bash
|
19 |
+
python app.py
|
20 |
+
```
|
21 |
+
|
22 |
+
### Environment Setup
|
23 |
+
```bash
|
24 |
+
# Create .env file with:
|
25 |
+
echo "OPENAI_API_KEY=your_openai_key_here" > .env
|
26 |
+
|
27 |
+
# Install dependencies (if requirements.txt exists):
|
28 |
+
pip install -r requirements.txt
|
29 |
+
|
30 |
+
# Otherwise install manually:
|
31 |
+
pip install gradio requests python-dotenv
|
32 |
+
```
|
33 |
+
|
34 |
+
### Testing
|
35 |
+
- **Manual Testing**: Run the app and test through the Gradio interface at `http://localhost:7862`
|
36 |
+
- **API Testing**: Test OpenAI API connectivity by running article generation
|
37 |
+
|
38 |
+
## Code Architecture
|
39 |
+
|
40 |
+
### Core Classes
|
41 |
+
|
42 |
+
1. **`Config`** (`app.py:16-28`) - Central configuration management
|
43 |
+
- OpenAI API key handling
|
44 |
+
- Model configurations (`gpt-4o-mini`, `gpt-4o`, `gpt-4-turbo-preview`)
|
45 |
+
- File path constants
|
46 |
+
|
47 |
+
2. **`ArticleKnowledgeBase`** (`app.py:30-52`) - Reference articles management
|
48 |
+
- Loads and manages content from `articles.txt`
|
49 |
+
- Provides context for AI article generation
|
50 |
+
- Handles file reading errors gracefully
|
51 |
+
|
52 |
+
3. **`SEOArticleGenerator`** (`app.py:54-209`) - Core article generation logic
|
53 |
+
- OpenAI API integration
|
54 |
+
- Prompt engineering for SEO optimization
|
55 |
+
- Article editing functionality
|
56 |
+
- Keyword tracking and analysis
|
57 |
+
|
58 |
+
4. **`SEOArticleBot`** (`app.py:211-274`) - Application interface layer
|
59 |
+
- Gradio interface integration
|
60 |
+
- User input validation
|
61 |
+
- Response formatting and status reporting
|
62 |
+
|
63 |
+
### Key Features
|
64 |
+
|
65 |
+
- **Article Generation**: Creates SEO-optimized articles based on context and keywords
|
66 |
+
- **Article Editing**: Allows users to refine existing articles with specific instructions
|
67 |
+
- **Keyword Tracking**: Monitors keyword usage and density
|
68 |
+
- **Reference Style Matching**: Uses articles.txt content to maintain consistent writing style
|
69 |
+
- **Multiple AI Models**: Supports different OpenAI GPT models with quality/speed tradeoffs
|
70 |
+
|
71 |
+
### Prompt Engineering
|
72 |
+
|
73 |
+
The system uses sophisticated prompt engineering (`app.py:84-124`) with:
|
74 |
+
- **System prompts** defining expert SEO writer persona
|
75 |
+
- **Writing guidelines** for style, SEO requirements, structure, and quality
|
76 |
+
- **Context integration** from reference articles
|
77 |
+
- **Keyword optimization** with natural incorporation requirements
|
78 |
+
|
79 |
+
### Error Handling
|
80 |
+
|
81 |
+
- **API Key Validation**: Checks for OpenAI API key before requests
|
82 |
+
- **File Loading**: Graceful handling of missing `articles.txt`
|
83 |
+
- **API Error Handling**: Comprehensive error reporting for OpenAI API failures
|
84 |
+
- **Input Validation**: Ensures required fields are provided
|
85 |
+
|
86 |
+
## Reference Articles System
|
87 |
+
|
88 |
+
The `articles.txt` file contains reference articles that guide the AI's writing style. Current articles focus on:
|
89 |
+
- Video marketing strategies
|
90 |
+
- Small business video needs
|
91 |
+
- OTT advertising benefits
|
92 |
+
- Business video types
|
93 |
+
- Brand awareness and conversions
|
94 |
+
|
95 |
+
When adding new reference articles, maintain the format:
|
96 |
+
```
|
97 |
+
## Article [Number]
|
98 |
+
|
99 |
+
[Article Title]
|
100 |
+
[Article Content]
|
101 |
+
```
|
102 |
+
|
103 |
+
## Configuration Notes
|
104 |
+
|
105 |
+
- **Default Model**: `gpt-4o` (balanced quality/speed)
|
106 |
+
- **Server Configuration**: Runs on `0.0.0.0:7862` with sharing enabled
|
107 |
+
- **Article Length**: Targets 800-1500 words
|
108 |
+
- **Keyword Density**: Maintains 1-2% without stuffing
|
109 |
+
- **Temperature**: 0.7 for balanced creativity and consistency
|
110 |
+
|
111 |
+
## Development Tips
|
112 |
+
|
113 |
+
- Test OpenAI API connectivity before making changes
|
114 |
+
- The reference articles significantly impact output quality - update them regularly
|
115 |
+
- Keyword tracking helps optimize SEO performance
|
116 |
+
- Use the edit feature for iterative improvements rather than regenerating
|
117 |
+
- Monitor API usage and costs when testing
|
118 |
+
|
119 |
+
## Deployment Considerations
|
120 |
+
|
121 |
+
- Ensure `.env` file is properly configured and secure
|
122 |
+
- `articles.txt` should be populated with high-quality reference content
|
123 |
+
- Consider rate limiting for production usage
|
124 |
+
- Monitor OpenAI API usage and costs
|
125 |
+
- The app auto-generates sharing links - may need to disable for production
|
CLAUDE_DEPLOYMENT.md
ADDED
@@ -0,0 +1,461 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Comprehensive Railway Deployment Plan - Platform-Agnostic Docker Approach
|
2 |
+
|
3 |
+
## Executive Summary
|
4 |
+
|
5 |
+
**Yes, using Docker is the perfect approach for platform portability.** After comprehensive research, here's a complete deployment strategy that keeps you platform-independent while optimizing for Railway initially.
|
6 |
+
|
7 |
+
## Core Architecture Strategy
|
8 |
+
|
9 |
+
### 1. **Docker-First Approach (Platform Agnostic)**
|
10 |
+
- **Multi-stage Dockerfile** for optimized production builds
|
11 |
+
- **Standard environment variable patterns** (works on Railway, Render, AWS, GCP, etc.)
|
12 |
+
- **Portable configuration** using Docker Compose for local development
|
13 |
+
- **Zero vendor lock-in** - same container runs anywhere
|
14 |
+
|
15 |
+
### 2. **FastAPI + Gradio Integration (2025 Best Practices)**
|
16 |
+
```python
|
17 |
+
# main.py - FastAPI wrapper
|
18 |
+
from fastapi import FastAPI
|
19 |
+
import gradio as gr
|
20 |
+
from your_app import create_interface
|
21 |
+
|
22 |
+
app = FastAPI()
|
23 |
+
gradio_app = create_interface()
|
24 |
+
|
25 |
+
# Mount Gradio with authentication
|
26 |
+
app = gr.mount_gradio_app(
|
27 |
+
app, gradio_app,
|
28 |
+
path="/",
|
29 |
+
auth_dependency=authenticate_user
|
30 |
+
)
|
31 |
+
```
|
32 |
+
|
33 |
+
## Complete Tech Stack (2025 Production Ready)
|
34 |
+
|
35 |
+
### Core Dependencies
|
36 |
+
```txt
|
37 |
+
fastapi==0.104.1
|
38 |
+
gradio==4.44.0
|
39 |
+
uvicorn[standard]==0.24.0
|
40 |
+
slowapi==0.1.9 # Rate limiting
|
41 |
+
redis==5.0.1 # For production rate limiting
|
42 |
+
python-multipart==0.0.6 # File uploads
|
43 |
+
pydantic==2.5.0 # Data validation
|
44 |
+
python-dotenv==1.0.0 # Environment variables
|
45 |
+
```
|
46 |
+
|
47 |
+
### Production Security Stack
|
48 |
+
```txt
|
49 |
+
bcrypt==4.1.2 # Password hashing
|
50 |
+
python-jose[cryptography]==3.3.0 # JWT tokens
|
51 |
+
passlib[bcrypt]==1.7.4 # Password validation
|
52 |
+
```
|
53 |
+
|
54 |
+
## Docker Configuration (Platform Portable)
|
55 |
+
|
56 |
+
### Multi-Stage Dockerfile
|
57 |
+
```dockerfile
|
58 |
+
# Stage 1: Build stage
|
59 |
+
FROM python:3.11-slim as builder
|
60 |
+
|
61 |
+
WORKDIR /app
|
62 |
+
COPY requirements.txt .
|
63 |
+
RUN pip install --no-cache-dir --user -r requirements.txt
|
64 |
+
|
65 |
+
# Stage 2: Production stage
|
66 |
+
FROM python:3.11-slim
|
67 |
+
|
68 |
+
# Create non-root user for security
|
69 |
+
RUN useradd --create-home --shell /bin/bash app
|
70 |
+
|
71 |
+
# Copy installed packages from builder
|
72 |
+
COPY --from=builder /root/.local /home/app/.local
|
73 |
+
COPY . /app
|
74 |
+
|
75 |
+
# Set ownership and switch to non-root user
|
76 |
+
RUN chown -R app:app /app
|
77 |
+
USER app
|
78 |
+
WORKDIR /app
|
79 |
+
|
80 |
+
# Make sure local packages are in PATH
|
81 |
+
ENV PATH=/home/app/.local/bin:$PATH
|
82 |
+
|
83 |
+
# Expose port
|
84 |
+
EXPOSE 8000
|
85 |
+
|
86 |
+
# Health check
|
87 |
+
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
88 |
+
CMD curl -f http://localhost:8000/health || exit 1
|
89 |
+
|
90 |
+
# Start command
|
91 |
+
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
92 |
+
```
|
93 |
+
|
94 |
+
### Docker Compose (Local Development)
|
95 |
+
```yaml
|
96 |
+
version: '3.8'
|
97 |
+
services:
|
98 |
+
app:
|
99 |
+
build: .
|
100 |
+
ports:
|
101 |
+
- "8000:8000"
|
102 |
+
environment:
|
103 |
+
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
104 |
+
- AUTH_USERNAME=${AUTH_USERNAME}
|
105 |
+
- AUTH_PASSWORD=${AUTH_PASSWORD}
|
106 |
+
volumes:
|
107 |
+
- ./articles.txt:/app/articles.txt
|
108 |
+
depends_on:
|
109 |
+
- redis
|
110 |
+
|
111 |
+
redis:
|
112 |
+
image: redis:7-alpine
|
113 |
+
ports:
|
114 |
+
- "6379:6379"
|
115 |
+
```
|
116 |
+
|
117 |
+
## Authentication System (Production Ready)
|
118 |
+
|
119 |
+
### Simple Session-Based Auth
|
120 |
+
```python
|
121 |
+
from fastapi import Request, HTTPException, Depends
|
122 |
+
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
123 |
+
import secrets
|
124 |
+
import os
|
125 |
+
|
126 |
+
security = HTTPBasic()
|
127 |
+
|
128 |
+
def authenticate_user(request: Request) -> str:
|
129 |
+
"""Authentication for Gradio mount"""
|
130 |
+
auth_header = request.headers.get("authorization")
|
131 |
+
if not auth_header:
|
132 |
+
return None
|
133 |
+
|
134 |
+
# Extract credentials from Basic auth
|
135 |
+
try:
|
136 |
+
credentials = HTTPBasicCredentials(
|
137 |
+
username=extract_username(auth_header),
|
138 |
+
password=extract_password(auth_header)
|
139 |
+
)
|
140 |
+
|
141 |
+
if (credentials.username == os.getenv("AUTH_USERNAME") and
|
142 |
+
credentials.password == os.getenv("AUTH_PASSWORD")):
|
143 |
+
return credentials.username
|
144 |
+
except:
|
145 |
+
pass
|
146 |
+
|
147 |
+
return None
|
148 |
+
```
|
149 |
+
|
150 |
+
## Rate Limiting (Production Ready)
|
151 |
+
|
152 |
+
### SlowAPI with Redis Backend
|
153 |
+
```python
|
154 |
+
from slowapi import Limiter, _rate_limit_exceeded_handler
|
155 |
+
from slowapi.util import get_remote_address
|
156 |
+
from slowapi.errors import RateLimitExceeded
|
157 |
+
import redis
|
158 |
+
|
159 |
+
# Redis connection for production
|
160 |
+
redis_client = redis.Redis(
|
161 |
+
host=os.getenv("REDIS_HOST", "localhost"),
|
162 |
+
port=int(os.getenv("REDIS_PORT", "6379")),
|
163 |
+
decode_responses=True
|
164 |
+
)
|
165 |
+
|
166 |
+
limiter = Limiter(
|
167 |
+
key_func=get_remote_address,
|
168 |
+
storage_uri=f"redis://{os.getenv('REDIS_HOST', 'localhost')}:6379",
|
169 |
+
default_limits=["100/hour"]
|
170 |
+
)
|
171 |
+
|
172 |
+
app.state.limiter = limiter
|
173 |
+
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
|
174 |
+
|
175 |
+
@app.post("/generate")
|
176 |
+
@limiter.limit("10/hour")
|
177 |
+
async def generate_article(request: Request):
|
178 |
+
# Your generation logic
|
179 |
+
pass
|
180 |
+
```
|
181 |
+
|
182 |
+
## Environment Variables (Platform Portable)
|
183 |
+
|
184 |
+
### Production Environment Variables
|
185 |
+
```bash
|
186 |
+
# Core Application
|
187 |
+
OPENAI_API_KEY=your_openai_key_here
|
188 |
+
AUTH_USERNAME=your_username
|
189 |
+
AUTH_PASSWORD=your_secure_password
|
190 |
+
|
191 |
+
# Rate Limiting
|
192 |
+
REDIS_HOST=localhost
|
193 |
+
REDIS_PORT=6379
|
194 |
+
RATE_LIMIT_PER_HOUR=10
|
195 |
+
|
196 |
+
# Server Configuration
|
197 |
+
PORT=8000
|
198 |
+
HOST=0.0.0.0
|
199 |
+
WORKERS=1
|
200 |
+
|
201 |
+
# Security
|
202 |
+
SECRET_KEY=your-secret-key-here
|
203 |
+
ENVIRONMENT=production
|
204 |
+
```
|
205 |
+
|
206 |
+
## Platform Deployment Configs
|
207 |
+
|
208 |
+
### 1. Railway (Primary)
|
209 |
+
```json
|
210 |
+
{
|
211 |
+
"$schema": "https://railway.app/railway.schema.json",
|
212 |
+
"build": {
|
213 |
+
"builder": "DOCKERFILE"
|
214 |
+
},
|
215 |
+
"deploy": {
|
216 |
+
"startCommand": "uvicorn main:app --host 0.0.0.0 --port $PORT"
|
217 |
+
}
|
218 |
+
}
|
219 |
+
```
|
220 |
+
|
221 |
+
### 2. Render (Backup)
|
222 |
+
```yaml
|
223 |
+
services:
|
224 |
+
- type: web
|
225 |
+
name: seo-workflow
|
226 |
+
env: docker
|
227 |
+
dockerfilePath: ./Dockerfile
|
228 |
+
envVars:
|
229 |
+
- key: OPENAI_API_KEY
|
230 |
+
sync: false
|
231 |
+
- key: AUTH_USERNAME
|
232 |
+
sync: false
|
233 |
+
```
|
234 |
+
|
235 |
+
### 3. AWS ECS/Fargate (Enterprise)
|
236 |
+
```json
|
237 |
+
{
|
238 |
+
"family": "seo-workflow",
|
239 |
+
"networkMode": "awsvpc",
|
240 |
+
"requiresCompatibilities": ["FARGATE"],
|
241 |
+
"cpu": "256",
|
242 |
+
"memory": "512",
|
243 |
+
"containerDefinitions": [
|
244 |
+
{
|
245 |
+
"name": "seo-app",
|
246 |
+
"image": "your-registry/seo-workflow:latest",
|
247 |
+
"portMappings": [{"containerPort": 8000}]
|
248 |
+
}
|
249 |
+
]
|
250 |
+
}
|
251 |
+
```
|
252 |
+
|
253 |
+
## Security Best Practices (Production)
|
254 |
+
|
255 |
+
### 1. Secrets Management
|
256 |
+
- **Railway**: Use Railway's sealed variables for sensitive data
|
257 |
+
- **Generic**: Environment variables only, never hardcoded
|
258 |
+
- **Docker**: Multi-stage builds to avoid secrets in final image
|
259 |
+
|
260 |
+
### 2. Network Security
|
261 |
+
- **HTTPS**: Railway provides automatic SSL
|
262 |
+
- **CORS**: Configure for your domain only
|
263 |
+
- **Rate Limiting**: Protect against API abuse
|
264 |
+
|
265 |
+
### 3. Application Security
|
266 |
+
- **Non-root user**: Container runs as non-root user
|
267 |
+
- **Health checks**: Built-in health monitoring
|
268 |
+
- **Input validation**: Pydantic models for API validation
|
269 |
+
|
270 |
+
## Migration Strategy
|
271 |
+
|
272 |
+
### From Railway to Any Platform
|
273 |
+
1. **Same Docker image** works everywhere
|
274 |
+
2. **Environment variables** are standard across platforms
|
275 |
+
3. **Database/Redis** connection strings are configurable
|
276 |
+
4. **Zero code changes** required
|
277 |
+
|
278 |
+
### Platform Comparison
|
279 |
+
| Feature | Railway | Render | AWS | GCP |
|
280 |
+
|---------|---------|---------|-----|-----|
|
281 |
+
| Docker Support | β
| β
| β
| β
|
|
282 |
+
| Auto SSL | β
| β
| β | β |
|
283 |
+
| Environment Variables | β
| β
| β
| β
|
|
284 |
+
| Redis | β | β | β
| β
|
|
285 |
+
| Cost (Monthly) | $5+ | $7+ | $10+ | $10+ |
|
286 |
+
|
287 |
+
## Implementation Timeline
|
288 |
+
|
289 |
+
### Phase 1: Containerization (2 hours)
|
290 |
+
- Create FastAPI wrapper with authentication
|
291 |
+
- Implement rate limiting with SlowAPI
|
292 |
+
- Build Docker configuration
|
293 |
+
|
294 |
+
### Phase 2: Production Setup (1 hour)
|
295 |
+
- Environment variable configuration
|
296 |
+
- Security hardening
|
297 |
+
- Health checks and monitoring
|
298 |
+
|
299 |
+
### Phase 3: Deployment (30 minutes)
|
300 |
+
- Railway deployment
|
301 |
+
- Environment variable setup
|
302 |
+
- Testing and validation
|
303 |
+
|
304 |
+
### Phase 4: Platform Portability (30 minutes)
|
305 |
+
- Document migration process
|
306 |
+
- Create deployment configs for backup platforms
|
307 |
+
- Test portability
|
308 |
+
|
309 |
+
## Key Benefits
|
310 |
+
|
311 |
+
β
**Platform Independent**: Docker runs anywhere
|
312 |
+
β
**Production Ready**: Authentication, rate limiting, security
|
313 |
+
β
**Cost Effective**: Protects OpenAI API usage
|
314 |
+
β
**Scalable**: Easy to add features or migrate
|
315 |
+
β
**Maintainable**: Standard Docker/FastAPI patterns
|
316 |
+
β
**Secure**: Industry-standard security practices
|
317 |
+
|
318 |
+
This approach gives you maximum flexibility while starting with Railway as your primary platform. You can migrate to any other platform with minimal effort since everything is containerized and uses standard patterns.
|
319 |
+
|
320 |
+
---
|
321 |
+
|
322 |
+
## Multi-App Deployment Strategy
|
323 |
+
|
324 |
+
### Option 1: Single Container (Consolidated)
|
325 |
+
**Architecture**: One FastAPI app with multiple Gradio apps mounted at different paths
|
326 |
+
|
327 |
+
```python
|
328 |
+
# main.py - Consolidated approach
|
329 |
+
from fastapi import FastAPI
|
330 |
+
import gradio as gr
|
331 |
+
from seo_app import create_seo_interface
|
332 |
+
from image_app import create_image_interface
|
333 |
+
from chat_app import create_chat_interface
|
334 |
+
|
335 |
+
app = FastAPI()
|
336 |
+
|
337 |
+
# Mount multiple Gradio apps
|
338 |
+
seo_app = create_seo_interface()
|
339 |
+
image_app = create_image_interface()
|
340 |
+
chat_app = create_chat_interface()
|
341 |
+
|
342 |
+
app = gr.mount_gradio_app(app, seo_app, path="/seo", auth_dependency=auth_seo)
|
343 |
+
app = gr.mount_gradio_app(app, image_app, path="/image", auth_dependency=auth_image)
|
344 |
+
app = gr.mount_gradio_app(app, chat_app, path="/chat", auth_dependency=auth_chat)
|
345 |
+
```
|
346 |
+
|
347 |
+
**Pros**:
|
348 |
+
- β
**Cheapest**: Single Railway service ($5/month)
|
349 |
+
- β
**Shared resources**: Redis, authentication, monitoring
|
350 |
+
- β
**Easy management**: One deployment, one domain
|
351 |
+
- β
**Resource efficient**: Shared FastAPI server overhead
|
352 |
+
|
353 |
+
**Cons**:
|
354 |
+
- β **Coupling**: One app failure affects all
|
355 |
+
- β **Scaling limitations**: Can't scale apps individually
|
356 |
+
- β **Dependency conflicts**: All apps share same environment
|
357 |
+
|
358 |
+
### Option 2: Independent Containers (Microservices)
|
359 |
+
**Architecture**: Separate FastAPI + Gradio deployment for each app
|
360 |
+
|
361 |
+
```bash
|
362 |
+
# Three separate Railway services
|
363 |
+
seo-workflow-app # $5/month
|
364 |
+
image-generator-app # $5/month
|
365 |
+
chat-assistant-app # $5/month
|
366 |
+
```
|
367 |
+
|
368 |
+
**Pros**:
|
369 |
+
- β
**Independence**: Apps can fail/scale independently
|
370 |
+
- β
**Technology flexibility**: Different Python versions, dependencies
|
371 |
+
- β
**User isolation**: Separate authentication and rate limiting
|
372 |
+
- β
**Easier debugging**: Clear separation of concerns
|
373 |
+
|
374 |
+
**Cons**:
|
375 |
+
- β **Higher cost**: 3x Railway services ($15/month)
|
376 |
+
- β **Management overhead**: Multiple deployments, domains
|
377 |
+
- β **Resource duplication**: Separate Redis, auth systems
|
378 |
+
|
379 |
+
### Cost Analysis (Railway Pricing)
|
380 |
+
|
381 |
+
| Approach | Monthly Cost | Resource Usage | Scalability |
|
382 |
+
|----------|-------------|----------------|-------------|
|
383 |
+
| **Single Container** | $5 | 512MB RAM, 1 CPU | Limited |
|
384 |
+
| **Independent Containers** | $15 | 3x 512MB RAM, 3x CPU | High |
|
385 |
+
| **Hybrid** | $10 | 2 services (critical + non-critical) | Moderate |
|
386 |
+
|
387 |
+
### Recommendation: **Single Container for Your Use Case**
|
388 |
+
|
389 |
+
**Why Single Container is Best for You:**
|
390 |
+
|
391 |
+
1. **Cost Effective**: $5/month vs $15/month (3x savings)
|
392 |
+
2. **Low Traffic**: "Handful of users" doesn't justify microservices overhead
|
393 |
+
3. **Shared Resources**: All apps can share rate limiting, Redis, auth
|
394 |
+
4. **Easy Management**: One deployment, one domain, one set of environment variables
|
395 |
+
5. **Resource Efficient**: FastAPI can handle multiple Gradio apps easily
|
396 |
+
|
397 |
+
### Implementation Strategy
|
398 |
+
|
399 |
+
```python
|
400 |
+
# main.py - Multi-app architecture
|
401 |
+
from fastapi import FastAPI, Request
|
402 |
+
import gradio as gr
|
403 |
+
|
404 |
+
app = FastAPI()
|
405 |
+
|
406 |
+
# Different auth functions for different apps
|
407 |
+
def auth_seo(request: Request) -> str:
|
408 |
+
return authenticate_user(request, "seo_users")
|
409 |
+
|
410 |
+
def auth_image(request: Request) -> str:
|
411 |
+
return authenticate_user(request, "image_users")
|
412 |
+
|
413 |
+
# Mount apps with different authentication
|
414 |
+
app = gr.mount_gradio_app(app, seo_app, path="/seo", auth_dependency=auth_seo)
|
415 |
+
app = gr.mount_gradio_app(app, image_app, path="/image", auth_dependency=auth_image)
|
416 |
+
|
417 |
+
# Different rate limits per app
|
418 |
+
@app.middleware("http")
|
419 |
+
async def rate_limit_middleware(request: Request, call_next):
|
420 |
+
if request.url.path.startswith("/seo"):
|
421 |
+
# Apply SEO-specific rate limiting
|
422 |
+
pass
|
423 |
+
elif request.url.path.startswith("/image"):
|
424 |
+
# Apply image-specific rate limiting
|
425 |
+
pass
|
426 |
+
return await call_next(request)
|
427 |
+
```
|
428 |
+
|
429 |
+
### File Structure for Multi-App
|
430 |
+
```
|
431 |
+
consolidated_apps/
|
432 |
+
βββ main.py # FastAPI with multiple mounts
|
433 |
+
βββ apps/
|
434 |
+
β βββ seo_workflow/
|
435 |
+
β β βββ app.py
|
436 |
+
β β βββ articles.txt
|
437 |
+
β βββ image_generator/
|
438 |
+
β β βββ app.py
|
439 |
+
β β βββ models/
|
440 |
+
β βββ chat_assistant/
|
441 |
+
β βββ app.py
|
442 |
+
β βββ prompts/
|
443 |
+
βββ shared/
|
444 |
+
β βββ auth.py # Shared authentication
|
445 |
+
β βββ rate_limiting.py # Shared rate limiting
|
446 |
+
β βββ utils.py
|
447 |
+
βββ requirements.txt # All dependencies
|
448 |
+
βββ Dockerfile # Single container
|
449 |
+
βββ docker-compose.yml # Local development
|
450 |
+
```
|
451 |
+
|
452 |
+
### When to Switch to Microservices
|
453 |
+
|
454 |
+
**Switch to independent containers when:**
|
455 |
+
- Individual apps get >100 users each
|
456 |
+
- Different apps need different scaling patterns
|
457 |
+
- You want to deploy apps independently
|
458 |
+
- Technology stacks diverge significantly
|
459 |
+
- You're comfortable with $15/month cost
|
460 |
+
|
461 |
+
**Current recommendation**: Start with single container, migrate to microservices when you hit scaling limits.
|
README.md
CHANGED
@@ -1,12 +1,6 @@
|
|
1 |
---
|
2 |
-
title:
|
3 |
-
emoji: π
|
4 |
-
colorFrom: gray
|
5 |
-
colorTo: purple
|
6 |
-
sdk: gradio
|
7 |
-
sdk_version: 5.39.0
|
8 |
app_file: app.py
|
9 |
-
|
|
|
10 |
---
|
11 |
-
|
12 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
1 |
---
|
2 |
+
title: seo_workflow
|
|
|
|
|
|
|
|
|
|
|
3 |
app_file: app.py
|
4 |
+
sdk: gradio
|
5 |
+
sdk_version: 5.37.0
|
6 |
---
|
|
|
|
api_test_results.json
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"gpt-4.1": {
|
3 |
+
"success": true,
|
4 |
+
"content": "Artificial intelligence (AI) is transforming marketing by enabling smarter, data-driven decisions. With AI, marketers can analyze vast amounts of customer data, predict behavior, and personalize content at scale. Chatbots powered by AI provide instant customer support, while algorithms optimize ad placements for higher ROI. AI tools also automate repetitive tasks, freeing up time for creative strategy. Predictive analytics help identify trends and customer preferences, allowing brands to tailor campaigns more effectively. As AI technology evolves, its role in marketing continues to grow, offering businesses a competitive edge through improved targeting, efficiency, and customer engagement. The future of marketing is undeniably AI-driven.",
|
5 |
+
"tokens_used": 144,
|
6 |
+
"model": "gpt-4.1",
|
7 |
+
"response_time": 3.49
|
8 |
+
},
|
9 |
+
"o3": {
|
10 |
+
"success": false,
|
11 |
+
"error": "HTTP 404: {\n \"error\": {\n \"message\": \"Your organization must be verified to use the model `o3`. Please go to: https://platform.openai.com/settings/organization/general and click on Verify Organization. If you just verified, it can take up to 15 minutes for access to propagate.\",\n \"type\": \"invalid_request_error\",\n \"param\": null,\n \"code\": \"model_not_found\"\n }\n}\n",
|
12 |
+
"model": "o3",
|
13 |
+
"response_time": 1.32
|
14 |
+
},
|
15 |
+
"gemini-2.5-pro": {
|
16 |
+
"success": true,
|
17 |
+
"content": "Artificial Intelligence (AI) is revolutionizing marketing, transforming how brands connect with consumers. By analyzing vast datasets, AI uncovers deep insights into customer behavior, enabling hyper-personalized campaigns that resonate on an individual level.\n\nFrom predictive analytics that forecast future trends to AI-powered chatbots offering 24/7 support, the technology automates and optimizes countless tasks. AI tools can draft ad copy, segment audiences with precision, and automate media buying for maximum ROI.\n\nThis shift allows marketers to move beyond guesswork, creating more efficient, data-driven strategies that enhance customer engagement and drive significant growth in a competitive digital landscape.",
|
18 |
+
"model": "gemini-2.5-pro",
|
19 |
+
"response_time": 16.51
|
20 |
+
}
|
21 |
+
}
|
app.py
ADDED
@@ -0,0 +1,472 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import os
|
3 |
+
import requests
|
4 |
+
import json
|
5 |
+
from dotenv import load_dotenv
|
6 |
+
import logging
|
7 |
+
from typing import List, Dict, Tuple
|
8 |
+
|
9 |
+
# Load environment variables
|
10 |
+
load_dotenv()
|
11 |
+
|
12 |
+
# Set up logging
|
13 |
+
logging.basicConfig(level=logging.INFO)
|
14 |
+
logger = logging.getLogger(__name__)
|
15 |
+
|
16 |
+
class Config:
|
17 |
+
"""Central configuration for the app"""
|
18 |
+
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
|
19 |
+
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
|
20 |
+
ARTICLES_FILE_PATH = "articles.txt"
|
21 |
+
|
22 |
+
# Model configurations
|
23 |
+
AI_MODELS = {
|
24 |
+
"gpt-4.1": "OpenAI GPT-4.1",
|
25 |
+
"o3": "OpenAI o3",
|
26 |
+
"gemini-2.5-pro": "Google Gemini 2.5 Pro"
|
27 |
+
}
|
28 |
+
|
29 |
+
DEFAULT_MODEL = "gpt-4.1"
|
30 |
+
|
31 |
+
class ArticleKnowledgeBase:
|
32 |
+
"""Handles loading and managing reference articles"""
|
33 |
+
|
34 |
+
def __init__(self):
|
35 |
+
self.articles = self._load_articles()
|
36 |
+
|
37 |
+
def _load_articles(self) -> str:
|
38 |
+
"""Load articles from file"""
|
39 |
+
try:
|
40 |
+
with open(Config.ARTICLES_FILE_PATH, 'r', encoding='utf-8') as f:
|
41 |
+
content = f.read().strip()
|
42 |
+
logger.info(f"Loaded {len(content.split())} words from articles.txt")
|
43 |
+
return content
|
44 |
+
except FileNotFoundError:
|
45 |
+
logger.warning(f"Articles file not found at {Config.ARTICLES_FILE_PATH}")
|
46 |
+
return "No reference articles found. Please add articles to articles.txt"
|
47 |
+
except Exception as e:
|
48 |
+
logger.error(f"Error loading articles: {str(e)}")
|
49 |
+
return "Error loading reference articles."
|
50 |
+
|
51 |
+
def get_context(self) -> str:
|
52 |
+
"""Get the articles context for the AI"""
|
53 |
+
return self.articles
|
54 |
+
|
55 |
+
class SEOArticleGenerator:
|
56 |
+
"""Handles article generation using OpenAI and Gemini models"""
|
57 |
+
|
58 |
+
def __init__(self):
|
59 |
+
self.openai_api_key = Config.OPENAI_API_KEY
|
60 |
+
self.gemini_api_key = Config.GEMINI_API_KEY
|
61 |
+
self.knowledge_base = ArticleKnowledgeBase()
|
62 |
+
|
63 |
+
def generate_article(
|
64 |
+
self,
|
65 |
+
context: str,
|
66 |
+
keywords: str,
|
67 |
+
model: str = "gpt-4.1",
|
68 |
+
previous_article: str = None,
|
69 |
+
edit_instructions: str = None
|
70 |
+
) -> Dict[str, any]:
|
71 |
+
"""Generate SEO-optimized article using OpenAI or Gemini models"""
|
72 |
+
|
73 |
+
# Check API keys based on model
|
74 |
+
if model.startswith("gemini-"):
|
75 |
+
if not self.gemini_api_key:
|
76 |
+
return {
|
77 |
+
"success": False,
|
78 |
+
"error": "Gemini API key not found. Please set GEMINI_API_KEY in .env file."
|
79 |
+
}
|
80 |
+
else:
|
81 |
+
if not self.openai_api_key:
|
82 |
+
return {
|
83 |
+
"success": False,
|
84 |
+
"error": "OpenAI API key not found. Please set OPENAI_API_KEY in .env file."
|
85 |
+
}
|
86 |
+
|
87 |
+
# Get reference articles
|
88 |
+
reference_articles = self.knowledge_base.get_context()
|
89 |
+
|
90 |
+
# Parse keywords
|
91 |
+
keyword_list = [k.strip() for k in keywords.split(',') if k.strip()]
|
92 |
+
|
93 |
+
# Build the system prompt
|
94 |
+
system_prompt = f"""You are an expert SEO content writer. Create engaging, high-quality articles that naturally incorporate SEO keywords while providing genuine value to readers.
|
95 |
+
|
96 |
+
<reference_articles>
|
97 |
+
{reference_articles}
|
98 |
+
</reference_articles>
|
99 |
+
|
100 |
+
Use the reference articles above to guide your writing style and quality standards.
|
101 |
+
|
102 |
+
Your goals:
|
103 |
+
- Write naturally and engagingly - let your expertise shine
|
104 |
+
- Incorporate the provided keywords organically (aim for 2-3 keywords minimum)
|
105 |
+
- Create valuable, actionable content that readers will actually want to read
|
106 |
+
- Use proper markdown formatting with clear headings and structure
|
107 |
+
- Target 800-1500 words unless context suggests otherwise
|
108 |
+
|
109 |
+
Trust your instincts as a writer - focus on creating content that both search engines and humans will love."""
|
110 |
+
|
111 |
+
# Build user prompt based on whether this is an edit or new article
|
112 |
+
if previous_article and edit_instructions:
|
113 |
+
user_prompt = f"""Please edit the following article based on these instructions:
|
114 |
+
|
115 |
+
<editing_instructions>
|
116 |
+
{edit_instructions}
|
117 |
+
</editing_instructions>
|
118 |
+
|
119 |
+
<current_article>
|
120 |
+
{previous_article}
|
121 |
+
</current_article>
|
122 |
+
|
123 |
+
<context>
|
124 |
+
{context}
|
125 |
+
</context>
|
126 |
+
|
127 |
+
<seo_keywords>
|
128 |
+
{', '.join(keyword_list)}
|
129 |
+
</seo_keywords>
|
130 |
+
|
131 |
+
Please provide the edited version of the article, maintaining the SEO keywords and improving based on the editing instructions."""
|
132 |
+
else:
|
133 |
+
user_prompt = f"""Write a comprehensive SEO-optimized article based on the following:
|
134 |
+
|
135 |
+
<context>
|
136 |
+
{context}
|
137 |
+
</context>
|
138 |
+
|
139 |
+
<seo_keywords>
|
140 |
+
{', '.join(keyword_list)}
|
141 |
+
</seo_keywords>
|
142 |
+
|
143 |
+
Remember to:
|
144 |
+
- Incorporate at least 2-3 of the provided keywords naturally
|
145 |
+
- Create an engaging, well-structured article
|
146 |
+
- Use markdown formatting
|
147 |
+
- Aim for 800-1500 words
|
148 |
+
- Make it valuable and informative for readers"""
|
149 |
+
|
150 |
+
try:
|
151 |
+
if model.startswith("gemini-"):
|
152 |
+
# Use Gemini API
|
153 |
+
response = requests.post(
|
154 |
+
f"https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent",
|
155 |
+
headers={
|
156 |
+
"Content-Type": "application/json",
|
157 |
+
"x-goog-api-key": self.gemini_api_key
|
158 |
+
},
|
159 |
+
json={
|
160 |
+
"contents": [{
|
161 |
+
"parts": [{
|
162 |
+
"text": f"{system_prompt}\n\n{user_prompt}"
|
163 |
+
}]
|
164 |
+
}]
|
165 |
+
}
|
166 |
+
)
|
167 |
+
|
168 |
+
if response.status_code == 200:
|
169 |
+
result = response.json()
|
170 |
+
article = result['candidates'][0]['content']['parts'][0]['text'].strip()
|
171 |
+
else:
|
172 |
+
return {
|
173 |
+
"success": False,
|
174 |
+
"error": f"Gemini API error: {response.status_code} - {response.text}"
|
175 |
+
}
|
176 |
+
else:
|
177 |
+
# Use OpenAI API
|
178 |
+
response = requests.post(
|
179 |
+
"https://api.openai.com/v1/chat/completions",
|
180 |
+
headers={
|
181 |
+
"Authorization": f"Bearer {self.openai_api_key}",
|
182 |
+
"Content-Type": "application/json"
|
183 |
+
},
|
184 |
+
json={
|
185 |
+
"model": model,
|
186 |
+
"messages": [
|
187 |
+
{"role": "system", "content": system_prompt},
|
188 |
+
{"role": "user", "content": user_prompt}
|
189 |
+
],
|
190 |
+
"temperature": 0.7,
|
191 |
+
"max_tokens": 3000
|
192 |
+
}
|
193 |
+
)
|
194 |
+
|
195 |
+
if response.status_code == 200:
|
196 |
+
result = response.json()
|
197 |
+
article = result['choices'][0]['message']['content'].strip()
|
198 |
+
else:
|
199 |
+
return {
|
200 |
+
"success": False,
|
201 |
+
"error": f"OpenAI API error: {response.status_code} - {response.text}"
|
202 |
+
}
|
203 |
+
|
204 |
+
# Count keyword occurrences
|
205 |
+
keyword_counts = {}
|
206 |
+
for keyword in keyword_list:
|
207 |
+
keyword_counts[keyword] = article.lower().count(keyword.lower())
|
208 |
+
|
209 |
+
return {
|
210 |
+
"success": True,
|
211 |
+
"article": article,
|
212 |
+
"keyword_counts": keyword_counts,
|
213 |
+
"word_count": len(article.split())
|
214 |
+
}
|
215 |
+
|
216 |
+
except Exception as e:
|
217 |
+
logger.error(f"Error generating article: {str(e)}")
|
218 |
+
return {
|
219 |
+
"success": False,
|
220 |
+
"error": f"Error generating article: {str(e)}"
|
221 |
+
}
|
222 |
+
|
223 |
+
class SEOArticleBot:
|
224 |
+
"""Main application class"""
|
225 |
+
|
226 |
+
def __init__(self):
|
227 |
+
self.generator = SEOArticleGenerator()
|
228 |
+
|
229 |
+
def generate_article(
|
230 |
+
self,
|
231 |
+
context: str,
|
232 |
+
keywords: str,
|
233 |
+
model: str
|
234 |
+
) -> Tuple[str, str, str]:
|
235 |
+
"""Generate new article"""
|
236 |
+
|
237 |
+
if not context.strip():
|
238 |
+
return "", "Please provide context for the article", ""
|
239 |
+
|
240 |
+
if not keywords.strip():
|
241 |
+
return "", "Please provide at least one SEO keyword", ""
|
242 |
+
|
243 |
+
result = self.generator.generate_article(context, keywords, model)
|
244 |
+
|
245 |
+
if result["success"]:
|
246 |
+
# Create status message with keyword counts
|
247 |
+
keyword_info = "\n".join([f"- {k}: {v} occurrences" for k, v in result["keyword_counts"].items()])
|
248 |
+
status = f"β
Article generated successfully!\n\nWord count: {result['word_count']}\n\nKeyword usage:\n{keyword_info}"
|
249 |
+
|
250 |
+
# Create a preview/info section
|
251 |
+
info = f"**Article Stats:**\n- Words: {result['word_count']}\n- Keywords incorporated: {len([k for k, v in result['keyword_counts'].items() if v > 0])}/{len(result['keyword_counts'])}"
|
252 |
+
|
253 |
+
return result["article"], status, info
|
254 |
+
else:
|
255 |
+
return "", f"β Error: {result['error']}", ""
|
256 |
+
|
257 |
+
def edit_article(
|
258 |
+
self,
|
259 |
+
current_article: str,
|
260 |
+
context: str,
|
261 |
+
keywords: str,
|
262 |
+
edit_instructions: str,
|
263 |
+
model: str
|
264 |
+
) -> Tuple[str, str]:
|
265 |
+
"""Edit existing article based on instructions"""
|
266 |
+
|
267 |
+
if not current_article.strip():
|
268 |
+
return current_article, "No article to edit. Please generate an article first."
|
269 |
+
|
270 |
+
if not edit_instructions.strip():
|
271 |
+
return current_article, "Please provide editing instructions"
|
272 |
+
|
273 |
+
result = self.generator.generate_article(
|
274 |
+
context,
|
275 |
+
keywords,
|
276 |
+
model,
|
277 |
+
previous_article=current_article,
|
278 |
+
edit_instructions=edit_instructions
|
279 |
+
)
|
280 |
+
|
281 |
+
if result["success"]:
|
282 |
+
keyword_info = "\n".join([f"- {k}: {v} occurrences" for k, v in result["keyword_counts"].items()])
|
283 |
+
status = f"β
Article edited successfully!\n\nWord count: {result['word_count']}\n\nKeyword usage:\n{keyword_info}"
|
284 |
+
return result["article"], status
|
285 |
+
else:
|
286 |
+
return current_article, f"β Error: {result['error']}"
|
287 |
+
|
288 |
+
def create_interface():
|
289 |
+
"""Create the Gradio interface"""
|
290 |
+
app = SEOArticleBot()
|
291 |
+
|
292 |
+
with gr.Blocks(title="SEO Article Generator", theme=gr.themes.Default()) as demo:
|
293 |
+
gr.Markdown("""
|
294 |
+
# π SEO Article Generator Bot
|
295 |
+
Generate SEO-optimized articles based on your context and keywords. The bot uses reference articles to match writing style and quality.
|
296 |
+
""")
|
297 |
+
|
298 |
+
with gr.Row():
|
299 |
+
# Left Column - Inputs
|
300 |
+
with gr.Column(scale=1):
|
301 |
+
gr.Markdown("### π Article Configuration")
|
302 |
+
|
303 |
+
context_input = gr.Textbox(
|
304 |
+
label="Article Context",
|
305 |
+
placeholder="Provide the main topic, key points, and any specific information you want the article to cover...",
|
306 |
+
lines=10
|
307 |
+
)
|
308 |
+
|
309 |
+
keywords_input = gr.Textbox(
|
310 |
+
label="SEO Keywords (comma-separated)",
|
311 |
+
placeholder="e.g., digital marketing, SEO tips, content strategy, online presence, search rankings",
|
312 |
+
lines=2
|
313 |
+
)
|
314 |
+
|
315 |
+
model_dropdown = gr.Dropdown(
|
316 |
+
choices=list(Config.AI_MODELS.keys()),
|
317 |
+
value=Config.DEFAULT_MODEL,
|
318 |
+
label="AI Model"
|
319 |
+
)
|
320 |
+
|
321 |
+
generate_btn = gr.Button(
|
322 |
+
"π Generate Article",
|
323 |
+
variant="primary",
|
324 |
+
size="lg"
|
325 |
+
)
|
326 |
+
|
327 |
+
# Status and info section
|
328 |
+
status_output = gr.Textbox(
|
329 |
+
label="Status",
|
330 |
+
interactive=False,
|
331 |
+
lines=6
|
332 |
+
)
|
333 |
+
|
334 |
+
article_info = gr.Markdown("", visible=True)
|
335 |
+
|
336 |
+
# Right Column - Article Display and Editing
|
337 |
+
with gr.Column(scale=2):
|
338 |
+
gr.Markdown("### π Generated Article")
|
339 |
+
|
340 |
+
article_display = gr.Markdown(
|
341 |
+
value="*Your generated article will appear here...*",
|
342 |
+
elem_classes=["article-display"]
|
343 |
+
)
|
344 |
+
|
345 |
+
# Hidden textbox to store the raw article for editing
|
346 |
+
article_storage = gr.Textbox(visible=False)
|
347 |
+
|
348 |
+
# Editing section
|
349 |
+
with gr.Group():
|
350 |
+
gr.Markdown("### βοΈ Edit Article")
|
351 |
+
|
352 |
+
edit_instructions = gr.Textbox(
|
353 |
+
label="Editing Instructions",
|
354 |
+
placeholder="e.g., Make the introduction more engaging, add more statistics in the second section, expand on the benefits...",
|
355 |
+
lines=3
|
356 |
+
)
|
357 |
+
|
358 |
+
edit_btn = gr.Button(
|
359 |
+
"π Apply Edits",
|
360 |
+
variant="secondary"
|
361 |
+
)
|
362 |
+
|
363 |
+
# Footer with instructions
|
364 |
+
gr.Markdown("""
|
365 |
+
---
|
366 |
+
**π‘ Tips for best results:**
|
367 |
+
- Provide detailed context about your topic and target audience
|
368 |
+
- Include 3-5 relevant SEO keywords (the bot will use at least 2-3)
|
369 |
+
- Use the edit feature to refine specific sections
|
370 |
+
- Reference articles in `articles.txt` are used to match writing style
|
371 |
+
""")
|
372 |
+
|
373 |
+
# Event handlers
|
374 |
+
def generate_handler(context, keywords, model):
|
375 |
+
article, status, info = app.generate_article(context, keywords, model)
|
376 |
+
if article:
|
377 |
+
return gr.update(value=article), article, status, gr.update(value=info)
|
378 |
+
else:
|
379 |
+
return gr.update(value="*Article generation failed. Check the status message.*"), "", status, gr.update(value="")
|
380 |
+
|
381 |
+
def edit_handler(current_article, context, keywords, edit_instructions, model):
|
382 |
+
edited_article, status = app.edit_article(
|
383 |
+
current_article, context, keywords, edit_instructions, model
|
384 |
+
)
|
385 |
+
return gr.update(value=edited_article), edited_article, status
|
386 |
+
|
387 |
+
# Wire up events
|
388 |
+
generate_btn.click(
|
389 |
+
fn=generate_handler,
|
390 |
+
inputs=[context_input, keywords_input, model_dropdown],
|
391 |
+
outputs=[article_display, article_storage, status_output, article_info]
|
392 |
+
)
|
393 |
+
|
394 |
+
edit_btn.click(
|
395 |
+
fn=edit_handler,
|
396 |
+
inputs=[article_storage, context_input, keywords_input, edit_instructions, model_dropdown],
|
397 |
+
outputs=[article_display, article_storage, status_output]
|
398 |
+
)
|
399 |
+
|
400 |
+
# Add custom CSS
|
401 |
+
demo.css = """
|
402 |
+
.article-display {
|
403 |
+
max-height: 600px;
|
404 |
+
overflow-y: auto;
|
405 |
+
padding: 20px;
|
406 |
+
border: 1px solid #e5e7eb;
|
407 |
+
border-radius: 8px;
|
408 |
+
background-color: #ffffff;
|
409 |
+
line-height: 1.6;
|
410 |
+
}
|
411 |
+
.article-display h2 {
|
412 |
+
color: #1f2937;
|
413 |
+
margin-top: 24px;
|
414 |
+
margin-bottom: 16px;
|
415 |
+
}
|
416 |
+
.article-display h3 {
|
417 |
+
color: #374151;
|
418 |
+
margin-top: 20px;
|
419 |
+
margin-bottom: 12px;
|
420 |
+
}
|
421 |
+
.article-display p {
|
422 |
+
margin-bottom: 16px;
|
423 |
+
color: #4b5563;
|
424 |
+
}
|
425 |
+
.article-display ul, .article-display ol {
|
426 |
+
margin-bottom: 16px;
|
427 |
+
padding-left: 24px;
|
428 |
+
}
|
429 |
+
.article-display li {
|
430 |
+
margin-bottom: 8px;
|
431 |
+
}
|
432 |
+
"""
|
433 |
+
|
434 |
+
return demo
|
435 |
+
|
436 |
+
if __name__ == "__main__":
|
437 |
+
# Check for required files and keys
|
438 |
+
if not os.path.exists(".env"):
|
439 |
+
print("Warning: .env file not found. Please create one with:")
|
440 |
+
print("OPENAI_API_KEY=your_openai_key")
|
441 |
+
|
442 |
+
if not os.path.exists(Config.ARTICLES_FILE_PATH):
|
443 |
+
print(f"Warning: {Config.ARTICLES_FILE_PATH} not found.")
|
444 |
+
print("Creating a sample articles.txt file...")
|
445 |
+
|
446 |
+
# Create a sample articles.txt file
|
447 |
+
sample_content = """=== Article 1: The Ultimate Guide to Content Marketing ===
|
448 |
+
|
449 |
+
Content marketing has become the cornerstone of digital marketing strategies...
|
450 |
+
|
451 |
+
[Add your reference articles here]
|
452 |
+
|
453 |
+
=== Article 2: SEO Best Practices for 2024 ===
|
454 |
+
|
455 |
+
Search engine optimization continues to evolve...
|
456 |
+
|
457 |
+
[Add more articles as needed]
|
458 |
+
"""
|
459 |
+
|
460 |
+
with open(Config.ARTICLES_FILE_PATH, 'w', encoding='utf-8') as f:
|
461 |
+
f.write(sample_content)
|
462 |
+
|
463 |
+
print(f"Created {Config.ARTICLES_FILE_PATH}. Please add your reference articles to this file.")
|
464 |
+
|
465 |
+
# Launch the app
|
466 |
+
demo = create_interface()
|
467 |
+
demo.launch(
|
468 |
+
share=True,
|
469 |
+
debug=True,
|
470 |
+
server_name="0.0.0.0",
|
471 |
+
server_port=7862
|
472 |
+
)
|
articles.txt
ADDED
@@ -0,0 +1,304 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
## Articel 1
|
2 |
+
|
3 |
+
How Video Marketing Increases Sales & Brand Awareness
|
4 |
+
Innovating the right marketing tools is crucial for helping brands stand out and get a leg up on their competitors. Video marketing is one of the best tools for captivating an audience. By combining engaging visuals, compelling storytelling, and effective calls to action, video content captures attention and delivers messages in a way that resonates deeply with audiences. No matter the size of your business, video marketing will make a significant difference in reaching your desired target audience, and helping you achieve your business goals.
|
5 |
+
The Power of Video Marketing
|
6 |
+
βOne video has the power to increase your consumer base to unimaginable heights.β - Matt Hamilton, Producer/Videographer at Hoegger Communications. Whether itβs a viral social media post or a carefully crafted commercial that resonates with a large audience, the power of video marketing is stronger now than ever before. Now, we understand that making a viral video or an award-winning commercial is easier said than done, believe us, weβve earned every one of our 300+ awards through countless hours of brainstorming sessions, late-night drafts, and strategy meetings. Successful video marketing is more than just pressing record on a camera - it requires a well-thought-out strategy.
|
7 |
+
But hereβs the good news: weβve got the experience (and the awards) to guide you. Let us share some proven tips to help you kickstart your next video project with confidence:
|
8 |
+
|
9 |
+
Team Hoeggerβs Video Marketing Strategies
|
10 |
+
Implementing the following strategies will ensure that your video marketing efforts deliver measurable results.
|
11 |
+
1. Understand Your Audience: Knowing your target demographic helps tailor content to their interests and needs. For example, an engaging tutorial might appeal to one group, while a testimonial video could resonate more with another.
|
12 |
+
2. Use Storytelling: Videos that tell a story are more memorable and impactful. Whether itβs showcasing your companyβs journey or illustrating a customer success story, storytelling builds an emotional connection.
|
13 |
+
3. Optimize for SEO: Using relevant keywords in your titles and descriptions will allow your videos to be put in front of the right audiences, which will increase your videoβs chances of success.
|
14 |
+
4. Leverage Social Media Platforms: Tailor your videos for platforms such as Instagram, YouTube, and TikTok. Short-form videos work well on Instagram Reels and TikTok, while YouTube is ideal for long-form content.
|
15 |
+
5. Add a Clear Call to Action (CTA): End your videos with a strong CTA encouraging viewers to visit your website, make a purchase, or share the video.
|
16 |
+
How to Use Video in Digital Marketing
|
17 |
+
Integrating video into your digital marketing plan can yield impressive results. Hereβs how to make the most of this medium:
|
18 |
+
1. Enhancing Social Media Campaigns
|
19 |
+
For small local businesses in Wichita Falls and large metropolitan corporations like Dallas / Fort Worth, the rules of social media are the same. Short, engaging videos perform best on TikTok and Instagram, while longer-form videos do better on YouTube. Platforms like Facebook and LinkedIn are perfect for more in-depth content like company introductions or business updates. These strategies become even more effective when paired with professional video advertising services designed to create impactful video campaigns, tailored specifically for engaging local audiences like we do in Wichita Falls.
|
20 |
+
2. Boosting Website Engagement
|
21 |
+
Including videos on your homepage or landing pages increases dwell time, a key factor in SEO. Explainer videos, customer testimonials, and product demos provide value to visitors and guide them through the buyerβs journey. Research shows that landing pages with videos can boost conversion rates by up to 80%.
|
22 |
+
3. Improving Email Marketing
|
23 |
+
Embedding videos in email campaigns increases click-through rates by up to 300%. A personalized message or a quick product demonstration video can grab attention and encourage recipients to take action.
|
24 |
+
4. Paid Advertising
|
25 |
+
Platforms like Google Ads and YouTube offer powerful tools to reach your target audience through video ads. Pair this with precise geo-targeting - especially in areas like Wichita Falls - to ensure your content reaches the right viewers.
|
26 |
+
Why Video Builds Brand Awareness
|
27 |
+
Videos enable brands to showcase their personality, share authentic stories, and connect with audiences on a personal level, building trust and relatability through genuine communication. Here are some ways they help:
|
28 |
+
β Visual Storytelling: Videos combine visuals, sound, and emotion to create a lasting impression. A compelling brand story can elevate your business above competitors.
|
29 |
+
β Shareability: Videos are highly shareable, especially on social media. When viewers share your content, it extends your reach and exposes your brand to new audiences.
|
30 |
+
β Authority and Credibility: High-quality, informative videos position your brand as an expert in your field. For example, collaborating with a Wichita Falls video content specialist ensures your content meets professional standards and resonates with your audience.
|
31 |
+
Local Success: Video Advertising Services in Wichita Falls
|
32 |
+
Local businesses in Wichita Falls can significantly benefit from video marketing. Collaborating with local video professionals allows businesses in Wichita Falls to produce authentic, high-quality content that resonates with the community and reflects its unique culture. Whether itβs a promotional video for a new product or a testimonial featuring a satisfied customer, localized video content helps build trust and foster strong relationships within the community. People enjoy seeing updates from local businesses. There is a more personal connection with local consumers when they see a video from a business owner that they have interacted with.
|
33 |
+
Measuring the Impact of Video Marketing
|
34 |
+
The success of video marketing lies in its measurable impact. Key performance indicators (KPIs) like view counts, click-through rates, and conversions provide valuable insights into the effectiveness of your campaigns. Tools like Google Analytics and social media insights can help track performance and refine strategies for better results.
|
35 |
+
Why Invest in Professional Video Production
|
36 |
+
Although creating videos in-house is an option for some businesses, professional production elevates the quality and impact of content, ensuring it aligns with strategic goals and audience expectations. Companies specializing in video advertising services in Wichita Falls bring expertise in scripting, filming, and editing, ensuring your videos align with your brandβs vision and goals. Professional production also enhances credibility, leaving a positive impression on viewers.
|
37 |
+
Where to Start?
|
38 |
+
If you implement some of these video marketing strategies into your digital campaigns, video marketing can help your business build trust and credibility, increase brand awareness, and most importantly - generate more sales. You donβt have to spend an arm and a leg to start implementing effective video marketing strategies. Hoegger Communications built a reputation on helping small businesses grow in Wichita Falls - our video content specialists are ready to help your business see the success that can come from a professionally crafted video at a reasonable, and affordable price that makes sense for you! Stop by our office, talk to one of our team members, and let us show you why National and Local brands trust Hoegger Communications to start their video marketing journey.
|
39 |
+
|
40 |
+
|
41 |
+
|
42 |
+
## Article 2
|
43 |
+
|
44 |
+
Do You Need Small Business Video in Wichita Falls?
|
45 |
+
Yes. Video is one of the most powerful tools for engaging audiences, building brand recognition, and driving conversions. Video helps grab a potential customerβs attention, it helps build trust and helps develop credibility within the community. Whether itβs showcasing your products or services, telling your brandβs story, or connecting with customers on a personal level, video has the unique ability to convey emotion, authenticity, and value in ways that other mediums canβt. For a small business in Wichita Falls, where the market is limited, video marketing is not a luxury - itβs a necessity.
|
46 |
+
H2: Why Video is Essential for Businesses
|
47 |
+
Video is the cornerstone of modern marketing, offering businesses a dynamic way to connect with audiences. It combines visuals, sound, and storytelling to create memorable experiences that resonate with viewers. While there are hundreds of reasons why video is important, weβll focus on three main elements: Brand Awareness, Conversions, and Future of Advertising.
|
48 |
+
1. Video Increases Engagement and Brand Awareness
|
49 |
+
Consumers are far more likely to engage with video content than with text or static images. Videos capture attention quickly, hold it longer, and leave a lasting impression, making them a powerful tool for building brand recognition. Whether it's a compelling story, a product demonstration, or a behind-the-scenes look at your business, video can connect with audiences on a personal level. Additionally, videos are highly shareable, meaning they can amplify your message as viewers share them across social media platforms, further increasing your reach and solidifying your brand in the minds of potential customers.
|
50 |
+
According to multiple industry studies:
|
51 |
+
β’ Video posts get 1200% more shares than text and images combined, making it one of the most powerful tools for organic reach.
|
52 |
+
β’ Viewers retain 95% of a message when they watch it in a video, compared to just 10% when reading text, reinforcing why video is the preferred content format.
|
53 |
+
β’ Video increases website dwell time, which signals quality to search engines, ultimately improving rankings and visibility.
|
54 |
+
2. Video Converts Viewers into Customers
|
55 |
+
A well-crafted video does more than just grab attention - it motivates viewers to βtake actionβ. By presenting information in a visually engaging and easily digestible format, video helps potential customers understand the value of your product or service quickly and effectively. Whether itβs a testimonial that builds trust, a product demo that showcases benefits, or a call-to-action that encourages immediate responses, video creates a direct path from interest to decision. With the ability to evoke emotions and address customer pain points, video is a highly effective tool for turning viewers into loyal customers.
|
56 |
+
Here are some stats to consider:
|
57 |
+
β’ Landing pages with video increase conversions by 80%, making them a must-have for any high-performing website.
|
58 |
+
β’ 72% of customers prefer learning about a product via video instead of text, proving that video is an essential part of the buyerβs journey.
|
59 |
+
β’ 90% of consumers say watching a product video helps them decide whether to buy, highlighting its role in driving purchasing decisions.
|
60 |
+
3. Video is the Future of Advertising
|
61 |
+
From Google Ads to social media commercials, video ads consistently outperform static content in capturing attention and driving results. As digital platforms increasingly prioritize video in their algorithms, businesses that invest in video advertising are more likely to reach their target audiences and stay ahead of the competition. Video offers a dynamic and engaging way to tell your story, showcase your products, and connect with consumers in real time. With the growing popularity of video-centric platforms like YouTube, TikTok, and Instagram Reels, it's clear that video is not just the present of advertising - itβs the future.
|
62 |
+
β’ YouTube is the second-largest search engine after Google, demonstrating videoβs dominance in the digital landscape.
|
63 |
+
β’ TikTok and Instagram Reels lead social media engagement, showing that short-form videos are key to capturing audience attention.
|
64 |
+
β’ Google prioritizes video in search results, boosting SEO performance and driving organic traffic for brands leveraging video marketing.
|
65 |
+
H2: Where Businesses Can Use Video Marketing
|
66 |
+
Professional video content isn't just for websites - it powers every aspect of digital marketing. Hereβs where businesses can leverage video to increase brand visibility, improve ad performance, and drive more sales.
|
67 |
+
1. Social Media Platforms
|
68 |
+
Video needs to be βnativeβ to the social media platform in which itβs going to be shared. Here are the types of videos you need to make an impact on social media:
|
69 |
+
β’ Instagram & TikTok: Short-form videos, Reels, and Stories increase organic reach and engagement.
|
70 |
+
β’ Facebook & LinkedIn: Longer professional videos showcase expertise and company culture.
|
71 |
+
β’ YouTube: The go-to platform for long-form educational and promotional content.
|
72 |
+
2. Google & Social Media Ads
|
73 |
+
Paid advertising is dominated by video, delivering better ROI than static photo/text ads.
|
74 |
+
β’ Google Video Ads: Google prioritizes video ads, boosting visibility across YouTube and search results.
|
75 |
+
β’ Facebook & Instagram Ads: Video ads get higher click-through rates (CTR) and lower cost per click (CPC).
|
76 |
+
β’ TikTok Ads: With 1 billion+ active users, TikTok video ads help businesses connect with younger audiences.
|
77 |
+
3. Website & Landing Pages
|
78 |
+
Videos increase engagement and help convert website visitors into customers.
|
79 |
+
β’ Homepage videos: Instantly showcase your brand story, mission, and offerings.
|
80 |
+
β’ Explainer videos: Break down complex products or services into digestible, engaging content.
|
81 |
+
β’ Testimonial videos: Build trust and credibility by featuring real customers sharing positive experiences.
|
82 |
+
4. Email Marketing & Sales Funnels
|
83 |
+
Including a video in an email, boosts click-through rates by 200-300%.
|
84 |
+
β’ Product demo videos encourage more conversions.
|
85 |
+
β’ Behind-the-scenes videos create a personal connection with subscribers.
|
86 |
+
5. Traditional Advertising & Live Events
|
87 |
+
For brick-and-mortar businesses, video enhances in-person marketing strategies.
|
88 |
+
β’ TV commercials and digital ads keep your business top-of-mind.
|
89 |
+
β’ Looped video displays in-store grab customer attention.
|
90 |
+
β’ Trade show presentations help businesses stand out in competitive environments.
|
91 |
+
H2: What Value does a Professional Video Production Bring?
|
92 |
+
Planning, Expertise, Quality, and Results.
|
93 |
+
Professional video production delivers immense value by creating polished, high-quality videos that establish credibility, build trust, and enhance brand authority. Production companies, like Hoegger Communications here in Wichita Falls, strategically plan videos to achieve specific goals - whether it's answering customer questions, launching a campaign, or targeting a particular audience.
|
94 |
+
Well-executed video content also boosts search engine rankings, as Google prioritizes video in search results, driving more organic traffic to your website. Moreover, professional video production ensures a greater return on investment, with businesses using video marketing experiencing 49% faster revenue growth, generating 66% more leads, and helping 90% of marketers attract new business. By partnering with a professional video production company, you can create impactful content that drives measurable growth and delivers long-term results because itβs planned.
|
95 |
+
H2: Is DIY Enough?
|
96 |
+
Yes, DIY video can be enough for certain projects, especially when resources are limited. However, creating a truly impactful marketing video requires more than just filming - it demands strategic planning, technical expertise, and a polished execution to ensure your message resonates.
|
97 |
+
While smartphones and editing tools make video creation accessible, professional video production brings a level of quality that sets your brand apart. Superior lighting, sound, editing, and storytelling ensure your video not only captures attention but leaves a lasting impression.
|
98 |
+
Ultimately, while DIY might work in some situations, if youβre going to invest your time and money into producing a video, gambling on the results isnβt ideal. A professional production company helps you stay ahead by crafting a video that maximizes your investment and delivers meaningful, measurable results.
|
99 |
+
H2: Hoegger Communications: Your Video Production Partner
|
100 |
+
We understand the power of video. Our team of experienced videographers and producers are passionate about crafting compelling video content that helps businesses in Wichita Falls, TX achieve their marketing goals.
|
101 |
+
Matt Hamilton, Producer/Videographer at Hoegger Communication, puts it best: βWe approach every project as if it were our first. We maintain a mindset of wanting to exceed the clientβs expectations and treat each project as our first. I believe this passion is what distinguishes us from other video production companies and is a significant factor contributing to the growth of our video team.β
|
102 |
+
Whether you need:
|
103 |
+
β’ A corporate video to showcase your company culture
|
104 |
+
β’ A commercial to promote your latest product or service
|
105 |
+
β’ Engaging social media content to connect with your audience
|
106 |
+
We've got you covered. We specialize in award-winning professional videography services, offering commercial videography in Wichita Falls to businesses of all sizes. Our work has been recognized with multiple ADDY Awards, a testament to our commitment to creativity, quality, and impactful storytelling in video marketing.
|
107 |
+
H2: Ready to Elevate Your Brand with Video?
|
108 |
+
Letβs start with a discovery call to see how our award-winning video production services in Wichita Falls can help you achieve your business goals. Together, weβll bring your vision to life with high-quality, engaging video content that makes an impact.
|
109 |
+
|
110 |
+
|
111 |
+
## Article 3
|
112 |
+
|
113 |
+
5 Ways Your Business Will Benefit From OTT
|
114 |
+
There is no denying that streaming services are on the rise. Successful businesses are ceasing the opportunity to use this rise of streaming platforms to help their brand grow to new heights. How are they doing this? Three simple letters. OTT.
|
115 |
+
Before we dive into the top 5 ways OTT can skyrocket your business to the top level of marketing, we should probably explain what OTT is.
|
116 |
+
What is OTT Advertising?
|
117 |
+
OTT stands for "over-the-top." It refers to an advertising method that delivers content over the Internet without the help of traditional cable, satellite, or broadcast TV services.
|
118 |
+
Examples of OTT advertising can be seen on Disney+, Max, YouTube, Apple TV, Hulu, Crunchyroll, and any of the other countless streaming platforms we have access to in 2025. These ads include:
|
119 |
+
β Pre-roll ads β Short ads that play before a video and may or may not be skipped.
|
120 |
+
|
121 |
+
β Mid-roll ads β Ads that play in the middle of a video and may or may not be skipped. These are the ads that show up at the worst time in shows and movies. Right when the killer is about to be revealed, you get hit with an ad for the new Doritos Locos Taco from Taco Bell.
|
122 |
+
|
123 |
+
β Post-roll ads β These ads play after a video and may or may not be skipped. An example of these ads can be seen on YouTube. When your video is over, a post-roll ad will typically appear before your next video plays.
|
124 |
+
|
125 |
+
β Bumper ads β Short, unskippable ads that can appear anywhere in a video. These ads appear across multiple digital platforms. Depending on the placementβ¦ At Hoegger Communications, we are known for providing the best OTT marketing services in Wichita Falls. We produce one-of-a-kind video content for our clientsβ digital advertising campaigns. If you want to climb the marketing ladder, give us a call today!...these ads can catch the attention of your audience and produce great results.
|
126 |
+
|
127 |
+
β Sponsored ads β Ads where businesses pay to have their video shown. These videos are typically not skippable.
|
128 |
+
Now that we have gone over what OTT is and some examples of these types of ads, letβs talk about how implementing OTT can help your business succeed.
|
129 |
+
What Can OTT Do for Your Business?
|
130 |
+
Businesses can see drastic increases in their sales and brand popularity with OTT. There are countless benefits to using OTT properly. The top five benefits your business can experience include:
|
131 |
+
1. Increased brand awareness β OTT has been shown to increase ad recall from consumers by 11.5 percent and brand message association by 4.3 percent. Getting the right video in front of your target audience at the right time yields higher attention rates, ultimately leading to more sales for your business.
|
132 |
+
|
133 |
+
2. Targeted advertising β Your business can ensure the right demographic is viewing your ads. Age, gender, location, and many other key demographics can be targeted in your OTT messages.
|
134 |
+
|
135 |
+
3. Cost-effective β How much your business will spend on OTT depends on a few factors, such as the popularity of the platform you want to run your ads on and whether you want prime-time spots. However, the overall cost per thousand views (CPM) is significantly lower than a commercial spot and other forms of advertising.
|
136 |
+
|
137 |
+
4. Measurable results in real-time β We know you want the most accurate data possible when running ads and campaigns. With OTT, consumer interactions are updated in real time, allowing your business to have the most accurate information on the performance of your ads.
|
138 |
+
|
139 |
+
5. Increased engagement β Using targeted demographics, you can make your ads more personal to viewers, leading to a higher chance of conversion. OTT ads can go out on multiple devices, such as smartphones, computers, laptops, and smart TVsβessentially devices that almost everyone has access to. This will increase the reach of your ads, which will produce more leads.
|
140 |
+
Affordable OTT Streaming Services for Small Business Advertising
|
141 |
+
β Tubi (Owned by Fox)
|
142 |
+
β Ad Formats: Pre-roll, mid-roll, and post-roll video ads.
|
143 |
+
β Targeting: Demographics, interests, and device targeting.
|
144 |
+
β Audience: 74+ million monthly active users.
|
145 |
+
β Why Itβs Affordable: 100% free, ad-supported, and lower CPMs compared to premium platforms.
|
146 |
+
β Best For: Small businesses, local advertisers, and brands looking for broad reach.
|
147 |
+
|
148 |
+
β Pluto TV (Owned by Paramount)
|
149 |
+
β Ad Formats: Video ads (15-30 seconds), branded content integrations.
|
150 |
+
β Targeting: Demographics, behavioral, geographic, and contextual targeting.
|
151 |
+
β Audience: 80+ million active users per month.
|
152 |
+
β Why Itβs Affordable: Free, ad-supported model with lower ad rates than subscription-based platforms.
|
153 |
+
β Best For: Brands looking for exposure on a variety of niche channels.
|
154 |
+
|
155 |
+
β Crackle (Owned by Chicken Soup for the Soul Entertainment)
|
156 |
+
β Ad Formats: Pre-roll, mid-roll video ads, and sponsorships.
|
157 |
+
β Targeting: Audience demographics, geographic location, and behavioral insights.
|
158 |
+
β Audience: 40+ million active users.
|
159 |
+
β Why Itβs Affordable: Lower competition and cost-effective CPMs.
|
160 |
+
β Best For: Niche brands and businesses looking to target entertainment-based audiences.
|
161 |
+
|
162 |
+
β Xumo (Owned by Comcast)
|
163 |
+
β Ad Formats: 15-30 second video ads, channel sponsorships.
|
164 |
+
β Targeting: Programmatic and demographic targeting.
|
165 |
+
β Audience: 10+ million monthly users.
|
166 |
+
β Why Itβs Affordable: No subscription fees, making it an attractive option for cost-conscious advertisers.
|
167 |
+
β Best For: Local businesses, startups, and brands focused on news, sports, and lifestyle audiences.
|
168 |
+
|
169 |
+
β Peacock (Free Tier) (Owned by NBCUniversal)
|
170 |
+
β Ad Formats: Traditional video ads, interactive ads, and sponsorships.
|
171 |
+
β Targeting: Interest-based, location-based, and behavioral targeting.
|
172 |
+
β Audience: 30+ million monthly active users on the free tier.
|
173 |
+
β Why Itβs Affordable: The free version of Peacock allows businesses to advertise without the high costs of premium networks.
|
174 |
+
β Best For: Businesses looking for a mix of premium content exposure and affordability.
|
175 |
+
β
|
176 |
+
Top Overall OTT Streaming Platforms for Business Advertising
|
177 |
+
1. Hulu (Owned by Disney)
|
178 |
+
β Ad Formats: Video ads (15-30 sec), interactive ads, binge ads, pause ads.
|
179 |
+
β Targeting: Advanced options including demographics, interests, viewing behavior, and even household income.
|
180 |
+
β Audience: 48+ million subscribers.
|
181 |
+
β Why Itβs Great: Hulu has a premium, engaged audience, making it highly effective for brand recall and conversions.
|
182 |
+
β Best For: Mid-to-large businesses looking for premium ad inventory and targeted reach.
|
183 |
+
|
184 |
+
2. YouTube TV (Owned by Google)
|
185 |
+
β Ad Formats: Skippable and non-skippable video ads, bumper ads, and display ads.
|
186 |
+
β Targeting: Googleβs advanced audience data, including search and YouTube behavior.
|
187 |
+
β Audience: 8+ million subscribers, but access to YouTubeβs 2+ billion users.
|
188 |
+
β Why Itβs Great: Cross-platform targeting and integration with Google Ads make YouTube TV highly efficient.
|
189 |
+
β Best For: Businesses with strong video ad creatives looking for measurable results.
|
190 |
+
|
191 |
+
3. Roku (Owned by Roku, Inc.)
|
192 |
+
β Ad Formats: Video ads, home screen takeovers, sponsored content.
|
193 |
+
β Targeting: Demographics, interests, location, and Rokuβs proprietary data.
|
194 |
+
β Audience: 70+ million active users.
|
195 |
+
β Why Itβs Great: Rokuβs data-driven targeting and high engagement rates make it a top choice.
|
196 |
+
β Best For: Brands looking to target cord-cutters and digital-first audiences.
|
197 |
+
|
198 |
+
4. Amazon Freevee (Owned by Amazon)
|
199 |
+
β Ad Formats: Pre-roll, mid-roll video ads, interactive ads, custom sponsorships.
|
200 |
+
β Targeting: Amazonβs powerful customer data, shopping habits, and demographics.
|
201 |
+
β Audience: 50+ million monthly users.
|
202 |
+
β Why Itβs Great: Access to Amazonβs ecosystem allows for precise targeting and retargeting.
|
203 |
+
β Best For: E-commerce brands, tech companies, and businesses looking to leverage Amazonβs insights.
|
204 |
+
|
205 |
+
5. Paramount+ (Owned by Paramount Global)
|
206 |
+
β Ad Formats: 15-30 sec video ads, interactive ads, sponsorships.
|
207 |
+
β Targeting: Demographics, interests, geo-targeting, and behavioral tracking.
|
208 |
+
β Audience: 60+ million subscribers (including the ad-supported tier).
|
209 |
+
β Why Itβs Great: Premium content with extensive targeting capabilities.
|
210 |
+
β Best For: Businesses looking to advertise alongside popular shows and live sports events.
|
211 |
+
|
212 |
+
Key Takeaways for OTT Advertising
|
213 |
+
Streaming is an ever-growing advertising medium, and it would be wise for you to take steps toward getting your businessβs ads displayed on these platforms as soon as possible. With countless streaming sites, there are a variety of options to choose from when deciding which platform will align with your marketing goals the best.
|
214 |
+
If you are looking for cost-effective OTT advertising, Tubi, Pluto TV, Crackle, Xumo, and Peacock Free offer affordable ad placements with broad audience reach. If targeting a premium audience with advanced ad options is your priority, platforms like Hulu, YouTube TV, Roku, Amazon Freevee, and Paramount+ provide strong engagement and measurable results.
|
215 |
+
OTT ads allow you to get your videos in front of the right audience for an affordable price. Take your business and advertising to the next level by taking advantage of all that OTT has to offer.
|
216 |
+
Ready to get Started?
|
217 |
+
If you donβt know where to start, donβt worryβTeam Hoegger is here to help! Give us a call to set up a free consultation, and we will provide a detailed marketing plan that will lay out the best OTT strategies for your business!
|
218 |
+
|
219 |
+
|
220 |
+
## Article 4
|
221 |
+
|
222 |
+
The Top 5 Types of Videos Every Business Should Have
|
223 |
+
|
224 |
+
Think of the most memorable advertisements youβve seen. Chances are youβre thinking of a commercial you couldnβt get out of your head, and maybe a famous video from years or even decades ago. Video marketing captures attention and captivates your audience, fostering deeper engagement and building lasting connections.
|
225 |
+
|
226 |
+
At Hoegger Communications, we understand that entering the business video production arena can have its own challenges. Luckily, youβve found the right group to make this daunting process fun and stress-free. As a video production company located in Wichita Falls, Texas, weβve won more than 300 awards for our video work. Weβve mastered the art of combining compelling storytelling and stunning visuals. Explore how video marketing can propel your business forward with confidence, and create a lasting impact on the future of your business.
|
227 |
+
|
228 |
+
In this blog post, weβll explore the five best types of video for marketing that your business needs to enhance brand recognition, engage your target audience, and drive traffic to the right places.
|
229 |
+
|
230 |
+
1. Explainer Videos
|
231 |
+
|
232 |
+
An explainer video simplifies more complex concepts and presents them in a user-friendly format. This powerful tool can help your potential customers gain a better understanding of your products or services. The best part? Itβs entertaining and educational at the same time.
|
233 |
+
|
234 |
+
Examples and Use Cases:
|
235 |
+
β Whiteboard animation: This is the most well-known format of explainer video, and can be used to keep the viewer engaged and focused on the message itself.
|
236 |
+
β Product demonstration: Features real people explaining the concept. This format adds a personal touch and builds trust with new customers and potential customers.
|
237 |
+
β Character animation: Uses animated characters to tell a story and explain the product or service. For complex or abstract ideas, this format can be more engaging and memorable for the viewer.
|
238 |
+
|
239 |
+
Key Benefits:
|
240 |
+
β Increased Engagement and Attention: Videos are inherently more engaging than text. They capture attention and hold it longer, especially with compelling visuals.
|
241 |
+
β Improved Understanding and Retention: They break down complex topics into simple, understandable concepts, improving comprehension.
|
242 |
+
β Enhanced Brand Awareness: Engaging videos are more likely to be shared on social media, expanding your reach and brand visibility.
|
243 |
+
|
244 |
+
2. Customer Testimonial Videos
|
245 |
+
|
246 |
+
Social proof is a powerful tool in business video production, and customer testimonials are one of the best ways to build trust and credibility. Featuring real customers while sharing their positive experiences with your product or service can make a massive impact on potential customers.
|
247 |
+
|
248 |
+
Examples and Use Cases:
|
249 |
+
β Direct-to-Camera Testimonial: A customer speaks directly to the camera about their experience. This is the most common and straightforward format, creating a personal connection with the viewer.
|
250 |
+
β Short-Form Testimonial (Social Media): Bite-sized testimonials designed for social media platforms. These are typically under a minute long and focus on a key takeaway or positive experience.
|
251 |
+
β Before And After Testimonial: The customer shares their "before" situation (the problem they faced) and their "after" situation (how your product/service helped them). This format clearly demonstrates the value and impact of your offering.
|
252 |
+
|
253 |
+
Key Benefits:
|
254 |
+
β Build Trust and Credibility: Potential customers are likely to believe other customers when discussing their positive experiences.
|
255 |
+
β Humanizing Your Brand: Testimonials put a human face on your brand, making it more relatable and approachable. They allow potential customers to connect with real people who have benefited from your products or services.
|
256 |
+
β Authenticity: Raw, unscripted testimonials feel incredibly authentic and genuine, which resonates with viewers.
|
257 |
+
|
258 |
+
3. Promotional Videos
|
259 |
+
|
260 |
+
Looking to generate excitement for a new product, service, promotion, or event? Promotional videos drive immediate action and create the buzz you need for a successful launch.
|
261 |
+
|
262 |
+
Examples and Use Cases:
|
263 |
+
β Product Launches: Generate hype and anticipation for a new product by showcasing its features and benefits. These can range from short, attention-grabbing teasers to more in-depth demonstrations.
|
264 |
+
β Seasonal Promotions: Promote special offers, discounts, and sales during holidays or specific times of the year.
|
265 |
+
β Event Promotions: Promote upcoming events, such as grand openings, anniversary celebrations, workshops, or webinars. These videos can highlight speakers, showcase the venue, and generate excitement for attending.
|
266 |
+
|
267 |
+
Key Benefits:
|
268 |
+
β Generate Excitement and Hype: Promotional videos are designed to create buzz and anticipation for your offering.
|
269 |
+
β Drive Immediate Action: They often include clear calls to action, encouraging viewers to take advantage of a limited-time offer, register for an event, or make a purchase.
|
270 |
+
β Enhance Brand Image: High-quality promotional videos can enhance your brand image and position you as a leader in your industry.
|
271 |
+
|
272 |
+
4. Social Media Videos
|
273 |
+
|
274 |
+
Social media videos are short, engaging videos designed specifically for social media platforms. They're crucial for capturing attention in fast-paced feeds, building community, and driving traffic where you need it most.
|
275 |
+
|
276 |
+
Examples and Use Cases:
|
277 |
+
β Behind-the-Scenes: Offer followers a glimpse into your company culture, daily operations, or the making of your products. This humanizes your brand and fosters connection.
|
278 |
+
β Product Demos (Short & Sweet): Quick, snappy demonstrations of your product or serviceβs key features and benefits. Focus on solving a specific problem or showcasing a unique selling point.
|
279 |
+
β Live Streams: Engage with your audience in real-time through Q&As, product launches, or behind-the-scenes glimpses. Live streams foster a sense of community and immediacy.
|
280 |
+
|
281 |
+
β Key Benefits:
|
282 |
+
β Increased Engagement and Reach: Social media videos are highly shareable, leading to increased reach and engagement with your target audience.
|
283 |
+
β Improved Brand Awareness: Consistent video content helps build brand recognition and strengthens your brand's presence on social media.
|
284 |
+
β Stronger Community Building: Engage with your audience through comments, shares, and live streams, fostering a sense of community around your brand.
|
285 |
+
|
286 |
+
5. Corporate Videos
|
287 |
+
|
288 |
+
Corporate videos are professional videos produced for a variety of internal and external communication purposes. They aim to communicate a company's message, values, and story in a compelling and engaging way. Unlike promotional videos, which often focus on sales and marketing, corporate videos have a broader range of objectives.
|
289 |
+
|
290 |
+
Examples and Use Cases:
|
291 |
+
β Company Overview/About Us: Introduce your company, its mission, history, values, and culture. These videos are often used on your websiteβs βAbout Usβ page, and for onboarding new employees.
|
292 |
+
β Recruitment Videos: Attract top talent by showcasing your company culture, employee benefits, and career opportunities. These videos can be used on job boards and company websites.
|
293 |
+
β Brand Storytelling: Tell the story of your brand, its origins, and its vision for the future. These videos connect with audiences on an emotional level and build brand loyalty.
|
294 |
+
|
295 |
+
|
296 |
+
Key Benefits:
|
297 |
+
β Enhanced Communication: Videos are a more engaging and effective way to communicate complex information compared to text or presentations.
|
298 |
+
β Stronger Brand Identity: Corporate videos help define and communicate your brand's identity, values, and culture.
|
299 |
+
β Improved Employee Engagement: Video training and internal communications can boost employee engagement and knowledge retention.
|
300 |
+
|
301 |
+
|
302 |
+
Create Impactful Video Content With Hoegger Communications
|
303 |
+
|
304 |
+
When youβre looking to transform your business with video production, we have the experience (and the awards) to help. Our team is dedicated to you and invested in your success. Letβs explore the best types of video for marketing together - it doesnβt cost anything to set up a meeting with our energetic team!
|
requirements.txt
ADDED
File without changes
|
test_apis.py
ADDED
@@ -0,0 +1,187 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
"""
|
3 |
+
API Testing Script for SEO Article Generator
|
4 |
+
Tests OpenAI and Gemini API endpoints without deploying the full app
|
5 |
+
"""
|
6 |
+
|
7 |
+
import os
|
8 |
+
import requests
|
9 |
+
import json
|
10 |
+
from dotenv import load_dotenv
|
11 |
+
import time
|
12 |
+
from typing import Dict, Any
|
13 |
+
|
14 |
+
# Load environment variables
|
15 |
+
load_dotenv()
|
16 |
+
|
17 |
+
class APITester:
|
18 |
+
def __init__(self):
|
19 |
+
self.openai_api_key = os.getenv("OPENAI_API_KEY")
|
20 |
+
self.gemini_api_key = os.getenv("GEMINI_API_KEY")
|
21 |
+
|
22 |
+
# Test prompt
|
23 |
+
self.test_prompt = "Write a short 100-word article about AI in marketing."
|
24 |
+
|
25 |
+
# Models to test
|
26 |
+
self.models = {
|
27 |
+
"gpt-4.1": "openai",
|
28 |
+
"o3": "openai",
|
29 |
+
"gemini-2.5-pro": "gemini"
|
30 |
+
}
|
31 |
+
|
32 |
+
def test_openai_api(self, model: str) -> Dict[str, Any]:
|
33 |
+
"""Test OpenAI API endpoint"""
|
34 |
+
print(f"\nπ Testing OpenAI model: {model}")
|
35 |
+
|
36 |
+
if not self.openai_api_key:
|
37 |
+
return {"success": False, "error": "OpenAI API key not found"}
|
38 |
+
|
39 |
+
try:
|
40 |
+
response = requests.post(
|
41 |
+
"https://api.openai.com/v1/chat/completions",
|
42 |
+
headers={
|
43 |
+
"Authorization": f"Bearer {self.openai_api_key}",
|
44 |
+
"Content-Type": "application/json"
|
45 |
+
},
|
46 |
+
json={
|
47 |
+
"model": model,
|
48 |
+
"messages": [
|
49 |
+
{"role": "user", "content": self.test_prompt}
|
50 |
+
],
|
51 |
+
"temperature": 0.7,
|
52 |
+
"max_tokens": 200
|
53 |
+
},
|
54 |
+
timeout=30
|
55 |
+
)
|
56 |
+
|
57 |
+
if response.status_code == 200:
|
58 |
+
result = response.json()
|
59 |
+
content = result['choices'][0]['message']['content'].strip()
|
60 |
+
return {
|
61 |
+
"success": True,
|
62 |
+
"content": content,
|
63 |
+
"tokens_used": result.get('usage', {}).get('total_tokens', 'N/A'),
|
64 |
+
"model": model
|
65 |
+
}
|
66 |
+
else:
|
67 |
+
return {
|
68 |
+
"success": False,
|
69 |
+
"error": f"HTTP {response.status_code}: {response.text}",
|
70 |
+
"model": model
|
71 |
+
}
|
72 |
+
|
73 |
+
except requests.exceptions.Timeout:
|
74 |
+
return {"success": False, "error": "Request timeout (30s)", "model": model}
|
75 |
+
except Exception as e:
|
76 |
+
return {"success": False, "error": str(e), "model": model}
|
77 |
+
|
78 |
+
def test_gemini_api(self, model: str) -> Dict[str, Any]:
|
79 |
+
"""Test Gemini API endpoint"""
|
80 |
+
print(f"\nπ Testing Gemini model: {model}")
|
81 |
+
|
82 |
+
if not self.gemini_api_key:
|
83 |
+
return {"success": False, "error": "Gemini API key not found"}
|
84 |
+
|
85 |
+
try:
|
86 |
+
response = requests.post(
|
87 |
+
f"https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent",
|
88 |
+
headers={
|
89 |
+
"Content-Type": "application/json",
|
90 |
+
"x-goog-api-key": self.gemini_api_key
|
91 |
+
},
|
92 |
+
json={
|
93 |
+
"contents": [{
|
94 |
+
"parts": [{
|
95 |
+
"text": self.test_prompt
|
96 |
+
}]
|
97 |
+
}]
|
98 |
+
},
|
99 |
+
timeout=30
|
100 |
+
)
|
101 |
+
|
102 |
+
if response.status_code == 200:
|
103 |
+
result = response.json()
|
104 |
+
content = result['candidates'][0]['content']['parts'][0]['text'].strip()
|
105 |
+
return {
|
106 |
+
"success": True,
|
107 |
+
"content": content,
|
108 |
+
"model": model
|
109 |
+
}
|
110 |
+
else:
|
111 |
+
return {
|
112 |
+
"success": False,
|
113 |
+
"error": f"HTTP {response.status_code}: {response.text}",
|
114 |
+
"model": model
|
115 |
+
}
|
116 |
+
|
117 |
+
except requests.exceptions.Timeout:
|
118 |
+
return {"success": False, "error": "Request timeout (30s)", "model": model}
|
119 |
+
except Exception as e:
|
120 |
+
return {"success": False, "error": str(e), "model": model}
|
121 |
+
|
122 |
+
def test_all_models(self):
|
123 |
+
"""Test all models and provide comprehensive report"""
|
124 |
+
print("π Starting API Testing for SEO Article Generator")
|
125 |
+
print("=" * 60)
|
126 |
+
|
127 |
+
results = {}
|
128 |
+
|
129 |
+
for model, provider in self.models.items():
|
130 |
+
start_time = time.time()
|
131 |
+
|
132 |
+
if provider == "openai":
|
133 |
+
result = self.test_openai_api(model)
|
134 |
+
elif provider == "gemini":
|
135 |
+
result = self.test_gemini_api(model)
|
136 |
+
|
137 |
+
end_time = time.time()
|
138 |
+
result["response_time"] = round(end_time - start_time, 2)
|
139 |
+
results[model] = result
|
140 |
+
|
141 |
+
# Print immediate result
|
142 |
+
if result["success"]:
|
143 |
+
print(f"β
{model}: SUCCESS ({result['response_time']}s)")
|
144 |
+
print(f" Content preview: {result['content'][:100]}...")
|
145 |
+
if 'tokens_used' in result:
|
146 |
+
print(f" Tokens used: {result['tokens_used']}")
|
147 |
+
else:
|
148 |
+
print(f"β {model}: FAILED ({result['response_time']}s)")
|
149 |
+
print(f" Error: {result['error']}")
|
150 |
+
|
151 |
+
# Summary report
|
152 |
+
print("\n" + "=" * 60)
|
153 |
+
print("π TESTING SUMMARY")
|
154 |
+
print("=" * 60)
|
155 |
+
|
156 |
+
successful = [m for m, r in results.items() if r["success"]]
|
157 |
+
failed = [m for m, r in results.items() if not r["success"]]
|
158 |
+
|
159 |
+
print(f"β
Successful: {len(successful)}/{len(self.models)} models")
|
160 |
+
print(f"β Failed: {len(failed)}/{len(self.models)} models")
|
161 |
+
|
162 |
+
if successful:
|
163 |
+
print(f"\nπ Working models: {', '.join(successful)}")
|
164 |
+
avg_time = sum(results[m]["response_time"] for m in successful) / len(successful)
|
165 |
+
print(f"β‘ Average response time: {avg_time:.2f}s")
|
166 |
+
|
167 |
+
if failed:
|
168 |
+
print(f"\nβ οΈ Failed models: {', '.join(failed)}")
|
169 |
+
print("\nError details:")
|
170 |
+
for model in failed:
|
171 |
+
print(f" β’ {model}: {results[model]['error']}")
|
172 |
+
|
173 |
+
return results
|
174 |
+
|
175 |
+
def main():
|
176 |
+
"""Main testing function"""
|
177 |
+
tester = APITester()
|
178 |
+
results = tester.test_all_models()
|
179 |
+
|
180 |
+
# Save results to file for later reference
|
181 |
+
with open("api_test_results.json", "w") as f:
|
182 |
+
json.dump(results, f, indent=2)
|
183 |
+
|
184 |
+
print(f"\nπΎ Detailed results saved to: api_test_results.json")
|
185 |
+
|
186 |
+
if __name__ == "__main__":
|
187 |
+
main()
|