ihtesham0345 commited on
Commit
38098b4
·
1 Parent(s): 30aa031

feat: Add multi-platform social media analysis with 7 platform-specific endpoints

Browse files

- Add 7 new platform analyzers: YouTube, Instagram, LinkedIn, Facebook, X/Twitter, TikTok, Pinterest
- Create shared model loader with .env configurable MODEL_ID (0.5B/1.5B/7B)
- Refactor services with shared JSON repair and validation utilities
- Update schemas with 7 platform-specific Pydantic response models
- Add temperature tuning per platform (precise 0.3 to creative 0.45)
- Implement professional error handling with safe_analyze wrapper
- Add .env.example for model configuration documentation
- Keep backward-compatible original /analyze-seo route

.env.example ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ # AI Model Configuration
2
+ # Options:
3
+ # Qwen/Qwen2.5-0.5B-Instruct (Nano - ~1GB, fastest, CPU-friendly)
4
+ # Qwen/Qwen2.5-1.5B-Instruct (Goldilocks - ~3GB, recommended for GPU)
5
+ # Qwen/Qwen2.5-7B-Instruct (Smartest - needs 4-bit quantization + GPU)
6
+ MODEL_ID=Qwen/Qwen2.5-0.5B-Instruct
.gitignore CHANGED
@@ -3,3 +3,5 @@ __pycache__/
3
  .env
4
  venv/
5
  .DS_Store
 
 
 
3
  .env
4
  venv/
5
  .DS_Store
6
+ *.safetensors
7
+ *.bin
main.py CHANGED
@@ -1,38 +1,115 @@
1
  from fastapi import FastAPI, HTTPException
2
- from models.schemas import SEORequest, SEOResponse
 
 
 
 
3
  from services.analyzer import analyze_seo_content
 
 
 
 
 
 
 
4
  import uvicorn
5
- import os
6
 
7
  app = FastAPI(
8
- title="SEO Keyword Analyzer API",
9
- description="Professional SEO Analysis using Generative AI",
10
- version="1.0.0"
11
  )
12
 
13
- @app.get("/")
14
- def read_root():
15
- return {"status": "active", "service": "SEO Analyzer API"}
16
 
17
- @app.post("/analyze-seo", response_model=SEOResponse)
18
- def analyze_seo(request: SEORequest):
19
- """
20
- Analyzes the provided content and returns a detailed SEO strategy.
21
- """
22
  try:
23
- print(f"📥 Received request: {request.content[:50]}...")
24
- result = analyze_seo_content(request.content)
25
-
26
- if "error" in result and result["error"]:
27
- print(f"❌ Logic Error: {result['error']}")
28
  raise HTTPException(status_code=500, detail=str(result["error"]))
29
-
30
  return result
 
 
31
  except Exception as e:
32
- import traceback
33
- error_msg = f"CRITICAL SERVER ERROR: {str(e)}\n{traceback.format_exc()}"
34
- print(error_msg)
35
- raise HTTPException(status_code=500, detail=error_msg)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
  if __name__ == "__main__":
38
  uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
 
1
  from fastapi import FastAPI, HTTPException
2
+ from models.schemas import (
3
+ SEORequest, SEOResponse,
4
+ YouTubeResponse, InstagramResponse, LinkedInResponse,
5
+ FacebookResponse, TwitterResponse, TikTokResponse, PinterestResponse
6
+ )
7
  from services.analyzer import analyze_seo_content
8
+ from services.youtube_analyzer import analyze_youtube
9
+ from services.instagram_analyzer import analyze_instagram
10
+ from services.linkedin_analyzer import analyze_linkedin
11
+ from services.facebook_analyzer import analyze_facebook
12
+ from services.twitter_analyzer import analyze_twitter
13
+ from services.tiktok_analyzer import analyze_tiktok
14
+ from services.pinterest_analyzer import analyze_pinterest
15
  import uvicorn
16
+ import traceback
17
 
18
  app = FastAPI(
19
+ title="Social Media SEO Analyzer API",
20
+ description="Professional social media content analysis using Generative AI. Covers YouTube, Instagram, LinkedIn, Facebook, X/Twitter, TikTok, and Pinterest.",
21
+ version="2.0.0"
22
  )
23
 
24
+ ERROR_MASK = "An internal error occurred. Please try again later."
 
 
25
 
26
+ def safe_analyze(analyze_fn, content: str):
 
 
 
 
27
  try:
28
+ result = analyze_fn(content)
29
+ if isinstance(result, dict) and result.get("error"):
 
 
 
30
  raise HTTPException(status_code=500, detail=str(result["error"]))
 
31
  return result
32
+ except HTTPException:
33
+ raise
34
  except Exception as e:
