import streamlit as st import requests import boto3 import os from dotenv import load_dotenv import json # ─────────────────────────────────────────────────────────────────────────────── # Load environment variables (make sure your .env has NEWS_API_KEY, SERPER_API_KEY, AWS_REGION, etc.) load_dotenv() NEWS_API_KEY = os.getenv("NEWS_API_KEY") SERPER_API_KEY = os.getenv("SERPER_API_KEY") AWS_REGION = os.getenv("AWS_REGION") # Setup AWS Bedrock client bedrock = boto3.client("bedrock-runtime", region_name=AWS_REGION) # ─────────────────────────────────────────────────────────────────────────────── # Page configuration st.set_page_config( page_title="📰 News Summarizer Agent (Dark Mode)", page_icon="📰", layout="wide", # wide layout for more room initial_sidebar_state="expanded" ) # ─────────────────────────────────────────────────────────────────────────────── # Custom CSS for dark UI st.markdown( """ """, unsafe_allow_html=True ) # ─────────────────────────────────────────────────────────────────────────────── # Sidebar (for API info, instructions, etc.) with st.sidebar: st.markdown("## ℹ️ About This App") st.markdown( """ - Enter any **topic**, **company name**, or **keywords** above and click **Get News**. - This app will fetch up to 3 articles from **NewsAPI** and 2 from **Serper**, then summarize them using AWS Bedrock (Claude). - You need valid `NEWS_API_KEY`, `SERPER_API_KEY`, and AWS credentials / region configured. """ ) st.markdown("---") st.markdown("## 🔑 API Keys") st.markdown( """ - **NEWS_API_KEY**: Used to fetch articles from NewsAPI.org - **SERPER_API_KEY**: Used to fetch Google‐News‐style results via Serper.dev - **AWS Credentials**: For invoking Claude on Bedrock """ ) st.markdown("---") st.markdown("## 📚 Resources") st.markdown( """ - [NewsAPI Documentation](https://newsapi.org/docs) - [Serper.dev Docs](https://serper.dev/docs) - [AWS Bedrock Claude Guide](https://docs.aws.amazon.com/bedrock/latest/developerguide/claude.html) """ ) # ─────────────────────────────────────────────────────────────────────────────── # Main Title / Header st.markdown('
📰 News Summarizer Agent (Dark Mode)
', unsafe_allow_html=True) st.markdown( '
Get the top 5 latest news with concise summaries on any topic or company you choose.
', unsafe_allow_html=True ) # Input box and button query = st.text_input( label="", placeholder="e.g. Artificial Intelligence, Tesla, Global Markets...", key="search_query" ) btn = st.button("Get News", use_container_width=True) # ─────────────────────────────────────────────────────────────────────────────── # Functions to fetch articles and summarize def get_newsapi_articles(q: str): """ Fetch up to 3 articles from NewsAPI based on query, sorted by published date (newest first). """ url = "https://newsapi.org/v2/everything" params = { "q": q, "sortBy": "publishedAt", "pageSize": 3, "apiKey": NEWS_API_KEY } try: response = requests.get(url, params=params, timeout=10) response.raise_for_status() data = response.json() return data.get("articles", []) except Exception as e: st.error(f"Error fetching from NewsAPI: {e}") return [] def get_serper_articles(q: str): """ Fetch up to 2 news snippets from Serper (Google News API wrapper). """ url = "https://google.serper.dev/news" headers = {"X-API-KEY": SERPER_API_KEY} data = {"q": q} try: response = requests.post(url, headers=headers, json=data, timeout=10) response.raise_for_status() data = response.json() return data.get("news", [])[:2] except Exception as e: st.error(f"Error fetching from Serper: {e}") return [] def summarize_with_bedrock(title: str, content: str) -> str: """ Send the article title + content to AWS Bedrock (Claude) and get a 3–5 line summary. """ prompt = ( f"Summarize the following news article in 3–5 lines:\n\n" f"Title: {title}\n\nContent: {content}\n\nSummary:" ) body = { "anthropic_version": "bedrock-2023-05-31", "messages": [{"role": "user", "content": prompt}], "max_tokens": 300, "temperature": 0.7 } try: response = bedrock.invoke_model( modelId="anthropic.claude-3-sonnet-20240229-v1:0", body=json.dumps(body), contentType="application/json", ) result = json.loads(response["body"].read()) # The response format may vary; adjust indexing if necessary return result["content"][0]["text"].strip() except Exception as e: return f"⚠️ Failed to summarize: {e}" # ─────────────────────────────────────────────────────────────────────────────── # When button is clicked if btn and query: # Use Markdown-style bold instead of raw HTML in st.info st.info(f"🔍 Searching for news on **{query}**...") # Fetch articles newsapi_articles = get_newsapi_articles(query) serper_articles = get_serper_articles(query) # Collect and normalize all_articles = [] # From NewsAPI for art in newsapi_articles: title = art.get("title") or "No Title" content = art.get("description") or art.get("content") or "" url = art.get("url") or "" if content: all_articles.append({"title": title, "content": content, "url": url}) # From Serper for art in serper_articles: title = art.get("title") or "No Title" content = art.get("snippet") or "" url = art.get("link") or "" if content: all_articles.append({"title": title, "content": content, "url": url}) # If no articles found if not all_articles: st.warning("No articles found for that query. Try something else?") else: # Loop through each article and display as a “card” for i, article in enumerate(all_articles, start=1): with st.spinner(f"🖋️ Summarizing Article {i}..."): summary = summarize_with_bedrock(article["title"], article["content"]) # Build a “card” using HTML inside st.markdown card_html = f"""

🗞️ News {i}

{article['title']}
{summary}
Read Full Article »
""" st.markdown(card_html, unsafe_allow_html=True) # ─────────────────────────────────────────────────────────────────────────────── # Footer (optional) st.markdown( """
""", unsafe_allow_html=True )