- Fix text selection menu not showing: wire contentRef via inputRef on multiline TextField - Fix blog title not truncating: add min-w-0 for flex item overflow - Fix outline generation 500: escape curly braces in f-string prompt template - Fix content generation 'NoneType not callable': replace SessionLocal() with get_session_for_user(), add db param to MediumBlogGenerator, fix signature mismatch in database_task_manager - Fix writing assistant suggest 500: add auth + user_id to API endpoint and service, replace sync requests with httpx.AsyncClient - Fix hallucination detector 404: explicitly include router in main.py and app.py - Fix missing error_data in task failure responses - Hide CopilotKit web inspector button - Remove hardcoded fallback suggestions from SmartTypingAssist - Fix stale closure refs in SmartTypingAssist handleTypingChange - Add two-column editor layout, stats bar, section hover menu - Various subscription, billing, and research module improvements
128 lines
5.1 KiB
Python
128 lines
5.1 KiB
Python
"""
|
|
Section Enhancer - AI-powered section enhancement and improvement.
|
|
|
|
Enhances individual outline sections for better engagement and value.
|
|
"""
|
|
|
|
from loguru import logger
|
|
from models.blog_models import BlogOutlineSection
|
|
import json
|
|
|
|
|
|
class SectionEnhancer:
|
|
"""Enhances individual outline sections using AI."""
|
|
|
|
async def enhance(self, section: BlogOutlineSection, focus: str, user_id: str) -> BlogOutlineSection:
|
|
"""Enhance a section using AI with research context.
|
|
|
|
Args:
|
|
section: Outline section to enhance
|
|
focus: Enhancement focus (e.g., "general improvement")
|
|
user_id: User ID (required for subscription checks and usage tracking)
|
|
|
|
Returns:
|
|
Enhanced outline section
|
|
|
|
Raises:
|
|
ValueError: If user_id is not provided
|
|
"""
|
|
if not user_id:
|
|
raise ValueError("user_id is required for section enhancement (subscription checks and usage tracking)")
|
|
|
|
enhancement_prompt = f"""
|
|
Enhance the following blog section to make it more engaging, comprehensive, and valuable:
|
|
|
|
Current Section:
|
|
Heading: {section.heading}
|
|
Subheadings: {', '.join(section.subheadings)}
|
|
Key Points: {', '.join(section.key_points)}
|
|
Target Words: {section.target_words}
|
|
Keywords: {', '.join(section.keywords)}
|
|
|
|
Enhancement Focus: {focus}
|
|
|
|
Improve:
|
|
1. Make subheadings more specific and actionable
|
|
2. Add more comprehensive key points with data/insights
|
|
3. Include practical examples and case studies
|
|
4. Address common questions and objections
|
|
5. Optimize for SEO with better keyword integration
|
|
|
|
Respond with JSON:
|
|
{{
|
|
"heading": "Enhanced heading",
|
|
"subheadings": ["enhanced subheading 1", "enhanced subheading 2"],
|
|
"key_points": ["enhanced point 1", "enhanced point 2"],
|
|
"target_words": 400,
|
|
"keywords": ["keyword1", "keyword2"]
|
|
}}
|
|
"""
|
|
|
|
try:
|
|
from services.llm_providers.main_text_generation import llm_text_gen
|
|
|
|
enhancement_schema = {
|
|
"type": "object",
|
|
"properties": {
|
|
"heading": {"type": "string"},
|
|
"subheadings": {"type": "array", "items": {"type": "string"}},
|
|
"key_points": {"type": "array", "items": {"type": "string"}},
|
|
"target_words": {"type": "integer"},
|
|
"keywords": {"type": "array", "items": {"type": "string"}}
|
|
},
|
|
"required": ["heading", "subheadings", "key_points", "target_words", "keywords"]
|
|
}
|
|
|
|
raw = llm_text_gen(
|
|
prompt=enhancement_prompt,
|
|
system_prompt=None,
|
|
user_id=user_id
|
|
)
|
|
|
|
# Parse JSON from LLM response (works with both string and dict return types)
|
|
import re
|
|
if isinstance(raw, str):
|
|
cleaned = raw.strip()
|
|
if cleaned.startswith('```json'):
|
|
cleaned = cleaned[7:]
|
|
if cleaned.startswith('```'):
|
|
cleaned = cleaned[3:]
|
|
if cleaned.endswith('```'):
|
|
cleaned = cleaned[:-3]
|
|
cleaned = cleaned.strip()
|
|
try:
|
|
enhanced_data = json.loads(cleaned)
|
|
except json.JSONDecodeError:
|
|
json_match = re.search(r'\{.*\}', cleaned, re.DOTALL)
|
|
if json_match:
|
|
try:
|
|
enhanced_data = json.loads(json_match.group(0))
|
|
except json.JSONDecodeError as e:
|
|
logger.warning(f"Section enhancement returned invalid JSON: {e}")
|
|
return section
|
|
else:
|
|
logger.warning(f"Section enhancement returned non-JSON string: {cleaned[:200]}")
|
|
return section
|
|
elif isinstance(raw, dict):
|
|
enhanced_data = raw
|
|
else:
|
|
logger.warning(f"Unexpected LLM response type: {type(raw)}")
|
|
return section
|
|
|
|
if 'error' in enhanced_data:
|
|
logger.warning(f"AI section enhancement failed: {enhanced_data.get('error', 'Unknown error')}")
|
|
else:
|
|
return BlogOutlineSection(
|
|
id=section.id,
|
|
heading=enhanced_data.get('heading', section.heading),
|
|
subheadings=enhanced_data.get('subheadings', section.subheadings),
|
|
key_points=enhanced_data.get('key_points', section.key_points),
|
|
references=section.references,
|
|
target_words=enhanced_data.get('target_words', section.target_words),
|
|
keywords=enhanced_data.get('keywords', section.keywords)
|
|
)
|
|
except Exception as e:
|
|
logger.warning(f"AI section enhancement failed: {e}")
|
|
|
|
return section
|