35
+ print(f"CRITICAL SERVER ERROR: {e}\n{traceback.format_exc()}")
36
+ raise HTTPException(status_code=500, detail=ERROR_MASK)
37
+
38
+ # ---------------------------------------------------------------------------
39
+ # Root
40
+ # ---------------------------------------------------------------------------
41
+
42
+ @app.get("/")
43
+ def read_root():
44
+ return {"status": "active", "service": "Social Media SEO Analyzer API", "version": "2.0.0"}
45
+
46
+ # ---------------------------------------------------------------------------
47
+ # Generic SEO
48
+ # ---------------------------------------------------------------------------
49
+
50
+ @app.post("/analyze-seo", response_model=SEOResponse)
51
+ def analyze_seo(request: SEORequest):
52
+ return safe_analyze(analyze_seo_content, request.content)
53
+
54
+ # ---------------------------------------------------------------------------
55
+ # YouTube
56
+ # ---------------------------------------------------------------------------
57
+
58
+ @app.post("/analyze/youtube", response_model=YouTubeResponse)
59
+ def analyze_youtube_route(request: SEORequest):
60
+ return safe_analyze(analyze_youtube, request.content)
61
+
62
+ # ---------------------------------------------------------------------------
63
+ # Instagram
64
+ # ---------------------------------------------------------------------------
65
+
66
+ @app.post("/analyze/instagram", response_model=InstagramResponse)
67
+ def analyze_instagram_route(request: SEORequest):
68
+ return safe_analyze(analyze_instagram, request.content)
69
+
70
+ # ---------------------------------------------------------------------------
71
+ # LinkedIn
72
+ # ---------------------------------------------------------------------------
73
+
74
+ @app.post("/analyze/linkedin", response_model=LinkedInResponse)
75
+ def analyze_linkedin_route(request: SEORequest):
76
+ return safe_analyze(analyze_linkedin, request.content)
77
+
78
+ # ---------------------------------------------------------------------------
79
+ # Facebook
80
+ # ---------------------------------------------------------------------------
81
+
82
+ @app.post("/analyze/facebook", response_model=FacebookResponse)
83
+ def analyze_facebook_route(request: SEORequest):
84
+ return safe_analyze(analyze_facebook, request.content)
85
+
86
+ # ---------------------------------------------------------------------------
87
+ # X / Twitter
88
+ # ---------------------------------------------------------------------------
89
+
90
+ @app.post("/analyze/twitter", response_model=TwitterResponse)
91
+ def analyze_twitter_route(request: SEORequest):
92
+ return safe_analyze(analyze_twitter, request.content)
93
+
94
+ # ---------------------------------------------------------------------------
95
+ # TikTok
96
+ # ---------------------------------------------------------------------------
97
+
98
+ @app.post("/analyze/tiktok", response_model=TikTokResponse)
99
+ def analyze_tiktok_route(request: SEORequest):
100
+ return safe_analyze(analyze_tiktok, request.content)
101
+
102
+ # ---------------------------------------------------------------------------
103
+ # Pinterest
104
+ # ---------------------------------------------------------------------------
105
+
106
+ @app.post("/analyze/pinterest", response_model=PinterestResponse)
107
+ def analyze_pinterest_route(request: SEORequest):
108
+ return safe_analyze(analyze_pinterest, request.content)
109
+
110
+ # ---------------------------------------------------------------------------
111
+ # Entrypoint
112
+ # ---------------------------------------------------------------------------
113
 
114
  if __name__ == "__main__":
115
  uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
models/schemas.py CHANGED
@@ -1,6 +1,10 @@
1
  from pydantic import BaseModel
2
  from typing import List, Optional
3
 
 
 
 
 
4
  class KeywordData(BaseModel):
5
  keyword: str
6
  search_volume: str
@@ -22,3 +26,132 @@ class SEOResponse(BaseModel):
22
  content_titles: List[str]
23
  target_audience: str
24
  error: Optional[str] = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from pydantic import BaseModel
2
  from typing import List, Optional
3
 
4
+ # ---------------------------------------------------------------------------
5
+ # Existing generic SEO
6
+ # ---------------------------------------------------------------------------
7
+
8
  class KeywordData(BaseModel):
9
  keyword: str
10
  search_volume: str
 
26
  content_titles: List[str]
27
  target_audience: str
28
  error: Optional[str] = None
