Files
DS_TASK_AI_VIEWS/backend/main.py
T

319 lines
10 KiB
Python

"""FastAPI backend for DS Task AI News"""
from fastapi import FastAPI, HTTPException, Query
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import List, Dict, Any, Optional
import uvicorn
from config import settings
from news_fetcher import NewsFetcher
from recommender import NewsRecommender
from groq_integration import GroqLLMService
# Initialize FastAPI app
app = FastAPI(
title="DS Task AI News API",
description="AI-powered news retrieval and recommendation system",
version="1.0.0"
)
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # In production, specify actual origins
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Initialize components
news_fetcher = NewsFetcher()
recommender = NewsRecommender()
groq_service = GroqLLMService()
# Pydantic models
class NewsQuery(BaseModel):
query: str
top_k: int = 5
class InterestsQuery(BaseModel):
interests: List[str]
top_k: int = 10
class SearchQuery(BaseModel):
query: str
source: Optional[str] = None
top_k: int = 10
# API Endpoints
@app.get("/")
async def root():
"""Health check endpoint"""
return {
"message": "DS Task AI News API is running!",
"version": "1.0.0",
"status": "healthy"
}
@app.get("/health")
async def health_check():
"""Detailed health check"""
stats = recommender.get_store_stats()
return {
"status": "healthy",
"vector_store": stats,
"settings": {
"embedding_model": settings.embedding_model,
"vector_db_type": settings.vector_db_type,
"rss_feeds_count": len(settings.rss_feeds)
}
}
@app.post("/fetch-news")
async def fetch_news():
"""Fetch news from RSS feeds and add to vector store"""
try:
# Fetch news articles
result = news_fetcher.fetch_and_save_news()
if not result["success"]:
raise HTTPException(status_code=500, detail=result.get("message", "Failed to fetch news"))
# Add articles to vector store
articles = result["articles"]
store_result = recommender.add_articles_to_store(articles)
if not store_result["success"]:
raise HTTPException(status_code=500, detail=store_result.get("message", "Failed to add articles to store"))
return {
"success": True,
"message": "News fetched and processed successfully",
"articles_fetched": result["articles_count"],
"articles_stored": store_result["articles_added"],
"total_articles": store_result["total_articles"]
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Error fetching news: {str(e)}")
@app.get("/recommend-news")
async def recommend_news(
article_id: str = Query(..., description="ID of the article to find similar articles for"),
top_k: int = Query(5, description="Number of recommendations to return")
):
"""Get news recommendations based on article ID"""
try:
recommendations = recommender.recommend_by_article_id(article_id, top_k)
return {
"success": True,
"article_id": article_id,
"recommendations": recommendations,
"count": len(recommendations)
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Error getting recommendations: {str(e)}")
@app.post("/recommend-by-query")
async def recommend_by_query(query_data: NewsQuery):
"""Get news recommendations based on text query"""
try:
recommendations = recommender.recommend_by_query(query_data.query, query_data.top_k)
return {
"success": True,
"query": query_data.query,
"recommendations": recommendations,
"count": len(recommendations)
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Error getting recommendations: {str(e)}")
@app.post("/recommend-by-interests")
async def recommend_by_interests(interests_data: InterestsQuery):
"""Get news recommendations based on user interests"""
try:
recommendations = recommender.recommend_by_interests(interests_data.interests, interests_data.top_k)
return {
"success": True,
"interests": interests_data.interests,
"recommendations": recommendations,
"count": len(recommendations)
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Error getting recommendations: {str(e)}")
@app.get("/trending")
async def get_trending_news(top_k: int = Query(10, description="Number of trending articles to return")):
"""Get trending news articles"""
try:
trending = recommender.get_trending_articles(top_k)
return {
"success": True,
"trending_articles": trending,
"count": len(trending)
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Error getting trending news: {str(e)}")
@app.get("/articles")
async def get_all_articles(
source: Optional[str] = Query(None, description="Filter by news source"),
limit: int = Query(50, description="Maximum number of articles to return")
):
"""Get all articles with optional filtering"""
try:
if source:
articles = recommender.get_articles_by_source(source, limit)
else:
all_articles = recommender.vector_store.get_all_articles()
articles = sorted(all_articles, key=lambda x: x.get('published_date', ''), reverse=True)[:limit]
return {
"success": True,
"articles": articles,
"count": len(articles),
"source_filter": source
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Error getting articles: {str(e)}")
@app.post("/search")
async def search_articles(search_data: SearchQuery):
"""Advanced search with filters"""
try:
filters = {}
if search_data.source:
filters['source'] = search_data.source
results = recommender.search_articles(search_data.query, filters, search_data.top_k)
return {
"success": True,
"query": search_data.query,
"filters": filters,
"results": results,
"count": len(results)
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Error searching articles: {str(e)}")
@app.get("/stats")
async def get_stats():
"""Get system statistics"""
try:
stats = recommender.get_store_stats()
# Add RSS feed information
stats['rss_feeds'] = settings.rss_feeds
stats['embedding_model'] = settings.embedding_model
stats['groq_available'] = groq_service.is_available()
return {
"success": True,
"statistics": stats
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Error getting stats: {str(e)}")
@app.post("/enhance-article")
async def enhance_article_with_ai(article_data: Dict[str, Any]):
"""Enhance an article with AI-generated summary, sentiment, and keywords"""
try:
if not groq_service.is_available():
raise HTTPException(status_code=503, detail="Groq LLM service not available")
enhanced_article = groq_service.enhance_article(article_data)
return {
"success": True,
"original_article": article_data,
"enhanced_article": enhanced_article
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Error enhancing article: {str(e)}")
@app.post("/generate-insights")
async def generate_news_insights():
"""Generate insights from recent news articles"""
try:
if not groq_service.is_available():
raise HTTPException(status_code=503, detail="Groq LLM service not available")
# Get recent articles
recent_articles = recommender.get_trending_articles(top_k=10)
if not recent_articles:
raise HTTPException(status_code=404, detail="No recent articles found")
insights = groq_service.generate_insights(recent_articles)
return {
"success": True,
"insights": insights,
"based_on_articles": len(recent_articles)
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Error generating insights: {str(e)}")
@app.post("/fetch-and-enhance-news")
async def fetch_and_enhance_news():
"""Fetch news and enhance with AI features"""
try:
# Fetch news articles
result = news_fetcher.fetch_and_save_news()
if not result["success"]:
raise HTTPException(status_code=500, detail=result.get("message", "Failed to fetch news"))
articles = result["articles"]
# Enhance with AI if Groq is available
if groq_service.is_available():
# Enhance first 5 articles as example
enhanced_articles = groq_service.batch_enhance_articles(articles[:5])
# Add enhanced articles to vector store
store_result = recommender.add_articles_to_store(enhanced_articles)
else:
# Add regular articles to vector store
store_result = recommender.add_articles_to_store(articles)
if not store_result["success"]:
raise HTTPException(status_code=500, detail=store_result.get("message", "Failed to add articles to store"))
return {
"success": True,
"message": "News fetched and processed successfully",
"articles_fetched": result["articles_count"],
"articles_enhanced": 5 if groq_service.is_available() else 0,
"articles_stored": store_result["articles_added"],
"total_articles": store_result["total_articles"],
"ai_features_enabled": groq_service.is_available()
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Error fetching and enhancing news: {str(e)}")
# Run the application
if __name__ == "__main__":
uvicorn.run(
"main:app",
host=settings.host,
port=settings.port,
reload=settings.debug
)