import streamlit as st import pandas as pd from typing import Union, List, Dict from groq import Groq import os from duckduckgo_search import DDGS # Set page configuration with fullscreen layout and custom theme st.set_page_config( page_title="Z-Alpha News Analysis", layout="wide", initial_sidebar_state="collapsed", page_icon="🔍" ) # Execute JavaScript to make the app fullscreen on load st.markdown(""" """, unsafe_allow_html=True) # Enhanced CSS for a more beautiful Google DeepMind-inspired styling with green background st.markdown(""" """, unsafe_allow_html=True) class DuckDuckGoSearch: """ Custom DuckDuckGo search implementation with robust error handling and result processing. Uses the duckduckgo_search library to fetch and format news results. """ def __init__(self): # Initialize the DuckDuckGo search session self.ddgs = DDGS() def __call__(self, query: str, max_results: int = 5) -> str: try: # Perform the search and get results # The news method is more appropriate for recent news analysis search_results = list(self.ddgs.news( query, max_results=max_results, region='wt-wt', # Worldwide results safesearch='on' )) if not search_results: return "No results found. Try modifying your search query." # Format the results into a readable string formatted_results = [] for idx, result in enumerate(search_results, 1): # Extract available fields with fallbacks for missing data title = result.get('title', 'No title available') snippet = result.get('body', result.get('snippet', 'No description available')) source = result.get('source', 'Unknown source') url = result.get('url', result.get('link', 'No link available')) date = result.get('date', 'Date not available') # Format each result with available information formatted_results.append( f"{idx}. Title: {title}\n" f" Date: {date}\n" f" Source: {source}\n" f" Summary: {snippet}\n" f" URL: {url}\n" ) return "\n".join(formatted_results) except Exception as e: # Provide detailed error information for debugging error_msg = f"Search error: {str(e)}\nTry again with a different search term or check your internet connection." print(f"DuckDuckGo search error: {str(e)}") # For logging return error_msg class GroqLLM: """ LLM interface using Groq's LLama model. Handles API communication and response processing. """ def __init__(self, model_name="llama-3.1-8B-Instant"): self.client = Groq(api_key=os.environ.get("GROQ_API_KEY")) self.model_name = model_name def __call__(self, prompt: Union[str, dict, List[Dict]]) -> str: try: # Convert prompt to string if it's a complex structure prompt_str = str(prompt) if isinstance(prompt, (dict, list)) else prompt # Make API call to Groq completion = self.client.chat.completions.create( model=self.model_name, messages=[{ "role": "user", "content": prompt_str }], temperature=0.7, max_tokens=1024, stream=False ) return completion.choices[0].message.content if completion.choices else "Error: No response generated" except Exception as e: error_msg = f"Error generating response: {str(e)}" print(error_msg) # For logging return error_msg def create_analysis_prompt(topic: str, search_results: str) -> str: """ Creates a detailed prompt for news analysis, structuring the request to get comprehensive and well-organized results from the LLM. """ return f"""Analyze the following news information about {topic}. Search Results: {search_results} Please provide a comprehensive analysis including: 1. Key Points Summary: - Main events and developments - Critical updates and changes 2. Stakeholder Analysis: - Primary parties involved - Their roles and positions 3. Impact Assessment: - Immediate implications - Potential long-term effects - Broader context and significance 4. Multiple Perspectives: - Different viewpoints on the issue - Areas of agreement and contention 5. Fact Check & Reliability: - Verification of major claims - Consistency across sources - Source credibility assessment Please format the analysis in a clear, journalistic style with section headers.""" def log_agent_activity(prompt: str, result: str, agent_name: str): """ Creates an expandable log of agent activities in the Streamlit interface for transparency and debugging purposes. """ with st.expander("🔍 View Agent Activity Log"): st.markdown("

Agent Activity Log

", unsafe_allow_html=True) st.markdown(f"

Agent: {agent_name}

