Hammad712 commited on
Commit
d3fed97
Β·
1 Parent(s): b3cb317

added content relevance

Browse files
app/content_relevence/content_relevance_service.py ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # content_relevance_service.py
2
+ """
3
+ Business logic service for Content Relevance analysis.
4
+ """
5
+ import json
6
+ import logging
7
+ import google.generativeai as genai
8
+ from typing import Dict, Any
9
+ from app.page_speed.config import settings
10
+
11
+ # Create a module-level logger
12
+ glogger = logging.getLogger(__name__)
13
+
14
+ class ContentRelevanceService:
15
+ """
16
+ Service class for generating Content Relevance reports via Gemini AI.
17
+ """
18
+ def __init__(self):
19
+ self.gemini_api_key = settings.gemini_api_key
20
+ if self.gemini_api_key:
21
+ glogger.info("Configuring Gemini AI for Content Relevance reporting.")
22
+ genai.configure(api_key=self.gemini_api_key)
23
+ else:
24
+ glogger.warning("No Gemini API key found. Reporting will fail if called.")
25
+
26
+ def generate_content_relevance_report(self, data: Dict[str, Any]) -> str:
27
+ """
28
+ Generate a Content Relevance report using Gemini AI.
29
+ """
30
+ glogger.info("Starting Content Relevance report generation.")
31
+ if not self.gemini_api_key:
32
+ glogger.error("Gemini API key not configured")
33
+ raise Exception("Gemini API key not configured")
34
+
35
+ try:
36
+ prompt = self._create_relevance_prompt(data)
37
+ glogger.debug("Relevance prompt: %s", prompt[:200])
38
+ response = genai.GenerativeModel("gemini-2.0-flash").generate_content(prompt)
39
+ text = getattr(response, "text", None)
40
+ if not text:
41
+ glogger.error("Empty response from Gemini")
42
+ raise Exception("Empty response from Gemini")
43
+ glogger.info("Content Relevance report generated successfully.")
44
+ return text.strip()
45
+ except Exception as e:
46
+ glogger.error("Error during report generation: %s", e, exc_info=True)
47
+ raise
48
+
49
+ def _create_relevance_prompt(self, data: Dict[str, Any]) -> str:
50
+ """
51
+ Build the enhanced prompt for Content Relevance analysis, including benchmarks, examples, and impact estimates.
52
+ """
53
+ keywords = data.get('keywords', [])
54
+ keyword_list = ", ".join(keywords)
55
+ return f"""
56
+ You are a **Content Strategy Expert**. Analyze the following content metrics and target keywords for relevance, coverage, and practical SEO impact. Provide a detailed report in Markdown, using structured sections do not add tables in the report, with the following enhancements:
57
+
58
+ 1. **Summary of Relevance**:
59
+ - Brief overview of alignment with keywords: {keyword_list}
60
+ - Overall Content Relevance Score: {data.get('contentRelevanceScore')} (out of 10)
61
+
62
+ 2. **Metric Breakdown**:
63
+ For each metric below, include:
64
+ - **Value** (from data)
65
+ - **Benchmark** (ideal or industry standard)
66
+ - **Status**: good / needs improvement / critical
67
+ - **Why It Matters**: concise rationale
68
+ - **Specific Example**: show where/how to improve (e.g., exact H1 text with keyword)
69
+ - **Expected Impact**: estimated uplift (e.g., `+5% relevance`)
70
+
71
+ - **Keyword Coverage Score**: {data.get('keywordCoverageScore')}
72
+ - **Density Score**: {data.get('densityScore')}% (ideal 1–3%)
73
+ - **Readability**: {data.get('readabilityScoreOutOf10')} / 10 (ideal β‰₯ 6)
74
+ - **Word Count**: {data.get('wordCount')} words (benchmark 1500–3000)
75
+ - **Media Richness**: Images = {data.get('imageCount')}, Videos = {data.get('videoCount')} (ideal β‰₯ 2 videos)
76
+
77
+ 3. **Top Strengths**:
78
+ - List top 3 areas where the actual values exceed benchmarks, referencing metric names and values.
79
+
80
+ 4. **Key Issues & Recommendations**:
81
+ For each of the top 3 issues, provide:
82
+ - **Issue**: name and value vs. benchmark
83
+ - **Actionable Fix**: code or content snippet example, e.g.:
84
+ ```html
85
+ <h1>{keywords[0].capitalize()} Services for Your Business</h1>
86
+ ```
87
+ - **Effort**: low / medium / high
88
+ - **Expected Impact**: e.g., `+10% coverage`, `+3 readability`
89
+
90
+ 5. **Priority Action Plan**:
91
+ - Top 5 actions, with columns: Priority (1–5), Action, Effort, Expected Impact.
92
+
93
+ 6. **Monitoring & Next Steps**:
94
+ - Weekly or monthly tracking recommendations
95
+
96
+ 7. **Bonus**: Suggest 2 related long-tail keywords to enhance depth.
97
+
98
+ Make the report engaging, use code blocks, and bullet lists where appropriate. Do not output JSONβ€”provide a human-readable Markdown report. and do not write anything outside the report format."""
99
+
100
+
101
+ def generate_content_priority(self, report: str) -> Dict[str, Any]:
102
+ """
103
+ Generate prioritized content relevance recommendations based on the AI-generated report.
104
+
105
+ Args:
106
+ report (str): The Markdown-formatted content relevance report.
107
+
108
+ Returns:
109
+ Dict[str, Any]: Dictionary mapping priority levels to recommendation lists.
110
+
111
+ Raises:
112
+ Exception: If priority generation fails.
113
+ """
114
+ glogger.info("Generating prioritized suggestions from the content relevance report.")
115
+ if not self.gemini_api_key:
116
+ msg = "Gemini API key not configured"
117
+ glogger.error(msg)
118
+ raise Exception(msg)
119
+ try:
120
+ model = genai.GenerativeModel("gemini-2.0-flash")
121
+ prompt = f"""
122
+ You are a **Content Strategy Expert**. Extract all actionable recommendations from the following content relevance report and organize them into a JSON object with keys: "high", "medium", "low".
123
+
124
+ For each recommendation, include:
125
+ - "recommendation": the action text
126
+ - "impact": the expected impact (e.g. "+5% relevance")
127
+ - "effort": low/medium/high
128
+
129
+ Important:
130
+ - Respond with *only* a valid JSON object.
131
+ - Do NOT include any commentary or explanation outside the JSON.
132
+ of t
133
+
134
+ Report:
135
+ {report}
136
+
137
+ Respond with only a JSON object.
138
+ """
139
+ response = model.generate_content(prompt)
140
+ raw = (response.text or "").strip()
141
+ glogger.debug("Raw priority response: %s", raw[:200])
142
+ # Extract JSON
143
+ start = raw.find('{')
144
+ end = raw.rfind('}')
145
+ if start == -1 or end == -1 or end <= start:
146
+ raise ValueError("No JSON object found in response")
147
+ json_str = raw[start:end+1]
148
+ suggestions = json.loads(json_str)
149
+ if not isinstance(suggestions, dict):
150
+ raise ValueError("Parsed JSON is not a dictionary")
151
+ for key in ("high", "medium", "low", "unknown"):
152
+ suggestions.setdefault(key, [])
153
+ glogger.info("Priority suggestions generated successfully.")
154
+ return suggestions
155
+ except json.JSONDecodeError as je:
156
+ msg = f"Failed to parse JSON: {je}"
157
+ glogger.error(msg, exc_info=True)
158
+ raise Exception(msg)
159
+ except Exception as e:
160
+ msg = f"Error generating content priority suggestions: {e}"
161
+ glogger.error(msg, exc_info=True)
162
+ raise
app/content_relevence/models.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # models.py
2
+ from pydantic import BaseModel
3
+ from typing import Any, Dict, List, Optional
4
+ import logging
5
+
6
+ # Optionally create a logger here if you need to log model-related events
7
+ model_logger = logging.getLogger(__name__)
8
+
9
+ class ContentRelevanceRequest(BaseModel):
10
+ data: Dict[str, Any]
11
+
12
+ def __init__(self, **data):
13
+ super().__init__(**data)
14
+ model_logger.debug("Initialized ContentRelevanceRequest with data: %s", self.data)
15
+
16
+ class ContentRelevanceResponse(BaseModel):
17
+ success: bool
18
+ report: str
19
+ priorities: Dict[str, Any]
20
+
21
+ def __init__(self, **data):
22
+ super().__init__(**data)
23
+ model_logger.debug(
24
+ "Initialized ContentRelevanceResponse with success=%s, keys: %s",
25
+ self.success,
26
+ list(self.priorities.keys()) if self.priorities else []
27
+ )
app/content_relevence/routes.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # routes.py
2
+ from fastapi import APIRouter, HTTPException, Request
3
+ import logging
4
+ from .content_relevance_service import ContentRelevanceService
5
+ from .models import ContentRelevanceRequest, ContentRelevanceResponse
6
+
7
+ # Create a module-level logger
8
+ router_logger = logging.getLogger(__name__)
9
+
10
+ router = APIRouter(prefix="/content-relevance", tags=["ContentRelevance"])
11
+ service = ContentRelevanceService()
12
+
13
+ @router.post("/report", response_model=ContentRelevanceResponse)
14
+ async def generate_full_content_relevance(request: Request, payload: ContentRelevanceRequest):
15
+ """
16
+ Generate a full Content Relevance report and corresponding prioritized suggestions.
17
+ """
18
+ router_logger.info("Received content relevance request from %s", request.client.host)
19
+ router_logger.debug("Payload data: %s", payload.data)
20
+
21
+ try:
22
+ report = service.generate_content_relevance_report(payload.data)
23
+ router_logger.info("Report generated successfully")
24
+
25
+ priorities = service.generate_content_priority(report)
26
+ router_logger.info("Priorities extracted successfully")
27
+
28
+ return ContentRelevanceResponse(success=True, report=report, priorities=priorities)
29
+
30
+ except Exception as e:
31
+ router_logger.error("Error during content relevance processing: %s", e, exc_info=True)
32
+ raise HTTPException(status_code=500, detail=str(e))
app/main.py CHANGED
@@ -15,7 +15,7 @@ from app.page_speed.models import HealthResponse
15
  from app.rag.routes import router as rag_router
