cognee 1.2.2.dev1__py3-none-any.whl → 1.2.2.dev2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cognee/modules/migration/snapshot.py +42 -8
- cognee/tests/test_cogx_roundtrip.py +12 -0
- cognee/tests/unit/migration/test_snapshot.py +53 -0
- {cognee-1.2.2.dev1.dist-info → cognee-1.2.2.dev2.dist-info}/METADATA +1 -1
- {cognee-1.2.2.dev1.dist-info → cognee-1.2.2.dev2.dist-info}/RECORD +9 -9
- {cognee-1.2.2.dev1.dist-info → cognee-1.2.2.dev2.dist-info}/WHEEL +0 -0
- {cognee-1.2.2.dev1.dist-info → cognee-1.2.2.dev2.dist-info}/entry_points.txt +0 -0
- {cognee-1.2.2.dev1.dist-info → cognee-1.2.2.dev2.dist-info}/licenses/LICENSE +0 -0
- {cognee-1.2.2.dev1.dist-info → cognee-1.2.2.dev2.dist-info}/licenses/NOTICE.md +0 -0
|
@@ -19,7 +19,7 @@ so no property is lost.
|
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
21
|
import json
|
|
22
|
-
from typing import Any, Dict, List, Optional, Type, Union
|
|
22
|
+
from typing import Any, Dict, Iterable, List, Optional, Tuple, Type, Union
|
|
23
23
|
|
|
24
24
|
from pydantic import BaseModel, ConfigDict, Field, SerializeAsAny, create_model, field_validator
|
|
25
25
|
|
|
@@ -59,13 +59,41 @@ class _DynamicDataPoint(DataPoint):
|
|
|
59
59
|
model_config = ConfigDict(arbitrary_types_allowed=True, extra="allow")
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
_dynamic_models: Dict[str, Type[DataPoint]] = {}
|
|
62
|
+
_dynamic_models: Dict[Tuple[str, Tuple[str, ...], Tuple[str, ...]], Type[DataPoint]] = {}
|
|
63
63
|
|
|
64
64
|
|
|
65
|
-
def _dynamic_model(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
def _dynamic_model(
|
|
66
|
+
type_name: str,
|
|
67
|
+
property_names: Optional[Iterable[str]] = None,
|
|
68
|
+
index_fields: Optional[List[str]] = None,
|
|
69
|
+
) -> Type[DataPoint]:
|
|
70
|
+
"""A DataPoint subclass named ``type_name`` that DECLARES the record's
|
|
71
|
+
properties as fields and bakes ``index_fields`` into the CLASS-level
|
|
72
|
+
metadata default.
|
|
73
|
+
|
|
74
|
+
Instance-level state is not enough on either count: downstream model
|
|
75
|
+
machinery only sees declared fields. ``get_graph_from_model`` iterates
|
|
76
|
+
declared fields when collecting node properties (``extra="allow"`` values
|
|
77
|
+
are silently dropped — imported DocumentChunk text never reached storage),
|
|
78
|
+
and rebuilds nodes through ``copy_model``, which re-applies class field
|
|
79
|
+
defaults (a plain DataPoint metadata default of ``index_fields: []``
|
|
80
|
+
dropped the embeddable-field markers, so nothing was vector-indexed).
|
|
81
|
+
"""
|
|
82
|
+
extra_fields = tuple(
|
|
83
|
+
sorted(
|
|
84
|
+
name
|
|
85
|
+
for name in (property_names or ())
|
|
86
|
+
if name != "metadata" and name not in DataPoint.model_fields
|
|
87
|
+
)
|
|
88
|
+
)
|
|
89
|
+
key = (type_name, extra_fields, tuple(index_fields or ()))
|
|
90
|
+
if key not in _dynamic_models:
|
|
91
|
+
field_definitions: Dict[str, Any] = {name: (Any, None) for name in extra_fields}
|
|
92
|
+
field_definitions["metadata"] = (dict, {"index_fields": list(index_fields or [])})
|
|
93
|
+
_dynamic_models[key] = create_model(
|
|
94
|
+
type_name, __base__=_DynamicDataPoint, **field_definitions
|
|
95
|
+
)
|
|
96
|
+
return _dynamic_models[key]
|
|
69
97
|
|
|
70
98
|
|
|
71
99
|
def _clean_properties(properties: Dict[str, Any]) -> Dict[str, Any]:
|
|
@@ -101,14 +129,20 @@ def rehydrate_node(
|
|
|
101
129
|
except Exception: # noqa: BLE001 — fall back to a dynamic model
|
|
102
130
|
pass
|
|
103
131
|
|
|
132
|
+
# Carry the record's properties and embeddable fields onto the dynamic
|
|
133
|
+
# CLASS so they survive downstream field iteration and copy_model
|
|
134
|
+
# rebuilds (see _dynamic_model).
|
|
135
|
+
metadata = props.get("metadata")
|
|
136
|
+
index_fields = metadata.get("index_fields") if isinstance(metadata, dict) else None
|
|
137
|
+
|
|
104
138
|
try:
|
|
105
139
|
# Created lazily: only when the registered class is absent or fails.
|
|
106
|
-
return _dynamic_model(type_name)(**props)
|
|
140
|
+
return _dynamic_model(type_name, props, index_fields)(**props)
|
|
107
141
|
except Exception: # noqa: BLE001 — final fallback below
|
|
108
142
|
pass
|
|
109
143
|
|
|
110
144
|
base_safe = {key: value for key, value in props.items() if key in _BASE_FIELDS}
|
|
111
|
-
return _dynamic_model(type_name)(**base_safe)
|
|
145
|
+
return _dynamic_model(type_name, base_safe, index_fields)(**base_safe)
|
|
112
146
|
|
|
113
147
|
|
|
114
148
|
class GraphEdge(BaseModel):
|
|
@@ -155,6 +155,18 @@ async def main():
|
|
|
155
155
|
assert len(restored_results) != 0, "Search on the restored instance returned nothing."
|
|
156
156
|
logger.info("Restored search results: %s", restored_results)
|
|
157
157
|
|
|
158
|
+
# Chunk-level retrieval needs the imported DocumentChunk text to be
|
|
159
|
+
# vector-indexed (regression: rehydrated raw nodes lost their
|
|
160
|
+
# index_fields, so restored archives had no DocumentChunk_text
|
|
161
|
+
# collection and chunk/hybrid retrieval failed with NoDataError).
|
|
162
|
+
chunk_results = await cognee.search(
|
|
163
|
+
query_type=SearchType.CHUNKS, query_text="rooftop observatory", datasets=[dataset_name]
|
|
164
|
+
)
|
|
165
|
+
assert len(chunk_results) != 0, (
|
|
166
|
+
"Chunk search on the restored instance returned nothing — imported "
|
|
167
|
+
"DocumentChunk text was not vector-indexed."
|
|
168
|
+
)
|
|
169
|
+
|
|
158
170
|
print("COGX roundtrip e2e passed: export -> fresh store -> import -> search.")
|
|
159
171
|
|
|
160
172
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Unit tests for the Pydantic-native export (GraphSnapshot). Pure: no DB, no LLM."""
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
3
4
|
from uuid import uuid4
|
|
4
5
|
|
|
5
6
|
from cognee.infrastructure.engine import DataPoint
|
|
@@ -68,6 +69,58 @@ class TestRehydrateNode:
|
|
|
68
69
|
assert isinstance(node, Entity)
|
|
69
70
|
assert node.metadata["index_fields"] == ["name"]
|
|
70
71
|
|
|
72
|
+
def test_dynamic_model_bakes_index_fields_into_class_default(self):
|
|
73
|
+
node = rehydrate_node(
|
|
74
|
+
{
|
|
75
|
+
"id": str(uuid4()),
|
|
76
|
+
"type": "AlienChunk",
|
|
77
|
+
"text": "Zorg lives on Mars.",
|
|
78
|
+
"metadata": {"index_fields": ["text"]},
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
# The CLASS default must carry index_fields, not just the instance:
|
|
82
|
+
# get_graph_from_model rebuilds nodes via copy_model from class field
|
|
83
|
+
# defaults, so instance-only metadata never reaches the vector index.
|
|
84
|
+
assert type(node).model_fields["metadata"].default["index_fields"] == ["text"]
|
|
85
|
+
|
|
86
|
+
def test_index_fields_and_text_survive_graph_extraction(self):
|
|
87
|
+
"""Regression: imported DocumentChunk raw nodes fail real-class
|
|
88
|
+
validation (flat properties lack the required relation fields) and
|
|
89
|
+
fall back to a dynamic model. Downstream model machinery only sees
|
|
90
|
+
DECLARED fields, so the dynamic class must declare the record's
|
|
91
|
+
properties AND carry index_fields as its class metadata default —
|
|
92
|
+
otherwise get_graph_from_model drops the chunk's text (extra="allow"
|
|
93
|
+
values are not iterated) and/or its embeddable-field markers
|
|
94
|
+
(copy_model re-applies class defaults), the restored archive has no
|
|
95
|
+
DocumentChunk_text collection, and hybrid retrieval fails with
|
|
96
|
+
NoDataError."""
|
|
97
|
+
from cognee.modules.graph.utils import get_graph_from_model
|
|
98
|
+
|
|
99
|
+
node = rehydrate_node(
|
|
100
|
+
{
|
|
101
|
+
"id": str(uuid4()),
|
|
102
|
+
"type": "DocumentChunk",
|
|
103
|
+
"text": "Alice lives in Berlin.",
|
|
104
|
+
"chunk_index": 0,
|
|
105
|
+
"metadata": {"index_fields": ["text"]},
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
assert node.metadata["index_fields"] == ["text"]
|
|
109
|
+
assert node.text == "Alice lives in Berlin."
|
|
110
|
+
|
|
111
|
+
nodes, _ = asyncio.run(
|
|
112
|
+
get_graph_from_model(node, added_nodes={}, added_edges={}, visited_properties={})
|
|
113
|
+
)
|
|
114
|
+
(extracted,) = nodes
|
|
115
|
+
assert extracted.metadata["index_fields"] == ["text"], (
|
|
116
|
+
"index_fields lost during graph extraction — imported chunk text "
|
|
117
|
+
"will not be vector-indexed"
|
|
118
|
+
)
|
|
119
|
+
assert getattr(extracted, "text", None) == "Alice lives in Berlin.", (
|
|
120
|
+
"chunk text lost during graph extraction — undeclared extra fields "
|
|
121
|
+
"are dropped, so the imported chunk stores and indexes as empty"
|
|
122
|
+
)
|
|
123
|
+
|
|
71
124
|
|
|
72
125
|
class TestGraphSnapshot:
|
|
73
126
|
def _snapshot(self, link_relations=False):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cognee
|
|
3
|
-
Version: 1.2.2.
|
|
3
|
+
Version: 1.2.2.dev2
|
|
4
4
|
Summary: Cognee - is a library for enriching LLM context with a semantic layer for better understanding and reasoning.
|
|
5
5
|
Project-URL: Homepage, https://www.cognee.ai
|
|
6
6
|
Project-URL: Repository, https://github.com/topoteretes/cognee
|
|
@@ -787,7 +787,7 @@ cognee/modules/migration/export.py,sha256=v-GPc1oT41cmXpuU_vqOCX0Us_9sWQZFoVwMTN
|
|
|
787
787
|
cognee/modules/migration/formats.py,sha256=-o2hqVwSHXz2O7d_bsxdACZjTd-h8goKoLQ_UqFBcSw,5785
|
|
788
788
|
cognee/modules/migration/import_source.py,sha256=ch7hSReqAKdt2jzbuY-FyL-hXgDJhrzQWyzE1vgNFx4,11475
|
|
789
789
|
cognee/modules/migration/loader.py,sha256=1fWim7Zblt-DggcIJ56EjZj0IFP3Up0AxmKztCHNoSU,23935
|
|
790
|
-
cognee/modules/migration/snapshot.py,sha256=
|
|
790
|
+
cognee/modules/migration/snapshot.py,sha256=z81JRsbLr7Z4jawueOn48OfRkL9fHGe8lRVHFJfm7R8,10773
|
|
791
791
|
cognee/modules/migration/sources/__init__.py,sha256=HofNJVBO45VaaEWHnC_I7lV5XFtsMLqbm_bpiD3-0Ck,348
|
|
792
792
|
cognee/modules/migration/sources/base.py,sha256=WXhgaRX9M7GCvLmyzvWkREgGCvTliJwk_DGK7LjicVE,2135
|
|
793
793
|
cognee/modules/migration/sources/cogx_archive.py,sha256=x4wz5E-Gim7_vLkB9JFJ9NiZB_JIFKt5ISA1VZNToGs,1569
|
|
@@ -1261,7 +1261,7 @@ cognee/tests/test_advanced_pdf_loader.py,sha256=0Jdg4ZZ67_3dg5Z0bCYzubHSLYDQtNgH
|
|
|
1261
1261
|
cognee/tests/test_agent_memory_e2e.py,sha256=I1d52atkk0r0dycBG_FTu7_gOyQL-tYt067dW-GTQF0,6681
|
|
1262
1262
|
cognee/tests/test_chunk_associations.py,sha256=OnsX3a9pLV_Yaj7Xyb297mOciihEz3TFK5uwhbjh_80,8182
|
|
1263
1263
|
cognee/tests/test_cognee_server_start.py,sha256=nld8SkG4WHUPiHVz6872ImWmcERcUnNN_-6Gf8fUj0E,7572
|
|
1264
|
-
cognee/tests/test_cogx_roundtrip.py,sha256=
|
|
1264
|
+
cognee/tests/test_cogx_roundtrip.py,sha256=K0kiGxZ1COFsytTS5zfhymMwClWCUTOQK7VTsWOZxgk,7522
|
|
1265
1265
|
cognee/tests/test_concurrent_cognify_e2e.py,sha256=tdmp0QA6uatFd5YL0bBahk8GWq-GG8LKrhiDfrP4jWQ,5682
|
|
1266
1266
|
cognee/tests/test_concurrent_subprocess_access.py,sha256=xh72FclRY1LnwUbpmIQTzvK7Yayoov6oewWmHivPTxE,3041
|
|
1267
1267
|
cognee/tests/test_conversation_history.py,sha256=80be_RWiT4acwZ6agSQY2ch69hmJ8MN-RSeL2qIx0pM,18186
|
|
@@ -1547,7 +1547,7 @@ cognee/tests/unit/migration/test_migration.py,sha256=5tqGzk01dsfhGxDDRzyumQ2fotb
|
|
|
1547
1547
|
cognee/tests/unit/migration/test_provenance_ctx.py,sha256=GHiAA8NLs2JWaeztyFJuaeeuo9NScgzUNtQGX5wguuA,3057
|
|
1548
1548
|
cognee/tests/unit/migration/test_push.py,sha256=GYRZXhAKHsyWSNLYa23PDCM9qX5BoXlRY9EWRe-8TwY,16905
|
|
1549
1549
|
cognee/tests/unit/migration/test_remember_router_cogx.py,sha256=Q72DFoaW9gjdGZI07EXSgU_pQlhnr0-5LlDx1TY_h-A,12156
|
|
1550
|
-
cognee/tests/unit/migration/test_snapshot.py,sha256=
|
|
1550
|
+
cognee/tests/unit/migration/test_snapshot.py,sha256=dfXsSqT6X9DIRFS9Qac5emimLyjOR08j5YvJyc9KPOk,7383
|
|
1551
1551
|
cognee/tests/unit/modules/agent_memory/test_agent_memory.py,sha256=AGgsjsS1BTvdJbgX6M4peNgkbMBbRSHGH2BRD2sLuXc,40791
|
|
1552
1552
|
cognee/tests/unit/modules/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
1553
1553
|
cognee/tests/unit/modules/agents/conftest.py,sha256=5QcS2mitl-wl1ytZVm1_T7SAofrPhgBbs4P3I95nLJE,1027
|
|
@@ -1713,9 +1713,9 @@ distributed/workers/data_point_saving_worker.py,sha256=kmaQy2A2J7W3k9Gd5lyoiT0XY
|
|
|
1713
1713
|
distributed/workers/graph_saving_worker.py,sha256=KVds58rBh-m3TnOyouE4cqSDnzIHNRqw-Td06abOGS0,4961
|
|
1714
1714
|
kuzu/__init__.py,sha256=NqYsYyycqMZOUte0_3r_eO5VJGe_BCNdOdLPpJTvgdk,224
|
|
1715
1715
|
kuzu/database.py,sha256=JfHFK1YAFKrnSId0p_5sqFwhX2IWfKo0MFNEPPNeQ9k,154
|
|
1716
|
-
cognee-1.2.2.
|
|
1717
|
-
cognee-1.2.2.
|
|
1718
|
-
cognee-1.2.2.
|
|
1719
|
-
cognee-1.2.2.
|
|
1720
|
-
cognee-1.2.2.
|
|
1721
|
-
cognee-1.2.2.
|
|
1716
|
+
cognee-1.2.2.dev2.dist-info/METADATA,sha256=iNvFhyIiE25-hlxU5l-vrhJn-uosLnwDT8JnFnxGGss,26799
|
|
1717
|
+
cognee-1.2.2.dev2.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
1718
|
+
cognee-1.2.2.dev2.dist-info/entry_points.txt,sha256=GCCTsNg8gzOJkolq7dR7OK1VlIAO202dGDnMI8nm8oQ,55
|
|
1719
|
+
cognee-1.2.2.dev2.dist-info/licenses/LICENSE,sha256=pHHjSQj1DD8SDppW88MMs04TPk7eAanL1c5xj8NY7NQ,11344
|
|
1720
|
+
cognee-1.2.2.dev2.dist-info/licenses/NOTICE.md,sha256=6L3saP3kSpcingOxDh-SGjMS8GY79Rlh2dBNLaO0o5c,339
|
|
1721
|
+
cognee-1.2.2.dev2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|