", unsafe_allow_html=True) st.markdown("
", unsafe_allow_html=True) st.markdown("

Input Prompt:

", unsafe_allow_html=True) st.code(prompt, language="text") st.markdown("
", unsafe_allow_html=True) st.markdown("
", unsafe_allow_html=True) st.markdown("

Analysis Output:

", unsafe_allow_html=True) st.code(result, language="text") st.markdown("
", unsafe_allow_html=True) # Animated header with branded logo and description st.markdown("""
Z-Agent News Analysis

Intelligent news analysis powered by AI. Get comprehensive insights and multiple perspectives on any topic.

""", unsafe_allow_html=True) # Main content area st.markdown('
', unsafe_allow_html=True) # Initialize the components try: # Initialize LLM and search tool llm = GroqLLM() search_tool = DuckDuckGoSearch() # Input section with enhanced design st.markdown("

News Topic Analysis

", unsafe_allow_html=True) news_topic = st.text_input( "What news topic would you like to analyze?", placeholder="E.g., Recent developments in renewable energy, Tech industry layoffs, Global climate agreements...", key="news_topic_input" ) # Analysis options in a cleaner layout st.markdown("
", unsafe_allow_html=True) st.markdown("

Analysis Options

", unsafe_allow_html=True) col1, col2, col3 = st.columns([2, 2, 2]) with col1: st.markdown("
", unsafe_allow_html=True) st.markdown("

Search Depth

", unsafe_allow_html=True) search_depth = st.slider( "##", min_value=3, max_value=10, value=5, help="Number of news sources to analyze", key="search_depth_slider", label_visibility="collapsed" ) st.markdown("

Number of sources: " + str(search_depth) + "

", unsafe_allow_html=True) st.markdown("
", unsafe_allow_html=True) with col2: st.markdown("
", unsafe_allow_html=True) st.markdown("

Analysis Type

", unsafe_allow_html=True) analysis_type = st.selectbox( "##", ["Comprehensive", "Quick Summary", "Technical", "Simplified"], help="Choose the style and depth of analysis", key="analysis_type_select", label_visibility="collapsed" ) st.markdown("

Selected: " + analysis_type + "

", unsafe_allow_html=True) st.markdown("
", unsafe_allow_html=True) with col3: st.markdown("
", unsafe_allow_html=True) st.markdown("

Time Period

", unsafe_allow_html=True) time_period = st.selectbox( "##", ["Last 7 days", "Last 30 days", "Last 24 hours"], help="How recent should the news be", key="time_period_select", label_visibility="collapsed" ) st.markdown("

Selected: " + time_period + "

", unsafe_allow_html=True) st.markdown("
", unsafe_allow_html=True) # Generate analysis button st.markdown("
", unsafe_allow_html=True) col1, col2, col3 = st.columns([2, 2, 2]) with col2: analyze_button = st.button("ANALYZE NEWS") st.markdown('
', unsafe_allow_html=True) # Results section if analyze_button and news_topic: with st.spinner(""): try: # Progress indicators with enhanced styling progress_container = st.container() progress_container.markdown('
', unsafe_allow_html=True) progress_placeholder = progress_container.empty() progress_placeholder.markdown("""
Searching for recent news sources...
""", unsafe_allow_html=True) # Determine time frame from selection time_map = { "Last 24 hours": "24h", "Last 7 days": "7d", "Last 30 days": "30d" } time_frame = time_map.get(time_period, "7d") # Perform search search_results = search_tool( f"Latest news about {news_topic} {time_frame}", max_results=search_depth ) if not search_results.startswith(("Search error", "No results")): # Update progress progress_placeholder.markdown("""
Analyzing search results and generating insights...
""", unsafe_allow_html=True) # Create analysis prompt analysis_prompt = create_analysis_prompt(news_topic, search_results) # Get analysis from LLM analysis_result = llm(analysis_prompt) # Clear progress messages progress_container.empty() # Display results in tabs for better organization st.markdown('
', unsafe_allow_html=True) st.markdown(f'

Analysis: {news_topic}

', unsafe_allow_html=True) tab1, tab2 = st.tabs(["📊 Analysis", "📰 Sources"]) with tab1: st.markdown('
', unsafe_allow_html=True) st.markdown(analysis_result) st.markdown('
', unsafe_allow_html=True) with tab2: st.markdown("

News Sources Used in Analysis

", unsafe_allow_html=True) # Parse and display sources in a more structured way sources = search_results.split("\n\n") for source in sources: lines = source.strip().split("\n") if len(lines) >= 5: # Ensure we have enough lines title_line = lines[0].replace("1. Title: ", "").replace("2. Title: ", "").replace("3. Title: ", "").replace("4. Title: ", "").replace("5. Title: ", "") date_line = lines[1].replace(" Date: ", "") source_line = lines[2].replace(" Source: ", "") url_line = lines[4].replace(" URL: ", "") st.markdown(f"""