16
  from app.seo import routes as seo_routes
17
  from app.page_speed import routes as page_speed_routes
18
-
19
 
20
  # ------------------------
21
  # Configure root logger
@@ -59,6 +59,8 @@ app.include_router(rag_router)
59
 
60
  app.include_router(seo_routes.router)
61
 
 
 
62
  # Mount PageSpeed router
63
  app.include_router(page_speed_routes.router)
64
 
 
15
  from app.rag.routes import router as rag_router
16
  from app.seo import routes as seo_routes
17
  from app.page_speed import routes as page_speed_routes
18
+ from app.content_relevence import routes as content_relevance_routes
19
 
20
  # ------------------------
21
  # Configure root logger
 
59
 
60
  app.include_router(seo_routes.router)
61
 
62
+ app.include_router(content_relevance_routes.router)
63
+
64
  # Mount PageSpeed router
65
  app.include_router(page_speed_routes.router)
66
 
app/page_speed/services.py CHANGED
@@ -174,7 +174,8 @@ class PageSpeedService:
174
  - Do **not** output JSON or code blocks unless specifically required.
175
  - Use a tone that's **professional, helpful, and non-technical**.
176
  - Help the reader understand what needs fixing and why it matters for their website and users.
177
-
 
178
  Example phrasing:
179
  > "Your site currently loads in about 3.2 seconds for most users, which is considered average. Improving this can reduce bounce rates and improve conversions."
