AI Analysis and Content Strategy fixes. Enhanced Strategy Routes refactoring.

This commit is contained in:
ajaysi
2026-01-10 19:32:50 +05:30
parent 0b63ae7fc1
commit 8193cdba67
298 changed files with 45678 additions and 10952 deletions

View File

@@ -100,7 +100,8 @@ class ResearchConfig(BaseModel):
exa_category: Optional[str] = None # company, research paper, news, linkedin profile, github, tweet, movie, song, personal site, pdf, financial report
exa_include_domains: List[str] = [] # Domain whitelist
exa_exclude_domains: List[str] = [] # Domain blacklist
exa_search_type: Optional[str] = "auto" # "auto", "keyword", "neural"
exa_search_type: Optional[str] = "auto" # "auto", "keyword", "neural", "fast", "deep"
exa_additional_queries: Optional[List[str]] = None # Additional query variations for Deep search (only works with type="deep")
# Tavily-specific options
tavily_topic: Optional[str] = "general" # general, news, finance

View File

@@ -203,6 +203,10 @@ class ResearchIntent(BaseModel):
default_factory=list,
description="Specific aspects to focus on"
)
also_answering: List[str] = Field(
default_factory=list,
description="Additional questions or topics that should be addressed in the research results, even if not explicitly asked"
)
# Constraints
perspective: Optional[str] = Field(
@@ -258,6 +262,28 @@ class ResearchQuery(BaseModel):
provider: str = Field("exa", description="Preferred provider: exa, tavily, google")
priority: int = Field(1, ge=1, le=5, description="Priority 1-5, higher = more important")
expected_results: str = Field(..., description="What we expect to find with this query")
# Intent field links - which intent aspects this query addresses
addresses_primary_question: bool = Field(
False,
description="Does this query address the primary question?"
)
addresses_secondary_questions: List[str] = Field(
default_factory=list,
description="Which secondary questions does this query answer?"
)
targets_focus_areas: List[str] = Field(
default_factory=list,
description="Which focus areas does this query target?"
)
covers_also_answering: List[str] = Field(
default_factory=list,
description="Which 'also answering' topics does this query cover?"
)
justification: Optional[str] = Field(
None,
description="Why this query was generated"
)
class IntentInferenceRequest(BaseModel):
@@ -309,7 +335,15 @@ class IntentDrivenResearchResult(BaseModel):
primary_answer: str = Field(..., description="Direct answer to primary question")
secondary_answers: Dict[str, str] = Field(
default_factory=dict,
description="Answers to secondary questions (question → answer)"
description="Answers to secondary questions (question → answer, null if not found)"
)
focus_areas_coverage: Dict[str, Optional[str]] = Field(
default_factory=dict,
description="Summary of what was found for each focus area (area → summary, null if not covered)"
)
also_answering_coverage: Dict[str, Optional[str]] = Field(
default_factory=dict,
description="Information found about each 'also answering' topic (topic → info, null if not found)"
)
# Deliverables (populated based on user's expected_deliverables)

View File

@@ -0,0 +1,58 @@
"""
Research Project Models
Database models for research project persistence and state management.
Similar to PodcastProject, but for research projects.
"""
from sqlalchemy import Column, Integer, String, DateTime, Boolean, JSON, Index
from datetime import datetime
# Use the same Base as subscription models for consistency
from models.subscription_models import Base
class ResearchProject(Base):
"""
Database model for research project state.
Stores complete research project state to enable cross-device resume.
"""
__tablename__ = "research_projects"
# Primary fields
id = Column(Integer, primary_key=True, autoincrement=True)
project_id = Column(String(255), unique=True, nullable=False, index=True) # User-facing project ID
user_id = Column(String(255), nullable=False, index=True) # Clerk user ID
# Project metadata
title = Column(String(500), nullable=True) # Project title
keywords = Column(JSON, nullable=False) # List of keywords
industry = Column(String(255), nullable=True)
target_audience = Column(String(255), nullable=True)
research_mode = Column(String(50), nullable=True, default="comprehensive") # basic, comprehensive, expert
# Project state (stored as JSON)
config = Column(JSON, nullable=True) # ResearchConfig
intent_analysis = Column(JSON, nullable=True) # AnalyzeIntentResponse
confirmed_intent = Column(JSON, nullable=True) # ResearchIntent
intent_result = Column(JSON, nullable=True) # IntentDrivenResearchResponse
legacy_result = Column(JSON, nullable=True) # BlogResearchResponse (for backward compatibility)
trends_config = Column(JSON, nullable=True) # Google Trends configuration
# UI state
current_step = Column(Integer, default=1, nullable=False) # 1=Input, 2=Progress, 3=Results
# Status
status = Column(String(50), default="draft", nullable=False, index=True) # draft, in_progress, completed, archived
is_favorite = Column(Boolean, default=False, index=True)
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow, nullable=False, index=True)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False, index=True)
# Composite indexes for common query patterns
__table_args__ = (
Index('idx_user_status_created', 'user_id', 'status', 'created_at'),
Index('idx_user_favorite_updated', 'user_id', 'is_favorite', 'updated_at'),
)

View File

@@ -137,6 +137,7 @@ class APIUsageLog(Base):
endpoint = Column(String(200), nullable=False)
method = Column(String(10), nullable=False)
model_used = Column(String(100), nullable=True) # e.g., "gemini-2.5-flash"
actual_provider_name = Column(String(50), nullable=True) # e.g., "wavespeed", "google", "huggingface" - tracks real provider behind generic enum
# Usage Metrics
tokens_input = Column(Integer, default=0)