{title_line}

{source_line} {date_line}
Read original article
""", unsafe_allow_html=True) st.markdown('
', unsafe_allow_html=True) # Log the activity log_agent_activity( analysis_prompt, analysis_result, "News Analysis Agent" ) else: progress_container.empty() st.error(f"""
{search_results}
""", unsafe_allow_html=True) except Exception as e: st.error(f"""
An error occurred during analysis: {str(e)}
""", unsafe_allow_html=True) elif analyze_button: # This handles the case when analyze button is clicked but no topic is entered st.warning("""
Please enter a news topic to analyze.
""", unsafe_allow_html=True) # Tips and usage guidance section - displayed when no analysis is in progress if not analyze_button or not news_topic: st.markdown('
', unsafe_allow_html=True) st.markdown("

Tips for Better Results

", unsafe_allow_html=True) st.markdown("""
💡
Be specific with your topic. Instead of "climate change", try "recent climate legislation in the EU".
🔍
Adjust search depth to find the right balance between comprehensive coverage and analysis speed.
📊
Choose the right analysis type based on your needs:
  • Comprehensive: Full analysis with multiple perspectives
  • Quick Summary: Brief overview of key points
  • Technical: Detailed analysis with industry-specific terminology
  • Simplified: Easy-to-understand breakdown of complex topics
⏱️
Select an appropriate time period based on how recent you want the news to be.
""", unsafe_allow_html=True) st.markdown('
', unsafe_allow_html=True) # Example topics for quick selection st.markdown('
', unsafe_allow_html=True) st.markdown("

Try These Topics

", unsafe_allow_html=True) col1, col2, col3 = st.columns(3) with col1: if st.button("🌍 Climate Change Policies"): st.session_state.news_topic_input = "Recent climate change policies and agreements" st.experimental_rerun() with col2: if st.button("💰 Cryptocurrency Trends"): st.session_state.news_topic_input = "Latest developments in cryptocurrency markets" st.experimental_rerun() with col3: if st.button("🔬 AI Research Breakthroughs"): st.session_state.news_topic_input = "Recent breakthroughs in artificial intelligence research" st.experimental_rerun() st.markdown('
', unsafe_allow_html=True) # Footer with attribution and version info st.markdown(""" """, unsafe_allow_html=True) except Exception as e: # Global error handling to catch any unforeseen issues st.error(f"""
Application Error: {str(e)}
""", unsafe_allow_html=True) st.markdown("""

Troubleshooting Tips:

""", unsafe_allow_html=True) # Add a hidden feature to reset the application state if st.sidebar.button("Reset Application", key="reset_app"): for key in st.session_state.keys(): del st.session_state[key] st.experimental_rerun() # Optional: Add a feedback mechanism with st.sidebar: st.markdown("### Feedback") feedback = st.text_area("Share your thoughts or report issues", placeholder="Your feedback helps us improve...") if st.button("Submit Feedback"): st.success("Thank you for your feedback!") # In a production app, you would save this feedback to a database