Spaces:
Sleeping
Sleeping
| # ----------------------------- | |
| # Standard Python imports | |
| # ----------------------------- | |
| # Import standard libraries needed for the chatbot. | |
| # - os: for operating system operations like file paths. | |
| # - streamlit: to build the web app interface. | |
| # - json: for structured data handling and storing results. | |
| # These imports are essential for session management and UI rendering. | |
| import os | |
| import streamlit as st | |
| import json | |
| # ----------------------------- | |
| # Import OpenAI and SerpAPI | |
| # ----------------------------- | |
| # Attempt to import OpenAI client to interact with GPT models. | |
| # Attempt to import SerpAPI client to perform web searches. | |
| # If these packages are not installed, provide informative errors. | |
| # Ensures graceful failure and user guidance on missing dependencies. | |
| try: | |
| from openai import OpenAI | |
| except ModuleNotFoundError: | |
| print("OpenAI package not installed. Check requirements.txt.") | |
| raise | |
| try: | |
| from serpapi.google_search import GoogleSearch | |
| except ModuleNotFoundError: | |
| print("google-search-results package not installed. Check requirements.txt.") | |
| raise | |
| # ----------------------------- | |
| # API Keys from Streamlit Secrets | |
| # ----------------------------- | |
| # Retrieve API keys from Streamlit's secure secrets manager. | |
| # The OpenAI key allows GPT model calls. | |
| # The SerpAPI key allows performing web searches. | |
| # If keys are missing, stop the app and prompt the user to configure them. | |
| openai_api_key = st.secrets.get("OPENAI_API_KEY") | |
| serpapi_key = st.secrets.get("SERPAPI_KEY") | |
| if not openai_api_key: | |
| st.error("OpenAI API key not found. Add it to Streamlit secrets.") | |
| st.stop() | |
| # ----------------------------- | |
| # Initialize OpenAI client | |
| # ----------------------------- | |
| # Create an OpenAI client using the provided API key. | |
| # This client will be used to send prompts and receive GPT responses. | |
| # Centralizes GPT calls and handles authentication. | |
| client = OpenAI(api_key=openai_api_key) | |
| # ----------------------------- | |
| # Chat session state | |
| # ----------------------------- | |
| # Initialize or retrieve the chat session history stored in Streamlit. | |
| # This allows the chatbot to maintain conversation context across messages. | |
| # If this is the first interaction, start with a friendly assistant message. | |
| if "messages" not in st.session_state: | |
| st.session_state["messages"] = [{"role": "assistant", "content": "Hi! Paste a URL or ask a question."}] | |
| # Display existing messages in the chat interface | |
| for msg in st.session_state.messages: | |
| st.chat_message(msg["role"]).write(msg["content"]) | |
| # ----------------------------- | |
| # Helper: Web search using SerpAPI | |
| # ----------------------------- | |
| # Defines a function to search the web for a given query using SerpAPI. | |
| # Returns a list of top search results containing title, link, and snippet. | |
| # Handles missing API keys and exceptions gracefully, providing informative feedback. | |
| def search_web(query: str): | |
| if not serpapi_key: | |
| return [] | |
| try: | |
| params = {"q": query, "api_key": serpapi_key, "num": 3} | |
| search = GoogleSearch(params) | |
| results = search.get_dict() | |
| snippets = [] | |
| if "organic_results" in results: | |
| for r in results["organic_results"][:3]: | |
| snippets.append({ | |
| "title": r.get("title", "No title"), | |
| "link": r.get("link", ""), | |
| "snippet": r.get("snippet", "") | |
| }) | |
| return snippets | |
| except Exception as e: | |
| return [{"title": "Error", "snippet": str(e), "link": ""}] | |
| # ----------------------------- | |
| # Helper: Assess URL credibility | |
| # ----------------------------- | |
| # Wraps the credibility scoring function from assess_credibility.py. | |
| # Returns a dictionary with a score (0β1), star rating, and textual explanation. | |
| # Handles exceptions and returns default score if analysis fails. | |
| def assess_url(url: str): | |
| try: | |
| from assess_credibility import assess_url_credibility | |
| result = assess_url_credibility(url) | |
| score = result.get("score", 0.0) | |
| stars = int(round(score * 5)) | |
| result["stars"] = "β " * stars + "β" * (5 - stars) | |
| return result | |
| except Exception as e: | |
| return {"score": 0.0, "stars": "βββββ", "explanation": f"Error analyzing URL: {e}"} | |
| # ----------------------------- | |
| # Main interaction with intent detection | |
| # ----------------------------- | |
| # Handles user input and decides whether to perform: | |
| # 1. URL credibility scoring | |
| # 2. Skip web search for trivial greetings or casual chat | |
| # 3. Web search + credibility scoring + GPT for questions | |
| # Uses a hybrid approach: keyword list for trivial messages + GPT intent check for other inputs. | |
| if prompt := st.chat_input("Ask a question or enter a URL"): | |
| st.session_state.messages.append({"role": "user", "content": prompt}) | |
| st.chat_message("user").write(prompt) | |
| # ----------------------------- | |
| # Step 1: Check if input is a URL | |
| # ----------------------------- | |
| if prompt.startswith("http"): | |
| with st.spinner("π Assessing credibility..."): | |
| result = assess_url(prompt) | |
| # Display score with stars and explanation | |
| st.markdown(f"**Credibility Score: {result['stars']}**") | |
| st.caption(result.get("explanation", "")) | |
| st.session_state.messages.append({"role": "assistant", "content": json.dumps(result)}) | |
| else: | |
| # ----------------------------- | |
| # Step 2: Check for trivial messages | |
| # ----------------------------- | |
| NO_SEARCH_KEYWORDS = [ | |
| "hello", "hi", "hey", "good morning", "good afternoon", | |
| "thanks", "thank you", "how are you" | |
| ] | |
| prompt_clean = prompt.lower().strip() | |
| if any(kw in prompt_clean for kw in NO_SEARCH_KEYWORDS): | |
| msg = "Hello! How can I help you today?" | |
| st.session_state.messages.append({"role": "assistant", "content": msg}) | |
| st.chat_message("assistant").write(msg) | |
| else: | |
| # ----------------------------- | |
| # Step 3: Optional GPT intent classification | |
| # ----------------------------- | |
| try: | |
| intent_prompt = f""" | |
| Classify the following message: | |
| '{prompt}' | |
| Reply with either: | |
| - SEARCH: if the message is a question that requires web lookup | |
| - NO_SEARCH: if it's a greeting, thanks, or casual chat | |
| Only reply with SEARCH or NO_SEARCH. | |
| """ | |
| intent_response = client.chat.completions.create( | |
| model="gpt-4o-mini", | |
| messages=[{"role": "user", "content": intent_prompt}] | |
| ) | |
| intent = intent_response.choices[0].message.content.strip() | |
| except Exception: | |
| intent = "SEARCH" | |
| if intent == "NO_SEARCH": | |
| msg = "Hello! How can I help you?" | |
| st.session_state.messages.append({"role": "assistant", "content": msg}) | |
| st.chat_message("assistant").write(msg) | |
| else: | |
| # ----------------------------- | |
| # Step 4: Web search + credibility scoring + GPT response | |
| # ----------------------------- | |
| with st.spinner("π Searching the web..."): | |
| web_results = search_web(prompt) | |
| if not web_results: | |
| st.warning("No search results found.") | |
| st.stop() | |
| # Assess credibility for each result (stars + explanation) | |
| for r in web_results: | |
| score_dict = assess_url(r["link"]) if r["link"] else {"score": 0.0, "stars": "βββββ", "explanation": "No link"} | |
| r["credibility_score"] = score_dict.get("score", 0) | |
| r["credibility_explanation"] = score_dict.get("explanation", "") | |
| r["credibility_stars"] = score_dict.get("stars", "βββββ") | |
| # Prepare context for GPT without displaying individual sources | |
| context = "\n\n".join( | |
| [f"Source: {r['link']}\nSnippet: {r['snippet']}\nCredibility: {r['credibility_stars']} ({r['credibility_score']:.2f}) - {r['credibility_explanation']}" | |
| for r in web_results] | |
| ) | |
| system_message = { | |
| "role": "system", | |
| "content": ( | |
| "You are a helpful assistant. Use the web results and their credibility scores to answer the user's question. " | |
| "Highlight the credibility rating in your answer.\n\n" | |
| f"{context}" | |
| ) | |
| } | |
| messages = [system_message] + st.session_state.messages | |
| # Generate GPT response | |
| try: | |
| with st.spinner("π€ Generating answer..."): | |
| response = client.chat.completions.create( | |
| model="gpt-4o-mini", | |
| messages=messages | |
| ) | |
| msg = response.choices[0].message.content | |
| except Exception as e: | |
| msg = f"Error with OpenAI API: {e}" | |
| st.session_state.messages.append({"role": "assistant", "content": msg}) | |
| st.chat_message("assistant").write(msg) | |