This is an automated email from the ASF dual-hosted git repository. imbajin pushed a commit to branch goal-scan in repository https://gitbox.apache.org/repos/asf/hugegraph-ai.git
commit a8d7b2e306c7bf8d5e8209e81aa5630db3ce90ce Author: imbajin <[email protected]> AuthorDate: Sun May 31 11:52:13 2026 +0800 fix(llm): preserve extracted graph value types - stop stringifying property graph values after extraction - add extraction-to-commit round-trip contract coverage - update code-scan ledgers for CS-013 --- .workflow/code-scan/code-scan-state.json | 6 +- .../code-scan/reports/final-code-scan-report.md | 3 +- .workflow/code-scan/reports/issues.md | 6 +- .workflow/code-scan/reports/test-quality-ledger.md | 3 +- .../operators/llm_op/property_graph_extract.py | 3 - .../test_commit_to_hugegraph_load_into_graph.py | 65 +++++++++++++++++++++- .../llm_op/test_property_graph_extract.py | 7 +-- 7 files changed, 74 insertions(+), 19 deletions(-) diff --git a/.workflow/code-scan/code-scan-state.json b/.workflow/code-scan/code-scan-state.json index 19867c27..16845f25 100644 --- a/.workflow/code-scan/code-scan-state.json +++ b/.workflow/code-scan/code-scan-state.json @@ -58,7 +58,8 @@ "CS-001", "CS-002", "CS-004", - "CS-008" + "CS-008", + "CS-013" ], "files_touched": [ "docs/specs/2026-05-31-hugegraph-ai-code-scan-design.md", @@ -89,7 +90,6 @@ "hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph.py", "hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph_load_into_graph.py", "hugegraph-llm/src/tests/operators/index_op/test_vector_index_query.py", - "hugegraph-llm/src/tests/operators/llm_op/test_property_graph_extract.py", "hugegraph-python-client/src/tests/api/test_auth_routing.py", "hugegraph-python-client/src/tests/api/test_graph.py", "hugegraph-python-client/src/tests/api/test_gremlin.py", @@ -125,5 +125,5 @@ "uv run ruff check <edited-python-files>", "git status --short" ], - "next_recommended_action": "Fix graph import correctness: CS-013, then add round-trip contract tests." + "next_recommended_action": "Fix global config mutation and provider validation: CS-011 and CS-012." } diff --git a/.workflow/code-scan/reports/final-code-scan-report.md b/.workflow/code-scan/reports/final-code-scan-report.md index e51218fb..d6dc2eed 100644 --- a/.workflow/code-scan/reports/final-code-scan-report.md +++ b/.workflow/code-scan/reports/final-code-scan-report.md @@ -78,7 +78,7 @@ None. 1. Fix `CS-001` immediately: lock down `/logs` path handling and default admin credentials. Status: fixed after the scan. 2. Fix the client error-contract cluster: `CS-002`, `CS-004`, `CS-008`. Status: fixed after the scan. -3. Fix graph import correctness: `CS-013`, then add the missing round-trip tests. +3. Fix graph import correctness: `CS-013`, then add the missing round-trip tests. Status: fixed after the scan. 4. Fix global config mutation and provider validation: `CS-011`, `CS-012`. 5. Replace fake integration tests with production-flow smoke coverage before broader refactors. @@ -88,3 +88,4 @@ None. - `CS-002`: fixed by redacting sensitive request fields before client debug/error logs are emitted. - `CS-004`: fixed by raising `ResponseParseError` for malformed successful responses. - `CS-008`: fixed by preserving lower-level exceptions from Gremlin execution instead of wrapping every failure as `NotFoundError`. +- `CS-013`: fixed by preserving extracted property value types through the full extraction-to-commit path. diff --git a/.workflow/code-scan/reports/issues.md b/.workflow/code-scan/reports/issues.md index 04fed7fe..67e56604 100644 --- a/.workflow/code-scan/reports/issues.md +++ b/.workflow/code-scan/reports/issues.md @@ -57,7 +57,7 @@ Issue IDs use `CS-NNN`. Priorities are P0 highest through P5 lowest. - Module: `hugegraph-python-client` - Layer: transport / response validation - Paths: `hugegraph-python-client/src/pyhugegraph/utils/util.py:85`, `hugegraph-python-client/src/pyhugegraph/utils/util.py:136` -- Status: open +- Status: fixed - Evidence: `ResponseValidation.__call__()` catches broad `Exception` after successful HTTP parsing and returns the default `{}`. A `200 OK` response with malformed JSON becomes an empty dict. - Impact: Protocol drift or corrupt responses are indistinguishable from legitimate empty results. - Fix: successful-response parse failures now raise `ResponseParseError` instead of returning `{}`. @@ -168,8 +168,8 @@ Issue IDs use `CS-NNN`. Priorities are P0 highest through P5 lowest. - Status: open - Evidence: `filter_item()` converts non-string properties to strings, while `Commit2Graph` enforces schema Python types and skips mismatches. Edge writes can continue and the flow reports success. - Impact: KG import can partially fail while the UI reports success. -- Recommendation: Preserve schema-typed values or add a schema-aware normalization layer; aggregate and surface import failures. -- Test note: Added `FIXME:` comments in property graph and commit tests. +- Fix: `filter_item()` no longer stringifies non-string property values, so extracted JSON numeric values survive into `Commit2Graph` schema validation. +- Test note: Added extraction-to-commit round-trip coverage proving `PropertyGraphExtract.run()` preserves typed values accepted by `Commit2Graph`. ### CS-014: Graph/vector dependency failures are treated as empty retrieval diff --git a/.workflow/code-scan/reports/test-quality-ledger.md b/.workflow/code-scan/reports/test-quality-ledger.md index c9db6eb0..a909f039 100644 --- a/.workflow/code-scan/reports/test-quality-ledger.md +++ b/.workflow/code-scan/reports/test-quality-ledger.md @@ -11,7 +11,6 @@ Added specific `FIXME:` comments in these files: - `hugegraph-llm/src/tests/indices/test_faiss_vector_index.py` - `hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph.py` - `hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph_load_into_graph.py` -- `hugegraph-llm/src/tests/operators/llm_op/test_property_graph_extract.py` - `hugegraph-llm/src/tests/integration/test_graph_rag_pipeline.py` - `hugegraph-llm/src/tests/integration/test_rag_pipeline.py` - `hugegraph-llm/src/tests/integration/test_kg_construction.py` @@ -40,7 +39,6 @@ Added specific `FIXME:` comments in these files: - `PropertyKey` advanced builder payload. - `IndexLabel` multi-field order. - `Commit2Graph.load_into_graph()` partial-failure behavior. -- `PropertyGraphExtract -> Commit2Graph` typed round trip. - `GraphQueryNode` and `VectorQueryNode` dependency failure propagation. - `VectorQueryNode` top-k and threshold parameter pass-through. - `LiteLLMEmbedding` factory construction. @@ -59,3 +57,4 @@ Added specific `FIXME:` comments in these files: - `CS-002`: client transport and response-validation tests now prove password-like request fields are redacted from debug/error logs. - `CS-004`: response-validation tests now prove malformed successful JSON raises `ResponseParseError`. - `CS-008`: Gremlin tests now prove lower-level auth exceptions keep their original type through `GremlinManager.exec()`. +- `CS-013`: extraction-to-commit round-trip tests now prove schema-typed numeric values survive `PropertyGraphExtract.run()` and are accepted by `Commit2Graph`. diff --git a/hugegraph-llm/src/hugegraph_llm/operators/llm_op/property_graph_extract.py b/hugegraph-llm/src/hugegraph_llm/operators/llm_op/property_graph_extract.py index ec4f7f33..dbe6f907 100644 --- a/hugegraph-llm/src/hugegraph_llm/operators/llm_op/property_graph_extract.py +++ b/hugegraph-llm/src/hugegraph_llm/operators/llm_op/property_graph_extract.py @@ -73,9 +73,6 @@ def filter_item(schema, items) -> List[Dict[str, Any]]: for key in non_nullable_keys: if key not in item["properties"]: item["properties"][key] = "NULL" - for key, value in item["properties"].items(): - if not isinstance(value, str): - item["properties"][key] = str(value) filtered_items.append(item) return filtered_items diff --git a/hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph_load_into_graph.py b/hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph_load_into_graph.py index e9b6d803..d576f902 100644 --- a/hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph_load_into_graph.py +++ b/hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph_load_into_graph.py @@ -26,8 +26,8 @@ from hugegraph_llm.operators.llm_op.property_graph_extract import PropertyGraphE pytestmark = [pytest.mark.unit] -# FIXME: cover the extract -> commit round trip and assert that vertex type -# failures stop edge writes and surface an explicit import failure. +# FIXME: cover failure branches where vertex type errors stop edge writes and +# surface an explicit import failure. class TestCommit2GraphLoadIntoGraph(unittest.TestCase): @@ -326,6 +326,67 @@ class TestCommit2GraphLoadIntoGraph(unittest.TestCase): {"role": "Forrest Gump"}, ) + @patch("hugegraph_llm.operators.hugegraph_op.commit_to_hugegraph.Commit2Graph._handle_graph_creation") + def test_property_graph_extract_run_preserves_typed_values_for_commit(self, mock_handle_graph_creation): + """Test extracted typed properties survive the full extraction-to-commit path.""" + mock_handle_graph_creation.side_effect = [ + MagicMock(id="1:Tom Hanks"), + MagicMock(id="2:Forrest Gump"), + MagicMock(id="edge_id"), + ] + llm = MagicMock() + llm.generate.return_value = """{ + "vertices": [ + { + "label": "person", + "properties": { + "name": "Tom Hanks", + "age": 67 + } + }, + { + "label": "movie", + "properties": { + "title": "Forrest Gump", + "year": 1994 + } + } + ], + "edges": [ + { + "label": "acted_in", + "properties": { + "role": "Forrest Gump" + }, + "source": { + "label": "person", + "properties": { + "name": "Tom Hanks" + } + }, + "target": { + "label": "movie", + "properties": { + "title": "Forrest Gump" + } + } + } + ] + }""" + context = PropertyGraphExtract(llm=llm, example_prompt=None).run( + { + "schema": self.schema, + "chunks": ["Tom Hanks acted in Forrest Gump."], + } + ) + + self.assertIsInstance(context["vertices"][0]["properties"]["age"], int) + self.assertIsInstance(context["vertices"][1]["properties"]["year"], int) + + self.commit2graph.load_into_graph(context["vertices"], context["edges"], self.schema) + + self.assertEqual(mock_handle_graph_creation.call_count, 3) + @patch("hugegraph_llm.operators.hugegraph_op.commit_to_hugegraph.Commit2Graph._handle_graph_creation") def test_load_into_graph_raises_explicit_error_when_vertex_creation_fails(self, mock_handle_graph_creation): """Test failed vertex creation is reported before edge creation.""" diff --git a/hugegraph-llm/src/tests/operators/llm_op/test_property_graph_extract.py b/hugegraph-llm/src/tests/operators/llm_op/test_property_graph_extract.py index df27c868..dba5abfa 100644 --- a/hugegraph-llm/src/tests/operators/llm_op/test_property_graph_extract.py +++ b/hugegraph-llm/src/tests/operators/llm_op/test_property_graph_extract.py @@ -34,9 +34,6 @@ from hugegraph_llm.operators.llm_op.property_graph_extract import ( pytestmark = pytest.mark.contract -# FIXME: add a property-graph extraction to Commit2Graph contract test that -# preserves schema-typed values instead of stringifying numeric/date fields. - class TestPropertyGraphExtract(unittest.TestCase): def setUp(self): @@ -191,8 +188,8 @@ class TestPropertyGraphExtract(unittest.TestCase): # Check that title (non-nullable) was added with NULL value self.assertEqual(filtered_items[1]["properties"]["title"], "NULL") - # Check that year was converted to string - self.assertEqual(filtered_items[1]["properties"]["year"], "1994") + # Check that schema-typed values are preserved for Commit2Graph + self.assertEqual(filtered_items[1]["properties"]["year"], 1994) def test_extract_property_graph_by_llm(self): """Test the extract_property_graph_by_llm method."""
