# --- 1. SQLITE HACK (Must be top) --- __import__('pysqlite3') import sys sys.modules['sqlite3'] = sys.modules.pop('pysqlite3') import streamlit as st import os import tempfile import time import logging import warnings import json from langchain_community.document_loaders import PyPDFLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_chroma import Chroma from langchain_cohere import CohereEmbeddings from agent import get_agent_app # --- 2. CONFIG & SETUP --- st.set_page_config( page_title="AutoDev Agent", page_icon="πŸ€–", layout="wide", initial_sidebar_state="expanded" ) logging.basicConfig(level=logging.INFO) logger = logging.getLogger("AutoDev_UI") warnings.filterwarnings("ignore") # --- 3. CUSTOM CSS (Professional Dark/Glass Look) --- st.markdown(""" """, unsafe_allow_html=True) # --- SESSION STATE --- if "messages" not in st.session_state: st.session_state.messages = [] if "workflow" not in st.session_state: st.session_state.workflow = None # --- 4. HELPER FUNCTIONS --- def ingest_pdf(uploaded_file): """Ingests PDF into ChromaDB""" try: with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp: tmp.write(uploaded_file.getvalue()) tmp_path = tmp.name loader = PyPDFLoader(tmp_path) docs = loader.load() splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200) splits = splitter.split_documents(docs) api_key = os.environ.get("COHERE_API_KEY") if not api_key: return False, "COHERE_API_KEY missing in .env" embeddings = CohereEmbeddings(model="embed-english-v3.0", cohere_api_key=api_key) # Persistent path fix db = Chroma(persist_directory="./chroma_db_groq_v1", embedding_function=embeddings, collection_name="rag_groq_final") db.add_documents(splits) return True, len(splits) except Exception as e: return False, str(e) def format_agent_output(content): """Cleans up the raw content for display""" if isinstance(content, str): return content if isinstance(content, list): # Extract content from list of messages return "\n".join([msg.content for msg in content if hasattr(msg, 'content')]) return str(content) # --- 5. SIDEBAR --- with st.sidebar: st.title("πŸ€– AutoDev Agent") st.caption("v2.0 | Enterprise Edition") st.markdown("---") # PDF Upload st.subheader("πŸ“‚ Knowledge Base") uploaded_file = st.file_uploader("Upload CV/Docs", type=["pdf"]) if uploaded_file: if st.button("πŸ“₯ Ingest Document", use_container_width=True): with st.spinner("Processing Knowledge Graph..."): success, msg = ingest_pdf(uploaded_file) if success: st.success(f"Indexed {msg} chunks successfully!") time.sleep(1) st.rerun() else: st.error(f"Failed: {msg}") st.markdown("---") # Status Indicator if st.session_state.workflow: st.success("🟒 System Online") else: st.warning("πŸ”΄ System Offline") if st.button("🧹 Clear Chat History", use_container_width=True): st.session_state.messages = [] st.rerun() # --- 6. MAIN APP LOGIC --- st.subheader("πŸ’¬ Career & Tech Assistant") # Initialize Agent lazily if not st.session_state.workflow: with st.spinner("πŸš€ Booting up Multi-Agent System..."): try: st.session_state.workflow = get_agent_app() st.rerun() except Exception as e: st.error(f"Failed to initialize Agent: {e}") st.stop() # Display Chat History for msg in st.session_state.messages: avatar = "πŸ§‘β€πŸ’»" if msg["role"] == "user" else "πŸ€–" with st.chat_message(msg["role"], avatar=avatar): st.markdown(msg["content"]) # --- 7. INPUT HANDLING --- if prompt := st.chat_input("Ask about skills, projects, or upload a doc..."): # 1. User Message Display st.session_state.messages.append({"role": "user", "content": prompt}) with st.chat_message("user", avatar="πŸ§‘β€πŸ’»"): st.markdown(prompt) # 2. Agent Processing with st.chat_message("assistant", avatar="πŸ€–"): # Placeholder for final answer response_placeholder = st.empty() # Status container for "Thinking" with st.status("🧠 Orchestrating Agents...", expanded=True) as status: final_response = "I couldn't generate a response." try: inputs = {"messages": [{"role": "user", "content": prompt}]} # Streaming Loop for event in st.session_state.workflow.stream(inputs): for agent_name, agent_data in event.items(): # Extract the actual message content cleanly raw_messages = agent_data.get("messages", []) if raw_messages: last_msg = raw_messages[-1] content = last_msg.content # Determine Icon based on Agent icon = "βš™οΈ" if "graph" in agent_name: icon = "πŸ•ΈοΈ" elif "vector" in agent_name: icon = "πŸ“„" elif "web" in agent_name: icon = "🌐" elif "supervisor" in agent_name: icon = "πŸ‘”" # 1. Show high-level step st.write(f"**{icon} {agent_name.replace('_', ' ').title()}**") # 2. Show details in dropdown (No ugly JSON) with st.expander(f"View {agent_name} Output", expanded=False): st.markdown(f"```text\n{content}\n```") # Update final response tracking final_response = content status.update(label="βœ… Task Completed", state="complete", expanded=False) except Exception as e: status.update(label="❌ Error Occurred", state="error") st.error(f"Runtime Error: {e}") # 3. Final Answer Display (Clean) response_placeholder.markdown(final_response) st.session_state.messages.append({"role": "assistant", "content": final_response})