DarshanaD commited on
Commit
4eefe1b
Β·
1 Parent(s): 383c8e6

Initial commit

Browse files
Files changed (3) hide show
  1. README.md +3 -3
  2. app.py +513 -54
  3. requirements.txt +10 -1
README.md CHANGED
@@ -1,10 +1,10 @@
1
  ---
2
- title: News Summarizer Agent
3
  emoji: πŸ’¬
4
  colorFrom: yellow
5
  colorTo: purple
6
- sdk: gradio
7
- sdk_version: 5.0.1
8
  app_file: app.py
9
  pinned: false
10
  license: apache-2.0
 
1
  ---
2
+ title: Assignment1KG
3
  emoji: πŸ’¬
4
  colorFrom: yellow
5
  colorTo: purple
6
+ sdk: streamlit
7
+ sdk_version: 1.35.0
8
  app_file: app.py
9
  pinned: false
10
  license: apache-2.0
app.py CHANGED
@@ -1,64 +1,523 @@
1
- import gradio as gr
2
- from huggingface_hub import InferenceClient
3
-
4
  """
5
- For more information on `huggingface_hub` Inference API support, please check the docs: https://huggingface.co/docs/huggingface_hub/v0.22.2/en/guides/inference
 
6
  """
7
- client = InferenceClient("HuggingFaceH4/zephyr-7b-beta")
8
-
9
-
10
- def respond(
11
- message,
12
- history: list[tuple[str, str]],
13
- system_message,
14
- max_tokens,
15
- temperature,
16
- top_p,
17
- ):
18
- messages = [{"role": "system", "content": system_message}]
19
-
20
- for val in history:
21
- if val[0]:
22
- messages.append({"role": "user", "content": val[0]})
23
- if val[1]:
24
- messages.append({"role": "assistant", "content": val[1]})
25
 
26
- messages.append({"role": "user", "content": message})
 
 
 
 
 
 
 
 
 
27
 
28
- response = ""
 
 
29
 
30
- for message in client.chat_completion(
31
- messages,
32
- max_tokens=max_tokens,
33
- stream=True,
34
- temperature=temperature,
35
- top_p=top_p,
36
- ):
37
- token = message.choices[0].delta.content
38
 
39
- response += token
40
- yield response
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
- """
44
- For information on how to customize the ChatInterface, peruse the gradio docs: https://www.gradio.app/docs/chatinterface
45
- """
46
- demo = gr.ChatInterface(
47
- respond,
48
- additional_inputs=[
49
- gr.Textbox(value="You are a friendly Chatbot.", label="System message"),
50
- gr.Slider(minimum=1, maximum=2048, value=512, step=1, label="Max new tokens"),
51
- gr.Slider(minimum=0.1, maximum=4.0, value=0.7, step=0.1, label="Temperature"),
52
- gr.Slider(
53
- minimum=0.1,
54
- maximum=1.0,
55
- value=0.95,
56
- step=0.05,
57
- label="Top-p (nucleus sampling)",
58
- ),
59
- ],
60
- )
61
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
  if __name__ == "__main__":
64
- demo.launch()
 
