Skip to main content

Backend-agnostic one-shot GraphRAG: fuse vector + graph (label/class/relation) seeds with MMR diversity into a single synthesis. Zero-infra default.

Project description

OmniFuse

Backend-agnostic, one-shot GraphRAG. Fire several retrieval strategies at once — vector/lexical passages + graph label-linking + class enumeration + relation expansion — and fuse them with MMR diversity into a single LLM synthesis. No iterative ReAct tool loop. Zero infra, zero lock-in: the full algorithm runs on a pure-Python in-memory backend (dict + BM25), and swaps to Fuseki / Qdrant / any LLM by passing objects that match three small protocols.

from omnifuse import build_inmemory, Node, Triple, Chunk

of = build_inmemory(nodes, triples, chunks)        # in-memory, no DB, no API key
print(of.search("불법도박 관련 규정").answer)

Why graph fusion (not just vectors)

Pure vector RAG answers from the top-k passages it happens to embed near the query. A graph store also gives you operations cosine similarity can't:

  • Complete enumerationall instances of a class ("list every regulation"), exact counts.
  • Relations / multi-hop — what an entity is connected to, 1-hop neighbors, paths.
  • Minority evidence survives — MMR diversity keeps the decisive exception/warning that near-duplicate passages would otherwise crowd out of a fixed top-k.

OmniFuse fuses both: the vector seed for content, the graph seeds for structure.

Design — algorithm as a library

The algorithm only talks to three typing.Protocols, never to a database:

class GraphStore(Protocol):
    def search_labels(self, query, *, limit=30) -> list[tuple[Node, float]]: ...   # full-text label search
    def class_instances(self, class_id, *, limit=1000) -> list[Node]: ...          # enumeration
    def neighbors(self, node_id, *, hops=1, limit=100) -> list[tuple[str,str,str]]: ...  # traversal
    def count_class(self, class_id) -> int: ...
    def get_node(self, node_id) -> Node | None: ...

class VectorStore(Protocol):
    def search(self, query, *, limit=20) -> list[tuple[Chunk, float]]: ...
    def fetch(self, ids) -> list[Chunk]: ...

class LLM(Protocol):
    def generate(self, prompt, *, system="", timeout=None) -> str: ...
  • Zero-infra defaultInMemoryGraph indexes node labels with BM25 (CJK character n-grams, so Korean/CJK search works with no morphological analyzer), and InMemoryVector uses cosine when embeddings are present, else BM25 lexical.
  • dependencies = [] — the core needs nothing but the standard library. Real backends are optional extras (pip install "xgen-omnifuse[fuseki,qdrant]").
  • Bring your own LLM — pass anything with generate(...); the bundled EchoLLM returns the fused evidence so the pipeline runs end-to-end with no API key.

The pipeline (OmniFuse.search)

  1. vector/lexical seed → adaptive top-k (score-distribution cut, not fixed k)
  2. graph label-linking → 1-hop relations
  3. class enumeration (complete list/count)
  4. HippoRAG — entities of the retrieved chunks → 1-hop expansion
  5. evidence assembled with MMR diversity (Jaccard, no embeddings needed)
  6. one LLM synthesis over the fused evidence
  7. honest evidence_nodes — only the nodes the answer actually cites

Install

pip install xgen-omnifuse            # core (zero deps)
pip install "xgen-omnifuse[dev]"     # + pytest, ruff

Run the demo with no install:

python examples/quickstart.py

Layout

src/omnifuse/
  protocols.py     # GraphStore / VectorStore / LLM  (the swap points)
  models.py        # Node, Triple, Chunk, SearchResult
  text.py          # tokenizer + BM25 (CJK n-grams)
  fusion.py        # MMR, adaptive top-k, relation ranking
  oneshot.py       # OmniFuse.search — the fusion algorithm
  backends/memory.py  # InMemoryGraph + InMemoryVector (zero infra)
  llm.py           # EchoLLM, CallableLLM
  facade.py        # build_inmemory(...)
examples/  tests/

Two interchangeable modes (same algorithm)

# (a) self-contained — zero infra
from omnifuse import build_inmemory
of = build_inmemory(nodes, triples, chunks)

# (b) backed by Apache Jena Fuseki (or any SPARQL endpoint) — graph-only or with a vector store
from omnifuse import OmniFuse, InMemoryVector
from omnifuse.backends.fuseki import FusekiGraph
graph = FusekiGraph("http://localhost:3030/ds/query", graph_uri="urn:my-graph", user="admin", password="…")
of = OmniFuse(graph, InMemoryVector([]))   # search() unchanged

FusekiGraph is stdlib-only (urllib) and uses portable FILTER(CONTAINS(...)), so it works on any SPARQL 1.1 store — not just jena-text.

Roadmap

  • backends/qdrant.py vector adapter; jena-text fast path for FusekiGraph
  • async pipeline (parallel seeds via asyncio.gather)
  • reranker / cross-encoder hook, query expansion
  • configurable ISA predicates and prompt templates (per domain/language)

CI / Releasing

  • ci.yml — runs pytest (3.10–3.12) + python -m build + twine check on every push/PR.
  • publish.yml — on a GitHub Release, builds and uploads to PyPI via Trusted Publishing (no token in the repo). One-time PyPI setup: project → Publishing → add pending publisher PlateerLab / xgen-omnifuse / publish.yml / pypi. (Token mode: add secrets.PYPI_API_TOKEN.)

Build locally:

pip install build && python -m build      # dist/*.tar.gz + *.whl

License

TBD.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

xgen_omnifuse-0.1.0.tar.gz (15.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

xgen_omnifuse-0.1.0-py3-none-any.whl (16.8 kB view details)

Uploaded Python 3

File details

Details for the file xgen_omnifuse-0.1.0.tar.gz.

File metadata

  • Download URL: xgen_omnifuse-0.1.0.tar.gz
  • Upload date:
  • Size: 15.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for xgen_omnifuse-0.1.0.tar.gz
Algorithm Hash digest
SHA256 725f5bacf9877c3f976f2fc625b6538912dfbbc5cf020916d103e496dca78c2e
MD5 f9f196491929707f8e7da3e7fcd3d1e6
BLAKE2b-256 932caae8d0c5fec59e2f56c0260af877e290e7761774ce36630bd51cebf059a9

See more details on using hashes here.

File details

Details for the file xgen_omnifuse-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: xgen_omnifuse-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 16.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for xgen_omnifuse-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ec6401513aa05785897e5f76f274a3da19d5a6996168c5a7fb8b9d405206605c
MD5 f0b690738d17432c84b6740eaeac3cc7
BLAKE2b-256 31e094a7c64361694e29b9b23081f1aaf3659b4d1ef6b59afc9e22d6ff48db81

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page