soumya-ai/Knowledge-Graph
0
1"""2Hugging Face Spaces entry point (Gradio).3 4Secrets: https://huggingface.co/spaces/soumya-ai/Knowledge-Graph/settings5Or use the in-app "Connect to Neo4j Aura" form if secrets are not loaded.6 7Requires src/rag_chain.py and src/retriever.py in the Space repo.8"""9 10from __future__ import annotations11 12import gradio as gr13 14from src.config import apply_runtime_config, get_config_status, settings15from src.rag_chain import ask, ask_with_context16 17EXAMPLES = [18 "What is the relationship between OpenAI and Microsoft?",19 "How does GraphRAG differ from standard RAG?",20 "What tools does LangChain integrate with for RAG?",21 "Who founded Neo4j and what query language does it use?",22]23 24 25def _format_debug(ctx) -> str:26 lines = [27 "### Retrieved context",28 "",29 f"**Retrieved:** {len(ctx.entities)} entities, "30 f"{len(ctx.graph_paths)} graph paths, {len(ctx.chunks)} chunks",31 "",32 ]33 if ctx.entities:34 lines.append("### Entities")35 for e in ctx.entities[:12]:36 lines.append(37 f"- **{e['name']}** ({e.get('type', '')}): {e.get('description', '')}"38 )39 if len(ctx.entities) > 12:40 lines.append(f"- _…and {len(ctx.entities) - 12} more_")41 if ctx.graph_paths:42 lines.append("\n### Graph paths")43 for p in ctx.graph_paths[:8]:44 lines.append(f"- {p}")45 if ctx.chunks:46 lines.append("\n### Source chunks (preview)")47 for i, c in enumerate(ctx.chunks[:3], 1):48 preview = c[:400] + ("…" if len(c) > 400 else "")49 lines.append(f"**Chunk {i}:** {preview}")50 return "\n".join(lines)51 52 53def query_graphrag(54 question: str,55 show_retrieval: bool,56 top_k: int,57) -> tuple[str, str]:58 if not question or not question.strip():59 return "Please enter a question.", ""60 61 try:62 if show_retrieval:63 result = ask_with_context(question.strip(), top_k=int(top_k))64 return result["answer"], _format_debug(result["context"])65 return ask(question.strip(), top_k=int(top_k)), ""66 except Exception as exc:67 return f"**Error:** {exc}", ""68 69 70def build_ui() -> gr.Blocks:71 with gr.Blocks(72 title="GraphRAG — Neo4j Aura + OpenAI",73 theme=gr.themes.Soft(),74 ) as demo:75 gr.Markdown(76 """77# GraphRAG — Neo4j Aura + OpenAI + LangChain78 79Ask questions over a **knowledge graph** in Neo4j Aura (vector search + graph paths + OpenAI).80 """81 )82 status_md = gr.Markdown(get_config_status())83 84 with gr.Accordion(85 "Connect to Neo4j Aura + OpenAI (use if Space secrets are missing)",86 open=not settings.neo4j_ready() or not settings.OPENAI_API_KEY,87 ):88 gr.Markdown(89 "Paste the same values as your local `.env`. "90 "Stored **only for this browser session** (not saved on Hugging Face)."91 )92 with gr.Row():93 neo4j_uri = gr.Textbox(94 label="NEO4J_URI",95 placeholder="neo4j+s://xxxx.databases.neo4j.io",96 value=settings.NEO4J_URI or "neo4j+s://3c5467e4.databases.neo4j.io",97 )98 neo4j_database = gr.Textbox(99 label="NEO4J_DATABASE",100 value=settings.NEO4J_DATABASE or "neo4j",101 )102 with gr.Row():103 neo4j_username = gr.Textbox(104 label="NEO4J_USERNAME",105 value=settings.NEO4J_USERNAME or "neo4j",106 )107 neo4j_password = gr.Textbox(108 label="NEO4J_PASSWORD",109 type="password",110 placeholder="Aura password",111 )112 openai_key = gr.Textbox(113 label="OPENAI_API_KEY",114 type="password",115 placeholder="sk-...",116 )117 connect_btn = gr.Button("Connect", variant="secondary")118 119 def do_connect(uri, user, pwd, db, oai_key):120 apply_runtime_config(121 neo4j_uri=uri or "",122 neo4j_username=user or "",123 neo4j_password=pwd or "",124 neo4j_database=db or "neo4j",125 openai_api_key=oai_key or "",126 )127 return test_connection_from_ui()128 129 connect_btn.click(130 do_connect,131 inputs=[neo4j_uri, neo4j_username, neo4j_password, neo4j_database, openai_key],132 outputs=[status_md],133 )134 135 with gr.Row():136 question = gr.Textbox(137 label="Your question",138 placeholder="e.g. How does GraphRAG differ from standard RAG?",139 lines=2,140 scale=4,141 )142 submit = gr.Button("Ask", variant="primary", scale=1)143 144 with gr.Row():145 show_retrieval = gr.Checkbox(146 label="Show retrieved context (entities, paths, chunks)",147 value=False,148 )149 top_k = gr.Slider(150 minimum=1,151 maximum=15,152 value=5,153 step=1,154 label="Top K chunks",155 )156 157 answer = gr.Markdown()158 retrieval = gr.Markdown(visible=False)159 160 def toggle_retrieval_panel(show: bool):161 return gr.update(visible=show)162 163 show_retrieval.change(164 toggle_retrieval_panel,165 inputs=[show_retrieval],166 outputs=[retrieval],167 )168 169 def run(q, show, k):170 ans, dbg = query_graphrag(q, show, k)171 return ans, dbg if show else ""172 173 submit.click(174 run,175 inputs=[question, show_retrieval, top_k],176 outputs=[answer, retrieval],177 )178 question.submit(179 run,180 inputs=[question, show_retrieval, top_k],181 outputs=[answer, retrieval],182 )183 184 gr.Examples(185 examples=[[ex, False, 5] for ex in EXAMPLES],186 inputs=[question, show_retrieval, top_k],187 label="Example questions",188 )189 190 gr.Markdown(191 f"""192---193**Models:** chat `{settings.OPENAI_MODEL}` · embeddings `{settings.OPENAI_EMBED_MODEL}` ({settings.OPENAI_EMBED_DIMENSIONS}d)194 """195 )196 197 return demo198 199 200def test_connection_from_ui() -> str:201 """After UI connect: refresh status banner."""202 return get_config_status()203 204 205demo = build_ui()206 207if __name__ == "__main__":208 import os209 210 port = int(os.getenv("PORT", "7860"))211 demo.queue(default_concurrency_limit=2).launch(212 server_name="0.0.0.0",213 server_port=port,214 show_error=True,215 )216 