From 20f6e3bd1c4c8e496e9b3ebc8ce19aa86d4812aa Mon Sep 17 00:00:00 2001 From: Bao Yansong Date: Fri, 23 Oct 2026 17:24:19 +0000 Subject: [PATCH 1/2] fix(core): update existing property graph relations on upsert --- .../llama_index/core/graph_stores/types.py | 3 -- .../tests/graph_stores/test_simple_lpg.py | 43 +++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/llama-index-core/llama_index/core/graph_stores/types.py b/llama-index-core/llama_index/core/graph_stores/types.py index a96ddbbe827..752f080d143 100644 --- a/llama-index-core/llama_index/core/graph_stores/types.py +++ b/llama-index-core/llama_index/core/graph_stores/types.py @@ -161,9 +161,6 @@ def get_triplets(self) -> List[Triplet]: def add_triplet(self, triplet: Triplet) -> None: """Add a triplet.""" subj, rel, obj = triplet - if (subj.id, rel.id, obj.id) in self.triplets: - return - self.triplets.add((subj.id, rel.id, obj.id)) self.nodes[subj.id] = subj self.nodes[obj.id] = obj diff --git a/llama-index-core/tests/graph_stores/test_simple_lpg.py b/llama-index-core/tests/graph_stores/test_simple_lpg.py index c3d2efbc582..09049940a62 100644 --- a/llama-index-core/tests/graph_stores/test_simple_lpg.py +++ b/llama-index-core/tests/graph_stores/test_simple_lpg.py @@ -1,6 +1,8 @@ import os import tempfile +import pytest + from llama_index.core.graph_stores.simple_labelled import SimplePropertyGraphStore from llama_index.core.graph_stores.types import ( EntityNode, @@ -144,3 +146,44 @@ def test_persist_utf8_round_trip() -> None: assert loaded_nodes[e1.id].name == "定义图" assert loaded_nodes[e2.id].name == "テスト" assert loaded_nodes[e3.id].name == "émojis_✨🚀" + + +@pytest.mark.asyncio +@pytest.mark.parametrize("use_async", [False, True]) +async def test_relation_upsert_updates_existing_properties(use_async: bool) -> None: + store = SimplePropertyGraphStore() + alice = EntityNode(name="Alice", properties={"role": "author"}) + bob = EntityNode(name="Bob", properties={"role": "reviewer"}) + original = Relation( + label="KNOWS", + source_id=alice.id, + target_id=bob.id, + properties={"confidence": 0.1}, + ) + other = Relation(label="MENTIONS", source_id=alice.id, target_id=bob.id) + updated = Relation( + label="KNOWS", + source_id=alice.id, + target_id=bob.id, + properties={"confidence": 0.9}, + ) + store.upsert_nodes([alice, bob]) + store.upsert_relations([original, other]) + + if use_async: + await store.aupsert_relations([updated]) + else: + store.upsert_relations([updated]) + + assert store.get_triplets(relation_names=["KNOWS"]) == [(alice, updated, bob)] + assert store.get_triplets(relation_names=["MENTIONS"]) == [(alice, other, bob)] + assert len(store.graph.triplets) == 2 + assert len(store.graph.relations) == 2 + assert store.get(ids=[alice.id, bob.id]) == [alice, bob] + + if use_async: + await store.aupsert_relations([updated]) + else: + store.upsert_relations([updated]) + assert len(store.graph.triplets) == 2 + assert store.get_triplets(relation_names=["KNOWS"]) == [(alice, updated, bob)] From 6ce58fd0e59e14b76a2d8be5107d865e31c313c0 Mon Sep 17 00:00:00 2001 From: Bao Yansong Date: Fri, 23 Oct 2026 17:28:23 +0000 Subject: [PATCH 2/2] test(core): isolate relation upsert regressions --- .../tests/graph_stores/test_simple_lpg.py | 43 ------------------ .../graph_stores/test_simple_lpg_upserts.py | 45 +++++++++++++++++++ 2 files changed, 45 insertions(+), 43 deletions(-) create mode 100644 llama-index-core/tests/graph_stores/test_simple_lpg_upserts.py diff --git a/llama-index-core/tests/graph_stores/test_simple_lpg.py b/llama-index-core/tests/graph_stores/test_simple_lpg.py index 09049940a62..c3d2efbc582 100644 --- a/llama-index-core/tests/graph_stores/test_simple_lpg.py +++ b/llama-index-core/tests/graph_stores/test_simple_lpg.py @@ -1,8 +1,6 @@ import os import tempfile -import pytest - from llama_index.core.graph_stores.simple_labelled import SimplePropertyGraphStore from llama_index.core.graph_stores.types import ( EntityNode, @@ -146,44 +144,3 @@ def test_persist_utf8_round_trip() -> None: assert loaded_nodes[e1.id].name == "定义图" assert loaded_nodes[e2.id].name == "テスト" assert loaded_nodes[e3.id].name == "émojis_✨🚀" - - -@pytest.mark.asyncio -@pytest.mark.parametrize("use_async", [False, True]) -async def test_relation_upsert_updates_existing_properties(use_async: bool) -> None: - store = SimplePropertyGraphStore() - alice = EntityNode(name="Alice", properties={"role": "author"}) - bob = EntityNode(name="Bob", properties={"role": "reviewer"}) - original = Relation( - label="KNOWS", - source_id=alice.id, - target_id=bob.id, - properties={"confidence": 0.1}, - ) - other = Relation(label="MENTIONS", source_id=alice.id, target_id=bob.id) - updated = Relation( - label="KNOWS", - source_id=alice.id, - target_id=bob.id, - properties={"confidence": 0.9}, - ) - store.upsert_nodes([alice, bob]) - store.upsert_relations([original, other]) - - if use_async: - await store.aupsert_relations([updated]) - else: - store.upsert_relations([updated]) - - assert store.get_triplets(relation_names=["KNOWS"]) == [(alice, updated, bob)] - assert store.get_triplets(relation_names=["MENTIONS"]) == [(alice, other, bob)] - assert len(store.graph.triplets) == 2 - assert len(store.graph.relations) == 2 - assert store.get(ids=[alice.id, bob.id]) == [alice, bob] - - if use_async: - await store.aupsert_relations([updated]) - else: - store.upsert_relations([updated]) - assert len(store.graph.triplets) == 2 - assert store.get_triplets(relation_names=["KNOWS"]) == [(alice, updated, bob)] diff --git a/llama-index-core/tests/graph_stores/test_simple_lpg_upserts.py b/llama-index-core/tests/graph_stores/test_simple_lpg_upserts.py new file mode 100644 index 00000000000..c1cf03c17ba --- /dev/null +++ b/llama-index-core/tests/graph_stores/test_simple_lpg_upserts.py @@ -0,0 +1,45 @@ +import pytest + +from llama_index.core.graph_stores.simple_labelled import SimplePropertyGraphStore +from llama_index.core.graph_stores.types import EntityNode, Relation + + +@pytest.mark.asyncio +@pytest.mark.parametrize("use_async", [False, True]) +async def test_relation_upsert_updates_existing_properties(use_async: bool) -> None: + store = SimplePropertyGraphStore() + alice = EntityNode(name="Alice", properties={"role": "author"}) + bob = EntityNode(name="Bob", properties={"role": "reviewer"}) + original = Relation( + label="KNOWS", + source_id=alice.id, + target_id=bob.id, + properties={"confidence": 0.1}, + ) + other = Relation(label="MENTIONS", source_id=alice.id, target_id=bob.id) + updated = Relation( + label="KNOWS", + source_id=alice.id, + target_id=bob.id, + properties={"confidence": 0.9}, + ) + store.upsert_nodes([alice, bob]) + store.upsert_relations([original, other]) + + if use_async: + await store.aupsert_relations([updated]) + else: + store.upsert_relations([updated]) + + assert store.get_triplets(relation_names=["KNOWS"]) == [(alice, updated, bob)] + assert store.get_triplets(relation_names=["MENTIONS"]) == [(alice, other, bob)] + assert len(store.graph.triplets) == 2 + assert len(store.graph.relations) == 2 + assert store.get(ids=[alice.id, bob.id]) == [alice, bob] + + if use_async: + await store.aupsert_relations([updated]) + else: + store.upsert_relations([updated]) + assert len(store.graph.triplets) == 2 + assert store.get_triplets(relation_names=["KNOWS"]) == [(alice, updated, bob)]