29
+
30
+ # ---------------------------------------------------------------------------
31
+ # YouTube
32
+ # ---------------------------------------------------------------------------
33
+
34
+ class VideoTitle(BaseModel):
35
+ title: str
36
+ expected_ctr: str
37
+
38
+ class YouTubeResponse(BaseModel):
39
+ video_titles: List[VideoTitle]
40
+ tags: List[str]
41
+ description_template: str
42
+ thumbnail_ideas: List[str]
43
+ best_posting_time: str
44
+ engagement_strategies: List[str]
45
+ competition_gap_analysis: str
46
+ error: Optional[str] = None
47
+
48
+ # ---------------------------------------------------------------------------
49
+ # Instagram
50
+ # ---------------------------------------------------------------------------
51
+
52
+ class CaptionData(BaseModel):
53
+ caption: str
54
+ tone: str
55
+
56
+ class HashtagSets(BaseModel):
57
+ small: List[str]
58
+ medium: List[str]
59
+ large: List[str]
60
+
61
+ class ContentIdeas(BaseModel):
62
+ reels: List[str]
63
+ carousels: List[str]
64
+ stories: List[str]
65
+
66
+ class InstagramResponse(BaseModel):
67
+ captions: List[CaptionData]
68
+ hashtag_sets: HashtagSets
69
+ content_ideas: ContentIdeas
70
+ best_posting_time: str
71
+ growth_strategies: List[str]
72
+ error: Optional[str] = None
73
+
74
+ # ---------------------------------------------------------------------------
75
+ # LinkedIn
76
+ # ---------------------------------------------------------------------------
77
+
78
+ class PostDraft(BaseModel):
79
+ headline: str
80
+ body: str
81
+ hook: str
82
+
83
+ class LinkedInResponse(BaseModel):
84
+ post_drafts: List[PostDraft]
85
+ hashtags: List[str]
86
+ article_topics: List[str]
87
+ thought_leadership_angles: List[str]
88
+ best_posting_time: str
89
+ industry_insights: str
90
+ error: Optional[str] = None
91
+
92
+ # ---------------------------------------------------------------------------
93
+ # Facebook
94
+ # ---------------------------------------------------------------------------
95
+
96
+ class PostIdea(BaseModel):
97
+ type: str
98
+ content: str
99
+
100
+ class FacebookResponse(BaseModel):
101
+ post_ideas: List[PostIdea]
102
+ engagement_hooks: List[str]
103
+ hashtags: List[str]
104
+ ad_copy_suggestions: List[str]
105
+ best_posting_time: str
106
+ page_growth_tips: List[str]
107
+ error: Optional[str] = None
108
+
109
+ # ---------------------------------------------------------------------------
110
+ # X / Twitter
111
+ # ---------------------------------------------------------------------------
112
+
113
+ class TweetThread(BaseModel):
114
+ tweets: List[str]
115
+ theme: str
116
+
117
+ class TwitterResponse(BaseModel):
118
+ tweet_threads: List[TweetThread]
119
+ viral_hooks: List[str]
120
+ hashtags: List[str]
121
+ best_posting_time: str
122
+ engagement_tactics: List[str]
123
+ error: Optional[str] = None
124
+
125
+ # ---------------------------------------------------------------------------
126
+ # TikTok
127
+ # ---------------------------------------------------------------------------
128
+
129
+ class VideoConcept(BaseModel):
130
+ hook: str
131
+ script_snippet: str
132
+ sound_suggestion: str
133
+
134
+ class TikTokResponse(BaseModel):
135
+ video_concepts: List[VideoConcept]
136
+ trending_angles: List[str]
137
+ hashtags: List[str]
138
+ best_posting_time: str
139
+ viral_strategies: List[str]
140
+ error: Optional[str] = None
141
+
142
+ # ---------------------------------------------------------------------------
143
+ # Pinterest
144
+ # ---------------------------------------------------------------------------
145
+
146
+ class PinIdea(BaseModel):
147
+ title: str
148
+ description: str
149
+ keyword_focus: str
150
+
151
+ class PinterestResponse(BaseModel):
152
+ pin_ideas: List[PinIdea]
153
+ board_organization: List[str]
154
+ seo_keywords: List[str]
155
+ best_posting_time: str
156
+ traffic_strategies: List[str]
157
+ error: Optional[str] = None
services/analyzer.py CHANGED
@@ -1,160 +1,49 @@
1
- import os
2
- import json
3
- import torch
4
- from transformers import pipeline, AutoModelForCausalLM, AutoTokenizer
5
- from dotenv import load_dotenv
6
- from pathlib import Path
7
-
8
- # Load environment variables
9
- env_path = Path(__file__).resolve().parent.parent.parent / ".env"
10
- load_dotenv(dotenv_path=env_path)
11
-
12
- # Configuration: Run Locally to bypass all API limits/errors
13
- # Qwen-0.5B is the "Nano" model - Ultra Fast, designed for Mobile/Limitless CPU use.
14
- MODEL_ID = "Qwen/Qwen2.5-0.5B-Instruct"
15
-
16
- print(f"⏳ Loading Local Model {MODEL_ID}... (This happens once)")
17
- try:
18
- # Initialize Local Pipeline
19
- # optimization: torch_dtype=torch.float16 reduces RAM usage significantly
20
- # device_map="auto" uses CPU or GPU if available
21
- pipe = pipeline(
22
- "text-generation",
23
- model=MODEL_ID,
24
- torch_dtype=torch.bfloat16, # Efficient for CPU
25
- device_map="auto",
26
- trust_remote_code=True
27
- )
28
- print("✅ Model Loaded Successfully!")
29
- except Exception as e:
30
- print(f"❌ Model Load Failed: {e}")
31
- pipe = None
32
-
33
- def clean_json_string(text: str) -> str:
34
- """Helper to extract JSON from markdown or raw text."""
35
- if "```json" in text:
36
- text = text.split("```json")[1].split("```")[0]
37
- elif "```" in text:
38
- text = text.split("```")[1].split("```")[0]
39
- return text.strip()
40
-
41
- def repair_json(json_str: str) -> str:
42
- """Attempt to repair truncated JSON by closing open brackets/braces."""
43
- json_str = json_str.strip()
44
-
45
- # Remove potentially invalid trailing content that isn't a closer
46
- # (e.g. if it ends with "keyword": "val", -> we need to remove comma)
47
- json_str = json_str.rstrip(", ")
48
-
49
- # Balance brackets
50
- open_braces = json_str.count("{")
51
- close_braces = json_str.count("}")
52
- open_brackets = json_str.count("[")
53
- close_brackets = json_str.count("]")
54
-
55
- if open_braces > close_braces:
56
- json_str += "}" * (open_braces - close_braces)
57
-
58
- if open_brackets > close_brackets:
59
- json_str += "]" * (open_brackets - close_brackets)
60
-
61
- return json_str
62
-
63
- def validate_and_fill_data(data: dict) -> dict:
64
- """Ensure all required fields exist in the response."""
65
- defaults = {
66
- "core_keywords": [],
67
- "related_phrases": [],
68
- "viral_hashtags": [],
69
- "content_titles": ["Content Strategy Guide", "SEO Optimization Tips", "Viral Content Ideas"], # Fallbacks
70
- "strategy_tips": ["Focus on user intent.", "Optimize for long-tail keywords.", "Improve meta tags."], # Fallbacks
71
- "target_audience": "General Audience interested in this topic."
72
- }
73
-
74
- for key, default_val in defaults.items():
75
- if key not in data or data[key] is None:
76
- data[key] = default_val
77
-
78
- return data
79
 
80
  def analyze_seo_content(content: str) -> dict:
