Spaces:
Runtime error
Runtime error
| import gradio as gr | |
| from typing import TypedDict, Sequence, Annotated | |
| from langgraph.graph import StateGraph, END | |
| from langchain_core.messages import HumanMessage, SystemMessage, BaseMessage, ToolMessage | |
| from langchain_community.tools import tool | |
| from langchain_google_genai import ChatGoogleGenerativeAI | |
| from langchain_groq import ChatGroq | |
| from dotenv import load_dotenv, find_dotenv | |
| from langgraph.graph import add_messages | |
| from langchain.text_splitter import RecursiveCharacterTextSplitter | |
| from langchain_community.document_loaders import PyPDFLoader | |
| from langchain_chroma import Chroma | |
| from langchain_huggingface import HuggingFaceEmbeddings | |
| import os, shutil, tempfile | |
| load_dotenv(find_dotenv()) | |
| # LLM Setup | |
| llm = ChatGroq( | |
| model="llama-3.1-8b-instant", | |
| temperature=0.6, | |
| ) | |
| # Embeddings | |
| embeddings = HuggingFaceEmbeddings(model="BAAI/bge-small-en-v1.5") | |
| retriever = None # Global retriever object | |
| # Tool (dynamic) | |
| def retriever_tool(query: str) -> str: | |
| """Searches and returns the information from the uploaded document.""" | |
| if retriever is None: | |
| return "Please upload a document first." | |
| docs = retriever.invoke(query) | |
| if not docs: | |
| return "I found no relevant information in the document." | |
| return "\n\n".join([f"Document {i+1}:\n{doc.page_content}" for i, doc in enumerate(docs)]) | |
| # Tool binding | |
| tools = [retriever_tool] | |
| tools_dict = {tool_.name: tool_ for tool_ in tools} | |
| llm = llm.bind_tools(tools) | |
| # Agent Setup | |
| class AgentState(TypedDict): | |
| messages: Annotated[Sequence[BaseMessage], add_messages] | |
| def should_continue(state: AgentState): | |
| result = state['messages'][-1] | |
| return hasattr(result, 'tool_calls') and len(result.tool_calls) > 0 | |
| system_prompt = """ | |
| You are an intelligent and reliable AI assistant, tasked with helping users explore and understand the content of an uploaded document. | |
| Your primary responsibility is to provide accurate, structured, and context-aware responses based strictly on the document's content. | |
| Responsibilities: | |
| - Clearly explain concepts, definitions, and ideas presented in the document using simple language and analogies when helpful. | |
| - Provide relevant examples, code snippets (especially in Python), or step-by-step explanations when the document covers technical or practical material. | |
| - Do not generate or assume any information that is not explicitly present in the document — avoid speculation and hallucination. | |
| - If a user question is not answered or supported by the document, clearly state that and offer to assist with what is available. | |
| - Prioritize clarity, factual accuracy, and helpfulness over creativity or guesswork. | |
| Your goal is to serve as a grounded guide. Always tie your responses directly to the document’s content and cite the source material or page when appropriate. | |
| Be humble in uncertainty and always maintain the user's trust through transparency and reliability. | |
| """ | |
| def call_llm(state: AgentState) -> AgentState: | |
| messages = list(state["messages"]) | |
| messages = [SystemMessage(content=system_prompt)] + messages | |
| response = llm.invoke(messages) | |
| return {"messages": [response]} | |
| def take_action(state: AgentState) -> AgentState: | |
| tool_calls = state["messages"][-1].tool_calls | |
| results = [] | |
| for t in tool_calls: | |
| print(f"Calling Tool: {t['name']} with query: {t['args'].get('query', 'No query provided')}") | |
| if t['name'] not in tools_dict: | |
| result = "Incorrect Tool Name. Please retry." | |
| else: | |
| result = tools_dict[t['name']].invoke(t['args'].get('query', '')) | |
| results.append(ToolMessage(tool_call_id=t['id'], name=t['name'], content=str(result))) | |
| return {"messages": results} | |
| graph = StateGraph(AgentState) | |
| graph.add_node("llm", call_llm) | |
| graph.add_node("retriever_agent", take_action) | |
| graph.add_conditional_edges("llm", should_continue, {True: "retriever_agent", False: END}) | |
| graph.add_edge("retriever_agent", "llm") | |
| graph.set_entry_point("llm") | |
| rag_agent = graph.compile() | |
| # PDF Processing | |
| def process_pdf(file): | |
| global retriever | |
| if not file: | |
| return "No PDF uploaded." | |
| loader = PyPDFLoader(file.name) | |
| pages = loader.load() | |
| splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200) | |
| pages_split = splitter.split_documents(pages) | |
| persist_directory = tempfile.mkdtemp() | |
| vectorstore = Chroma.from_documents( | |
| documents=pages_split, | |
| embedding=embeddings, | |
| persist_directory=persist_directory, | |
| collection_name="dynamic_doc" | |
| ) | |
| retriever = vectorstore.as_retriever(search_type="similarity", search_kwargs={"k": 5}) | |
| return f"PDF loaded successfully. {len(pages_split)} chunks indexed." | |
| # Gradio UI | |
| def gradio_chat(user_input, history): | |
| messages = [HumanMessage(content=user_input)] | |
| result = rag_agent.invoke({"messages": messages}) | |
| reply = result['messages'][-1].content | |
| history.append((user_input, reply)) | |
| return history, history | |
| with gr.Blocks(css=".scroll-area { overflow-y: auto; max-height: 85vh; }") as demo: | |
| gr.Markdown("# RAG Chat Assistant (Upload Your PDF)") | |
| with gr.Row(): | |
| # Left side - Chatbot + input + buttons | |
| with gr.Column(scale=2): | |
| chatbot = gr.Chatbot() | |
| user_msg = gr.Textbox(label="Ask a question", placeholder="What is reward modeling?") | |
| with gr.Row(): | |
| submit_btn = gr.Button("Submit") | |
| clear_btn = gr.Button("Clear") | |
| # Right side - PDF upload section | |
| with gr.Column(scale=1, elem_classes="scroll-area"): | |
| with gr.Row(): | |
| file_input = gr.File(label="Upload PDF", file_types=[".pdf"]) | |
| with gr.Row(): | |
| upload_button = gr.Button("Process PDF") | |
| upload_status = gr.Textbox(label="Status", interactive=False) | |
| # State | |
| state = gr.State([]) | |
| # Button Actions | |
| upload_button.click(fn=process_pdf, inputs=[file_input], outputs=[upload_status]) | |
| submit_btn.click(fn=gradio_chat, inputs=[user_msg, state], outputs=[chatbot, state]) | |
| clear_btn.click(lambda: ([], []), outputs=[chatbot, state]) | |
| # Gradio Chat Logic | |
| # def gradio_chat(user_input, history): | |
| # messages = [HumanMessage(content=user_input)] | |
| # result = rag_agent.invoke({"messages": messages}) | |
| # reply = result['messages'][-1].content | |
| # history.append((user_input, reply)) | |
| # return history, history | |
| # # Gradio UI | |
| # with gr.Blocks(css=".scroll-area { overflow-y: auto; max-height: 85vh; }") as demo: | |
| # gr.Markdown("# RAG Chat Assistant (Upload Your PDF)") | |
| # with gr.Row(): | |
| # with gr.Column(scale=1, elem_classes="scroll-area"): | |
| # file_input = gr.File(label="Upload PDF", file_types=[".pdf"]) | |
| # upload_status = gr.Textbox(label="Status", interactive=False) | |
| # upload_button = gr.Button("Process PDF") | |
| # with gr.Column(scale=2): | |
| # chatbot = gr.Chatbot() | |
| # user_msg = gr.Textbox(label="Ask a question", placeholder="What is reward modeling?") | |
| # submit_btn = gr.Button("Submit") | |
| # clear_btn = gr.Button("Clear") | |
| # state = gr.State([]) | |
| # upload_button.click(fn=process_pdf, inputs=[file_input], outputs=[upload_status]) | |
| # submit_btn.click(fn=gradio_chat, inputs=[user_msg, state], outputs=[chatbot, state]) | |
| # clear_btn.click(lambda: ([], []), outputs=[chatbot, state]) | |
| if __name__ == "__main__": | |
| demo.launch() |