1
+ #!/usr/bin/env python3
 
 
2
  """
3
+ News Summarizer Agent - Assignment 2
4
+ An agentic AI app that fetches and summarizes latest news on any topic using NewsAPI and Serper.dev
5
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
+ import os
8
+ import json
9
+ import boto3
10
+ import streamlit as st
11
+ import requests
12
+ from dotenv import load_dotenv
13
+ import logging
14
+ from typing import Dict, List, Any, Optional
15
+ from datetime import datetime, timedelta
16
+ import time
17
 
18
+ # Configure logging
19
+ logging.basicConfig(level=logging.INFO)
20
+ logger = logging.getLogger(__name__)
21
 
22
+ # Load environment variables
23
+ load_dotenv()
 
 
 
 
 
 
24
 
25
+ class NewsAgent:
26
+ """Agentic AI News Summarizer using multiple news sources"""
27
+
28
+ def __init__(self):
29
+ """Initialize the news agent with API credentials"""
30
+ self.setup_credentials()
31
+ self.setup_aws_bedrock()
32
+
33
+ def setup_credentials(self):
34
+ """Setup API credentials from environment variables"""
35
+ self.aws_access_key = os.getenv("AWS_ACCESS_KEY_ID")
36
+ self.aws_secret_key = os.getenv("AWS_SECRET_ACCESS_KEY")
37
+ self.aws_region = os.getenv("AWS_REGION", "us-east-1")
38
+
39
+ # News API credentials
40
+ self.newsapi_key = os.getenv("NEWSAPI_KEY")
41
+ self.serper_key = os.getenv("SERPER_API_KEY")
42
+
43
+ # Validate AWS credentials
44
+ if not all([self.aws_access_key, self.aws_secret_key]):
45
+ raise ValueError("Missing AWS credentials. Check your .env file.")
46
+
47
+ # Check if at least one news API key is available
48
+ if not self.newsapi_key and not self.serper_key:
49
+ st.warning("⚠️ No news API keys found. Please add NEWSAPI_KEY and/or SERPER_API_KEY to your .env file.")
50
+ elif (self.newsapi_key == "your_newsapi_key_from_newsapi.org" or not self.newsapi_key) and \
51
+ (self.serper_key == "your_serper_key_from_serper.dev" or not self.serper_key):
52
+ st.error("❌ **API Keys are still placeholders!** Please replace with real API keys from newsapi.org and serper.dev")
53
+
54
+ def setup_aws_bedrock(self):
55
+ """Initialize AWS Bedrock client"""
56
+ try:
57
+ self.bedrock_client = boto3.client(
58
+ 'bedrock-runtime',
59
+ aws_access_key_id=self.aws_access_key,
60
+ aws_secret_access_key=self.aws_secret_key,
61
+ region_name=self.aws_region
62
+ )
63
+ logger.info("AWS Bedrock client initialized successfully")
64
+ except Exception as e:
65
+ logger.error(f"Failed to initialize AWS Bedrock: {e}")
66
+ raise
67
+
68
+ def call_claude(self, prompt: str, max_tokens: int = 1000) -> str:
69
+ """Call AWS Bedrock Claude for AI processing"""
70
+ try:
71
+ body = {
72
+ "anthropic_version": "bedrock-2023-05-31",
73
+ "max_tokens": max_tokens,
74
+ "temperature": 0.3,
75
+ "top_p": 0.9,
76
+ "messages": [
77
+ {
78
+ "role": "user",
79
+ "content": prompt
80
+ }
81
+ ]
82
+ }
83
+
84
+ response = self.bedrock_client.invoke_model(
85
+ modelId="anthropic.claude-3-sonnet-20240229-v1:0",
86
+ contentType="application/json",
87
+ accept="application/json",
88
+ body=json.dumps(body)
89
+ )
90
+
91
+ response_body = json.loads(response['body'].read())
92
+ return response_body['content'][0]['text'].strip()
93
+
94
+ except Exception as e:
95
+ logger.error(f"Error calling Claude: {e}")
96
+ return f"Error: {e}"
97
+
98
+ def fetch_news_from_newsapi(self, topic: str, max_articles: int = 10) -> List[Dict]:
99
+ """Fetch news from NewsAPI"""
100
+ if not self.newsapi_key:
101
+ return []
102
+
103
+ try:
104
+ # Calculate date for recent news (last 7 days)
105
+ from_date = (datetime.now() - timedelta(days=7)).strftime('%Y-%m-%d')
106
+
107
+ url = "https://newsapi.org/v2/everything"
108
+ params = {
109
+ 'q': topic,
110
+ 'apiKey': self.newsapi_key,
111
+ 'language': 'en',
112
+ 'sortBy': 'publishedAt',
113
+ 'from': from_date,
114
+ 'pageSize': max_articles
115
+ }
116
+
117
+ response = requests.get(url, params=params, timeout=10)
118
+ response.raise_for_status()
119
+
120
+ data = response.json()
121
+ articles = data.get('articles', [])
122
+
123
+ news_items = []
124
+ for article in articles:
125
+ if article.get('title') and article.get('description'):
126
+ news_items.append({
127
+ 'title': article.get('title', ''),
128
+ 'description': article.get('description', ''),
129
+ 'content': article.get('content', ''),
130
+ 'url': article.get('url', ''),
131
+ 'published_at': article.get('publishedAt', ''),
132
+ 'source': article.get('source', {}).get('name', 'NewsAPI'),
133
+ 'api_source': 'NewsAPI'
134
+ })
135
+
136
+ logger.info(f"Fetched {len(news_items)} articles from NewsAPI")
137
+ return news_items
138
+
139
+ except Exception as e:
140
+ logger.error(f"Error fetching from NewsAPI: {e}")
141
+ return []
142
+
143
+ def fetch_news_from_serper(self, topic: str, max_articles: int = 10) -> List[Dict]:
144
+ """Fetch news from Serper.dev (Google News)"""
145
+ if not self.serper_key:
146
+ return []
147
+
148
+ try:
149
+ url = "https://google.serper.dev/news"
150
+ headers = {
151
+ 'X-API-KEY': self.serper_key,
152
+ 'Content-Type': 'application/json'
153
+ }
154
+
155
+ payload = {
156
+ 'q': topic,
157
+ 'num': max_articles,
158
+ 'tbm': 'nws'
159
+ }
160
+
161
+ response = requests.post(url, headers=headers, json=payload, timeout=10)
162
+ response.raise_for_status()
163
+
164
+ data = response.json()
165
+ articles = data.get('news', [])
166
+
167
+ news_items = []
168
+ for article in articles:
169
+ if article.get('title') and article.get('snippet'):
170
+ news_items.append({
171
+ 'title': article.get('title', ''),
172
+ 'description': article.get('snippet', ''),
173
+ 'content': article.get('snippet', ''),
174
+ 'url': article.get('link', ''),
175
+ 'published_at': article.get('date', ''),
176
+ 'source': article.get('source', 'Google News'),
177
+ 'api_source': 'Serper'
178
+ })
179
+
180
+ logger.info(f"Fetched {len(news_items)} articles from Serper")
181
+ return news_items
182
+
183
+ except Exception as e:
184
+ logger.error(f"Error fetching from Serper: {e}")
185
+ return []
186
+
187
+ def fetch_all_news(self, topic: str) -> List[Dict]:
188
+ """Fetch news from all available sources"""
189
+ all_news = []
190
+
191
+ # Check which APIs are available
192
+ newsapi_available = self.newsapi_key and self.newsapi_key != "your_newsapi_key_from_newsapi.org"
193
+ serper_available = self.serper_key and self.serper_key != "your_serper_key_from_serper.dev"
194
+
195
+ st.info(f"πŸ“‘ **Available APIs:** NewsAPI: {'βœ…' if newsapi_available else '❌'} | Serper: {'βœ…' if serper_available else '❌'}")
196
+
197
+ # Fetch from NewsAPI
198
+ if newsapi_available:
199
+ st.info("πŸ” **Fetching from NewsAPI...**")
200
+ newsapi_articles = self.fetch_news_from_newsapi(topic, 10)
201
+ all_news.extend(newsapi_articles)
202
+ st.success(f"βœ… **NewsAPI:** {len(newsapi_articles)} articles fetched")
203
+ else:
204
+ st.warning("⚠️ **NewsAPI:** Invalid or missing API key")
205
+
206
+ # Fetch from Serper
207
+ if serper_available:
208
+ st.info("πŸ” **Fetching from Serper.dev...**")
209
+ serper_articles = self.fetch_news_from_serper(topic, 10)
210
+ all_news.extend(serper_articles)
211
+ st.success(f"βœ… **Serper.dev:** {len(serper_articles)} articles fetched")
212
+ else:
213
+ st.warning("⚠️ **Serper.dev:** Invalid or missing API key")
214
+
215
+ if not newsapi_available and not serper_available:
216
+ st.error("❌ **No valid API keys found!** Please add real API keys to your .env file")
217
+ return []
218
+
219
+ # Remove duplicates based on title similarity
220
+ unique_news = self.remove_duplicate_articles(all_news)
221
+
222
+ # Sort by published date (most recent first)
223
+ unique_news.sort(key=lambda x: x.get('published_at', ''), reverse=True)
224
+
225
+ st.info(f"πŸ“Š **Total unique articles:** {len(unique_news)}")
226
+ return unique_news
227
+
228
+ def remove_duplicate_articles(self, articles: List[Dict]) -> List[Dict]:
229
+ """Remove duplicate articles based on title similarity"""
230
+ unique_articles = []
231
+ seen_titles = set()
232
+
233
+ for article in articles:
234
+ title = article.get('title', '').lower().strip()
235
+ # Simple deduplication - check if title contains similar words
236
+ title_words = set(title.split())
237
+
238
+ is_duplicate = False
239
+ for seen_title in seen_titles:
240
+ seen_words = set(seen_title.split())
241
+ # If 70% of words are common, consider it duplicate
242
+ if len(title_words.intersection(seen_words)) / max(len(title_words), len(seen_words)) > 0.7:
243
+ is_duplicate = True
244
+ break
245
+
246
+ if not is_duplicate:
247
+ unique_articles.append(article)
248
+ seen_titles.add(title)
249
+
250
+ return unique_articles
251
+
252
+ def generate_relevant_headline(self, original_title: str, content: str, topic: str) -> str:
253
+ """Generate a topic-relevant headline using Claude"""
254
+ prompt = f"""
255
+ You are a news headline editor. Create a relevant, engaging headline for this news article that specifically relates to the topic "{topic}".
256
+
257
+ Original Title: {original_title}
258
+ Article Content: {content[:500]}...
259
+
260
+ Requirements:
261
+ 1. The headline should be directly relevant to the topic "{topic}"
262
+ 2. Keep it concise (under 80 characters)
263
+ 3. Make it engaging and informative
264
+ 4. Focus on the aspect most relevant to the search topic
265
+ 5. Don't use clickbait language
266
+
267
+ Return only the headline, no explanations.
268
+ """
269
+
270
+ try:
271
+ relevant_headline = self.call_claude(prompt, max_tokens=100)
272
+ # Clean up the response
273
+ relevant_headline = relevant_headline.strip().strip('"').strip("'")
274
+ return relevant_headline if relevant_headline else original_title
275
+ except:
276
+ return original_title
277
+
278
+ def generate_summary(self, title: str, description: str, content: str, topic: str) -> str:
279
+ """Generate a 3-5 line summary using Claude"""
280
+ prompt = f"""
281
+ You are a news summarizer. Create a concise 3-5 line summary of this news article, focusing on aspects most relevant to the topic "{topic}".
282
+
283
+ Title: {title}
284
+ Description: {description}
285
+ Content: {content[:1000]}...
286
+
287
+ Requirements:
288
+ 1. Write exactly 3-5 lines
289
+ 2. Focus on information most relevant to "{topic}"
290
+ 3. Include key facts, numbers, and important details
291
+ 4. Write in clear, professional language
292
+ 5. Each line should be a complete sentence
293
+
294
+ Summary:
295
+ """
296
+
297
+ try:
298
+ summary = self.call_claude(prompt, max_tokens=200)
299
+ return summary.strip()
300
+ except Exception as e:
301
+ return f"Summary generation failed: {e}"
302
+
303
+ def process_news_agentic(self, topic: str) -> List[Dict]:
304
+ """Main agentic processing pipeline"""
305
+ st.info(f"πŸ€– **Agent Status:** Starting news analysis for '{topic}'...")
306
+
307
+ # Step 1: Fetch news from multiple sources
308
+ with st.spinner("πŸ” **Agent Action:** Fetching news from multiple sources..."):
309
+ all_news = self.fetch_all_news(topic)
310
+
311
+ if not all_news:
312
+ st.error("❌ **Agent Result:** No news articles found. Please check your API keys and try again.")
313
+ return []
314
+
315
+ st.success(f"βœ… **Agent Result:** Found {len(all_news)} unique articles")
316
+
317
+ # Step 2: Process top 5 articles
318
+ with st.spinner("🧠 **Agent Action:** Processing articles with AI..."):
319
+ top_articles = all_news[:5]
320
+ processed_articles = []
321
+
322
+ progress_bar = st.progress(0)
323
+ for i, article in enumerate(top_articles):
324
+ st.info(f"πŸ”„ **Agent Processing:** Article {i+1}/5 - {article['title'][:50]}...")
325
+
326
+ # Generate relevant headline
327
+ relevant_headline = self.generate_relevant_headline(
328
+ article['title'],
329
+ article.get('content', article['description']),
330
+ topic
331
+ )
332
+
333
+ # Generate summary
334
+ summary = self.generate_summary(
335
+ article['title'],
336
+ article['description'],
337
+ article.get('content', article['description']),
338
+ topic
339
+ )
340
+
341
+ processed_article = {
342
+ 'original_title': article['title'],
343
+ 'relevant_headline': relevant_headline,
344
+ 'summary': summary,
345
+ 'url': article['url'],
346
+ 'published_at': article['published_at'],
347
+ 'source': article['source'],
348
+ 'api_source': article['api_source']
349
+ }
350
+
351
+ processed_articles.append(processed_article)
352
+ progress_bar.progress((i + 1) / len(top_articles))
353
+
354
+ # Small delay to avoid overwhelming the API
355
+ time.sleep(0.5)
356
+
357
+ st.success("βœ… **Agent Complete:** All articles processed successfully!")
358
+ return processed_articles
359
+
360
+ def display_news_results(self, processed_articles: List[Dict], topic: str):
361
+ """Display the processed news results in Streamlit"""
362
+ st.header(f"πŸ“° Top 5 Latest News on '{topic}'")
363
+ st.markdown("---")
364
+
365
+ for i, article in enumerate(processed_articles, 1):
366
+ with st.container():
367
+ # Headline
368
+ st.subheader(f"{i}. {article['relevant_headline']}")
369
+
370
+ # Metadata
371
+ col1, col2, col3 = st.columns(3)
372
+ with col1:
373
+ st.caption(f"πŸ“… {article['published_at'][:10] if article['published_at'] else 'Date unavailable'}")
374
+ with col2:
375
+ st.caption(f"πŸ”— Source: {article['source']}")
376
+ with col3:
377
+ st.caption(f"πŸ€– Via: {article['api_source']}")
378
+
379
+ # Summary
380
+ st.markdown(f"**Summary:**")
381
+ st.write(article['summary'])
382
+
383
+ # Read more link
384
+ if article['url']:
385
+ st.markdown(f"[πŸ”— Read full article]({article['url']})")
386
+
387
+ # Original title (for reference)
388
+ with st.expander("πŸ“ Original Title"):
389
+ st.write(article['original_title'])
390
+
391
+ st.markdown("---")
392
 
393
+ def create_sample_env():
394
+ """Display sample .env file content"""
395
+ st.info("""
396
+ πŸ“ **Sample .env file:**
397
+ ```
398
+ # AWS Bedrock Credentials
399
+ AWS_ACCESS_KEY_ID="your_aws_access_key"
400
+ AWS_SECRET_ACCESS_KEY="your_aws_secret_key"
401
+ AWS_REGION="us-east-1"
402
+
403
+ # News API Keys (get from respective websites)
404
+ NEWSAPI_KEY="your_newsapi_key_from_newsapi.org"
405
+ SERPER_API_KEY="your_serper_key_from_serper.dev"
406
+ ```
407
+
408
+ **Get API Keys:**
409
+ - NewsAPI: https://newsapi.org/
410
+ - Serper.dev: https://serper.dev/
411
+ """)
412
 
413
+ def main():
414
+ """Main Streamlit application"""
415
+
416
+ # Page configuration
417
+ st.set_page_config(
418
+ page_title="πŸ“° News Summarizer Agent",
419
+ page_icon="πŸ€–",
420
+ layout="wide"
421
+ )
422
+
423
+ # Header
424
+ st.title("πŸ€– News Summarizer Agent")
425
+ st.markdown("*Powered by Agentic AI with AWS Bedrock Claude 3.5 Sonnet*")
426
+ st.markdown("Get the top 5 latest news on any topic with AI-generated relevant headlines and summaries.")
427
+
428
+ # Sidebar
429
+ with st.sidebar:
430
+ st.header("πŸ”§ Configuration")
431
+ create_sample_env()
432
+
433
+ st.header("πŸ“Š Agent Features")
434
+ st.markdown("""
435
+ βœ… **Multi-Source News Fetching**
436
+ - NewsAPI.org integration
437
+ - Serper.dev (Google News) integration
438
+
439
+ βœ… **Agentic AI Processing**
440
+ - Relevant headline generation
441
+ - Intelligent summarization
442
+ - Duplicate removal
443
+
444
+ βœ… **Smart Analysis**
445
+ - Topic-focused content
446
+ - Latest news prioritization
447
+ - Professional summaries
448
+ """)
449
+
450
+ # Initialize the agent
451
+ if 'news_agent' not in st.session_state:
452
+ try:
453
+ with st.spinner("πŸš€ Initializing News Agent..."):
454
+ st.session_state.news_agent = NewsAgent()
455
+ st.success("βœ… News Agent initialized successfully!")
456
+ except Exception as e:
457
+ st.error(f"❌ Failed to initialize News Agent: {e}")
458
+ st.stop()
459
+
460
+ # Main interface
461
+ st.header("πŸ” Search for News")
462
+
463
+ # Input section
464
+ col1, col2 = st.columns([3, 1])
465
+
466
+ with col1:
467
+ topic = st.text_input(
468
+ "Enter topic, company name, or keywords:",
469
+ placeholder="e.g., Tesla, Climate Change, Artificial Intelligence, Apple, Biden...",
470
+ help="Enter any topic you want to get the latest news about"
471
+ )
472
+
473
+ with col2:
474
+ search_button = st.button("πŸ” Get News", type="primary", use_container_width=True)
475
+
476
+ # Example topics
477
+ st.markdown("**πŸ’‘ Example Topics:**")
478
+ example_topics = ["Tesla", "Climate Change", "Artificial Intelligence", "Apple", "Bitcoin", "Space X", "Netflix", "Microsoft"]
479
+
480
+ cols = st.columns(4)
481
+ for i, example_topic in enumerate(example_topics):
482
+ with cols[i % 4]:
483
+ if st.button(example_topic, key=f"example_{i}"):
484
+ st.session_state.selected_topic = example_topic
485
+ topic = example_topic
486
+
487
+ # Process the search
488
+ if search_button and topic:
489
+ try:
490
+ # Process news using agentic AI
491
+ processed_articles = st.session_state.news_agent.process_news_agentic(topic)
492
+
493
+ if processed_articles:
494
+ # Display results
495
+ st.session_state.news_agent.display_news_results(processed_articles, topic)
496
+
497
+ # Download option
498
+ st.markdown("---")
499
+ col1, col2 = st.columns([1, 1])
500
+ with col1:
501
+ if st.button("πŸ“₯ Download Results as JSON"):
502
+ json_data = json.dumps(processed_articles, indent=2)
503
+ st.download_button(
504
+ label="πŸ“„ Download JSON",
505
+ data=json_data,
506
+ file_name=f"news_summary_{topic.replace(' ', '_')}.json",
507
+ mime="application/json"
508
+ )
509
+ else:
510
+ st.warning("⚠️ No news articles found for this topic. Try a different search term.")
511
+
512
+ except Exception as e:
513
+ st.error(f"❌ Error processing news: {e}")
514
+
515
+ elif search_button and not topic:
516
+ st.warning("⚠️ Please enter a topic to search for news.")
517
+
518
+ # Footer
519
+ st.markdown("---")
520
+ st.markdown("*πŸ€– Built with Agentic AI β€’ Powered by AWS Bedrock Claude 3.5 Sonnet β€’ News from NewsAPI & Serper.dev*")
521
 
522
  if __name__ == "__main__":
523
+ main()
requirements.txt CHANGED
@@ -1 +1,10 @@
1
- huggingface_hub==0.25.2
 
 
 
 
 
 
 
 
 
 
1
+ huggingface_hub==0.25.2
2
+ qdrant_client
3
+ streamlit
4
+ boto3
5
+ PyPDF2
6
+ chromadb
7
+ datasets
8
+
9
+ streamlit
10
+ boto3