81
- """
82
- Analyzes content using LOCAL Qwen-0.5B.
83
- """
84
- if pipe is None:
85
- return {"error": "Model failed to load on server startup. Check logs."}
86
-
87
- print(f"🧠 Running Local Inference on: {content[:50]}...")
88
-
89
- # Qwen Chat Template
90
  messages = [
91
- {
92
- "role": "system",
93
- "content": """You are a World-Class SEO Strategist. Return ONLY valid JSON.
94
- RULES:
95
- 1. Estimate 'search_volume' (High/Medium/Low).
96
- 2. 'competition' (Hard/Medium/Easy).
97
- 3. 'relevance' (0-100)."""
98
- },
99
- {
100
- "role": "user",
101
- "content": f"""Analyze topic: "{content[:2000]}"
102
-
103
- REQUIREMENTS:
104
- - **Core Keywords**: 30 high-value keywords.
105
- - **Related Phrases**: 10 variations.
106
- - **Viral Hashtags**: 8 tags.
107
- - **Content Titles**: 3 titles.
108
- - **Strategy Tips**: 3 tips.
109
- - **Target Audience**: Profile.
110
-
111
- JSON OUTPUT STRUCTURE:
112
- {{
113
- "core_keywords": [
114
- {{"keyword": "...", "search_volume": "...", "competition": "...", "relevance": 90}}, ... (Total 30 items)
115
- ],
116
- "related_phrases": ["..."],
117
- "viral_hashtags": [{{"tag": "#...", "post_count": "..."}}],
118
- "content_titles": ["..."],
119
- "strategy_tips": ["..."],
120
- "target_audience": "..."
121
- }}
122
- """
123
- }
124
  ]
125
 
126
- try:
127
- # Qwen/HuggingFace pipeline handles chat templates automatically
128
- outputs = pipe(
129
- messages,
130
- max_new_tokens=1500,
131
- do_sample=True,
132
- temperature=0.3, # Low temp for strict JSON
133
- top_p=0.9
134
- )
135
-
136
- content_text = outputs[0]["generated_text"][-1]["content"]
137
- cleaned_json = clean_json_string(content_text)
138
-
139
- try:
140
- data = json.loads(cleaned_json)
141
- except json.JSONDecodeError:
142
- print("⚠️ JSON Parse Error. Attempting repair...")
143
- cleaned_json = repair_json(cleaned_json)
144
- try:
145
- data = json.loads(cleaned_json)
146
- except json.JSONDecodeError as final_err:
147
- print(f"❌ Repair Failed: {final_err}")
148
- return {
149
- "error": "Optimization Failed. The model generated invalid JSON.",
150
- "raw_output": content_text[:500] + "..." # Debug info
151
- }
152
-
153
- # Ensure Schema Compliance
154
- data = validate_and_fill_data(data)
155
-
156
  return data
157
 
158
- except Exception as e:
159
- print(f"❌ Inference Error: {e}")
160
- return {"error": f"Local AI Failed: {str(e)}"}
 
