GraphRag / src /streamlit_app.py
Armando Medina
Update src/streamlit_app.py
3eb443c verified
Raw History Blame Contribute Delete
5.82 kB
import os
# --- Environment Setup ---
os.environ["HF_HOME"] = "/app/cache"
os.environ["TRANSFORMERS_CACHE"] = "/app/cache"
os.environ["SENTENCE_TRANSFORMERS_HOME"] = "/app/cache"
os.environ["CHROMA_DB_DIR"] = "/app/chroma_db"
os.environ["XDG_CONFIG_HOME"] = "/app/.streamlit"
import streamlit as st
st.set_page_config(layout="wide") # ✅ Must come first before any other st.* calls
# --- Libraries ---
import networkx as nx
import spacy
import subprocess
from docx import Document
import pdfplumber
from pyvis.network import Network
from sentence_transformers import SentenceTransformer
import chromadb
from chromadb.config import Settings
from transformers import pipeline
import tempfile
import streamlit.components.v1 as components
# --- Setup ---
@st.cache_resource
def ensure_spacy_model():
nlp = spacy.load("en_core_web_sm")
nlp.max_length = 2_000_000
return nlp
nlp = ensure_spacy_model()
embedder = SentenceTransformer("all-MiniLM-L6-v2")
os.makedirs("/app/chroma_db", exist_ok=True)
client = chromadb.PersistentClient(path="/app/chroma_db")
collection = client.get_or_create_collection(name="graphrag")
llm = pipeline("text2text-generation", model="google/flan-t5-large")
# --- Utils ---
def extract_text(upload):
if upload is None:
return ""
ext = os.path.splitext(upload.name)[1].lower()
with tempfile.NamedTemporaryFile(delete=False, suffix=ext) as tmp:
tmp.write(upload.read())
tmp_path = tmp.name
if ext == ".txt":
return open(tmp_path, "r", encoding="utf-8").read()
elif ext == ".pdf":
with pdfplumber.open(tmp_path) as pdf:
return "\n".join(page.extract_text() for page in pdf.pages if page.extract_text())
elif ext == ".docx":
doc = Document(tmp_path)
return "\n".join(p.text for p in doc.paragraphs)
return ""
def build_graph(text):
doc = nlp(text)
G = nx.Graph()
entity_data = [(ent.text.strip(), ent.label_) for ent in doc.ents if ent.text.strip()]
unique_entities = list(set(entity_data))
entity_texts = [ent[0] for ent in unique_entities]
embeddings = embedder.encode(entity_texts, convert_to_numpy=True).tolist()
for (text, label), emb in zip(unique_entities, embeddings):
G.add_node(text, label=label, embedding=emb)
unique_ids = [f"{ent}_{i}" for i, ent in enumerate(entity_texts)]
collection.add(documents=entity_texts, embeddings=embeddings, ids=unique_ids)
for sent in doc.sents:
ents = [ent.text.strip() for ent in sent.ents if ent.text.strip() in G.nodes]
for i in range(len(ents)):
for j in range(i + 1, len(ents)):
G.add_edge(ents[i], ents[j])
return G
def retrieve_relevant_entities(query, top_k=5):
query_emb = embedder.encode([query], convert_to_numpy=True).tolist()
results = collection.query(query_embeddings=query_emb, n_results=top_k)
return [match for match in results['ids'][0] if match in collection.peek()['ids']]
def expand_graph_context(graph, entities, depth=1):
subgraph_nodes = set()
for entity in entities:
if entity in graph:
neighbors = nx.single_source_shortest_path_length(graph, entity, cutoff=depth).keys()
subgraph_nodes.update(neighbors)
return graph.subgraph(subgraph_nodes)
def format_graph_context(subgraph):
lines = []
for node in subgraph.nodes(data=True):
lines.append(f"Entity: {node[0]}, Type: {node[1].get('label', 'N/A')}")
for edge in subgraph.edges():
lines.append(f"Connection: {edge[0]} ↔ {edge[1]}")
return "\n".join(lines)
def generate_answer(question, context_text):
prompt = f"""You are a compliance assistant. Use the following knowledge graph context to answer the question.
{context_text}
Question: {question}
Answer:"""
return llm(prompt, max_length=512, do_sample=False)[0]['generated_text']
def plot_graph(G, name="graph"):
import shutil
output_dir = "/app/tmp"
lib_dir = os.path.join(output_dir, "lib")
os.makedirs(output_dir, exist_ok=True)
original_cwd = os.getcwd()
os.chdir(output_dir)
try:
net = Network(
height="600px",
width="100%",
bgcolor="#222222",
font_color="white",
cdn_resources="local"
)
for node, data in G.nodes(data=True):
net.add_node(node, label=f"{node}\n[{data.get('label', '')}]")
for src, dst in G.edges():
net.add_edge(src, dst)
net.repulsion(node_distance=150, spring_length=200)
net.show_buttons(filter_=['physics'])
html_path = f"{name}.html"
net.save_graph(html_path)
return os.path.join(output_dir, html_path)
finally:
os.chdir(original_cwd)
# --- Streamlit App ---
st.title("\U0001F4D8 GraphRAG Agent")
uploaded_file = st.file_uploader("Upload document", type=["pdf", "txt", "docx"])
if uploaded_file:
with st.spinner("Processing document..."):
text = extract_text(uploaded_file)
G = build_graph(text)
html_file = plot_graph(G)
st.subheader("Entity Graph")
with open(html_file, 'r', encoding='utf-8') as f:
components.html(f.read(), height=650, scrolling=True)
st.download_button("Download Graph HTML", data=open(html_file, 'rb'), file_name="graph.html")
question = st.text_input("Ask a question (Graph-RAG):")
if question:
with st.spinner("Querying..."):
relevant_entities = retrieve_relevant_entities(question)
subgraph = expand_graph_context(G, relevant_entities)
context_text = format_graph_context(subgraph)
answer = generate_answer(question, context_text)
st.markdown(f"**Answer:** {answer}")