A sophisticated full-stack platform that ingests news from across the web, analyzes it using AI agents, and delivers personalized content through a modern web app and WhatsApp.
Features β’ Architecture β’ Installation β’ Documentation
The project is built on a decoupled monorepo structure, containing three main parts:
NewsVerse/
βββ π± frontend/ # React + TypeScript web application
βββ βοΈ backend/ # Python microservice pipeline
β βββ Scraping_Crawling/
β βββ Summarization/
β βββ Fact_Checker/
β βββ Sentiment_Analysis/
β βββ Name_Entity_Recognition/
β βββ Embedding_Creation/
β βββ Article_Scorer/
β βββ Recommendation_Engine/
β βββ Whatsapp_Messaging/
βββ π§ͺ Raw_code_developer/ # Development sandbox & experiments
Web Sources β Scraping β Processing Pipeline β Embeddings β Scoring β Recommendations β Users
β β β β β
MongoDB AI Agents Vector DB Quality Personalized
Scores Feed
| Area | Technology |
|---|---|
| Frontend | React 18+, TypeScript, Tailwind CSS, Vite, shadcn/ui |
| Backend | Python 3.10+, FastAPI, MongoDB, APScheduler |
| AI Framework | Agno Framework - Agent orchestration & LLM integration |
| AI / ML | LangChain, SentenceTransformers (all-MiniLM-L6-v2), scikit-learn |
| LLM Providers | Google Gemini (Primary), Groq (Failover via Agno) |
| Data Ingestion | Crawl4ai, BeautifulSoup, Requests |
| Messaging | Twilio API (WhatsApp) |
| Authentication | Google OAuth 2.0 |
| Vector Database | MongoDB (embedding storage) |
| Task Scheduling | APScheduler (CRON jobs) |
Agno is a powerful Python framework for building AI agents. In NewsVerse, we use Agno to:
Special Thanks: This project is built using the Agno Framework created by Ashpreet B. (CEO of Agno). The frameworkβs agent-based architecture made it seamless to build and manage multiple AI agents for different tasks.
High-performance Python web framework used for:
Modern frontend stack providing:
Document database storing:
Directory: backend/Scraping_Crawling/
Purpose: Fetch raw articles (links, titles, content) from multiple news sources.
π Broad Discovery β Crawl4ai
π― Reliable Extraction β Custom Parsers
parsers.py)def parse_bbc(soup):
"""Custom parser for BBC News articles."""
content = soup.find('article').text
return content
def parse_cnn(soup):
"""Custom parser for CNN articles."""
content = soup.find('div', class_='article__content').text
return content
PARSER_MAP = {
'bbc.com': parse_bbc,
'cnn.com': parse_cnn,
'hindustantimes.com': parse_ht,
'benzinga.com': parse_benzinga
}
{
"_id": "HT_20250908_141827_5388",
"source": "HT",
"title": "Vice-president election on Sept 9...",
"date": "2025-09-08",
"time": "13:43:25",
"content": "The stage is set for...",
"url": "https://www.hindustantimes.com/...",
"scraped_at": "2025-09-08T14:18:27.311+00:00",
"processed_status": {
"summarized": false,
"fact_checked": false,
"sentiment": false,
"ner": false,
"scored": false
}
}
Once raw articles are collected, a series of AI agents enrich the data. All modules feature resilient API handling with automatic failover from Gemini to Groq when rate limits are encountered.
Directory: backend/Summarization/
Agents Used: 2 (Summarization Agent, Story Agent)
Purpose: Generates two types of summaries for each article:
How it Works (run_summarization.py):
get_factual_summary() which:
openai/gpt-oss-120b)summary fieldget_story_summary() which:
openai/gpt-oss-120b)story_summary fieldAgent Prompts (agents.py):
Summarization Agent:
You are a news article summarizer. Summarize the given article text in 2-4 sentences.
Return JSON in this exact format:
{
"summary": "<short, concise summary of the article>"
}
Do NOT include anything outside the JSON object.
Story Agent:
You are a children's story writer. Read the article carefully and summarize it in a fun,
simple, and easy-to-read way for kids.
Rules:
1. Use simple language suitable for 6-12 year old children.
2. Make it engaging like a short story.
3. Keep the summary concise (3-5 sentences max).
4. Focus on the main events or important points, but avoid technical jargon.
5. Return JSON in this exact format:
{
"story_summary": "<summary written as a story for kids>"
}
6. Do NOT include anything outside the JSON object.
Output Format:
{
"summarization": {
"summary": "Concise factual summary...",
"story_summary": "Child-friendly story format..."
}
}
Directory: backend/Fact_Checker/**
Agents Used: 1 (Fact-Checker Agent with Web Search Tools)
Purpose: Verifies factual claims in articles using web search tools and returns a boolean verdict.
How it Works (fact_checker.py):
DuckDuckGoToolsGoogleSearchToolsWebBrowserToolsWebsiteToolsfact_check_results.jsonAgent Instructions (agents.py):
Step 1: Read the provided news article text.
Step 2: Extract the main factual claim from the article.
Step 3: Use ONLY ONE search (DuckDuckGo, GoogleSearch, WebBrowser, or WebsiteTools) for that claim.
Step 4: Compare the claim to the top 3 reputable search results (BBC, Reuters, AP, Bloomberg, etc.).
Step 5: Decide if the claim is factually correct (true or false).
Step 6: Output the result ONLY in a raw JSON object (no markdown block or surrounding text).
The JSON MUST have exactly two fields: 'llm_verdict' (boolean: true/false) and
'fact_check_explanation' (string: short reason).
Example: {"llm_verdict": true, "fact_check_explanation": "The claim is supported by multiple reputable sources."}
Output Format:
{
"fact_check": {
"llm_verdict": false,
"fact_check_explanation": "The article claims that Jagdeep Dhankhar resigned as Vice President on..."
}
}
Features:
Directory: backend/Sentiment_Analysis/
Agents Used: 1 (Sentiment Agent)
Purpose: Classifies article sentiment as Positive, Negative, or Neutral with reasoning.
How it Works (sentiment.py):
sentiment_analysis.jsonAgent Instructions (agents.py):
You are a sentiment evaluation agent. Analyze the tone and language of the article text.
Determine sentiment strictly as **Positive, Negative, or Neutral** based on these rules:
1. **Positive** β The article contains positive keywords (e.g., growth, profit, gain, recovery,
expansion, strong, successful), or the overall tone is optimistic and confidence-building.
2. **Negative** β The article contains negative keywords (e.g., loss, decline, fall, risk, weak,
downgrade, failure), or the overall tone is pessimistic, warning, or confidence-reducing.
3. **Neutral** β The article is mainly factual, descriptive, or balanced β with no clear
positive or negative tone. Includes objective reporting, announcements, or mixed signals.
4. Return only JSON in this exact format:
{
"sentiment": "<Positive|Negative|Neutral>",
"reason": "<short reason explaining the classification>"
}
5. Reason should be brief (1-2 sentences).
6. Do NOT include anything outside the JSON object.
Output Format:
{
"sentiment": "Neutral"
}
Features:
Directory: backend/Name_Entity_Recognition/
Agents Used: 1 (NER Agent)
Purpose: Extracts and aggregates named entities (Person, Location, Organization) from all articles a user has liked, storing them in the userβs profile.
How it Works (NER.py):
title_id_list (articles theyβve interacted with)ner_data field in MongoDBAgent Instructions (agents.py):
Extract all unique named entities from the following news article text and categorize them
as "Person", "Location" (including cities/countries/regions), or "Organization"
(companies, institutions).
Return strictly a JSON object in this format:
{
"Person": [list of unique person names],
"Location": [list of unique locations],
"Organization": [list of unique organizations]
}
Do not include any other text, comments, or explanations. Return valid JSON only.
Output Format (in User Collection):
{
"ner_data": {
"Person": ["Rohit Arya", "Deepak Kesarkar", "Ashish Shelar", ...],
"Location": ["Mumbai", "Pune", "Maharashtra", ...],
"Organization": ["BCCI", "School Education Department", ...]
}
}
Key Features:
Note: This module updates the User Collection, not the Article Collection, as it builds user preference profiles based on their reading history.
Directory: backend/Embedding_Creation/
Purpose: Converts article text into high-dimensional vector embeddings for semantic similarity matching.
How it Works (embeddings.py):
title and content for richer contextall-MiniLM-L6-v2)Model Details:
all-MiniLM-L6-v2 (SentenceTransformers)embedding fieldCode Example:
from Embedding_Creation.model_loader import embedding_model
# Combine title and content for richer embedding
text_to_embed = f"{article.get('title', '')} {article.get('content', '')}"
# Generate the embedding
embedding = embedding_model.encode(text_to_embed).tolist()
# Store in MongoDB
db_manager.updateArticleEmbedding(collection, article["_id"], embedding)
Key Features:
Directory: backend/Article_Scorer/
Purpose: This module assigns a hybrid βqualityβ score to each article by combining an AI-generated βknowledge depthβ score with a (potential) user-provided score. It is designed to be resilient, with a built-in failover from the Gemini API to Groq.
Agents Used: 1 (The Article Scoring Agent)
agents.py)This module uses a highly specific prompt with a 0-9 rubric based on βknowledge depthβ. The agent is instructed to return only a JSON object.
# This is the exact prompt from agents.py
"You are an evaluator of news articles.\n"
"Score each article from 0 to 9 based on knowledge depth:\n\n"
"0β2: Poor β highly superficial, incomplete, or factually questionable.\n"
"3β5: Moderate β covers basics but lacks depth or misses key points.\n"
"6β8: Good β detailed, covers multiple aspects, balanced and factual.\n"
"9: Exceptional β comprehensive, in-depth, authoritative, and well-structured.\n\n"
"Return valid JSON only in the format:\n"
"{\n"
' "score": <integer 0β9>,\n'
' "reason": "<short reason>"\n'
"}"
article_scorer.py)The main script article_scorer.py orchestrates the entire scoring process through several key steps:
The script fetches all articles from MongoDB and groups them by title to de-duplicate the scoring process. This ensures that duplicate articles (same title, different sources) receive the same score, avoiding redundant API calls.
For one representative article from each group, the script calls the get_llm_score function. This function is designed to be robust and resilient:
Process:
api_managerscore: Integer from 0-9reason: Short explanationAPI Failover Mechanism:
ResourceExhausted), the api_manager is instructed to switch_to_groq()The script iterates through the grouped articles to find any existing user_article_score (e.g., from a userβs manual rating or feedback).
Purpose: Incorporates user feedback into the final score when available.
The final_custom_score is a weighted average that combines AI analysis with user feedback.
Formula Used (article_scorer.py):
# This is the exact formula from article_scorer.py
final_score = round((llm_score * 0.6) + (user_score * 0.4), 2) if user_score is not None else llm_score
Scoring Logic:
final_score = (60% Γ llm_score) + (40% Γ user_score)final_score = llm_scoreThis weighted approach ensures that AI analysis carries more weight (60%) while still incorporating valuable user feedback (40%) when available.
Update MongoDB:
final_score and its components (llm_score, user_article_score) are saved back to all articles in the group in MongoDBSave Local Copy:
article_scores.json file for logging and backup purposesDirectory: backend/Recommendation_Engine/
Purpose: Matches users with most relevant articles using a sophisticated multi-stage pipeline that combines user behavior analysis, AI-powered profile generation, and vector similarity matching.
The recommendation system follows a precise, multi-step process that transforms raw user interactions into personalized article recommendations.
The pipeline begins when a user performs an action in the frontend:
ArticleCard.tsx)UserPreferences.tsx)These actions are logged in MongoDB, creating records of:
liked_article_ids β List of articles the user has interacted withexplicit_preferences β Raw text preferences (e.g., βI like AI and financeβ)user_analyzer.py)When a recommendation is needed, this script creates a unified βprofileβ of the userβs interests.
Process:
liked_article_idsexplicit_preferences (raw text)Output: A collection of raw, βnoisyβ text data (e.g., 5 liked articles + 3 preference phrases)
agents.py)The raw user data is processed by an AI agent to distill it into a clean, meaningful profile.
Agent: User Analyzer Agent (defined in backend/Recommendation_Engine/agents.py)
Example Prompt:
You are a user profile analyzer. Based on the following articles a user has liked
({liked_article_content}) and their stated interests ({explicit_preferences}),
generate a single, dense paragraph that summarizes this user's true, nuanced interests.
Identify key topics, entities, and recurring themes.
Example Transformation:
Input:
Agent Output:
βThis user is interested in high-growth technology, specifically in the electric vehicle and artificial intelligence sectors. They follow key companies like Tesla and NVIDIA, and are interested in the financial market implications of new tech.β
Result: A single, high-quality βinterest paragraphβ that captures the userβs true preferences.
embeddings.py)The clean βinterest paragraphβ from Step 2 is converted into a mathematical representation.
Process:
backend/Embedding_Creation/embeddings.py)all-MiniLM-L6-v2) to generate vector embeddings[1, 384] array)Storage: This vector is saved in the userβs MongoDB document for quick retrieval, avoiding recomputation on every request.
article_recommender.py)This is the core matching engine, triggered by:
News.tsx)whatsapp_sender.py)Process:
Fetch User Profile Vector
Load Article Vectors
Embedding_Creation/embeddings.py when articles were first scraped)Calculate Similarity
Code Implementation:
from sklearn.metrics.pairwise import cosine_similarity
# user_vector.shape is [1, 384]
# all_article_vectors.shape is [N, 384] (N = number of articles)
# This calculates the similarity of the user to EVERY article
similarity_scores = cosine_similarity(user_vector, all_article_vectors)
# Result is an array like: [0.91, 0.23, 0.88, 0.05, ...]
The final step sorts and delivers the most relevant articles.
Process:
similarity_scores array from highest to lowestNews.tsxResult: Users receive personalized article recommendations that match their interests, behavior, and stated preferences.
user_analyzer.py β Analyzes user behavior and preferencesagents.py β Contains the User Analyzer Agent (LLM-based profile generation)article_recommender.py β Core matching engine using cosine similarityengine.py β Orchestrates the recommendation pipelinemodel_loader.py β Loads embedding models for vectorizationDirectory: backend/Whatsapp_Messaging/
Purpose: Delivers personalized news recommendations to users via WhatsApp using Twilio API, with scheduled delivery based on user preferences.
How it Works:
Scheduled Tasks (scheduler_tasks.py):
Message Generation (whatsapp_sender.py):
Sending (whatsapp_service.py):
Integration:
preferred_time settingKey Features:
Files:
whatsapp_sender.py β Main sending logicwhatsapp_service.py β Twilio API integrationscheduler_tasks.py β Scheduled task managementrecommender.py β Article recommendation integrationThe NewsVerse platform uses MongoDB to store articles, user data, preferences, and recommendations. Below are the detailed schemas for each collection.
The main collection storing all scraped and processed articles.
Collection Name: articles (or similar, as configured)
Document Structure:
{
"_id": "HT_20250908_141827_5388",
"source": "HT",
"title": "Vice-president election on Sept 9: Numbers back NDA as Radhakrishnan bβ¦",
"date": "2025-09-08",
"time": "13:43:25",
"content": "The stage is set for CP Radhakrishnan andSudershan Reddyto battle it oβ¦",
"url": "https://www.hindustantimes.com/india-news/vice-president-election-on-sβ¦",
"scraped_at": "2025-09-08T14:18:27.311+00:00",
"summarization": {
"summary": "Factual summary text...",
"story_summary": "Child-friendly story format..."
},
"sentiment": "Neutral",
"fact_check": {
"llm_verdict": false,
"fact_check_explanation": "The article claims that Jagdeep Dhankhar resigned as Vice President onβ¦"
},
"article_score": {
"user_article_score": 5,
"llm_score": 6,
"final_custom_score": 5.6
},
"embedding": [/* Array of 384 dimensions */],
"rated_by": ["darshvaishnani1234@gmail.com"],
"processed_status": {
"summarized": true,
"fact_checked": true,
"sentiment": true,
"ner": true,
"scored": true
}
}
Key Fields:
_id: Unique identifier (format: {SOURCE}_{DATE}_{TIME}_{RANDOM})source: News source abbreviation (e.g., βHTβ, βBBCβ, βCNNβ)embedding: Vector embedding (384 dimensions) for similarity matchingarticle_score: Quality score from Article Scorer modulerated_by: Array of user emails who have rated this articleStores user profile information, preferences, and interaction history.
Collection Name: users (or similar, as configured)
Document Structure:
{
"_id": "68be98c16b193cc8e8317f73",
"email": "darshvaishnani1234@gmail.com",
"name": "Darsh Vaishnani",
"picture": "https://lh3.googleusercontent.com/a/ACg8ocJ_ZBbPNqLG1JJikCsw90INemaPXdβ¦",
"phone_number": "+919375981112",
"preferred_time": "01:38",
"rated_articles": ["BBC_20250908_141827_6617"],
"ner_data": {
"Person": [
"Rohit Arya",
"Deepak Kesarkar",
"Ashish Shelar",
"Mohsin Naqvi",
"Devajit Sakia",
"Shukla",
"Suryakumar Yadav",
"Salman Agha"
],
"Location": [/* Array of location entities */],
"Organization": [/* Array of organization entities */]
},
"title_id_list": [
"IndianExpress_20251031_230549_6828",
"IndianExpress_20251001_004427_9329"
],
"title_list": [
"Behind the Powai tragedy: Rohit Arya's long fight with Maharashtra's Sβ¦",
"BCCI ex-officio leaves ACC meeting midway in protest, says Mohsin Naqvβ¦"
],
"user_profile_vector": [/* Array of 384 dimensions - optional */],
"explicit_preferences": [/* Array of user-stated interests - optional */]
}
Key Fields:
rated_articles: Array of article IDs the user has rated/likedner_data: Named entities extracted from userβs liked articlestitle_id_list: IDs of articles the user has interacted withtitle_list: Titles of articles for quick referenceuser_profile_vector: Pre-computed embedding vector for recommendations (optional, cached)Stores the AI-generated detailed summary of user interests.
Collection Name: user_preference_analysis (or similar, as configured)
Document Structure:
{
"_id": {
"$oid": "690516fce88e1f8c72949dee"
},
"email": "darshvaishnani1234@gmail.com",
"name": "Darsh Vaishnani",
"detailed_summary": "Based on the provided entities, the user seems to be interested in news related to education initiatives in India, particularly in Maharashtra (given mentions of 'School Education Department', 'Mazi Shala Sundar Shala', 'School Education Commissionerate', 'Powai', 'Pune', 'Mumbai'). They also seem interested in events and campaigns like 'Mahatma Gandhi Jayanti Se Sardar Patel Jayanti Tak', 'Vikasit Bharat Buildothon', 'Veer Gatha 5.0', 'Ek Ped Ma Ke Naam', and 'Mission Life Eco Club'. There's also a strong interest in cricket, with mentions of 'Suryakumar Yadav', 'Salman Agha', 'Board of Control for Cricket (BCCI)', 'Asian Cricket Council (ACC)', and 'Pakistan Cricket Board (PCB)', implying an interest in India-Pakistan cricket relations and tournaments possibly held in 'Dubai'. The user may also follow news from 'The Indian Express'.\n"
}
Purpose: This collection stores the output from Step 2 of the Recommendation Pipeline (The Analysis Agent). The detailed_summary is the distilled interest paragraph that gets vectorized for recommendations.
Stores pre-computed article recommendations for each user.
Collection Name: recommended_articles (or similar, as configured)
Document Structure:
{
"_id": {
"$oid": "690516ae8b1f3d3714c87f83"
},
"email": "darshvaishnani1234@gmail.com",
"articles": [
{
"_id": "IndianExpress_20251031_230549_6828",
"title": "Behind the Powai tragedy: Rohit Arya's long fight with Maharashtra's School Education Dept",
"similarity": 0.2422
},
{
"_id": "IndianExpress_20251001_004427_9329",
"title": "BCCI ex-officio leaves ACC meeting midway in protest, says Mohsin Naqvi gave no clarity over Asia Cup trophy",
"similarity": 0.2483
}
// ... up to 10 articles
]
}
Key Fields:
email: User identifierarticles: Array of top 10 recommended articles
_id: Article identifiertitle: Article title for displaysimilarity: Cosine similarity score (0.0β1.0) indicating match qualityPurpose: This collection caches the results from Step 5 of the Recommendation Pipeline, allowing quick retrieval of personalized recommendations without recalculating similarity scores on every request.
.env file):
# Navigate to backend directory
cd backend
Mac/Linux:
python -m venv venv
source venv/bin/activate
Windows:
python -m venv venv
.\venv\Scripts\activate
pip install -r requirements.txt
Create a .env file in the backend/ directory:
# MongoDB
MONGO_URI=your_mongodb_connection_string
# Google OAuth
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
SECRET_KEY=your_secret_key
# LLM APIs
GEMINI_API_KEY=your_gemini_api_key
GROQ_API_KEY=your_groq_api_key
# Twilio
TWILIO_ACCOUNT_SID=your_twilio_sid
TWILIO_AUTH_TOKEN=your_twilio_token
TWILIO_PHONE_NUMBER=your_twilio_phone
# Frontend URL
FRONTEND_URL=http://localhost:5173
uvicorn main:app --reload
Server runs at:
π http://localhost:8000
API Documentation:
π http://localhost:8000/docs (Swagger UI)
π http://localhost:8000/redoc (ReDoc)
# Navigate to frontend directory
cd frontend
npm install
npm run dev
App runs at:
π http://localhost:5173
main.py)The FastAPI server provides the following key endpoints:
GET /auth/google - Initiate Google OAuth loginGET /auth/callback - OAuth callback handlerGET /auth/logout - Logout userGET /user - Get current user informationGET /articles - Get articles (with filtering/pagination)GET /articles/{article_id} - Get specific articlePOST /articles/{article_id}/rate - Rate an articleGET /recommendations - Get personalized recommendations for current userPOST /preferences - Update user preferencesPOST /pipeline/summarize - Run summarization pipelinePOST /pipeline/fact-check - Run fact-checking pipelinePOST /pipeline/sentiment - Run sentiment analysis pipelinePOST /pipeline/score - Run article scoring pipelinePOST /pipeline/preprocess - Run full preprocessing pipelinePOST /whatsapp/send - Send WhatsApp message (internal)Directory: Raw_code_developer/
This directory contains:
BBC_filtered_news_articles.jsonfact_check_results.jsonsentiment_results.jsonuser_database.jsonSee LICENSE file for details.
Built with β€οΈ using AI, React, and Python
Report Bug β’ Request Feature β’ Documentation