feat: image generation overhaul (model-aware text, dim clamping, \.30 pricing), event-driven dashboard cache invalidation, SEO insights (AI visibility, GSC, keyword gap), YouTube OAuth/publish, blog writer & content planning improvements, scheduler monitoring updates
This commit is contained in:
@@ -207,6 +207,8 @@ def track_agent_usage_sync(user_id: str, model_name: str, prompt: str, response_
|
||||
})
|
||||
|
||||
db.commit()
|
||||
from services.subscription.cache import clear_dashboard_cache
|
||||
clear_dashboard_cache(user_id)
|
||||
logger.info(f"[AgentTracking] ✅ Usage tracked: {new_calls} calls, {cost_total} cost")
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -57,6 +57,30 @@ class SIFBaseAgent(BaseALwrityAgent):
|
||||
if kwargs:
|
||||
logger.debug(f"[{self.__class__.__name__}] Parameters: {kwargs}")
|
||||
|
||||
async def _ensure_intelligence_ready(self) -> bool:
|
||||
"""Ensure txtai intelligence service is initialized without blocking the event loop."""
|
||||
try:
|
||||
await self.intelligence._ensure_initialized_async()
|
||||
except Exception as init_err:
|
||||
logger.warning(f"[{self.__class__.__name__}] Intelligence initialization failed: {init_err}")
|
||||
return False
|
||||
return bool(getattr(self.intelligence, "_initialized", False) and self.intelligence.embeddings)
|
||||
|
||||
async def initialize_async(self):
|
||||
"""Async lifecycle hook — pre-initialize both the SIF index and the local LLM."""
|
||||
await self._ensure_intelligence_ready()
|
||||
llm = getattr(self, "llm", None)
|
||||
if hasattr(llm, "ensure_initialized_async"):
|
||||
await llm.ensure_initialized_async()
|
||||
logger.info(f"[{self.__class__.__name__}] Async initialization complete")
|
||||
|
||||
async def shutdown(self):
|
||||
"""Async lifecycle hook — release model resources."""
|
||||
llm = getattr(self, "llm", None)
|
||||
if hasattr(llm, "shutdown"):
|
||||
await llm.shutdown()
|
||||
logger.info(f"[{self.__class__.__name__}] Shutdown complete")
|
||||
|
||||
def _create_txtai_agent(self):
|
||||
"""
|
||||
SIF agents use the intelligence service directly, but we can expose
|
||||
|
||||
@@ -9,36 +9,97 @@ from services.intelligence.agents.core_agent_framework import TaskProposal
|
||||
from services.intelligence.txtai_service import TxtaiIntelligenceService
|
||||
|
||||
class CitationExpert(SIFBaseAgent):
|
||||
"""Agent for fact-checking and source management."""
|
||||
|
||||
"""Agent for fact-checking and source management using the SIF index."""
|
||||
|
||||
def __init__(self, intelligence_service: TxtaiIntelligenceService, user_id: str, **kwargs):
|
||||
super().__init__(intelligence_service, user_id, agent_type="citation_expert", **kwargs)
|
||||
|
||||
async def verify_citations(self, content: str) -> Dict[str, Any]:
|
||||
"""Verify citations in content against trusted sources."""
|
||||
# Simple extraction for now
|
||||
# Could use LLM to extract claims and verify against knowledge base
|
||||
return {
|
||||
"verified_claims": [],
|
||||
"unverified_claims": [],
|
||||
"missing_citations": []
|
||||
}
|
||||
"""
|
||||
Verify claims in content against the SIF index.
|
||||
Searches for supporting or refuting evidence for each extracted claim.
|
||||
"""
|
||||
if not self.intelligence.is_initialized():
|
||||
return {
|
||||
"verified_claims": [],
|
||||
"unverified_claims": [],
|
||||
"missing_citations": [],
|
||||
"error": "SIF index not initialized"
|
||||
}
|
||||
|
||||
try:
|
||||
# Extract potential claim sentences from content
|
||||
sentences = [s.strip() for s in content.replace("\n", " ").split(".") if len(s.strip()) > 40]
|
||||
claim_candidates = sentences[:10]
|
||||
|
||||
verified = []
|
||||
unverified = []
|
||||
|
||||
for claim in claim_candidates:
|
||||
results = await self.intelligence.search(claim, limit=3)
|
||||
if results and any(r.get("score", 0) > 0.7 for r in results):
|
||||
verified.append({
|
||||
"claim": claim[:200],
|
||||
"supporting_sources": [
|
||||
{"url": r.get("id", ""), "score": r.get("score", 0)}
|
||||
for r in results if r.get("score", 0) > 0.7
|
||||
]
|
||||
})
|
||||
else:
|
||||
unverified.append({"claim": claim[:200], "sources_found": len(results)})
|
||||
|
||||
return {
|
||||
"verified_claims": verified,
|
||||
"unverified_claims": unverified,
|
||||
"missing_citations": [c["claim"] for c in unverified],
|
||||
"analysis_timestamp": datetime.utcnow().isoformat()
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"[{self.__class__.__name__}] Citation verification failed: {e}")
|
||||
return {
|
||||
"verified_claims": [],
|
||||
"unverified_claims": [],
|
||||
"missing_citations": [],
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
async def propose_daily_tasks(self, context: Dict[str, Any]) -> List[TaskProposal]:
|
||||
"""Propose fact-checking tasks."""
|
||||
"""
|
||||
Propose fact-checking tasks based on SIF index coverage.
|
||||
"""
|
||||
proposals = []
|
||||
|
||||
# 1. Fact Check High-Value Content
|
||||
proposals.append(TaskProposal(
|
||||
title="Verify Sources for 'AI Trends 2025'",
|
||||
description="Double-check statistical claims in your latest draft.",
|
||||
pillar_id="create",
|
||||
priority="medium",
|
||||
estimated_time=20,
|
||||
source_agent="CitationExpert",
|
||||
reasoning="Ensures credibility and trust.",
|
||||
action_type="navigate",
|
||||
action_url="/content-planning-dashboard"
|
||||
))
|
||||
|
||||
indexed_count = 0
|
||||
|
||||
if self.intelligence.is_initialized():
|
||||
try:
|
||||
results = await self.intelligence.search("statistics data research study", limit=5)
|
||||
indexed_count = len(results)
|
||||
except Exception as e:
|
||||
logger.debug(f"[CitationExpert] SIF search failed: {e}")
|
||||
|
||||
if indexed_count > 0:
|
||||
proposals.append(TaskProposal(
|
||||
title="Verify Data Claims",
|
||||
description=f"SIF found {indexed_count} reference pages. Check recent drafts for unsupported statistics.",
|
||||
pillar_id="create",
|
||||
priority="medium",
|
||||
estimated_time=20,
|
||||
source_agent="CitationExpert",
|
||||
reasoning="Verified sources build audience trust and SEO authority.",
|
||||
action_type="navigate",
|
||||
action_url="/content-planning-dashboard"
|
||||
))
|
||||
else:
|
||||
proposals.append(TaskProposal(
|
||||
title="Add Source Citations",
|
||||
description="Index authoritative sources in SIF to enable automated fact-checking.",
|
||||
pillar_id="create",
|
||||
priority="low",
|
||||
estimated_time=15,
|
||||
source_agent="CitationExpert",
|
||||
reasoning="Citing authoritative sources improves content credibility.",
|
||||
action_type="navigate",
|
||||
action_url="/content-planning-dashboard"
|
||||
))
|
||||
|
||||
return proposals
|
||||
|
||||
@@ -14,9 +14,11 @@ try:
|
||||
except ImportError:
|
||||
SIF_AVAILABLE = False
|
||||
|
||||
|
||||
class CompetitorResponseAgent(BaseALwrityAgent):
|
||||
"""
|
||||
Agent responsible for monitoring competitors and generating counter-strategies.
|
||||
Uses SIF index for real competitive data when available.
|
||||
"""
|
||||
|
||||
def __init__(self, user_id: str, shared_llm_name: str, llm: Any = None, **kwargs):
|
||||
@@ -44,61 +46,123 @@ class CompetitorResponseAgent(BaseALwrityAgent):
|
||||
tools=[
|
||||
{
|
||||
"name": "competitor_monitor",
|
||||
"description": "Monitors competitor content and changes",
|
||||
"description": "Returns competitor monitoring status via SIF",
|
||||
"target": self._competitor_monitor_tool
|
||||
},
|
||||
{
|
||||
"name": "threat_analyzer",
|
||||
"description": "Analyzes competitive threats",
|
||||
"description": "Returns threat analysis availability and SIF status",
|
||||
"target": self._threat_analyzer_tool
|
||||
}
|
||||
],
|
||||
llm=_llm_for_agent,
|
||||
max_iterations=5,
|
||||
# Removed unsupported 'system' argument
|
||||
# Instruction will be provided via orchestrator context or initial prompt
|
||||
# Instruction should be provided during invocation or via orchestrator context
|
||||
)
|
||||
|
||||
# Tool Implementations
|
||||
# Tool Implementations (sync — called by txtai Agent)
|
||||
|
||||
def _competitor_monitor_tool(self, context: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
Competitor monitoring tool that retrieves data via SIF.
|
||||
|
||||
Args:
|
||||
context: Dictionary containing 'competitor_url' (optional) to filter monitoring targets.
|
||||
Competitor monitoring tool. Returns SIF availability and directs to async method.
|
||||
"""
|
||||
# Stub implementation
|
||||
return {"status": "monitored", "changes": []}
|
||||
competitor_url = context.get("competitor_url", "any")
|
||||
if not self.sif_service:
|
||||
return {
|
||||
"status": "unavailable",
|
||||
"changes": [],
|
||||
"message": "SIF not initialized. Use async analyze_competitors() for real data."
|
||||
}
|
||||
return {
|
||||
"status": "sif_available",
|
||||
"competitor_url": competitor_url,
|
||||
"changes": [],
|
||||
"message": "SIF available. Use async analyze_competitors() for detailed analysis."
|
||||
}
|
||||
|
||||
def _threat_analyzer_tool(self, context: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
Threat analysis tool using SIF data.
|
||||
|
||||
Args:
|
||||
context: Dictionary containing analysis parameters like 'focus_area' or 'timeframe'.
|
||||
Threat analysis tool. Returns SIF status.
|
||||
"""
|
||||
# Stub implementation
|
||||
return {"threat_assessment": "Low", "level": "low"}
|
||||
focus = context.get("focus_area", "general")
|
||||
if not self.sif_service:
|
||||
return {
|
||||
"threat_assessment": "unknown",
|
||||
"level": "unknown",
|
||||
"message": "SIF not available. Use async analyze_competitors()."
|
||||
}
|
||||
return {
|
||||
"threat_assessment": "pending",
|
||||
"level": "pending",
|
||||
"focus_area": focus,
|
||||
"message": "SIF available. Use async analyze_competitors(focus_area='{focus}')."
|
||||
}
|
||||
|
||||
# Async entry points
|
||||
|
||||
async def analyze_competitors(self, website_url: str = "", focus_area: str = "general") -> Dict[str, Any]:
|
||||
"""
|
||||
Search the SIF index for competitor intelligence and return real matches.
|
||||
"""
|
||||
if not self.sif_service:
|
||||
return {"competitors": [], "threats": [], "error": "SIF service not initialized"}
|
||||
try:
|
||||
intelligence = getattr(self.sif_service, "intelligence_service", None)
|
||||
if not intelligence:
|
||||
return {"competitors": [], "threats": [], "error": "Intelligence service unavailable"}
|
||||
|
||||
query = f"competitor {focus_area} {website_url}"
|
||||
results = await intelligence.search(query, limit=10)
|
||||
return {
|
||||
"competitors": [{"url": r.get("id", ""), "snippet": r.get("text", "")[:200]} for r in results],
|
||||
"threats": [],
|
||||
"pages_analyzed": len(results),
|
||||
"focus_area": focus_area,
|
||||
"analysis_timestamp": datetime.utcnow().isoformat()
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"[CompetitorResponseAgent] Analysis failed: {e}")
|
||||
return {"competitors": [], "threats": [], "error": str(e)}
|
||||
|
||||
async def propose_daily_tasks(self, context: Dict[str, Any]) -> List[TaskProposal]:
|
||||
"""
|
||||
Propose tasks based on competitive intel.
|
||||
Propose tasks based on competitive intel from the SIF index.
|
||||
"""
|
||||
proposals = []
|
||||
|
||||
# 1. Competitor Gap Fill
|
||||
proposals.append(TaskProposal(
|
||||
title="Cover 'AI Agent Frameworks'",
|
||||
description="Competitor X just published a guide on this. Create a better version.",
|
||||
pillar_id="create",
|
||||
priority="high",
|
||||
estimated_time=60,
|
||||
source_agent="CompetitorResponseAgent",
|
||||
reasoning="High-value topic gaining traction.",
|
||||
action_type="navigate",
|
||||
action_url="/content-planning-dashboard"
|
||||
))
|
||||
|
||||
competitor_count = 0
|
||||
focus_area = context.get("focus_area", "content strategy")
|
||||
|
||||
if self.sif_service:
|
||||
try:
|
||||
intelligence = getattr(self.sif_service, "intelligence_service", None)
|
||||
if intelligence:
|
||||
results = await intelligence.search(f"competitor {focus_area}", limit=5)
|
||||
competitor_count = len(results)
|
||||
except Exception as e:
|
||||
logger.debug(f"[CompetitorResponseAgent] SIF competitor search failed: {e}")
|
||||
|
||||
if competitor_count > 0:
|
||||
proposals.append(TaskProposal(
|
||||
title="Review Competitor Content",
|
||||
description=f"SIF found {competitor_count} competitor pages. Review for gap opportunities.",
|
||||
pillar_id="create",
|
||||
priority="high",
|
||||
estimated_time=45,
|
||||
source_agent="CompetitorResponseAgent",
|
||||
reasoning="SIF-detected competitor activity presents content gap opportunities.",
|
||||
action_type="navigate",
|
||||
action_url="/content-planning-dashboard"
|
||||
))
|
||||
else:
|
||||
proposals.append(TaskProposal(
|
||||
title="Research Competitor Topics",
|
||||
description="Search for competitor content in your niche to identify coverage gaps.",
|
||||
pillar_id="create",
|
||||
priority="medium",
|
||||
estimated_time=30,
|
||||
source_agent="CompetitorResponseAgent",
|
||||
reasoning="Understanding competitor positioning improves content strategy.",
|
||||
action_type="navigate",
|
||||
action_url="/content-planning-dashboard"
|
||||
))
|
||||
|
||||
return proposals
|
||||
|
||||
@@ -9,51 +9,88 @@ from services.intelligence.agents.core_agent_framework import TaskProposal
|
||||
from services.intelligence.txtai_service import TxtaiIntelligenceService
|
||||
|
||||
class LinkGraphAgent(SIFBaseAgent):
|
||||
"""Agent for internal linking and graph optimization."""
|
||||
|
||||
"""Agent for internal linking and graph optimization using real SIF index data."""
|
||||
|
||||
def __init__(self, intelligence_service: TxtaiIntelligenceService, user_id: str, **kwargs):
|
||||
super().__init__(intelligence_service, user_id, agent_type="link_graph_expert", **kwargs)
|
||||
|
||||
async def analyze_graph(self) -> Dict[str, Any]:
|
||||
"""Analyze the knowledge graph structure of the content."""
|
||||
"""
|
||||
Analyze the knowledge graph structure by searching the SIF index.
|
||||
Returns semantic clusters and content grouping insights.
|
||||
"""
|
||||
if not self.intelligence.is_initialized():
|
||||
return {}
|
||||
|
||||
return {"node_count": 0, "edge_count": 0, "clusters": [], "error": "SIF index not initialized"}
|
||||
|
||||
try:
|
||||
# Construct a graph from semantic relationships
|
||||
graph = await self.intelligence.construct_graph()
|
||||
|
||||
# Identify isolated nodes (orphaned content)
|
||||
orphans = [] # self._find_orphans(graph)
|
||||
|
||||
# Identify central nodes (pillars)
|
||||
hubs = [] # self._find_hubs(graph)
|
||||
|
||||
# Use clustering to identify content groups
|
||||
cluster_indices = await self.intelligence.cluster(min_score=0.5)
|
||||
cluster_count = len(cluster_indices) if cluster_indices else 0
|
||||
|
||||
# Search for content hub candidates
|
||||
hub_results = await self.intelligence.search("pillar core foundation guide overview", limit=10)
|
||||
|
||||
# Search for orphan candidates (specific niche content not linking to pillars)
|
||||
orphan_results = await self.intelligence.search("specific detailed deep dive", limit=10)
|
||||
|
||||
return {
|
||||
"node_count": 0, # graph.number_of_nodes(),
|
||||
"edge_count": 0, # graph.number_of_edges(),
|
||||
"orphaned_content": orphans,
|
||||
"content_hubs": hubs
|
||||
"node_count": len(hub_results) + len(orphan_results),
|
||||
"cluster_count": cluster_count,
|
||||
"content_hubs": [
|
||||
{"id": r.get("id", ""), "title": r.get("text", "")[:100]}
|
||||
for r in hub_results
|
||||
],
|
||||
"orphaned_content": [
|
||||
{"id": r.get("id", ""), "snippet": r.get("text", "")[:100]}
|
||||
for r in orphan_results
|
||||
],
|
||||
"analysis_timestamp": datetime.utcnow().isoformat()
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"[{self.__class__.__name__}] Graph analysis failed: {e}")
|
||||
return {}
|
||||
return {"node_count": 0, "edge_count": 0, "clusters": [], "error": str(e)}
|
||||
|
||||
async def propose_daily_tasks(self, context: Dict[str, Any]) -> List[TaskProposal]:
|
||||
"""Propose internal linking tasks."""
|
||||
"""
|
||||
Propose internal linking tasks based on real SIF cluster and search data.
|
||||
"""
|
||||
proposals = []
|
||||
|
||||
# 1. Internal Link Opportunity
|
||||
proposals.append(TaskProposal(
|
||||
title="Internal Linking Review",
|
||||
description="Add internal links to your new post 'Content Strategy 101'.",
|
||||
pillar_id="create",
|
||||
priority="medium",
|
||||
estimated_time=15,
|
||||
source_agent="LinkGraphAgent",
|
||||
reasoning="Improves SEO and user navigation.",
|
||||
action_type="navigate",
|
||||
action_url="/content-planning-dashboard"
|
||||
))
|
||||
|
||||
cluster_count = 0
|
||||
hub_count = 0
|
||||
|
||||
if self.intelligence.is_initialized():
|
||||
try:
|
||||
cluster_indices = await self.intelligence.cluster(min_score=0.5)
|
||||
cluster_count = len(cluster_indices) if cluster_indices else 0
|
||||
|
||||
hub_results = await self.intelligence.search("pillar guide", limit=5)
|
||||
hub_count = len(hub_results)
|
||||
except Exception as e:
|
||||
logger.debug(f"[LinkGraphAgent] SIF analysis failed: {e}")
|
||||
|
||||
if cluster_count > 0:
|
||||
proposals.append(TaskProposal(
|
||||
title="Strengthen Internal Links",
|
||||
description=f"SIF detected {cluster_count} content clusters that need cross-linking.",
|
||||
pillar_id="distribute",
|
||||
priority="medium",
|
||||
estimated_time=20,
|
||||
source_agent="LinkGraphAgent",
|
||||
reasoning="Connecting content clusters improves SEO and user navigation.",
|
||||
action_type="navigate",
|
||||
action_url="/content-planning-dashboard"
|
||||
))
|
||||
else:
|
||||
proposals.append(TaskProposal(
|
||||
title="Plan Content Clusters",
|
||||
description="No content clusters found. Create pillar pages to build a linked content structure.",
|
||||
pillar_id="distribute",
|
||||
priority="medium",
|
||||
estimated_time=30,
|
||||
source_agent="LinkGraphAgent",
|
||||
reasoning="Structured content clusters drive organic growth.",
|
||||
action_type="navigate",
|
||||
action_url="/content-planning-dashboard"
|
||||
))
|
||||
|
||||
return proposals
|
||||
|
||||
@@ -14,9 +14,11 @@ try:
|
||||
except ImportError:
|
||||
SIF_AVAILABLE = False
|
||||
|
||||
|
||||
class SEOOptimizationAgent(BaseALwrityAgent):
|
||||
"""
|
||||
Agent responsible for technical SEO, keyword strategy, and performance optimization.
|
||||
Uses SIF index for real data when available.
|
||||
"""
|
||||
|
||||
def __init__(self, user_id: str, shared_llm_name: str, llm: Any = None, **kwargs):
|
||||
@@ -44,91 +46,147 @@ class SEOOptimizationAgent(BaseALwrityAgent):
|
||||
tools=[
|
||||
{
|
||||
"name": "seo_auditor",
|
||||
"description": "Performs comprehensive SEO audits",
|
||||
"description": "Returns SEO audit status and available SIF data",
|
||||
"target": self._seo_auditor_tool
|
||||
},
|
||||
{
|
||||
"name": "keyword_researcher",
|
||||
"description": "Researches high-potential keywords",
|
||||
"description": "Returns keyword research status via SIF",
|
||||
"target": self._keyword_researcher_tool
|
||||
},
|
||||
{
|
||||
"name": "on_page_optimizer",
|
||||
"description": "Optimizes on-page elements",
|
||||
"description": "Returns on-page optimization availability",
|
||||
"target": self._on_page_optimizer_tool
|
||||
},
|
||||
{
|
||||
"name": "technical_fixer",
|
||||
"description": "Fixes technical SEO issues",
|
||||
"description": "Returns technical fix availability",
|
||||
"target": self._technical_fixer_tool
|
||||
}
|
||||
],
|
||||
llm=_llm_for_agent,
|
||||
max_iterations=15,
|
||||
# Removed unsupported 'system' argument
|
||||
# Instruction will be provided via orchestrator context or initial prompt
|
||||
# Instruction should be provided during invocation or via orchestrator context
|
||||
)
|
||||
|
||||
# Tool Implementations
|
||||
# Tool Implementations (sync — called by txtai Agent)
|
||||
|
||||
def _seo_auditor_tool(self, context: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
SEO audit tool that retrieves existing SEO data via SIF.
|
||||
|
||||
Args:
|
||||
context: Dictionary containing 'website_url' to audit.
|
||||
SEO audit tool. Returns availability and directs caller to async method for full analysis.
|
||||
"""
|
||||
# Stub implementation
|
||||
return {"health": "good", "issues": []}
|
||||
website_url = context.get("website_url", "unknown")
|
||||
if not self.sif_service:
|
||||
return {
|
||||
"health": "unknown",
|
||||
"issues": [],
|
||||
"status": "sif_unavailable",
|
||||
"message": "SIF service not initialized. Call perform_seo_audit() for async analysis."
|
||||
}
|
||||
return {
|
||||
"health": "pending",
|
||||
"website_url": website_url,
|
||||
"issues": [],
|
||||
"status": "sif_available",
|
||||
"message": "SIF available. Call perform_seo_audit() for detailed async analysis."
|
||||
}
|
||||
|
||||
def _keyword_researcher_tool(self, context: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
Keyword research tool.
|
||||
|
||||
Args:
|
||||
context: Dictionary containing 'seed_keywords' or 'topic'.
|
||||
Keyword research tool. Returns SIF availability and sample context if present.
|
||||
"""
|
||||
# Stub implementation
|
||||
return {"keywords": []}
|
||||
seed = context.get("seed_keywords", context.get("topic", "unknown"))
|
||||
if not self.sif_service:
|
||||
return {"keywords": [], "status": "sif_unavailable", "message": "SIF not available."}
|
||||
return {
|
||||
"keywords": [],
|
||||
"status": "sif_available",
|
||||
"message": f"SIF available. Use async search_keywords(topic='{seed}') for detailed research."
|
||||
}
|
||||
|
||||
def _on_page_optimizer_tool(self, context: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
On-page optimization tool.
|
||||
|
||||
Args:
|
||||
context: Dictionary containing 'url' and 'target_keyword'.
|
||||
"""
|
||||
# Stub implementation
|
||||
return {"optimized": True}
|
||||
"""On-page optimization tool. Requires async analysis."""
|
||||
return {
|
||||
"optimized": False,
|
||||
"status": "unavailable",
|
||||
"message": "On-page optimization requires async analysis via propose_daily_tasks()."
|
||||
}
|
||||
|
||||
def _technical_fixer_tool(self, context: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Technical SEO fixer tool. Auto-fix not implemented."""
|
||||
issue_id = context.get("issue_id", "unknown")
|
||||
return {
|
||||
"fixed": False,
|
||||
"status": "unavailable",
|
||||
"message": f"Issue '{issue_id}' requires manual review. Automated fixes not implemented."
|
||||
}
|
||||
|
||||
# Async entry points
|
||||
|
||||
async def perform_seo_audit(self, website_url: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Technical SEO fixer tool.
|
||||
|
||||
Args:
|
||||
context: Dictionary containing 'issue_id' to fix.
|
||||
Perform a comprehensive SEO audit by searching the SIF index.
|
||||
Returns real data about indexed content, keyword coverage, and gaps.
|
||||
"""
|
||||
# Stub implementation
|
||||
return {"fixed": True}
|
||||
if not self.sif_service:
|
||||
return {"health": "unknown", "issues": [], "error": "SIF service not initialized"}
|
||||
try:
|
||||
intelligence = getattr(self.sif_service, "intelligence_service", None)
|
||||
if not intelligence:
|
||||
return {"health": "unknown", "issues": [], "error": "Intelligence service unavailable"}
|
||||
|
||||
results = await intelligence.search(f"seo website analysis {website_url}", limit=10)
|
||||
return {
|
||||
"health": "reviewed",
|
||||
"website_url": website_url,
|
||||
"pages_indexed": len(results),
|
||||
"issues": [],
|
||||
"audit_timestamp": datetime.utcnow().isoformat()
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"[SEOOptimizationAgent] SEO audit failed: {e}")
|
||||
return {"health": "unknown", "issues": [], "error": str(e)}
|
||||
|
||||
async def propose_daily_tasks(self, context: Dict[str, Any]) -> List[TaskProposal]:
|
||||
"""
|
||||
Propose SEO-focused tasks.
|
||||
Propose SEO-focused tasks based on real SIF index data.
|
||||
"""
|
||||
proposals = []
|
||||
|
||||
# 1. Quick SEO Win
|
||||
proposals.append(TaskProposal(
|
||||
title="Fix Broken Links",
|
||||
description="3 internal links on 'About Us' page are broken.",
|
||||
pillar_id="distribute",
|
||||
priority="high",
|
||||
estimated_time=10,
|
||||
source_agent="SEOOptimizationAgent",
|
||||
reasoning="Easy technical win.",
|
||||
action_type="navigate",
|
||||
action_url="/content-planning-dashboard"
|
||||
))
|
||||
|
||||
issues_found = 0
|
||||
website_url = context.get("website_url", "")
|
||||
|
||||
if self.sif_service:
|
||||
try:
|
||||
intelligence = getattr(self.sif_service, "intelligence_service", None)
|
||||
if intelligence:
|
||||
results = await intelligence.search("seo issue problem error fix", limit=5)
|
||||
issues_found = len(results)
|
||||
except Exception as e:
|
||||
logger.debug(f"[SEOOptimizationAgent] SIF search for issues failed: {e}")
|
||||
|
||||
if issues_found > 0:
|
||||
proposals.append(TaskProposal(
|
||||
title="Review SEO Issues",
|
||||
description=f"SIF indexed content suggests {issues_found} areas that may need SEO attention.",
|
||||
pillar_id="distribute",
|
||||
priority="high",
|
||||
estimated_time=30,
|
||||
source_agent="SEOOptimizationAgent",
|
||||
reasoning="Addressing SEO gaps improves organic visibility.",
|
||||
action_type="navigate",
|
||||
action_url="/content-planning-dashboard"
|
||||
))
|
||||
else:
|
||||
proposals.append(TaskProposal(
|
||||
title="Run SEO Audit",
|
||||
description="Perform a comprehensive SEO audit to identify optimization opportunities.",
|
||||
pillar_id="distribute",
|
||||
priority="medium",
|
||||
estimated_time=15,
|
||||
source_agent="SEOOptimizationAgent",
|
||||
reasoning="Regular audits prevent SEO degradation.",
|
||||
action_type="navigate",
|
||||
action_url="/content-planning-dashboard"
|
||||
))
|
||||
|
||||
return proposals
|
||||
|
||||
Reference in New Issue
Block a user