CoolFace
Apppublic

Sumayyea/TrendingAIBot

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
app.py347 linesDownload Raw Back to root
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")