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 from_triples

of = from_triples(                                  # nodes are inferred; no DB, no API key
    [("담보", "instanceOf", "규정"), ("담보", "한도", "5억")],
    chunks=[("c1", "담보 한도는 5억원이다", ["담보"])],
)
print(of.search("담보 한도").answer)

Load however you have the data — all zero-dep, same search():

from omnifuse import from_jsonl, from_csv, from_fuseki, build_inmemory
of = from_jsonl(triples="t.jsonl", chunks="c.jsonl")
of = from_csv(triples="triples.csv", chunks="chunks.csv")
of = from_fuseki("http://localhost:3030/ds/query", graph_uri="urn:g", user="admin", password="…")
of = build_inmemory(nodes, triples, chunks)         # explicit Node/Triple/Chunk

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)

Memory (remember / recall)

A growing store built on the same fusion search — remember() facts & notes over time, recall() pulls them back. Zero infra; notes auto-link to known entities; persists to JSONL.

from omnifuse import Memory

m = Memory()
m.add_fact("담보", "instanceOf", "규정")
m.remember("담보 한도는 5억원이다", triples=[("담보", "한도", "5억")])
print(m.recall("담보 한도").answer)     # fusion search over everything remembered
m.save("mem.jsonl"); m2 = Memory.load("mem.jsonl")

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.3.0.tar.gz (19.6 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.3.0-py3-none-any.whl (20.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: xgen_omnifuse-0.3.0.tar.gz
  • Upload date:
  • Size: 19.6 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.3.0.tar.gz
Algorithm Hash digest
SHA256 e3f531bd93650188ec677637e6a41a552394fa659776e550ebe3f88a2f3be1b5
MD5 46262adb228f38dcddb6c7b3eff0a664
BLAKE2b-256 cdc636ed47b352292f0eb4d5387d762b682635c5445eb19f633dd1a33509f22d

See more details on using hashes here.

File details

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

File metadata

  • Download URL: xgen_omnifuse-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 20.5 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.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9839a19bc1b07aea60ead947b19269ef4610f88b84fb0311e03041b76296d3b3
MD5 4d814cb91d85ab980d667bad549466c4
BLAKE2b-256 d514f1894aa86675268b86c940d70fd0cca9d46e29808f2581e6e7b6d0f3959c

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