{
  "markdown": "# hubmesh\n\n[![tests](https://github.com/DemigodDSK/hubmesh/actions/workflows/test.yml/badge.svg)](https://github.com/DemigodDSK/hubmesh/actions/workflows/test.yml)\n[![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue)](https://github.com/DemigodDSK/hubmesh)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/DemigodDSK/hubmesh/blob/main/LICENSE)\n[![Release](https://img.shields.io/github/v/release/DemigodDSK/hubmesh?include_prereleases)](https://github.com/DemigodDSK/hubmesh/releases)\n\n<!-- mcp-name: io.github.DemigodDSK/hubmesh -->\n\n**Centrality-aware GraphRAG retrieval planner. Drop-in layer over any vector DB.**\n\n`hubmesh` is a Python library that improves multi-hop RAG quality on top of an existing\nvector database. You don't replace your infrastructure — you add a smart planner between\nyour vector DB and your LLM.\n\n## What problem this solves\n\nNaive vector retrieval (\"embed query, get top-k by cosine similarity\") fails on multi-hop\nquestions like *\"Where was the founder of the company that acquired Slack born?\"* The\ncorrect answer requires retrieving entities along a reasoning path, not the single most\nsimilar item.\n\nGraphRAG and HippoRAG showed that running a small Personalized PageRank over a knowledge\ngraph at query time can substantially improve multi-hop retrieval. `hubmesh` extends\nthat line with two contributions:\n\n1. **Multi-component seed selection.** Instead of picking PPR seeds by raw query\n   similarity (which picks wrong-community seeds at high feature overlap), seeds are\n   chosen by a multi-component score combining query relevance, structural fit, and\n   coverage diversity.\n2. **Budget-aware context packing.** Once relevant entities are scored, pack them into\n   the LLM's context window with explicit coverage and redundancy control rather than\n   just truncating top-k.\n\nThe multi-component scoring pattern is adapted from the NNSI framework\n(Naidu Dsk, ICOMP'25 — to appear) for SDN topology\noptimization, repurposed here for retrieval planning.\n\n## Quickstart\n\n### In-memory (testing, small corpora)\n\n```python\nfrom hubmesh import Planner\nfrom hubmesh.adapters import InMemoryStore\n\nembed = ...   # callable: text -> np.ndarray\ndocs = [...]  # list of Document or strings or dicts\n\nstore = InMemoryStore.from_documents(docs, embed=embed)\nplanner = Planner(store=store, embed=embed)\nresult = planner.retrieve(query=\"...\", top_k=10, budget_tokens=4000)\n```\n\n### Qdrant adapter (production)\n\n```python\nfrom hubmesh import Planner\nfrom hubmesh.adapters import QdrantStore\n\nstore = QdrantStore.from_documents(docs)                          # in-memory\nstore = QdrantStore.from_documents(docs, path=\"./qdrant_data\")    # on-disk\nstore = QdrantStore.from_documents(docs, url=\"http://localhost:6333\")  # remote\n\nplanner = Planner(store=store, embed=embed)\nresult = planner.retrieve(query=\"...\", top_k=10)\n```\n\n### Chroma adapter\n\n```python\nfrom hubmesh.adapters import ChromaStore\n\nstore = ChromaStore.from_documents(docs)                          # ephemeral\nstore = ChromaStore.from_documents(docs, persist_directory=\"./chroma_data\")\nstore = ChromaStore.from_documents(docs, host=\"localhost\", port=8000)\n```\n\n### Multi-hop / KG mode\n\n```python\nfrom hubmesh.kg import build_entity_kg\nimport spacy\n\nnlp = spacy.load(\"en_core_web_sm\")\nkg = build_entity_kg(docs, nlp=nlp)\n\nplanner = Planner(store=store, kg=kg, nlp=nlp)\nresult = planner.retrieve(query=\"Where was the founder of the company that bought Slack born?\",\n                          top_k=10, budget_tokens=4000)\n\n# RetrievalResult includes reasoning paths showing why each doc was returned\nfor path in result.reasoning:\n    print(f\"  score={path.score:.3f}  {' → '.join(path.node_ids)}\")\n```\n\n### LLM-extracted KG (richer than spaCy)\n\n```python\nfrom hubmesh.kg_llm import build_entity_kg_llm\nfrom hubmesh.entity_linker import EmbeddingLinker, make_st_embedder\n\ndef llm(prompt):  # provider-agnostic — bring your own\n    return your_llm_call(prompt)\n\nkg = build_entity_kg_llm(docs, llm=llm, cache_path=\"kg_cache.json\")\n\n# optional: cross-document entity dedup — same Linker protocol as the spaCy path\nkg = build_entity_kg_llm(docs, llm=llm, cache_path=\"kg_cache.json\",\n                         linker=EmbeddingLinker(embed=make_st_embedder()))\n\nplanner = Planner(store=store, kg=kg)\n```\n\n### Better entity linking\n\n```python\nfrom hubmesh.kg import build_entity_kg\nfrom hubmesh.entity_linker import EmbeddingLinker, make_st_embedder\n\n# Cluster surface variations: \"United States\" / \"U.S.\" / \"USA\" → one entity\nlinker = EmbeddingLinker(embed=make_st_embedder(), threshold=0.82)\nkg = build_entity_kg(docs, linker=linker)\n```\n\n### Iterative multi-hop: let your agent drive\n\n```python\nr1 = planner.retrieve(query=question, top_k=5)\n\n# your agent reads r1, spots the bridge entity, then aims hop 2 at it:\nr2 = planner.retrieve(\n    query=question, top_k=5,\n    seed_entities=[\"Nimbus Analytics\"],           # merged with the query's own seeds\n    exclude_docs=[s.doc.id for s in r1.sources],  # don't re-retrieve consumed docs\n)\n```\n\nSeed mentions resolve through the alias index, so free-text entity names\nwork. The query path stays deterministic and LLM-free — the planning\nintelligence lives in the caller.\n\n### MCP server: plug hubmesh into any agent\n\n```bash\npip install \"hubmesh[mcp]\"\npython -m spacy download en_core_web_sm\n```\n\n```json\n{\"mcpServers\": {\"hubmesh\": {\"command\": \"hubmesh-mcp\"}}}\n```\n\nExposes the planner as deterministic operator tools over stdio —\n`index_corpus`, `retrieve` (seed-steerable, as above), `resolve_entities`,\n`entity_neighbors`, `path_between`, `get_document`, `graph_stats`,\n`list_corpora`. Your agent is the solver: it decomposes the question,\nreads each hop, and aims the next one; the server answers in\nmilliseconds with zero LLM calls. Corpora persist as plain JSON/NPZ\nunder `~/.hubmesh/corpora`.\n\nThe server warms up models and persisted corpora in the background at\nlaunch (~5-10s on first run), so tool calls stay fast from the start —\nrelevant for strict-timeout connector clients (Perplexity, etc.).\n\nFor web-based connector clients, serve SSE natively — no gateway\nprocess needed:\n\n```bash\nhubmesh-mcp --transport sse --port 8000 --allow-tunnel\nngrok http 8000     # paste https://<your-url>/sse into the connector\n```\n\nTunnel field notes (from a live Perplexity integration): **ngrok works**\n(free tier included); **cloudflared quick tunnels buffer SSE bodies**\nand hang tool calls; **supergateway is unnecessary** here and crashes\non reconnect. `--allow-tunnel` accepts the tunnel's forwarded Host\nheader — without it, proxied requests get 421 Misdirected Request.\n\nFull field report — setup, error decoder, a 9/9 test battery run\nthrough Perplexity, and two findings about reasoning-model behaviour —\nin [docs/perplexity.md](docs/perplexity.md).\n\n### Chunking long documents\n\n```python\nfrom hubmesh import chunk_by_sentences, chunk_documents\n\nchunks = chunk_documents(\n    [{\"id\": \"doc1\", \"text\": long_text}, ...],\n    strategy=\"sentences\", target_tokens=200,\n)\n# Then embed chunks and index normally\n```\n\n## Installation\n\n```bash\npip install hubmesh                   # core\npip install \"hubmesh[qdrant]\"         # Qdrant adapter\npip install \"hubmesh[chroma]\"         # Chroma adapter\npip install \"hubmesh[kg]\"             # entity-linked KG (spaCy)\npip install \"hubmesh[linker]\"         # embedding-based entity linker\npip install \"hubmesh[all]\"            # everything\npython -m spacy download en_core_web_sm   # required for KG mode\n```\n\n## Design\n\n```\nquery → first-pass ANN  → induced subgraph → multi-component scoring\n                              ↓                        ↓\n                       community anchoring → Personalized PageRank\n                              ↓                        ↓\n                              └─────→ ranking → budget-aware packing → context\n```\n\nEach layer is independently testable and replaceable. Adapters wrap your existing vector\nDB so you don't have to migrate.\n\n## Benchmarks\n\n**Headline:** on multi-hop QA, hubmesh's KG mode beats both naive cosine\nretrieval and a HippoRAG-style PPR-only ablation that uses the same KG,\nat every hop depth.\n\n| Benchmark | Setting | recall@10 vs naive |\n|---|---|---:|\n| **HotpotQA** dev, **N=7405** (full) | KG mode | **+5.90 pts** |\n| HotpotQA dev, N=500 | KG mode | **+5.0 pts** |\n| MuSiQue dev, N=300, 2-hop | KG mode | **+6.0 pts** |\n| MuSiQue dev, N=300, 3-hop | KG mode | +3.2 pts |\n| MuSiQue dev, N=300, 4-hop | KG mode | **+5.0 pts** |\n\nAll rows measured with v0.4.0 defaults (alias-indexed seeds + NNSI-KG\nconvergence; ablation JSONs committed in `benchmarks/`). Disclosed:\nconvergence trades top-rank precision for depth recall — recall@2 is\n**−0.75 pts vs naive on full dev** (dips ≤0.5 at smaller n); if you\nretrieve with `top_k=2`, set `use_convergence=False`. Multi-seed\nqueries cost ~1.5–1.8× (still zero LLM tokens, deterministic).\n\nvs PPR-only ablation on the same KG: **+29.8 pts** on HotpotQA at N=500\n(measured on v0.2.0) — the multi-component scoring is doing the work,\nnot just \"having a graph.\"\n\nOn the full N=7405 HotpotQA dev: hubmesh hits **75.2% supporting-fact\nrecall@10** vs naive cosine's **69.3%** (+4.21 pts at recall@5;\nrecall@2 −0.75, disclosed above).\n\nLatency: **~22 ms** mean / 26 ms p95 per query on a 7K-node KG (after PPR\nmatrix caching); ~3 s/query at the 66K-paragraph full-dev scale with\nv0.4 convergence on.\n\nSee [BENCHMARKS.md](BENCHMARKS.md) for the full methodology, ablations,\nper-hop breakdown, and notes on what this proves and doesn't.\n\nReproduce:\n```bash\npython benchmarks/run_hotpotqa.py --n 500 --kg\npython benchmarks/run_musique.py  --n 300 --kg\npython benchmarks/profile_query.py        # latency profile\n```\n\n## Status\n\nPre-alpha (v0.4.0). Core algorithms implemented and validated; adapters for\nin-memory, Qdrant, and Chroma; entity-linked KG with both spaCy NER and\nLLM-based extraction (both linker-aware); alias-indexed entity resolution;\nNNSI-KG scoring (multi-source convergence default-on, hub-discounted PPR\nopt-in); agent-driven iterative multi-hop via `seed_entities` /\n`exclude_docs`; MCP operator server (`hubmesh-mcp`, native SSE) with\nJSON/NPZ corpus persistence; document chunking; reasoning-path\nexplanation; PPR-cache latency optimisation. Pinecone / pgvector / Weaviate adapters\nand additional multi-hop benchmarks are tracked as\n[good first issues](https://github.com/DemigodDSK/hubmesh/issues).\n\n## Acknowledgements\n\nThe multi-component scoring pattern is adapted from the **Network Node Significance\nIndex (NNSI)** framework introduced in\nNaidu Dsk, \"A Framework for Improving Network Topology Based on Graph\nTheory in Software-Defined Networking\", 26th International Conference on\nInternet Computing & IoT (ICOMP'25), Las Vegas, July 2025 — proceedings\nto appear. Repurposed here from SDN topology optimization to retrieval\nplanning.\n\n## License\n\nMIT\n",
  "bytes": 10930,
  "sha": "d251f53746ad275a1d87b93d4d1a0385e0a09e91fb816ec88fe129ff4b4343b7",
  "repo_slug": "demigoddsk/hubmesh",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_demigoddsk_hubmesh_6695628e/readme"
}