Spaces:
Sleeping
Sleeping
| # app.py | |
| import os | |
| import streamlit as st | |
| from dotenv import load_dotenv | |
| from langchain.docstore.document import Document | |
| from langchain_community.retrievers import BM25Retriever | |
| from langchain.tools import Tool | |
| from langgraph.graph.message import add_messages | |
| from langgraph.graph import START, StateGraph | |
| from langgraph.prebuilt import ToolNode, tools_condition | |
| from langchain_core.messages import AnyMessage, HumanMessage | |
| from langchain_groq import ChatGroq | |
| from typing import TypedDict, Annotated | |
| import fitz # PyMuPDF | |
| # Load .env vars | |
| load_dotenv() | |
| os.environ["GROQ_API_KEY"] = os.getenv("GROQ_API_KEY") | |
| groq_api_key = os.getenv("GROQ_API_KEY") | |
| # --- PDF uploader and parser --- | |
| def parse_pdfs(uploaded_files): | |
| pdf_docs = [] | |
| for uploaded_file in uploaded_files: | |
| with fitz.open(stream=uploaded_file.read(), filetype="pdf") as doc: | |
| text = "" | |
| for page in doc: | |
| text += page.get_text() | |
| pdf_docs.append(Document(page_content=text, metadata={"source": uploaded_file.name})) | |
| return pdf_docs | |
| # --- Guest info retrieval --- | |
| def build_retriever(all_docs): | |
| return BM25Retriever.from_documents(all_docs) | |
| def extract_text(query: str, retriever): | |
| results = retriever.invoke(query) | |
| if results: | |
| return "\n\n".join([doc.page_content for doc in results[:3]]) | |
| else: | |
| return "لم يتم العثور على معلومات مطابقة في الملفات." | |
| # --- Streamlit UI --- | |
| st.set_page_config(page_title="NINU Agent", page_icon="🏛️") | |
| st.title("🏛️ NINU - Guest & PDF Assistant") | |
| st.markdown("** Hint:** NINU can help summarize lectures and quiz you step-by-step in simple English.") | |
| # Initialize session state to hold conversation history | |
| if "conversation_history" not in st.session_state: | |
| st.session_state.conversation_history = [] | |
| # User input area | |
| query = st.text_area("📝 اكتب سؤالك أو كمل مذاكرتك هنا:") | |
| uploaded_files = st.file_uploader("📄 ارفع ملفات PDF للمحاضرات", type=["pdf"], accept_multiple_files=True) | |
| if st.button("Ask NINU") and query: | |
| # 1. Parse PDF | |
| user_docs = parse_pdfs(uploaded_files) if uploaded_files else [] | |
| bm25_retriever = build_retriever(user_docs) | |
| # 2. Create Tool | |
| NINU_tool = Tool( | |
| name="NINU_Lec_retriever", | |
| func=lambda q: extract_text(q, bm25_retriever), | |
| description="Retrieves content from uploaded PDFs based on a query." | |
| ) | |
| # 3. Create LLM with tools | |
| llm = ChatGroq(model="deepseek-r1-distill-llama-70b", groq_api_key=groq_api_key) | |
| tools = [NINU_tool] | |
| llm_with_tools = llm.bind_tools(tools) | |
| class AgentState(TypedDict): | |
| messages: Annotated[list[AnyMessage], add_messages] | |
| def assistant(state: AgentState): | |
| return { | |
| "messages": [llm_with_tools.invoke(state["messages"])] | |
| } | |
| # 4. Build Agent Graph | |
| builder = StateGraph(AgentState) | |
| builder.add_node("assistant", assistant) | |
| builder.add_node("tools", ToolNode(tools)) | |
| builder.add_edge(START, "assistant") | |
| builder.add_conditional_edges("assistant", tools_condition) | |
| builder.add_edge("tools", "assistant") | |
| NINU = builder.compile() | |
| # 5. Prepare full conversation messages | |
| if len(st.session_state.conversation_history) == 0: | |
| # Add the custom prompt first | |
| intro_prompt = """ | |
| I uploaded a lecture PDF. I want you to study it with me step by step. | |
| - Summarize the lecture part by part. | |
| - Explain each part in very simple English like you're teaching a friend. | |
| - After each part, ask me 2-3 MCQ questions in English. | |
| - Wait for my answer before moving to the next part. | |
| - If I answer incorrectly, explain why. | |
| Let's begin! 💪 | |
| """ | |
| st.session_state.conversation_history.append(HumanMessage(content=intro_prompt)) | |
| # Add the new user message | |
| st.session_state.conversation_history.append(HumanMessage(content=query)) | |
| # 6. Invoke agent with full conversation | |
| response = NINU.invoke({"messages": st.session_state.conversation_history}) | |
| # 7. Add assistant response to history | |
| assistant_reply = response["messages"][-1] | |
| st.session_state.conversation_history.append(assistant_reply) | |
| # 8. Show output | |
| st.markdown("### NINU's Response:") | |
| st.write(assistant_reply.content) | |
| # 9. Show full conversation history (optional) | |
| with st.expander("🧾 Show full conversation history"): | |
| for msg in st.session_state.conversation_history: | |
| role = " You" if msg.type == "human" else " NINU" | |
| st.markdown(f"**{role}:** {msg.content}") | |