180
 
 
174
  - Do **not** output JSON or code blocks unless specifically required.
175
  - Use a tone that's **professional, helpful, and non-technical**.
176
  - Help the reader understand what needs fixing and why it matters for their website and users.
177
+ - Don't add anything outside the report format.
178
+
179
  Example phrasing:
180
  > "Your site currently loads in about 3.2 seconds for most users, which is considered average. Improving this can reduce bounce rates and improve conversions."
181
 
app/rag/prompt_library.py CHANGED
@@ -70,4 +70,27 @@ seo_prompt = ChatPromptTemplate.from_messages(
70
  ("system", seo_prompt_template),
71
  ("human", "{question}"),
72
  ]
73
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  ("system", seo_prompt_template),
71
  ("human", "{question}"),
72
  ]
73
+ )
74
+
75
+ # ──────────────────────────────────────────────────────────────────────────────
76
+ # 4. Prompt Template for Content Relevance RAG Chatbot
77
+ # ──────────────────────────────────────────────────────────────────────────────
78
+
79
+ # Prompt Template for Content Relevance RAG Chatbot
80
+ content_relevance_prompt_template = """
81
+ You are a Content Relevance Assistant specialized in evaluating and enhancing written content for keyword alignment and coverage.
82
+ Use the provided context (metrics and keyword list) to answer the user's question.
83
+ If the context lacks sufficient information, respond with "I don't know." Avoid fabricating details.
84
+
85
+ Retrieved context:
86
+ {context}
87
+
88
+ User's question:
89
+ {question}
90
+
91
+ Your response:
92
+ """
93
+ content_relevance_prompt = ChatPromptTemplate.from_messages([
94
+ ("system", content_relevance_prompt_template),
95
+ ("human", "{question}"),
96
+ ])
app/rag/routes.py CHANGED
@@ -20,7 +20,7 @@ router = APIRouter(prefix="/rag", tags=["rag"])
20
  @router.post("/initialization/{onboarding_id}/{doc_type}", response_model=SetupResponse)
