import os import streamlit as st import numpy as np import uuid import datetime from dotenv import load_dotenv from langchain_community.tools import DuckDuckGoSearchRun from langchain_groq import ChatGroq from langchain.chains import LLMChain from langchain.prompts import PromptTemplate # Load environment variables load_dotenv() # ────────────────────────────── Styling ────────────────────────────────────── def local_css(): st.markdown(""" """, unsafe_allow_html=True) # ────────────────────────── Session State ──────────────────────────────────── def init_session_state(): if 'messages' not in st.session_state: st.session_state.messages = [] if 'chat_sessions' not in st.session_state: st.session_state.chat_sessions = {} if 'current_session_id' not in st.session_state: st.session_state.current_session_id = str(uuid.uuid4()) if 'session_name' not in st.session_state: st.session_state.session_name = f"Chat {datetime.datetime.now().strftime('%b %d, %H:%M')}" def save_chat_session(): if st.session_state.current_session_id: st.session_state.chat_sessions[st.session_state.current_session_id] = { "name": st.session_state.session_name, "messages": st.session_state.messages, "timestamp": datetime.datetime.now().isoformat() } def load_chat_session(session_id): if session_id in st.session_state.chat_sessions: st.session_state.current_session_id = session_id st.session_state.messages = st.session_state.chat_sessions[session_id]["messages"] st.session_state.session_name = st.session_state.chat_sessions[session_id]["name"] def create_new_chat(): st.session_state.current_session_id = str(uuid.uuid4()) st.session_state.messages = [] st.session_state.session_name = f"Chat {datetime.datetime.now().strftime('%b %d, %H:%M')}" # ──────────────────────────── Models ───────────────────────────────────────── def setup_models(groq_api_key): llm = ChatGroq( model="llama-3.3-70b-versatile", groq_api_key=groq_api_key ) direct_prompt = PromptTemplate( input_variables=["question"], template=""" Answer the question in detailed form. Question: {question} Answer: """ ) direct_chain = LLMChain(llm=llm, prompt=direct_prompt) search_prompt = PromptTemplate( input_variables=["web_results", "question"], template=""" Use these web search results to give a comprehensive answer: Search Results: {web_results} Question: {question} Answer: """ ) search_chain = LLMChain(llm=llm, prompt=search_prompt) return direct_chain, search_chain, llm def decide_search(query: str, llm) -> tuple[bool, str | None]: decision_prompt = PromptTemplate( input_variables=["query"], template=""" You are a decision assistant. If the user's question needs up-to-date information from the web, respond with "SEARCH: ". Otherwise respond with "NO_SEARCH". Do not add anything else. Question: {query} """ ) decision_chain = LLMChain(llm=llm, prompt=decision_prompt) response = decision_chain.run({"query": query}).strip() if response.upper().startswith("SEARCH:"): return True, response[len("SEARCH:"):].strip() return False, None @st.cache_data def perform_search(keywords: str) -> str: return DuckDuckGoSearchRun().run(keywords) # ───────────────────────────── Main App ────────────────────────────────────── def main(): st.set_page_config( page_title="General Knowledge Assistant", page_icon="🧭", layout="wide", initial_sidebar_state="expanded" ) local_css() init_session_state() with st.sidebar: st.markdown("

🧭 Knowledge Assistant

", unsafe_allow_html=True) st.subheader("🔑 API Key") groq_api_key = os.environ.get("GROQ_API_KEY") or st.text_input("Groq API Key", type="password") if not groq_api_key: st.warning("Please provide the Groq API key to proceed.") st.stop() st.subheader("💬 Chat History") if st.button("➕ New Chat", key="new_chat"): create_new_chat() new_name = st.text_input("Chat Name", value=st.session_state.session_name) if new_name != st.session_state.session_name: st.session_state.session_name = new_name save_chat_session() st.markdown("#### Previous Chats") sorted_sessions = sorted( st.session_state.chat_sessions.items(), key=lambda x: x[1].get("timestamp", ""), reverse=True ) for session_id, session in sorted_sessions: preview = "New conversation" if session["messages"]: first_msg = session["messages"][0] if isinstance(first_msg, dict) and "content" in first_msg: preview = first_msg["content"] if len(preview) > 30: preview = preview[:30] + "..." style = "chat-history-item chat-history-active" if session_id == st.session_state.current_session_id else "chat-history-item" col1, col2 = st.columns([0.8, 0.2]) with col1: if st.button(session["name"], key=f"load_session_{session_id}"): load_chat_session(session_id) st.rerun() with col2: if st.button("🗑️", key=f"delete_{session_id}", help="Delete this chat"): if session_id in st.session_state.chat_sessions: del st.session_state.chat_sessions[session_id] if session_id == st.session_state.current_session_id: create_new_chat() st.rerun() direct_chain, search_chain, llm = setup_models(groq_api_key) st.markdown("""

🧭 General Knowledge Assistant

""", unsafe_allow_html=True) chat_container = st.container() user_input = st.chat_input("Ask me anything...") if user_input: st.session_state.messages.append({"role": "user", "content": user_input}) save_chat_session() with chat_container: typing_placeholder = st.empty() typing_placeholder.markdown("""
""", unsafe_allow_html=True) try: needs_search, terms = decide_search(user_input, llm) if needs_search and terms: web_results = perform_search(terms) answer = search_chain.run({"web_results": web_results, "question": user_input}) else: answer = direct_chain.run({"question": user_input}) st.session_state.messages.append({"role": "assistant", "content": answer}) save_chat_session() except Exception as e: err = f"Sorry, I encountered an error: {str(e)}" st.session_state.messages.append({"role": "assistant", "content": err}) save_chat_session() typing_placeholder.empty() st.rerun() with chat_container: if not st.session_state.messages: st.markdown("""

👋 Welcome to the General Knowledge Assistant!

Ask me anything about general knowledge, facts, or concepts.

I can search the web when needed to provide you with up-to-date information.

""", unsafe_allow_html=True) else: for msg in st.session_state.messages: if isinstance(msg, dict) and "role" in msg and "content" in msg: with st.chat_message(msg["role"]): st.write(msg["content"]) else: st.error(f"Invalid message format: {msg}") if __name__ == "__main__": main()