1
+ from services.utils import run_analysis, validate_and_fill_data_defaults
2
+
3
+ SYSTEM_PROMPT = """You are a World-Class SEO Strategist. Return ONLY valid JSON.
4
+ RULES:
5
+ 1. Estimate 'search_volume' (High/Medium/Low).
6
+ 2. 'competition' (Hard/Medium/Easy).
7
+ 3. 'relevance' (0-100)."""
8
+
9
+ DEFAULTS = {
10
+ "core_keywords": [],
11
+ "related_phrases": [],
12
+ "viral_hashtags": [],
13
+ "content_titles": ["Content Strategy Guide", "SEO Optimization Tips", "Viral Content Ideas"],
14
+ "strategy_tips": ["Focus on user intent.", "Optimize for long-tail keywords.", "Improve meta tags."],
15
+ "target_audience": "General Audience interested in this topic."
16
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
  def analyze_seo_content(content: str) -> dict:
 
 
 
 
 
 
 
 
 
19
  messages = [
20
+ {"role": "system", "content": SYSTEM_PROMPT},
21
+ {"role": "user", "content": f"""Analyze topic: "{content[:2000]}"
22
+
23
+ REQUIREMENTS:
24
+ - **Core Keywords**: 30 high-value keywords.
25
+ - **Related Phrases**: 10 variations.
26
+ - **Viral Hashtags**: 8 tags.
27
+ - **Content Titles**: 3 titles.
28
+ - **Strategy Tips**: 3 tips.
29
+ - **Target Audience**: Profile.
30
+
31
+ JSON OUTPUT STRUCTURE:
32
+ {{
33
+ "core_keywords": [
34
+ {{"keyword": "...", "search_volume": "...", "competition": "...", "relevance": 90}}, ... (Total 30 items)
35
+ ],
36
+ "related_phrases": ["..."],
37
+ "viral_hashtags": [{{"tag": "#...", "post_count": "..."}}],
38
+ "content_titles": ["..."],
39
+ "strategy_tips": ["..."],
40
+ "target_audience": "..."
41
+ }}"""}
 
 
 
 
 
 
 
 
 
 
 
42
  ]
43
 
44
+ data = run_analysis(messages, temperature=0.3)
45
+ if isinstance(data, dict) and "error" in data:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  return data
47
 
48
+ # Apply defaults for missing fields
49
+ return validate_and_fill_data_defaults(data, DEFAULTS)
 
services/facebook_analyzer.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from services.utils import run_analysis, validate_and_fill_data_defaults
2
+
3
+ SYSTEM_PROMPT = """You are a Facebook Marketing Expert specializing in organic reach, Facebook Groups, Page growth, and advertising psychology. You understand the Facebook algorithm, meaningful interactions, and ad copy optimization. Return ONLY valid JSON.
4
+
5
+ RULES:
6
+ 1. Post ideas must specify content type: text, image, video, link, poll, live
7
+ 2. Engagement hooks must target emotional triggers (curiosity, outrage, inspiration, humor, nostalgia)
8
+ 3. Ad copy must follow AIDA framework (Attention, Interest, Desire, Action)
9
+ 4. Hashtags: 3-5 relevant tags maximum
10
+ 5. Facebook algorithm prioritizes meaningful interactions (comments > shares > reactions)
11
+ 6. Native video gets 10x more reach than external links"""
12
+
13
+ DEFAULTS = {
14
+ "post_ideas": [],
15
+ "engagement_hooks": [],
16
+ "hashtags": [],
17
+ "ad_copy_suggestions": [],
18
+ "best_posting_time": "1-3 PM EST on weekdays",
19
+ "page_growth_tips": ["Post consistently", "Engage in relevant Groups", "Go live weekly"]
20
+ }
21
+
22
+ def analyze_facebook(content: str) -> dict:
23
+ messages = [
24
+ {"role": "system", "content": SYSTEM_PROMPT},
25
+ {"role": "user", "content": f"""Analyze topic for Facebook strategy: "{content[:2000]}"
26
+
27
+ REQUIREMENTS:
28
+ - **Post Ideas**: 8 post ideas with type (text/image/video/link/poll/live) and full content
29
+ - **Engagement Hooks**: 10 hooks targeting different emotional triggers
30
+ - **Hashtags**: 5 optimized hashtags
31
+ - **Ad Copy Suggestions**: 3 ad copies following AIDA framework
32
+ - **Best Posting Time**: Optimal day and time
33
+ - **Page Growth Tips**: 5 actionable tips
34
+
35
+ JSON OUTPUT STRUCTURE:
36
+ {{
37
+ "post_ideas": [
38
+ {{"type": "video", "content": "..."}}, ... (Total 8 items)
39
+ ],
40
+ "engagement_hooks": ["..."],
41
+ "hashtags": ["..."],
42
+ "ad_copy_suggestions": ["..."],
43
+ "best_posting_time": "...",
44
+ "page_growth_tips": ["..."]
45
+ }}"""}
46
+ ]
47
+ data = run_analysis(messages, temperature=0.35, max_new_tokens=2000)
48
+ if isinstance(data, dict) and "error" in data:
49
+ return data
50
+ return validate_and_fill_data_defaults(data, DEFAULTS)
services/instagram_analyzer.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from services.utils import run_analysis, validate_and_fill_data_defaults
2
+
3
+ SYSTEM_PROMPT = """You are a top Instagram Growth Strategist and Content Creator. You specialize in the Instagram algorithm, visual aesthetics, Reels, Carousels, Stories, and viral marketing. Return ONLY valid JSON.
4
+
5
+ RULES:
6
+ 1. Captions must have a strong hook in first line, value in middle, CTA at end
7
+ 2. Hash tag sets must be tiered: small (10k-50k posts), medium (50k-500k), large (500k+)
8
+ 3. Content ideas must specify format: Reel (<90s), Carousel (5-10 slides), Story (interactive)
9
+ 4. Growth strategies focused on: collaboration, trending audio, consistent posting, engagement pods
10
+ 5. Instagram algorithm prioritizes: saves > shares > comments > likes > reach"""
11
+
12
+ DEFAULTS = {
13
+ "captions": [],
14
+ "hashtag_sets": {"small": [], "medium": [], "large": []},
15
+ "content_ideas": {"reels": [], "carousels": [], "stories": []},
16
+ "best_posting_time": "9-11 AM EST on weekdays",
17
+ "growth_strategies": ["Post consistently", "Use trending audio", "Engage with niche community"]
18
+ }
19
+
20
+ def analyze_instagram(content: str) -> dict:
21
+ messages = [
22
+ {"role": "system", "content": SYSTEM_PROMPT},
23
+ {"role": "user", "content": f"""Analyze topic for Instagram strategy: "{content[:2000]}"
24
+
25
+ REQUIREMENTS:
26
+ - **Captions**: 5 full captions with different tones (educational, entertaining, inspirational, behind-the-scenes, promotional)
27
+ - **Hashtag Sets**: 10 small-tier, 10 medium-tier, 10 large-tier hashtags
28
+ - **Content Ideas**: 4 Reel concepts, 4 Carousel ideas, 4 Story concepts
29
+ - **Best Posting Time**: Optimal day and time
30
+ - **Growth Strategies**: 5 actionable strategies
31
+
32
+ JSON OUTPUT STRUCTURE:
33
+ {{
34
+ "captions": [
35
+ {{"caption": "...", "tone": "..."}}, ... (Total 5 items)
36
+ ],
37
+ "hashtag_sets": {{
38
+ "small": ["#...", "#..."],
39
+ "medium": ["#...", "#..."],
40
+ "large": ["#...", "#..."]
41
+ }},
42
+ "content_ideas": {{
43
+ "reels": ["..."],
44
+ "carousels": ["..."],
45
+ "stories": ["..."]
46
+ }},
47
+ "best_posting_time": "...",
48
+ "growth_strategies": ["..."]
49
+ }}"""}
50
+ ]
51
+ data = run_analysis(messages, temperature=0.4, max_new_tokens=2000)
52
+ if isinstance(data, dict) and "error" in data:
53
+ return data
54
+ return validate_and_fill_data_defaults(data, DEFAULTS)
services/linkedin_analyzer.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from services.utils import run_analysis, validate_and_fill_data_defaults
2
+
3
+ SYSTEM_PROMPT = """You are a distinguished LinkedIn Content Strategist and Personal Branding Expert. You specialize in B2B networking, professional storytelling, thought leadership, and the LinkedIn algorithm. Return ONLY valid JSON.
4
+
5
+ RULES:
6
+ 1. Post drafts must follow proven LinkedIn frameworks (hook → story → insight → CTA)
7
+ 2. Headlines must be curiosity-driven or contrarian within professional bounds
8
+ 3. Hashtags: max 5, mix of broad industry + niche specific
9
+ 4. Article topics should position the author as a thought leader
10
+ 5. Best post length: 900-1200 characters
11
+ 6. LinkedIn algorithm favors: dwell time > comments > reactions > shares"""
12
+
13
+ DEFAULTS = {
14
+ "post_drafts": [],
15
+ "hashtags": [],
16
+ "article_topics": [],
17
+ "thought_leadership_angles": [],
18
+ "best_posting_time": "7-9 AM EST on weekdays (Tue-Thu best)",
19
+ "industry_insights": "Focus on providing unique data-driven perspectives in your niche."
20
+ }
21
+
22
+ def analyze_linkedin(content: str) -> dict:
23
+ messages = [
24
+ {"role": "system", "content": SYSTEM_PROMPT},
25
+ {"role": "user", "content": f"""Analyze topic for LinkedIn strategy: "{content[:2000]}"
26
+
27
+ REQUIREMENTS:
28
+ - **Post Drafts**: 5 full LinkedIn posts with headline, body, and hook
29
+ - **Hashtags**: 5 optimized hashtags
30
+ - **Article Topics**: 5 long-form article topic ideas
31
+ - **Thought Leadership Angles**: 5 unique perspectives/angles
32
+ - **Best Posting Time**: Optimal day and time
33
+ - **Industry Insights**: 1 paragraph of key industry observations
34
+
35
+ JSON OUTPUT STRUCTURE:
36
+ {{
37
+ "post_drafts": [
38
+ {{"headline": "...", "body": "...", "hook": "..."}}, ... (Total 5 items)
39
+ ],
40
+ "hashtags": ["..."],
41
+ "article_topics": ["..."],
42
+ "thought_leadership_angles": ["..."],
43
+ "best_posting_time": "...",
44
+ "industry_insights": "..."
45
+ }}"""}
46
+ ]
47
+ data = run_analysis(messages, temperature=0.3, max_new_tokens=2000)
48
+ if isinstance(data, dict) and "error" in data:
49
+ return data
50
+ return validate_and_fill_data_defaults(data, DEFAULTS)
services/model_loader.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import torch
3
+ from transformers import pipeline
4
+ from dotenv import load_dotenv
5
+ from pathlib import Path
6
+
7
+ env_path = Path(__file__).resolve().parent.parent / ".env"
8
+ load_dotenv(dotenv_path=env_path)
9
+
10
+ MODEL_ID = os.getenv("MODEL_ID", "Qwen/Qwen2.5-0.5B-Instruct")
11
+
12
+ _pipe = None
13
+
14
+ def get_pipe():
15
+ global _pipe
16
+ if _pipe is None:
17
+ print(f"⏳ Loading Local Model {MODEL_ID}...")
18
+ try:
19
+ _pipe = pipeline(
20
+ "text-generation",
21
+ model=MODEL_ID,
22
+ torch_dtype=torch.bfloat16,
23
+ device_map="auto",
24
+ trust_remote_code=True
25
+ )
26
+ print("✅ Model Loaded Successfully!")
27
+ except Exception as e:
28
+ print(f"❌ Model Load Failed: {e}")
29
+ _pipe = None
30
+ return _pipe
31
+
32
+ def generate_text(messages, temperature=0.3, max_new_tokens=2000):
33
+ pipe = get_pipe()
34
+ if pipe is None:
35
+ return None
36
+ outputs = pipe(
37
+ messages,
38
+ max_new_tokens=max_new_tokens,
39
+ do_sample=True,
40
+ temperature=temperature,
41
+ top_p=0.9
42
+ )
43
+ return outputs[0]["generated_text"][-1]["content"]
services/pinterest_analyzer.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from services.utils import run_analysis, validate_and_fill_data_defaults
2
+
3
+ SYSTEM_PROMPT = """You are a Pinterest SEO Specialist and Visual Marketing Expert who understands how to optimize Pins for search discovery, board strategy, and traffic generation. Return ONLY valid JSON.
4
+
5
+ RULES:
6
+ 1. Pin ideas must have keyword-optimized titles and descriptions
7
+ 2. Board organization: categorize by theme, audience, and funnel stage
8
+ 3. SEO keywords: focus on long-tail, search-intent matching keywords
9
+ 4. Pinterest is a VISUAL SEARCH ENGINE, not social media
10
+ 5. Vertical 2:3 aspect ratio (1000 x 1500px) performs best
11
+ 6. Keyword-rich descriptions in every Pin
12
+ 7. Fresh content prioritized by algorithm"""
13
+
14
+ DEFAULTS = {
15
+ "pin_ideas": [],
16
+ "board_organization": [],
17
+ "seo_keywords": [],
18
+ "best_posting_time": "8-11 PM EST on weekends",
19
+ "traffic_strategies": ["Create Rich Pins", "Join group boards", "Use Idea Pins", "Seasonal planning"]
20
+ }
21
+
22
+ def analyze_pinterest(content: str) -> dict:
23
+ messages = [
24
+ {"role": "system", "content": SYSTEM_PROMPT},
25
+ {"role": "user", "content": f"""Analyze topic for Pinterest strategy: "{content[:2000]}"
26
+
27
+ REQUIREMENTS:
28
+ - **Pin Ideas**: 10 pins with keyword-optimized title, description, and keyword focus
29
+ - **Board Organization**: 5 board name suggestions with descriptions
30
+ - **SEO Keywords**: 20 long-tail keywords
31
+ - **Best Posting Time**: Optimal day and time
32
+ - **Traffic Strategies**: 5 strategies for driving traffic from Pinterest
33
+
34
+ JSON OUTPUT STRUCTURE:
35
+ {{
36
+ "pin_ideas": [
37
+ {{"title": "...", "description": "...", "keyword_focus": "..."}}, ... (Total 10 items)
38
+ ],
39
+ "board_organization": ["..."],
40
+ "seo_keywords": ["..."],
41
+ "best_posting_time": "...",
42
+ "traffic_strategies": ["..."]
43
+ }}"""}
44
+ ]
45
+ data = run_analysis(messages, temperature=0.3, max_new_tokens=2000)
46
+ if isinstance(data, dict) and "error" in data:
47
+ return data
48
+ return validate_and_fill_data_defaults(data, DEFAULTS)
services/tiktok_analyzer.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from services.utils import run_analysis, validate_and_fill_data_defaults
2
+
3
+ SYSTEM_PROMPT = """You are a Viral TikTok Strategist who deeply understands the For You Page (FYP) algorithm, trend culture, and short-form video psychology. Return ONLY valid JSON.
4
+
5
+ RULES:
6
+ 1. Video concepts must specify: hook (first 3 seconds), script snippet, and trending sound suggestion
7
+ 2. Trending angles must reference current content patterns (day in life, tutorials, POV, commentary, transitions)
8
+ 3. Hashtags: 3-5 mix of trending, niche, and broad
9
+ 4. Hooks should create curiosity gaps with 80%+ estimated retention rates
10
+ 5. TikTok algorithm: completion rate > rewatches > shares > comments > likes
11
+ 6. Vertical 9:16 video, 21-60 seconds optimal length
12
+ 7. First 2-3 seconds are CRITICAL for retention"""
13
+
14
+ DEFAULTS = {
15
+ "video_concepts": [],
16
+ "trending_angles": [],
17
+ "hashtags": [],
18
+ "best_posting_time": "7-9 AM or 7-10 PM local time",
19
+ "viral_strategies": ["Participate in trending challenges", "Use trending sounds", "Post 1-3x daily"]
20
+ }
21
+
22
+ def analyze_tiktok(content: str) -> dict:
23
+ messages = [
24
+ {"role": "system", "content": SYSTEM_PROMPT},
25
+ {"role": "user", "content": f"""Analyze topic for TikTok strategy: "{content[:2000]}"
26
+
27
+ REQUIREMENTS:
28
+ - **Video Concepts**: 8 concepts with hook, script snippet, and sound suggestion
29
+ - **Trending Angles**: 5 trending content angles for this topic
30
+ - **Hashtags**: 5 optimized hashtags (trending + niche + broad)
31
+ - **Best Posting Time**: Optimal times
32
+ - **Viral Strategies**: 5 strategies specific to this topic/niche
33
+
34
+ JSON OUTPUT STRUCTURE:
35
+ {{
36
+ "video_concepts": [
37
+ {{"hook": "...", "script_snippet": "...", "sound_suggestion": "..."}}, ... (Total 8 items)
38
+ ],
39
+ "trending_angles": ["..."],
40
+ "hashtags": ["..."],
41
+ "best_posting_time": "...",
42
+ "viral_strategies": ["..."]
43
+ }}"""}
44
+ ]
45
+ data = run_analysis(messages, temperature=0.45, max_new_tokens=2000)
46
+ if isinstance(data, dict) and "error" in data:
47
+ return data
48
+ return validate_and_fill_data_defaults(data, DEFAULTS)
services/twitter_analyzer.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from services.utils import run_analysis, validate_and_fill_data_defaults
2
+
3
+ SYSTEM_PROMPT = """You are a Viral Twitter/X Strategist who understands the platform's fast-paced, text-first nature. You specialize in thread writing, engagement hacking, and building followings through value-driven content. Return ONLY valid JSON.
4
+
5
+ RULES:
6
+ 1. Tweet threads must have a compelling hook tweet followed by value tweets and a strong CTA
7
+ 2. Viral hooks must fit in 280 characters and create curiosity gaps
8
+ 3. Hashtags: 1-2 max, used sparingly
9
+ 4. Threads should have 5-10 tweets each
10
+ 5. X algorithm prioritizes: replies > retweets > likes > views
11
+ 6. Engaging with others' content = 40% of growth strategy"""
12
+
13
+ DEFAULTS = {
14
+ "tweet_threads": [],
15
+ "viral_hooks": [],
16
+ "hashtags": [],
17
+ "best_posting_time": "7-9 AM EST on weekdays",
18
+ "engagement_tactics": ["Reply to industry leaders", "Quote tweet with your take", "Use polls"]
19
+ }
20
+
21
+ def analyze_twitter(content: str) -> dict:
22
+ messages = [
23
+ {"role": "system", "content": SYSTEM_PROMPT},
24
+ {"role": "user", "content": f"""Analyze topic for X/Twitter strategy: "{content[:2000]}"
25
+
26
+ REQUIREMENTS:
27
+ - **Tweet Threads**: 3 complete threads (each 5-10 tweets) with different themes/angles
28
+ - **Viral Hooks**: 10 single-tweet hooks (under 280 chars each)
29
+ - **Hashtags**: 2 optimized hashtags
30
+ - **Best Posting Time**: Optimal day and time
31
+ - **Engagement Tactics**: 5 specific tactics
32
+
33
+ JSON OUTPUT STRUCTURE:
34
+ {{
35
+ "tweet_threads": [
36
+ {{"tweets": ["..."], "theme": "..."}}, ... (Total 3 items)
37
+ ],
38
+ "viral_hooks": ["..."],
39
+ "hashtags": ["..."],
40
+ "best_posting_time": "...",
41
+ "engagement_tactics": ["..."]
42
+ }}"""}
43
+ ]
44
+ data = run_analysis(messages, temperature=0.35, max_new_tokens=2000)
45
+ if isinstance(data, dict) and "error" in data:
46
+ return data
47
+ return validate_and_fill_data_defaults(data, DEFAULTS)
services/utils.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+
3
+ def clean_json_string(text: str) -> str:
4
+ if "```json" in text:
5
+ text = text.split("```json")[1].split("```")[0]
6
+ elif "```" in text:
7
+ text = text.split("```")[1].split("```")[0]
8
+ return text.strip()
9
+
10
+ def repair_json(json_str: str) -> str:
11
+ json_str = json_str.strip()
12
+ json_str = json_str.rstrip(", ")
13
+ open_braces = json_str.count("{")
14
+ close_braces = json_str.count("}")
15
+ open_brackets = json_str.count("[")
16
+ close_brackets = json_str.count("]")
17
+ if open_braces > close_braces:
18
+ json_str += "}" * (open_braces - close_braces)
19
+ if open_brackets > close_brackets:
20
+ json_str += "]" * (open_brackets - close_brackets)
21
+ return json_str
22
+
23
+ def parse_and_repair(raw_text: str, max_preview=500):
24
+ cleaned = clean_json_string(raw_text)
25
+ try:
26
+ return json.loads(cleaned), None
27
+ except json.JSONDecodeError:
28
+ print("⚠️ JSON Parse Error. Attempting repair...")
29
+ repaired = repair_json(cleaned)
30
+ try:
31
+ return json.loads(repaired), None
32
+ except json.JSONDecodeError as e:
33
+ return None, {
34
+ "error": "Optimization Failed. The model generated invalid JSON.",
35
+ "raw_output": raw_text[:max_preview]
36
+ }
37
+
38
+ def run_analysis(messages, temperature=0.3, max_new_tokens=2000):
39
+ from services.model_loader import generate_text
40
+ raw = generate_text(messages, temperature, max_new_tokens)
41
+ if raw is None:
42
+ return {"error": "Model failed to load on server startup. Check logs."}
43
+ data, err = parse_and_repair(raw)
44
+ if err:
45
+ return err
46
+ return data
47
+
48
+ def validate_and_fill_data_defaults(data: dict, defaults: dict) -> dict:
49
+ for key, default_val in defaults.items():
50
+ if key not in data or data[key] is None:
51
+ data[key] = default_val
52
+ return data
services/youtube_analyzer.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from services.utils import run_analysis, validate_and_fill_data_defaults
2
+
3
+ SYSTEM_PROMPT = """You are a world-class YouTube SEO Strategist and Content Consultant. You know the YouTube algorithm inside out — CTR optimization, watch-time retention, keyword ranking, thumbnail psychology, and community engagement. Return ONLY valid JSON.
4
+
5
+ RULES:
6
+ 1. Video titles must be click-worthy with high CTR potential (use numbers, power words, curiosity gaps, brackets)
7
+ 2. Tags must include a mix of broad, mid-tail, and long-tail keywords
8
+ 3. Description templates must include keyword placement, timestamps, and call-to-action
9
+ 4. Thumbnail ideas should reference proven design principles (contrast, faces with emotion, text overlay)
10
+ 5. Engagement strategies focus on: comments, polls, community tab, end screens, cards"""
11
+
12
+ DEFAULTS = {
13
+ "video_titles": [],
14
+ "tags": [],
15
+ "description_template": "In this video we explore [TOPIC]. Make sure to like and subscribe!",
16
+ "thumbnail_ideas": ["Close-up expressive face with bold text overlay"],
17
+ "best_posting_time": "2-4 PM EST on weekdays",
18
+ "engagement_strategies": ["Pin a comment with a question", "Use end screens to suggest next video"],
19
+ "competition_gap_analysis": "Focus on underserved long-tail subtopics within this niche."
20
+ }
21
+
22
+ def analyze_youtube(content: str) -> dict:
23
+ messages = [
24
+ {"role": "system", "content": SYSTEM_PROMPT},
25
+ {"role": "user", "content": f"""Analyze topic for YouTube strategy: "{content[:2000]}"
26
+
27
+ REQUIREMENTS:
28
+ - **Video Titles**: 15 high-CTR titles with expected CTR percentage
29
+ - **Tags**: 20 keywords (broad, mid-tail, long-tail)
30
+ - **Description Template**: 1 full SEO-optimized description template
31
+ - **Thumbnail Ideas**: 5 specific thumbnail concepts
32
+ - **Best Posting Time**: Optimal day and time
33
+ - **Engagement Strategies**: 5 tactics
34
+ - **Competition Gap Analysis**: 1 paragraph
35
+
36
+ JSON OUTPUT STRUCTURE:
37
+ {{
38
+ "video_titles": [
39
+ {{"title": "...", "expected_ctr": "..."}}, ... (Total 15 items)
40
+ ],
41
+ "tags": ["..."],
42
+ "description_template": "...",
43
+ "thumbnail_ideas": ["..."],
44
+ "best_posting_time": "...",
45
+ "engagement_strategies": ["..."],
46
+ "competition_gap_analysis": "..."
47
+ }}"""}
48
+ ]
49
+ data = run_analysis(messages, temperature=0.3, max_new_tokens=2000)
50
+ if isinstance(data, dict) and "error" in data:
51
+ return data
52
+ return validate_and_fill_data_defaults(data, DEFAULTS)