21
  async def setup_rag_session(
22
  onboarding_id: str = Path(..., description="Unique onboarding identifier"),
23
- doc_type: str = Path(..., description="Type of document (e.g., page_speed, seo, uiux)"),
24
  body: SetupRequest = ...
25
  ):
26
  """
@@ -86,7 +86,7 @@ async def chat_with_user(
86
  onboarding_id: str = Path(...),
87
  doc_type: str = Path(...),
88
  chat_id: str = Path(...),
89
- prompt_type: str = Query(..., description="Prompt type, e.g., page_speed or seo"),
90
  body: ChatRequest = ...
91
  ):
92
  """
 
20
  @router.post("/initialization/{onboarding_id}/{doc_type}", response_model=SetupResponse)
21
  async def setup_rag_session(
22
  onboarding_id: str = Path(..., description="Unique onboarding identifier"),
23
+ doc_type: str = Path(..., description="Type of document (e.g., page_speed, seo, content_relevance or uiux)"),
24
  body: SetupRequest = ...
25
  ):
26
  """
 
86
  onboarding_id: str = Path(...),
87
  doc_type: str = Path(...),
88
  chat_id: str = Path(...),
89
+ prompt_type: str = Query(..., description="Prompt type, e.g., page_speed, content_relevance or seo"),
90
  body: ChatRequest = ...
91
  ):
92
  """
app/rag/utils.py CHANGED
@@ -14,7 +14,8 @@ from .logging_config import logger
14
  from .prompt_library import (
15
  default_user_prompt,
16
  page_speed_prompt,
17
- seo_prompt
 
18
  )
19
 
20
  # 1. Path with doc_type
@@ -91,6 +92,8 @@ def build_rag_chain(
91
  user_prompt = page_speed_prompt
92
  elif prompt_type == "seo":
93
  user_prompt = seo_prompt
 
 
94
  else:
95
  user_prompt = default_user_prompt
96
 
 
14
  from .prompt_library import (
15
  default_user_prompt,
16
  page_speed_prompt,
17
+ seo_prompt,
18
+ content_relevance_prompt
19
  )
20
 
21
  # 1. Path with doc_type
 
92
  user_prompt = page_speed_prompt
93
  elif prompt_type == "seo":
94
  user_prompt = seo_prompt
95
+ elif prompt_type == "content_relevance":
96
+ user_prompt = content_relevance_prompt
97
  else:
98
  user_prompt = default_user_prompt
99