Recovered state: integrated TrendSurferAgent, restored frontend/backend files, and cleaned up recovery scripts
This commit is contained in:
@@ -22,7 +22,7 @@ class EnhancedContentGenerator:
|
||||
self.transitioner = TransitionGenerator()
|
||||
self.flow = FlowAnalyzer()
|
||||
|
||||
async def generate_section(self, section: Any, research: Any, mode: str = "polished") -> Dict[str, Any]:
|
||||
async def generate_section(self, section: Any, research: Any, mode: str = "polished", user_id: str = None) -> Dict[str, Any]:
|
||||
prev_summary = self.memory.build_previous_sections_summary(limit=2)
|
||||
urls = self.url_manager.pick_relevant_urls(section, research)
|
||||
prompt = self._build_prompt(section, research, prev_summary, urls)
|
||||
@@ -33,6 +33,7 @@ class EnhancedContentGenerator:
|
||||
prompt=prompt,
|
||||
json_struct=None,
|
||||
system_prompt=None,
|
||||
user_id=user_id
|
||||
)
|
||||
if isinstance(ai_resp, dict) and ai_resp.get("text"):
|
||||
content_text = ai_resp.get("text", "")
|
||||
|
||||
@@ -254,4 +254,35 @@ class MediumBlogGenerator:
|
||||
logger.warning(f"Failed to cache content result: {cache_error}")
|
||||
# Don't fail the entire operation if caching fails
|
||||
|
||||
# Save content to user workspace if db session is available
|
||||
if user_id and db:
|
||||
try:
|
||||
# Construct full blog content
|
||||
full_content = f"# {result.title}\n\n"
|
||||
for section in result.sections:
|
||||
full_content += f"## {section.heading}\n\n"
|
||||
full_content += f"{section.content}\n\n"
|
||||
|
||||
# Save to workspace
|
||||
save_and_track_text_content(
|
||||
db=db,
|
||||
user_id=user_id,
|
||||
content=full_content,
|
||||
source_module="medium_blog_writer",
|
||||
title=result.title,
|
||||
description=f"Generated medium blog: {result.title}",
|
||||
tags=req.researchKeywords or ["medium_blog", "ai_generated"],
|
||||
asset_metadata={
|
||||
"model": result.model,
|
||||
"generation_time_ms": result.generation_time_ms,
|
||||
"word_count": sum(s.wordCount for s in result.sections)
|
||||
},
|
||||
subdirectory="medium_blogs"
|
||||
)
|
||||
logger.info(f"Saved medium blog content to user workspace for user {user_id}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to save medium blog content to workspace: {e}")
|
||||
elif not db:
|
||||
logger.warning("Database session not provided, skipping workspace save for medium blog")
|
||||
|
||||
return result
|
||||
|
||||
@@ -8,6 +8,7 @@ from typing import Dict, Any, List
|
||||
import time
|
||||
import uuid
|
||||
from loguru import logger
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from models.blog_models import (
|
||||
BlogResearchRequest,
|
||||
@@ -137,7 +138,7 @@ class BlogWriterService:
|
||||
return self.outline_service.rebalance_word_counts(outline, target_words)
|
||||
|
||||
# Content Generation Methods
|
||||
async def generate_section(self, request: BlogSectionRequest) -> BlogSectionResponse:
|
||||
async def generate_section(self, request: BlogSectionRequest, user_id: str = None) -> BlogSectionResponse:
|
||||
"""Generate section content from outline."""
|
||||
# Compose research-lite object with minimal continuity summary if available
|
||||
research_ctx: Any = getattr(request, 'research', None)
|
||||
@@ -146,6 +147,7 @@ class BlogWriterService:
|
||||
section=request.section,
|
||||
research=research_ctx,
|
||||
mode=(request.mode or "polished"),
|
||||
user_id=user_id
|
||||
)
|
||||
markdown = ai_result.get('content') or ai_result.get('markdown') or ''
|
||||
citations = []
|
||||
@@ -341,17 +343,18 @@ class BlogWriterService:
|
||||
# TODO: Move to content module
|
||||
return BlogPublishResponse(success=True, platform=request.platform, url="https://example.com/post")
|
||||
|
||||
async def generate_medium_blog_with_progress(self, req: MediumBlogGenerateRequest, task_id: str, user_id: str) -> MediumBlogGenerateResult:
|
||||
async def generate_medium_blog_with_progress(self, req: MediumBlogGenerateRequest, task_id: str, user_id: str, db: Session = None) -> MediumBlogGenerateResult:
|
||||
"""Use Gemini structured JSON to generate a medium-length blog in one call.
|
||||
|
||||
Args:
|
||||
req: Medium blog generation request
|
||||
task_id: Task ID for progress updates
|
||||
user_id: User ID (required for subscription checks and usage tracking)
|
||||
db: Database session (optional, for saving assets)
|
||||
"""
|
||||
if not user_id:
|
||||
raise ValueError("user_id is required for medium blog generation (subscription checks and usage tracking)")
|
||||
return await self.medium_blog_generator.generate_medium_blog_with_progress(req, task_id, user_id)
|
||||
return await self.medium_blog_generator.generate_medium_blog_with_progress(req, task_id, user_id, db)
|
||||
|
||||
async def analyze_flow_basic(self, request: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Analyze flow metrics for entire blog using single AI call (cost-effective)."""
|
||||
|
||||
@@ -20,7 +20,7 @@ from models.blog_models import (
|
||||
MediumBlogGenerateResult,
|
||||
)
|
||||
from services.blog_writer.blog_service import BlogWriterService
|
||||
|
||||
from services.database import SessionLocal
|
||||
|
||||
class DatabaseTaskManager:
|
||||
"""Database-backed task manager for blog writer operations."""
|
||||
@@ -423,7 +423,7 @@ class DatabaseTaskManager:
|
||||
operation="medium_blog_generation"
|
||||
)
|
||||
|
||||
asyncio.create_task(self._run_medium_generation_task(task_id, request))
|
||||
asyncio.create_task(self._run_medium_generation_task(task_id, request, user_id))
|
||||
return task_id
|
||||
|
||||
async def _run_research_task(self, task_id: str, request: BlogResearchRequest):
|
||||
@@ -512,6 +512,8 @@ class DatabaseTaskManager:
|
||||
result: MediumBlogGenerateResult = await self.service.generate_medium_blog_with_progress(
|
||||
request,
|
||||
task_id,
|
||||
user_id=request.user_id if hasattr(request, 'user_id') else (await self.get_task_status(task_id))['user_id'],
|
||||
db=self.db
|
||||
)
|
||||
|
||||
if not result or not getattr(result, "sections", None):
|
||||
|
||||
@@ -12,6 +12,9 @@ from datetime import datetime
|
||||
from typing import Dict, Any, List, Optional
|
||||
from utils.logger_utils import get_service_logger
|
||||
|
||||
# Service-specific logger
|
||||
logger = get_service_logger("blog_content_seo_analyzer")
|
||||
|
||||
from services.seo_analyzer import (
|
||||
ContentAnalyzer, KeywordAnalyzer,
|
||||
URLStructureAnalyzer, AIInsightGenerator
|
||||
@@ -24,9 +27,6 @@ class BlogContentSEOAnalyzer:
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the blog content SEO analyzer"""
|
||||
# Service-specific logger (no global reconfiguration)
|
||||
global logger
|
||||
logger = get_service_logger("blog_content_seo_analyzer")
|
||||
self.content_analyzer = ContentAnalyzer()
|
||||
self.keyword_analyzer = KeywordAnalyzer()
|
||||
self.url_analyzer = URLStructureAnalyzer()
|
||||
|
||||
Reference in New Issue
Block a user