Sumayyea/TrendingAIBot
0
1"""2AI Trends Assistant - Fetches trending AI papers (arXiv) and YouTube videos3"""4 5import os6import streamlit as st7import arxiv8import requests9from dotenv import load_dotenv10from typing import List, Dict11 12load_dotenv()13 14# ---------------------15# API Keys16# ---------------------17GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY")18GEMINI_API_URL = os.environ.get("GEMINI_API_URL", "").strip()19YOUTUBE_API_KEY = os.environ.get("YOUTUBE_API_KEY")20 21# ---------------------22# Super Prompt23# ---------------------24SYSTEM_PROMPT = """25Role: You are an expert AI research assistant who specializes in discovering and explaining trending topics, papers, and videos about Artificial Intelligence.26Goal: Help users explore and understand what's currently trending in AI.27"""28 29# ---------------------30# Gemini API wrapper31# ---------------------32def call_gemini(system_prompt: str, user_message: str, model: str = "gemini-2.0-flash") -> str:33 if not GEMINI_API_KEY:34 return "[Gemini unavailable] No GEMINI_API_KEY found."35 if not GEMINI_API_URL:36 return "[Gemini endpoint not configured] Set GEMINI_API_URL."37 try:38 headers = {39 "Authorization": f"Bearer {GEMINI_API_KEY}",40 "Content-Type": "application/json",41 }42 payload = {"system": system_prompt, "input": user_message, "model": model}43 resp = requests.post(GEMINI_API_URL, headers=headers, json=payload, timeout=30)44 resp.raise_for_status()45 data = resp.json()46 if isinstance(data, dict):47 if "output" in data:48 return data["output"]49 if "choices" in data and data["choices"]:50 ch0 = data["choices"][0]51 if "text" in ch0:52 return ch0["text"]53 if "message" in ch0 and "content" in ch0["message"]:54 return ch0["message"]["content"]55 return str(data)56 except Exception as e:57 return f"[Gemini request failed] {e}"58 59# ---------------------60# arXiv fetcher (IMPROVED)61# ---------------------62def fetch_arxiv(query: str, max_results: int = 5, sort_by: str = "Relevance") -> List[Dict]:63 """64 Fetch AI papers from arXiv with improved search relevance.65 66 Improvements:67 - Searches in title (ti:) and abstract (abs:) for better matching68 - Supports sorting by Relevance or Recent69 - Uses quoted phrases for multi-word queries70 - Filters to CS/AI-related categories71 """72 73 # Clean and prepare query74 query = query.strip()75 76 # Build search query - search in title and abstract77 # For multi-word queries, search as phrase AND individual terms78 words = query.split()79 80 if len(words) > 1:81 # Multi-word: search exact phrase in title OR abstract, plus individual terms82 phrase_query = f'"{query}"'83 title_search = f'ti:{phrase_query}'84 abstract_search = f'abs:{phrase_query}'85 86 # Also search individual important words (for broader matching)87 word_searches = " AND ".join([f'all:{w}' for w in words if len(w) > 2])88 89 # Combine: (exact phrase in title OR abstract) OR (all words present)90 search_query = f'({title_search} OR {abstract_search}) OR ({word_searches})'91 else:92 # Single word: search in title and abstract93 search_query = f'ti:{query} OR abs:{query}'94 95 # Add AI-related category filter (but use OR to not be too restrictive)96 ai_categories = "(cat:cs.AI OR cat:cs.LG OR cat:cs.CL OR cat:cs.CV OR cat:cs.NE OR cat:cs.MA OR cat:stat.ML)"97 full_query = f'({search_query}) AND {ai_categories}'98 99 # Determine sort criteria100 if sort_by == "Relevance":101 sort_criterion = arxiv.SortCriterion.Relevance102 else:103 sort_criterion = arxiv.SortCriterion.SubmittedDate104 105 search = arxiv.Search(106 query=full_query,107 max_results=max_results * 2, # Fetch extra to filter duplicates108 sort_by=sort_criterion,109 sort_order=arxiv.SortOrder.Descending110 )111 112 results = []113 seen_titles = set()114 115 try:116 for r in search.results():117 # Skip duplicates118 title_lower = r.title.lower().strip()119 if title_lower in seen_titles:120 continue121 seen_titles.add(title_lower)122 123 # Calculate a simple relevance indicator124 title_match = query.lower() in r.title.lower()125 abstract_match = query.lower() in r.summary.lower()126 127 results.append({128 "title": r.title,129 "url": r.entry_id,130 "pdf_url": r.pdf_url,131 "summary": r.summary[:400] + "..." if len(r.summary) > 400 else r.summary,132 "published": r.published.strftime("%Y-%m-%d") if r.published else "N/A",133 "updated": r.updated.strftime("%Y-%m-%d") if r.updated else "N/A",134 "authors": ", ".join([a.name for a in r.authors[:4]]) + ("..." if len(r.authors) > 4 else ""),135 "categories": ", ".join(r.categories[:3]),136 "title_match": title_match,137 "abstract_match": abstract_match138 })139 140 if len(results) >= max_results:141 break142 143 except Exception as e:144 st.error(f"arXiv API error: {e}")145 146 # Sort results: prioritize title matches, then abstract matches147 results.sort(key=lambda x: (x["title_match"], x["abstract_match"]), reverse=True)148 149 return results150 151 152def fetch_arxiv_simple(query: str, max_results: int = 5) -> List[Dict]:153 """154 Fallback simple search if advanced search returns no results.155 """156 search = arxiv.Search(157 query=query,158 max_results=max_results,159 sort_by=arxiv.SortCriterion.Relevance,160 sort_order=arxiv.SortOrder.Descending161 )162 163 results = []164 try:165 for r in search.results():166 results.append({167 "title": r.title,168 "url": r.entry_id,169 "pdf_url": r.pdf_url,170 "summary": r.summary[:400] + "..." if len(r.summary) > 400 else r.summary,171 "published": r.published.strftime("%Y-%m-%d") if r.published else "N/A",172 "updated": r.updated.strftime("%Y-%m-%d") if r.updated else "N/A",173 "authors": ", ".join([a.name for a in r.authors[:4]]) + ("..." if len(r.authors) > 4 else ""),174 "categories": ", ".join(r.categories[:3]),175 "title_match": False,176 "abstract_match": False177 })178 except Exception as e:179 st.error(f"arXiv API error: {e}")180 181 return results182 183# ---------------------184# YouTube fetcher (Official API)185# ---------------------186def fetch_youtube(query: str, max_results: int = 5) -> List[Dict]:187 """Fetch YouTube videos using official YouTube Data API v3."""188 if not YOUTUBE_API_KEY:189 return []190 191 try:192 url = "https://www.googleapis.com/youtube/v3/search"193 params = {194 "part": "snippet",195 "q": f"{query} AI tutorial",196 "type": "video",197 "maxResults": max_results,198 "order": "relevance",199 "key": YOUTUBE_API_KEY200 }201 202 response = requests.get(url, params=params, timeout=10)203 response.raise_for_status()204 data = response.json()205 206 videos = []207 for item in data.get("items", []):208 video_id = item["id"]["videoId"]209 snippet = item["snippet"]210 videos.append({211 "title": snippet.get("title", "Unknown"),212 "url": f"https://www.youtube.com/watch?v={video_id}",213 "channel": snippet.get("channelTitle", "Unknown"),214 "description": snippet.get("description", "")[:150] + "...",215 "thumbnail": snippet.get("thumbnails", {}).get("medium", {}).get("url", "")216 })217 return videos218 except Exception as e:219 st.error(f"YouTube API error: {e}")220 return []221 222# ---------------------223# Streamlit UI224# ---------------------225st.set_page_config(page_title="AI Trends Assistant", layout="centered")226 227st.title("AI Trends Assistant")228st.markdown("Fetch trending AI papers from arXiv and related YouTube videos.")229 230with st.sidebar:231 st.header("⚙️ Settings")232 mode = st.radio("Mode", ["Fetch Links", "Chat (Gemini)"])233 max_results = st.slider("Max results", min_value=1, max_value=20, value=5)234 235 st.markdown("---")236 st.subheader("arXiv Settings")237 arxiv_sort = st.radio("Sort papers by:", ["Relevance", "Recent"], index=0)238 239 st.markdown("---")240 st.markdown("**API Status:**")241 st.markdown(f"YouTube API: {'✅ Connected' if YOUTUBE_API_KEY else '❌ Not configured'}")242 st.markdown(f"Gemini API: {'✅ Connected' if GEMINI_API_KEY else '❌ Not configured'}")243 244if mode == "Fetch Links":245 st.subheader("🔍 Search Trending AI Topics")246 247 trending_topics = [248 "Custom query...",249 "generative AI",250 "large language models",251 "multimodal learning",252 "diffusion models",253 "reinforcement learning from human feedback",254 "vision transformer",255 "retrieval augmented generation",256 "graph neural networks",257 "federated learning",258 "AI agents",259 "neural radiance fields",260 "text to image generation",261 "chain of thought reasoning",262 "mixture of experts"263 ]264 265 selected_topic = st.selectbox("Select a trending topic:", trending_topics)266 267 if selected_topic == "Custom query...":268 user_query = st.text_input("Enter your search query:", value="", placeholder="e.g., generative AI, transformer models, etc.")269 else:270 user_query = selected_topic271 272 col1, col2 = st.columns(2)273 fetch_arxiv_btn = col1.checkbox("Fetch arXiv Papers", value=True)274 fetch_youtube_btn = col2.checkbox("Fetch YouTube Videos", value=True)275 276 if st.button("🚀 Fetch Results"):277 if not user_query.strip():278 st.warning("Please enter or select a query.")279 else:280 # arXiv Results281 if fetch_arxiv_btn:282 st.write("### 📄 arXiv Papers")283 st.caption(f"Searching for: **{user_query}** | Sorted by: **{arxiv_sort}**")284 285 with st.spinner("Fetching papers..."):286 papers = fetch_arxiv(user_query, max_results=max_results, sort_by=arxiv_sort)287 288 # Fallback to simple search if no results289 if not papers:290 st.info("Trying broader search...")291 papers = fetch_arxiv_simple(user_query, max_results=max_results)292 293 if not papers:294 st.warning("No arXiv results found. Try different keywords.")295 else:296 for i, p in enumerate(papers, 1):297 # Show relevance badge298 badge = ""299 if p.get("title_match"):300 badge = "🎯 "301 elif p.get("abstract_match"):302 badge = "✓ "303 304 with st.expander(f"{badge}{i}. {p['title']}", expanded=(i <= 3)):305 st.markdown(f"**Authors:** {p['authors']}")306 st.markdown(f"**Published:** {p['published']} | **Updated:** {p['updated']}")307 st.markdown(f"**Categories:** `{p['categories']}`")308 st.markdown(f"**Abstract:** {p['summary']}")309 310 col1, col2 = st.columns(2)311 col1.markdown(f"📄 [View Paper]({p['url']})")312 col2.markdown(f"📥 [Download PDF]({p['pdf_url']})")313 314 # YouTube Results315 if fetch_youtube_btn:316 st.write("### 🎬 YouTube Videos")317 318 if not YOUTUBE_API_KEY:319 st.warning("⚠️ YouTube API key not configured. Add `YOUTUBE_API_KEY` to your Hugging Face Secrets.")320 else:321 with st.spinner("Fetching videos..."):322 videos = fetch_youtube(user_query, max_results=max_results)323 324 if not videos:325 st.info("No YouTube results found.")326 else:327 for v in videos:328 col1, col2 = st.columns([1, 3])329 with col1:330 if v.get("thumbnail"):331 st.image(v["thumbnail"], width=120)332 with col2:333 st.markdown(f"**[{v['title']}]({v['url']})**")334 st.caption(f"📺 {v['channel']}")335 336else:337 st.subheader("💬 Chat with AI Trends Assistant")338 user_msg = st.text_area("Your message:", value="What's trending in multimodal AI research?")339 340 if st.button("Send"):341 with st.spinner("Calling Gemini..."):342 reply = call_gemini(SYSTEM_PROMPT, user_msg)343 st.markdown("**Assistant:**")344 st.write(reply)345 346st.markdown("---")347st.caption("Data sources: arXiv API, YouTube Data API v3")