Spaces:
Runtime error
Runtime error
Download app.py from teohteiktoe/langgraph: direct link, hf CLI and curl.
- Browser
- Download file 4.98 kB
-
https://huggingface.co/spaces/teohteiktoe/langgraph/resolve/main/app.py
- Command line
-
hf download hf://spaces/teohteiktoe/langgraph/app.py
-
curl -L -o app.py https://huggingface.co/spaces/teohteiktoe/langgraph/resolve/main/app.py
4.98 kB
| import os | |
| import tempfile | |
| from typing import TypedDict, List | |
| import gradio as gr | |
| from langchain_google_genai import ChatGoogleGenerativeAI | |
| from langchain_huggingface import HuggingFaceEmbeddings | |
| from langchain_chroma import Chroma | |
| from langchain_text_splitters import RecursiveCharacterTextSplitter | |
| from langchain_core.documents import Document | |
| from langgraph.graph import StateGraph, END | |
| # Optional readers | |
| from pypdf import PdfReader | |
| import docx | |
| EMBEDDING_MODEL = "sentence-transformers/all-MiniLM-L6-v2" | |
| llm = ChatGoogleGenerativeAI( | |
| model="gemini-2.5-flash", | |
| google_api_key=os.getenv("GOOGLE_API_KEY"), | |
| temperature=0.2, | |
| ) | |
| embeddings = HuggingFaceEmbeddings(model_name=EMBEDDING_MODEL) | |
| VECTORSTORE = None | |
| RETRIEVER = None | |
| class AgentState(TypedDict): | |
| question: str | |
| context: str | |
| answer: str | |
| route: str | |
| def read_file(path: str) -> str: | |
| ext = os.path.splitext(path)[1].lower() | |
| if ext == ".pdf": | |
| reader = PdfReader(path) | |
| return "\n".join(page.extract_text() or "" for page in reader.pages) | |
| if ext == ".docx": | |
| d = docx.Document(path) | |
| return "\n".join(p.text for p in d.paragraphs) | |
| if ext in [".txt", ".md", ".csv"]: | |
| with open(path, "r", encoding="utf-8", errors="ignore") as f: | |
| return f.read() | |
| return "" | |
| def build_rag(files): | |
| global VECTORSTORE, RETRIEVER | |
| if not files: | |
| return "Please upload at least one PDF, DOCX, TXT, MD, or CSV file." | |
| docs: List[Document] = [] | |
| for f in files: | |
| text = read_file(f.name) | |
| if text.strip(): | |
| docs.append(Document(page_content=text, metadata={"source": os.path.basename(f.name)})) | |
| if not docs: | |
| return "No readable text found. Please upload PDF, DOCX, TXT, MD, or CSV files." | |
| splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200) | |
| chunks = splitter.split_documents(docs) | |
| persist_dir = tempfile.mkdtemp(prefix="chroma_") | |
| VECTORSTORE = Chroma.from_documents( | |
| documents=chunks, | |
| embedding=embeddings, | |
| persist_directory=persist_dir, | |
| collection_name="rag_collection", | |
| ) | |
| RETRIEVER = VECTORSTORE.as_retriever(search_kwargs={"k": 4}) | |
| return f"Knowledge base ready. Loaded {len(docs)} file(s) and created {len(chunks)} chunks." | |
| def retrieve_node(state: AgentState): | |
| global RETRIEVER | |
| if RETRIEVER is None: | |
| return {"context": "", "route": "general"} | |
| docs = RETRIEVER.invoke(state["question"]) | |
| context = "\n\n".join( | |
| [f"Source: {d.metadata.get('source', 'uploaded file')}\n{d.page_content}" for d in docs] | |
| ) | |
| return {"context": context, "route": "rag" if context.strip() else "general"} | |
| def router_node(state: AgentState): | |
| return state.get("route", "general") | |
| def rag_answer_node(state: AgentState): | |
| prompt = f""" | |
| You are a helpful RAG assistant. Answer using ONLY the context below. | |
| If the answer is not in the context, say: "I cannot find this in the uploaded documents." | |
| Context: | |
| {state['context']} | |
| Question: | |
| {state['question']} | |
| """ | |
| return {"answer": llm.invoke(prompt).content} | |
| def general_answer_node(state: AgentState): | |
| prompt = f""" | |
| The user has not built a usable knowledge base yet, or no relevant context was found. | |
| Answer briefly and remind the user to upload documents for RAG-based answers. | |
| Question: {state['question']} | |
| """ | |
| return {"answer": llm.invoke(prompt).content} | |
| graph = StateGraph(AgentState) | |
| graph.add_node("retrieve", retrieve_node) | |
| graph.add_node("rag_answer", rag_answer_node) | |
| graph.add_node("general_answer", general_answer_node) | |
| graph.set_entry_point("retrieve") | |
| graph.add_conditional_edges( | |
| "retrieve", | |
| router_node, | |
| {"rag": "rag_answer", "general": "general_answer"}, | |
| ) | |
| graph.add_edge("rag_answer", END) | |
| graph.add_edge("general_answer", END) | |
| app_graph = graph.compile() | |
| def ask_rag(question): | |
| if not question or not question.strip(): | |
| return "Please enter a question." | |
| result = app_graph.invoke({"question": question, "context": "", "answer": "", "route": ""}) | |
| return result["answer"] | |
| with gr.Blocks(title="Gemini LangGraph RAG") as demo: | |
| gr.Markdown("# Gemini LangGraph RAG on Hugging Face") | |
| gr.Markdown("Upload documents, build a temporary Chroma knowledge base, then ask questions.") | |
| with gr.Row(): | |
| files = gr.File( | |
| label="Upload documents", | |
| file_count="multiple", | |
| file_types=[".pdf", ".docx", ".txt", ".md", ".csv"], | |
| ) | |
| status = gr.Textbox(label="Status") | |
| build_btn = gr.Button("Build Knowledge Base") | |
| build_btn.click(build_rag, inputs=files, outputs=status) | |
| question = gr.Textbox(label="Question", placeholder="Ask about your uploaded documents") | |
| answer = gr.Textbox(label="Answer", lines=10) | |
| ask_btn = gr.Button("Ask RAG") | |
| ask_btn.click(ask_rag, inputs=question, outputs=answer) | |
| if __name__ == "__main__": | |
| demo.launch(server_name="0.0.0.0", server_port=7860) | |