Python API¶
from spectrafuse import (SpectRAGIngestion, SpectRAGRetrieval, make_settings, DEFAULTS,
enable_logging, __version__)
SpectRAGIngestion¶
The offline pipeline (paper Table 1): load → chunk → embed → edge signals → weighted graph → spectral coordinates → save. Run it once per document set.
| Argument | Meaning |
|---|---|
index_dir |
the folder to write the index to. An existing SpectraFuse index there is replaced; any other non-empty folder raises IndexError_ |
settings |
a dict of settings to change |
entity_fn |
optional text -> list of entities; the default is rule-based (details) |
question_fn |
optional text -> list of questions, for utility edges (details) |
Raises SettingsError or UnknownSettingError for invalid settings.
ingest(path)¶
Ingest a file or a folder (searched recursively for .txt, .md, .markdown and .pdf),
save the index, and return a summary. Each file is one document; its doc_id is its path
relative to the folder.
Raises NoDocumentsError if no file with text is found.
ingest_documents(docs)¶
Ingest documents that are already in memory: a list of {"doc_id": ..., "text": ...} dicts.
Every doc_id must be unique. Raises DocumentError for a malformed list and NoDocumentsError
if every text is empty.
The summary¶
Both methods return the dict that is also saved as info.json:
| Key | Meaning |
|---|---|
documents, chunks |
how many of each |
signal_edges |
edges per signal, before fusion: {"semantic": 28, "entity": 14, ...} |
graph_edges |
edges in the fused graph |
weights_used |
the signal weights after empty signals shared theirs |
spectral_dims, clusters |
coordinates per chunk and spectral clusters (fewer than asked for on very small corpora) |
ann_backend |
the vector-search backend used: exact, faiss or hnsw |
format_version, spectrafuse_version |
the index format (2) and the package version that wrote it |
seconds |
how long ingestion took |
Lower-level steps¶
make_chunks(docs) returns the chunks with their entities, references and utility questions;
make_graph(signals) fuses edge signals into (edges, weights); make_spectral(edges,
node_count) returns (coords, eigenvalues, clusters). ingest_documents() calls them in turn.
SpectRAGRetrieval¶
The online pipeline (paper Table 2, Algorithm 1): seeds → graph expansion → proximity re-scoring → token-budget selection → context. Create it once, then search many times.
| Argument | Meaning |
|---|---|
index_dir |
an index made by SpectRAGIngestion |
settings |
settings to change. The object starts from the settings the index was built with. Graph settings rebuild the graph in memory from the saved signals |
embed_fn |
required if the index was built with your own embed function: pass the same function again |
rerank_fn |
optional (question, texts) -> scores, used by selector="anchor_expand_ce" |
Raises IndexError_ if the folder is missing, incomplete, or written by a newer index format.
search(question, token_budget=None, settings=None, query_vector=None)¶
Find the context for a question.
| Argument | Meaning |
|---|---|
question |
the question text |
token_budget |
tokens of context; None uses the token_budget setting (12,000) |
settings |
overrides for this call only, e.g. {"selector": "mmr", "alpha": 0.5}; graph settings are not allowed here |
query_vector |
an embedding of the question, if you already have one (skips embedding) |
Returns a dict:
| Key | Contents |
|---|---|
question |
the question |
context |
the selected chunks as one text, grouped by document (### Source: <doc_id> blocks; documents ordered by their best chunk, chunks in reading order) |
prompt |
a grounded prompt containing the context and the question |
chunks |
the selected chunks, in the order they were picked (see below) |
tokens |
the tokens used, never more than the budget |
seeds |
chunk ids of the seeds |
candidates |
chunk ids of every chunk the graph walk reached |
Each item of chunks:
| Key | Contents |
|---|---|
id, doc_id, position, section, tokens, text |
the chunk: its id, document, place in the document, section path (a list of headings), size and text |
rank |
1 for the first chunk picked |
reason |
core, tail or backfill (the spectrag selector); mmr; anchor or expand (anchor_expand_ce) |
hop |
0 for a seed, 1 or 2 for chunks reached through the graph |
score |
the re-scored value: (1 − α)·relevance + α·proximity |
relevance, proximity |
the two parts of the score, each 0…1 |
answer(question, llm_fn, token_budget=None, settings=None)¶
search(), then llm_fn(result["prompt"]). Returns the search result with an extra answer key.
llm_fn is any function prompt -> text, e.g. spectrafuse.utils.llm.ollama_llm("gemma3:4b").
rebuild_graph(overrides=None)¶
Change graph settings (edge_weights, edge_threshold, graph_k, spectral_dims,
n_clusters) without re-embedding: the saved signals are fused again and the spectral
coordinates recomputed. In memory only; the index on disk is unchanged.
retrieve(question, settings=None, query_vector=None)¶
Stages 1–4 without formatting, for inspection and evaluation. Returns (seeds, scored, picks):
seeds:seeds(ids),relevance,dense,bm25,utilityandrrfscores for every chunk, and therankingsthat were fused.scored:candidates(ids), and aligned with themhop,relevance,ppr,spectral,proximityandscores.picks:[(candidate number, reason), ...]in pick order;scored["candidates"][k]is the chunk id.
Attributes¶
| Attribute | Contents |
|---|---|
settings |
the settings in use |
index |
the loaded index: chunks, embeddings, edges, spectral, eigenvalues, info, … |
embed |
the embedder, as embed(texts, kind="query") |
Settings helpers¶
| Name | Meaning |
|---|---|
DEFAULTS |
the default settings (a dict; do not modify it) |
make_settings(overrides=None) |
a fresh copy of the defaults with your changes, checked; raises UnknownSettingError or SettingsError |
spectrafuse.config.EDGE_WEIGHTS_CONFIG_1, EDGE_WEIGHTS_CONFIG_2 |
the paper's two edge-weight sets (1 is the default) |
Logging and version¶
| Name | Meaning |
|---|---|
enable_logging(level="INFO") |
print SpectraFuse's messages to stderr: one timed line per pipeline step; "DEBUG" adds one line per question |
__version__ |
the package version, e.g. "0.2.0" |
INDEX_FORMAT |
the index format this version writes and reads (2) |
Utilities¶
| Function | Module | Meaning |
|---|---|---|
ollama_llm(model="gemma3:4b", url=..., num_ctx=None, timeout=600, max_tokens=None, retries=2) |
spectrafuse.utils.llm |
an llm_fn for a local Ollama model, temperature 0 |
make_question_fn(llm_fn, count=3) |
spectrafuse.utils.questions |
a question_fn that asks an LLM for utility questions |
save_graph_view(index_dir, path="graph.html", question=None, settings=None, max_nodes=1500) |
spectrafuse.utils.graph_view |
write the graph view; returns the path |
graph_data(index_dir, question=None, settings=None, max_nodes=1500) |
spectrafuse.utils.graph_view |
the graph view's data as Python dicts |
tune(search, questions, grid, objective="recall", budget=None, trials=None, seed=0) |
spectrafuse.utils.tuning |
hyperparameter search; returns trials, best first |
load_questions(path) |
spectrafuse.utils.evaluation |
read a questions JSON file |
evaluate_retrieval(search, questions, budget) |
spectrafuse.utils.evaluation |
SpectraFuse against dense and hybrid top-k with the same budget; returns rows and summary |
evaluate_answers(search, questions, budget, llm_fn) |
spectrafuse.utils.evaluation |
the same for answers (needs answers in each question): exact match, token F1, contains |
build_prompt(question, context, short=False) |
spectrafuse.utils.prompt |
the grounded prompt; short=True asks for a bare answer |
count_tokens(text) |
spectrafuse.utils.tokens |
the token count SpectraFuse uses (tiktoken with the tokens extra, otherwise an estimate) |
Every module in spectrafuse.utils is short and does one step; read the source for the
lower-level functions. The source on GitHub
links each one to its section or equation in the paper.