cognee 1.2.2.dev2__py3-none-any.whl → 1.2.2.dev3__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.
@@ -81,6 +81,7 @@ async def persist_agent_trace_feedbacks_in_knowledge_graph_pipeline(
81
81
  extraction_tasks=extraction_tasks,
82
82
  enrichment_tasks=enrichment_tasks,
83
83
  dataset=dataset_to_write[0].id,
84
+ user=user,
84
85
  data=[{}],
85
86
  run_in_background=run_in_background,
86
87
  )
@@ -52,13 +52,14 @@ async def persist_sessions_in_knowledge_graph_pipeline(
52
52
  extraction_tasks = [Task(extract_user_sessions, session_ids=session_ids)]
53
53
 
54
54
  enrichment_tasks = [
55
- Task(cognify_session, dataset_id=dataset_to_write[0].id),
55
+ Task(cognify_session, dataset_id=dataset_to_write[0].id, user=user),
56
56
  ]
57
57
 
58
58
  result = await memify(
59
59
  extraction_tasks=extraction_tasks,
60
60
  enrichment_tasks=enrichment_tasks,
61
61
  dataset=dataset_to_write[0].id,
62
+ user=user,
62
63
  data=[{}],
63
64
  run_in_background=run_in_background,
64
65
  )
@@ -77,16 +77,6 @@ async def import_memory_source(
77
77
  external_id, node ids from entity names) make re-running an interrupted
78
78
  or repeated import safe.
79
79
  """
80
- from cognee.modules.migrations.startup import run_migrations_and_block
81
-
82
- # Imports are writes, so they take the same migration gate as
83
- # remember()/cognify() (the remember() MemorySource dispatch happens
84
- # before its own gate). This also records the data-migration revision —
85
- # stamping a fresh store at head — BEFORE the imported rows arrive;
86
- # without it the populated store has no recorded revision and the first
87
- # migration-aware startup replays the entire data chain over it.
88
- await run_migrations_and_block(dataset_name, user)
89
-
90
80
  node_set = node_set or [f"import:{source.source_system}"]
91
81
 
92
82
  if source.mode == "preserve" and getattr(source, "replayable", False):
@@ -11,7 +11,6 @@ Two targets, selected by the source's import mode:
11
11
  the source (``valid_at``/``invalid_at``) is preserved as edge properties.
12
12
  """
13
13
 
14
- import dataclasses
15
14
  from dataclasses import dataclass, field
16
15
  from datetime import datetime
17
16
  from types import SimpleNamespace
@@ -428,12 +427,6 @@ def _provenance_ctx(ctx):
428
427
  add_data_points stamps ledger provenance from ``ctx.data_item.id``, which
429
428
  exists on Data ORM records but not on raw ingestion DataItems. Substitute
430
429
  the DataItem's deterministic ``data_id`` so provenance still lands.
431
-
432
- Only ``data_item`` is replaced; every other PipelineContext field is
433
- carried over via ``dataclasses.replace`` so consumers that read ctx
434
- attributes (e.g. ``add_data_points`` reads ``ctx.pipeline_run_id``) never
435
- see a field-stripped context — a hand-copied field list here silently
436
- drops any parameter later added to PipelineContext.
437
430
  """
438
431
  if ctx is None:
439
432
  return None
@@ -441,7 +434,12 @@ def _provenance_ctx(ctx):
441
434
  if data_item is None or hasattr(data_item, "id"):
442
435
  return ctx
443
436
  data_id = getattr(data_item, "data_id", None)
444
- return dataclasses.replace(ctx, data_item=SimpleNamespace(id=data_id) if data_id else None)
437
+ return SimpleNamespace(
438
+ user=getattr(ctx, "user", None),
439
+ dataset=getattr(ctx, "dataset", None),
440
+ data_item=SimpleNamespace(id=data_id) if data_id else None,
441
+ extras=getattr(ctx, "extras", None),
442
+ )
445
443
 
446
444
 
447
445
  # Edge batches can run larger than node batches: edges are small tuples and
@@ -19,7 +19,7 @@ so no property is lost.
19
19
  """
20
20
 
21
21
  import json
22
- from typing import Any, Dict, Iterable, List, Optional, Tuple, Type, Union
22
+ from typing import Any, Dict, List, Optional, Type, Union
23
23
 
24
24
  from pydantic import BaseModel, ConfigDict, Field, SerializeAsAny, create_model, field_validator
25
25
 
@@ -59,41 +59,13 @@ class _DynamicDataPoint(DataPoint):
59
59
  model_config = ConfigDict(arbitrary_types_allowed=True, extra="allow")
60
60
 
61
61
 
62
- _dynamic_models: Dict[Tuple[str, Tuple[str, ...], Tuple[str, ...]], Type[DataPoint]] = {}
62
+ _dynamic_models: Dict[str, Type[DataPoint]] = {}
63
63
 
64
64
 
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]
65
+ def _dynamic_model(type_name: str) -> Type[DataPoint]:
66
+ if type_name not in _dynamic_models:
67
+ _dynamic_models[type_name] = create_model(type_name, __base__=_DynamicDataPoint)
68
+ return _dynamic_models[type_name]
97
69
 
98
70
 
99
71
  def _clean_properties(properties: Dict[str, Any]) -> Dict[str, Any]:
@@ -129,20 +101,14 @@ def rehydrate_node(
129
101
  except Exception: # noqa: BLE001 — fall back to a dynamic model
130
102
  pass
131
103
 
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
-
138
104
  try:
139
105
  # Created lazily: only when the registered class is absent or fails.
140
- return _dynamic_model(type_name, props, index_fields)(**props)
106
+ return _dynamic_model(type_name)(**props)
141
107
  except Exception: # noqa: BLE001 — final fallback below
142
108
  pass
143
109
 
144
110
  base_safe = {key: value for key, value in props.items() if key in _BASE_FIELDS}
145
- return _dynamic_model(type_name, base_safe, index_fields)(**base_safe)
111
+ return _dynamic_model(type_name)(**base_safe)
146
112
 
147
113
 
148
114
  class GraphEdge(BaseModel):
@@ -5,11 +5,16 @@ import cognee
5
5
 
6
6
  from cognee.exceptions import CogneeValidationError, CogneeSystemError
7
7
  from cognee.shared.logging_utils import get_logger
8
+ from cognee.modules.users.models import User
8
9
 
9
10
  logger = get_logger("cognify_session")
10
11
 
11
12
 
12
- async def cognify_session(data: str, dataset_id: Optional[UUID | str] = None) -> None:
13
+ async def cognify_session(
14
+ data: str,
15
+ dataset_id: Optional[UUID | str] = None,
16
+ user: Optional[User] = None,
17
+ ) -> None:
13
18
  """
14
19
  Process and cognify session data into the knowledge graph.
15
20
 
@@ -32,9 +37,14 @@ async def cognify_session(data: str, dataset_id: Optional[UUID | str] = None) ->
32
37
 
33
38
  logger.info("Processing session data for cognification")
34
39
 
35
- await cognee.add(data, dataset_id=dataset_id, node_set=["user_sessions_from_cache"])
40
+ await cognee.add(
41
+ data,
42
+ dataset_id=dataset_id,
43
+ node_set=["user_sessions_from_cache"],
44
+ user=user,
45
+ )
36
46
  logger.debug("Session data added to cognee with node_set: user_sessions")
37
- await cognee.cognify(datasets=[dataset_id])
47
+ await cognee.cognify(datasets=[dataset_id], user=user)
38
48
  logger.info("Session data successfully cognified")
39
49
 
40
50
  except CogneeValidationError:
@@ -48,6 +48,7 @@ async def test_persist_agent_trace_feedbacks_pipeline_wires_memify_tasks():
48
48
 
49
49
  memify_kwargs = memify_mock.call_args.kwargs
50
50
  assert memify_kwargs["dataset"] == "dataset-1"
51
+ assert memify_kwargs["user"] == user
51
52
  assert memify_kwargs["data"] == [{}]
52
53
  assert len(memify_kwargs["extraction_tasks"]) == 1
53
54
  assert len(memify_kwargs["enrichment_tasks"]) == 1
@@ -22,7 +22,6 @@ from cognee.modules.migration.sources.base import MemorySource
22
22
  # fails here because package __init__ files rebind submodule names to
23
23
  # same-named functions (e.g. ``cognee.api.v1.remember.remember``).
24
24
  add_module = importlib.import_module("cognee.api.v1.add")
25
- migrations_startup_module = importlib.import_module("cognee.modules.migrations.startup")
26
25
  pipeline_module = importlib.import_module("cognee.modules.run_custom_pipeline")
27
26
  remember_module = importlib.import_module("cognee.api.v1.remember.remember")
28
27
  serve_state = importlib.import_module("cognee.api.v1.serve.state")
@@ -76,13 +75,7 @@ def install_sinks(monkeypatch):
76
75
  ``add_data_points`` sink. It returns a run-info dict so pipeline_run_id
77
76
  propagation can be asserted.
78
77
  """
79
- sinks = SimpleNamespace(
80
- pipeline_calls=[],
81
- add_calls=[],
82
- remember_calls=[],
83
- graph_flushes=[],
84
- migration_gate_calls=[],
85
- )
78
+ sinks = SimpleNamespace(pipeline_calls=[], add_calls=[], remember_calls=[], graph_flushes=[])
86
79
 
87
80
  async def fake_run_custom_pipeline(**kwargs):
88
81
  sinks.pipeline_calls.append(kwargs)
@@ -105,24 +98,10 @@ def install_sinks(monkeypatch):
105
98
  result.items_processed = len(data)
106
99
  return result
107
100
 
108
- async def fake_run_migrations_and_block(datasets, user):
109
- # writes_before_gate lets tests assert the gate fired before any
110
- # imported data was stored (stamping must precede the rows).
111
- sinks.migration_gate_calls.append(
112
- {
113
- "datasets": datasets,
114
- "user": user,
115
- "writes_before_gate": len(sinks.add_calls) + len(sinks.graph_flushes),
116
- }
117
- )
118
-
119
101
  monkeypatch.setattr(pipeline_module, "run_custom_pipeline", fake_run_custom_pipeline)
120
102
  monkeypatch.setattr(storage_module, "add_data_points", fake_add_data_points)
121
103
  monkeypatch.setattr(add_module, "add", fake_add)
122
104
  monkeypatch.setattr(remember_module, "remember", fake_remember)
123
- monkeypatch.setattr(
124
- migrations_startup_module, "run_migrations_and_block", fake_run_migrations_and_block
125
- )
126
105
  return sinks
127
106
 
128
107
 
@@ -341,27 +320,6 @@ class TestSkippedFacts:
341
320
  assert _flushed_edges(sinks) == []
342
321
 
343
322
 
344
- class TestMigrationGate:
345
- """import_memory_source must take the run_migrations_and_block gate.
346
-
347
- The gate stamps a fresh store's migration revision at head BEFORE the
348
- imported rows arrive; skipping it leaves the populated store with no
349
- recorded revision, so the first migration-aware startup replays the whole
350
- data-migration chain over the freshly imported data.
351
- """
352
-
353
- @pytest.mark.parametrize("mode", ["preserve", "re-derive", "hybrid"])
354
- def test_gate_fires_once_before_any_write(self, monkeypatch, mode):
355
- sinks = install_sinks(monkeypatch)
356
- source = FakeSource(_sample_records(), mode=mode)
357
-
358
- asyncio.run(import_memory_source(source, dataset_name="ds"))
359
-
360
- (gate_call,) = sinks.migration_gate_calls
361
- assert gate_call["datasets"] == "ds"
362
- assert gate_call["writes_before_gate"] == 0
363
-
364
-
365
323
  class TestRememberDispatch:
366
324
  """remember() routing for MemorySource inputs (guards, kwargs forwarding)."""
367
325
 
@@ -1,6 +1,5 @@
1
1
  """Unit tests for the Pydantic-native export (GraphSnapshot). Pure: no DB, no LLM."""
2
2
 
3
- import asyncio
4
3
  from uuid import uuid4
5
4
 
6
5
  from cognee.infrastructure.engine import DataPoint
@@ -69,58 +68,6 @@ class TestRehydrateNode:
69
68
  assert isinstance(node, Entity)
70
69
  assert node.metadata["index_fields"] == ["name"]
71
70
 
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
-
124
71
 
125
72
  class TestGraphSnapshot:
126
73
  def _snapshot(self, link_relations=False):
@@ -19,9 +19,12 @@ async def test_cognify_session_success():
19
19
  await cognify_session(session_data, dataset_id="123")
20
20
 
21
21
  mock_add.assert_called_once_with(
22
- session_data, dataset_id="123", node_set=["user_sessions_from_cache"]
22
+ session_data,
23
+ dataset_id="123",
24
+ node_set=["user_sessions_from_cache"],
25
+ user=None,
23
26
  )
24
- mock_cognify.assert_called_once()
27
+ mock_cognify.assert_called_once_with(datasets=["123"], user=None)
25
28
 
26
29
 
27
30
  @pytest.mark.asyncio
@@ -106,6 +109,32 @@ async def test_cognify_session_with_special_characters():
106
109
  await cognify_session(session_data, dataset_id="123")
107
110
 
108
111
  mock_add.assert_called_once_with(
109
- session_data, dataset_id="123", node_set=["user_sessions_from_cache"]
112
+ session_data,
113
+ dataset_id="123",
114
+ node_set=["user_sessions_from_cache"],
115
+ user=None,
110
116
  )
111
- mock_cognify.assert_called_once()
117
+ mock_cognify.assert_called_once_with(datasets=["123"], user=None)
118
+
119
+
120
+ @pytest.mark.asyncio
121
+ async def test_cognify_session_passes_user_to_add_and_cognify():
122
+ """Test user is forwarded to cognee.add/cognee.cognify."""
123
+ session_data = (
124
+ "Session ID: test_session\n\nQuestion: What is AI?\n\nAnswer: AI is artificial intelligence"
125
+ )
126
+ user = object()
127
+
128
+ with (
129
+ patch("cognee.add", new_callable=AsyncMock) as mock_add,
130
+ patch("cognee.cognify", new_callable=AsyncMock) as mock_cognify,
131
+ ):
132
+ await cognify_session(session_data, dataset_id="123", user=user)
133
+
134
+ mock_add.assert_called_once_with(
135
+ session_data,
136
+ dataset_id="123",
137
+ node_set=["user_sessions_from_cache"],
138
+ user=user,
139
+ )
140
+ mock_cognify.assert_called_once_with(datasets=["123"], user=user)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cognee
3
- Version: 1.2.2.dev2
3
+ Version: 1.2.2.dev3
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
@@ -602,8 +602,8 @@ cognee/memify_pipelines/consolidate_entity_descriptions.py,sha256=E3lNaVSQBZG9gm
602
602
  cognee/memify_pipelines/create_triplet_embeddings.py,sha256=oniU5JUClgpmxgR860nOX0Wu7TjlXNXE9zH2OnSlD_E,1730
603
603
  cognee/memify_pipelines/global_context_index.py,sha256=-fi--mYVazD_Kzjbek_IE-q7GRquP77pmnaqbRpMMEk,2700
604
604
  cognee/memify_pipelines/memify_default_tasks.py,sha256=SmXqkQCufI8zYdx239-ncjNQqZE-E10AIGbajPMPln8,908
605
- cognee/memify_pipelines/persist_agent_trace_feedbacks_in_knowledge_graph.py,sha256=gycJh3PvuHZjrscbg7Dp2esN4U34FT-uM27shb_aZLc,3601
606
- cognee/memify_pipelines/persist_sessions_in_knowledge_graph.py,sha256=jRV1mwTlNZi08vGkYW6IRMNkwstDFxUC4dqjAB7bHFg,2469
605
+ cognee/memify_pipelines/persist_agent_trace_feedbacks_in_knowledge_graph.py,sha256=5i35i9KTpOCMYuxncqXDId6rDcNnmv1BTSN7hipTHNQ,3620
606
+ cognee/memify_pipelines/persist_sessions_in_knowledge_graph.py,sha256=gtpqVR6qb4FKl6gG4C0Q3av3h3e1GjYlEjkdnfX_q2c,2503
607
607
  cognee/memory/__init__.py,sha256=LOsCYZ0aMKAb0wdpe1aUBdu4RoTjKy_4_XLNCULW_Iw,298
608
608
  cognee/memory/entries.py,sha256=obAHjOybB3Hqr-y37_9ZKa9KMycBLqjieIfsdzWKIfA,5872
609
609
  cognee/migration/__init__.py,sha256=KIgvAJwxU9AnthJmwIxw_qvz-KDHteHpWf7ZXyv9i_4,1946
@@ -785,9 +785,9 @@ cognee/modules/migration/archive.py,sha256=ptBMXeJiWQwas2n0Yf-tD2cdqVfU_WaBxw9sa
785
785
  cognee/modules/migration/cogx.py,sha256=Z_phNpzX3p9upeKxevtRE_RjLht1Ly9jYw6koYkfPXU,11079
786
786
  cognee/modules/migration/export.py,sha256=v-GPc1oT41cmXpuU_vqOCX0Us_9sWQZFoVwMTNyLOCQ,8210
787
787
  cognee/modules/migration/formats.py,sha256=-o2hqVwSHXz2O7d_bsxdACZjTd-h8goKoLQ_UqFBcSw,5785
788
- cognee/modules/migration/import_source.py,sha256=ch7hSReqAKdt2jzbuY-FyL-hXgDJhrzQWyzE1vgNFx4,11475
789
- cognee/modules/migration/loader.py,sha256=1fWim7Zblt-DggcIJ56EjZj0IFP3Up0AxmKztCHNoSU,23935
790
- cognee/modules/migration/snapshot.py,sha256=z81JRsbLr7Z4jawueOn48OfRkL9fHGe8lRVHFJfm7R8,10773
788
+ cognee/modules/migration/import_source.py,sha256=Ndy_wJsyP4ZMHtdAKkjD1sIP4GMlkMbz4dlsld1MOHU,10907
789
+ cognee/modules/migration/loader.py,sha256=IXyH0svZm8VoURymfZCQ-3WZdqPGNPzBQ-y6UjNenpk,23699
790
+ cognee/modules/migration/snapshot.py,sha256=wYOSuX_N9R3AF5NaHUyX4YbgXil2y7bhJWS3A0srAU4,9084
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
@@ -1179,7 +1179,7 @@ cognee/tasks/memify/__init__.py,sha256=Bpkm0TdrCnhY76b1orosMVfA3H7a5Dn-mIxIVodNb
1179
1179
  cognee/tasks/memify/apply_feedback_weights.py,sha256=vlAQUb0Xb4egS3Bf-EyWnxwfhsZP-WPAO14nBwNT_Gw,7717
1180
1180
  cognee/tasks/memify/apply_frequency_weights.py,sha256=CZb5ITSIBkgRoQuaYq8yletm6vEsDLKiUGZ9Ng3y8O8,6570
1181
1181
  cognee/tasks/memify/cognify_agent_trace_feedback.py,sha256=fS7upXpo_-N_l5x0nl9mKiDElMww5yvH1iO5fPh75OA,2005
1182
- cognee/tasks/memify/cognify_session.py,sha256=m-t3U-IrWJMPM-csTay854qf3rOzPxfdVcoZprJb8X0,1687
1182
+ cognee/tasks/memify/cognify_session.py,sha256=z4zj88E0mEV7LCjbFDXh3AdLnaEazmgaWSXHoHVl0RM,1857
1183
1183
  cognee/tasks/memify/extract_agent_trace_feedbacks.py,sha256=V7_ZscgwlCZh07cthAXdOy1EEG4gsk0xDAOIpEOEG14,5226
1184
1184
  cognee/tasks/memify/extract_feedback_qas.py,sha256=VANI24LkOfFxvES8B9bHXuWiUD8OaAW00JHFBIdI9Vc,3218
1185
1185
  cognee/tasks/memify/extract_subgraph.py,sha256=Al1A5d3bhmd-rd9T9is_ppTfK9JsTb3Es9mBWTiJjB8,220
@@ -1261,7 +1261,6 @@ 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=K0kiGxZ1COFsytTS5zfhymMwClWCUTOQK7VTsWOZxgk,7522
1265
1264
  cognee/tests/test_concurrent_cognify_e2e.py,sha256=tdmp0QA6uatFd5YL0bBahk8GWq-GG8LKrhiDfrP4jWQ,5682
1266
1265
  cognee/tests/test_concurrent_subprocess_access.py,sha256=xh72FclRY1LnwUbpmIQTzvK7Yayoov6oewWmHivPTxE,3041
1267
1266
  cognee/tests/test_conversation_history.py,sha256=80be_RWiT4acwZ6agSQY2ch69hmJ8MN-RSeL2qIx0pM,18186
@@ -1540,14 +1539,13 @@ cognee/tests/unit/interfaces/graph/get_graph_from_model_unit_test.py,sha256=IZQ7
1540
1539
  cognee/tests/unit/interfaces/graph/test_expand_with_nodes_and_edges.py,sha256=h3ZRdvtfdJA77aC_Cz7UCXMWXuGXxeLtVwk4CnVD1uc,6429
1541
1540
  cognee/tests/unit/interfaces/graph/test_weighted_edges.py,sha256=Xi-iVyhVJ4YzAgG-eGvx9kZLtwCMhq2Ca43xNkaO_K4,13315
1542
1541
  cognee/tests/unit/memify_pipelines/test_apply_feedback_weights_pipeline.py,sha256=B96e_wv7TBbLANknkgR7Wu26-TwxQrYDgWg6bFWQNeI,3248
1543
- cognee/tests/unit/memify_pipelines/test_persist_agent_trace_feedbacks_pipeline.py,sha256=5qfut4sRZvlqghjK_o-d1uceSqitrMRFDHknsSZIsiw,2589
1542
+ cognee/tests/unit/memify_pipelines/test_persist_agent_trace_feedbacks_pipeline.py,sha256=T01IppNOKbRL9apCxb5eQEjdzt3Zy0Py8ppOGqF9iNc,2630
1544
1543
  cognee/tests/unit/migration/test_export_roundtrip.py,sha256=CSB9G_KtHMEbDWa8UR6oU0hi3jy5G2Qv3WGc1cs5Vtw,18167
1545
- cognee/tests/unit/migration/test_import_source.py,sha256=gkcX2ZvMA8M5crvVQrtha7PT4i5Cs1CsJkb22V6mRow,17514
1544
+ cognee/tests/unit/migration/test_import_source.py,sha256=ZyuY4AjHugKEa6T4vWu4Vh_RtwcHH7JwnFxHTrtUPxw,15932
1546
1545
  cognee/tests/unit/migration/test_migration.py,sha256=5tqGzk01dsfhGxDDRzyumQ2fotbiKW7XWfjonrMUy3M,19833
1547
- cognee/tests/unit/migration/test_provenance_ctx.py,sha256=GHiAA8NLs2JWaeztyFJuaeeuo9NScgzUNtQGX5wguuA,3057
1548
1546
  cognee/tests/unit/migration/test_push.py,sha256=GYRZXhAKHsyWSNLYa23PDCM9qX5BoXlRY9EWRe-8TwY,16905
1549
1547
  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=dfXsSqT6X9DIRFS9Qac5emimLyjOR08j5YvJyc9KPOk,7383
1548
+ cognee/tests/unit/migration/test_snapshot.py,sha256=tdzSUAHq187ZaN8gMBlF2WBe9ABv6kBw6VYu6Os4sHU,4898
1551
1549
  cognee/tests/unit/modules/agent_memory/test_agent_memory.py,sha256=AGgsjsS1BTvdJbgX6M4peNgkbMBbRSHGH2BRD2sLuXc,40791
1552
1550
  cognee/tests/unit/modules/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1553
1551
  cognee/tests/unit/modules/agents/conftest.py,sha256=5QcS2mitl-wl1ytZVm1_T7SAofrPhgBbs4P3I95nLJE,1027
@@ -1570,7 +1568,7 @@ cognee/tests/unit/modules/graph/test_resolve_edges_to_text.py,sha256=Pz4CTFHEE8R
1570
1568
  cognee/tests/unit/modules/graph/test_retrieve_existing_edges.py,sha256=VIolEF-RdQ_9kqOed8XQ5Irl6NH2MreIXPxA1uG7SeI,3004
1571
1569
  cognee/tests/unit/modules/memify_tasks/test_apply_feedback_weights.py,sha256=tISeQ7T_6sokaVpvWb6aEec3Hgg6bHPsMYSArqlYpow,9874
1572
1570
  cognee/tests/unit/modules/memify_tasks/test_cognify_agent_trace_feedback.py,sha256=pF1ELFjcHgCIxHnV4hVdUEy9lvWTICap6v93zuMV6DI,2753
1573
- cognee/tests/unit/modules/memify_tasks/test_cognify_session.py,sha256=PUduJ3D0x0hqW7eOuFwuz0ifMpR6Fa67nxyQfa2MwjA,3837
1571
+ cognee/tests/unit/modules/memify_tasks/test_cognify_session.py,sha256=rp_uRURlfujpY6e2Mo61X2kj-VFAfqVUDrHdAAXNLzI,4783
1574
1572
  cognee/tests/unit/modules/memify_tasks/test_extract_agent_trace_feedbacks.py,sha256=XOvmx3dSCgSfDldFOjGlds9mPPk074QcYTXmj5m_HxU,13729
1575
1573
  cognee/tests/unit/modules/memify_tasks/test_extract_feedback_qas.py,sha256=CZdduhZCXczui7XNk2Y0R2a9gDSX-SvfnKOdMTzkUpo,6320
1576
1574
  cognee/tests/unit/modules/memify_tasks/test_extract_user_sessions.py,sha256=6WLyquWcVWeRVCoUk2i6uwpFk-xl-0s_WMruQYijzas,7542
@@ -1713,9 +1711,9 @@ distributed/workers/data_point_saving_worker.py,sha256=kmaQy2A2J7W3k9Gd5lyoiT0XY
1713
1711
  distributed/workers/graph_saving_worker.py,sha256=KVds58rBh-m3TnOyouE4cqSDnzIHNRqw-Td06abOGS0,4961
1714
1712
  kuzu/__init__.py,sha256=NqYsYyycqMZOUte0_3r_eO5VJGe_BCNdOdLPpJTvgdk,224
1715
1713
  kuzu/database.py,sha256=JfHFK1YAFKrnSId0p_5sqFwhX2IWfKo0MFNEPPNeQ9k,154
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,,
1714
+ cognee-1.2.2.dev3.dist-info/METADATA,sha256=XMMcyeQT0DAv0pnHBXUlg-TKN6SGk4-fK6j31Cf2T9E,26799
1715
+ cognee-1.2.2.dev3.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
1716
+ cognee-1.2.2.dev3.dist-info/entry_points.txt,sha256=GCCTsNg8gzOJkolq7dR7OK1VlIAO202dGDnMI8nm8oQ,55
1717
+ cognee-1.2.2.dev3.dist-info/licenses/LICENSE,sha256=pHHjSQj1DD8SDppW88MMs04TPk7eAanL1c5xj8NY7NQ,11344
1718
+ cognee-1.2.2.dev3.dist-info/licenses/NOTICE.md,sha256=6L3saP3kSpcingOxDh-SGjMS8GY79Rlh2dBNLaO0o5c,339
1719
+ cognee-1.2.2.dev3.dist-info/RECORD,,
@@ -1,174 +0,0 @@
1
- """E2E: export a cognified dataset as a COGX archive, restore it into a fresh
2
- store, and search the restored knowledge graph.
3
-
4
- This is the Cognee-to-Cognee instance-migration path (the local half of
5
- push/pull): ``cognee.export(format="cogx")`` on the source machine,
6
- ``cognee.remember(COGXArchiveSource(...))`` on the target. The test covers
7
- the full system contract:
8
-
9
- - the preserve-mode import survives the pipeline context adaptation
10
- (regression: ``ctx.pipeline_run_id`` was dropped, crashing every import),
11
- - the import stamps the fresh store's migration revision at head BEFORE the
12
- rows arrive (regression: an unstamped store replayed the entire
13
- data-migration chain on the first server start after a restore),
14
- - the restored graph is populated and answerable via search.
15
-
16
- The test runs in multi-user mode (backend access control enabled — pinned
17
- below): each dataset_database row must be head-stamped when the import
18
- creates it.
19
- """
20
-
21
- import asyncio
22
- import os
23
- import pathlib
24
- import tempfile
25
-
26
- import cognee
27
- from cognee.modules.migration.sources.cogx_archive import COGXArchiveSource
28
- from cognee.modules.search.types import SearchType
29
- from cognee.shared.logging_utils import get_logger
30
-
31
- logger = get_logger()
32
-
33
- TEXT = """The Aurora Skyline Hotel is a boutique hotel in Berlin founded by Clara Novak in 2015.
34
- The hotel is known for its rooftop observatory and partners with the Berlin Astronomy Club.
35
- Clara Novak previously managed the Lindenhof Resort in Munich before moving to Berlin.
36
- """
37
-
38
- QUERY = "Who founded the Aurora Skyline Hotel and what is the hotel known for?"
39
-
40
-
41
- async def _wipe_store():
42
- """Reset to an empty store, as on a machine that never ran Cognee."""
43
- await cognee.prune.prune_data()
44
- await cognee.prune.prune_system(metadata=True)
45
-
46
-
47
- async def _assert_revision_at_head():
48
- """The restored store's data-migration bookkeeping must be stamped at head.
49
-
50
- Without the stamp the store is indistinguishable from a pre-migration one,
51
- and the first migration-aware startup replays the whole chain over the
52
- imported data. In multi-user mode the stamp lives on the per-dataset
53
- ``dataset_database`` rows, head-stamped when the import creates them.
54
- """
55
- from cognee.modules.data.methods.get_dataset_databases import get_dataset_databases
56
- from cognee.modules.migrations.migration import head_revision
57
- from cognee.modules.migrations.registry import MIGRATIONS
58
-
59
- head = head_revision(MIGRATIONS)
60
-
61
- rows = await get_dataset_databases()
62
- assert rows, "Import created no dataset_database bookkeeping row for the fresh store."
63
- for row in rows:
64
- assert row.migration_revision == head, (
65
- f"Imported dataset {row.dataset_id} is stamped at {row.migration_revision!r}, "
66
- f"expected head {head!r} — the first startup would replay the migration chain."
67
- )
68
-
69
-
70
- async def main():
71
- # Disable session-turn gating so searches return direct retrieval results
72
- # (same rationale as test_library.py).
73
- os.environ["AUTO_FEEDBACK"] = "False"
74
-
75
- # This test runs in multi-user mode: the stamp assertion reads the
76
- # per-dataset bookkeeping rows that mode maintains. The flag is read
77
- # dynamically, so setting it here wins over any ambient .env.
78
- os.environ["ENABLE_BACKEND_ACCESS_CONTROL"] = "true"
79
-
80
- data_directory_path = str(
81
- pathlib.Path(
82
- os.path.join(pathlib.Path(__file__).parent, ".data_storage/test_cogx_roundtrip")
83
- ).resolve()
84
- )
85
- cognee.config.data_root_directory(data_directory_path)
86
- cognee_directory_path = str(
87
- pathlib.Path(
88
- os.path.join(pathlib.Path(__file__).parent, ".cognee_system/test_cogx_roundtrip")
89
- ).resolve()
90
- )
91
- cognee.config.system_root_directory(cognee_directory_path)
92
-
93
- dataset_name = "hotel_knowledge"
94
-
95
- with tempfile.TemporaryDirectory() as temporary_directory:
96
- # The archive must live OUTSIDE the data/system roots so wiping the
97
- # store between phases cannot delete it (it is the "file shipped to
98
- # the other machine").
99
- archive_dir = pathlib.Path(temporary_directory) / "hotel_cogx"
100
-
101
- # --- Phase 1: source instance — build a graph and export it. -------
102
- await _wipe_store()
103
-
104
- await cognee.add([TEXT], dataset_name)
105
- await cognee.cognify([dataset_name])
106
-
107
- source_results = await cognee.search(
108
- query_type=SearchType.GRAPH_COMPLETION, query_text=QUERY, datasets=[dataset_name]
109
- )
110
- assert len(source_results) != 0, "Search on the source instance returned nothing."
111
- logger.info("Source search results: %s", source_results)
112
-
113
- export_result = await cognee.export(dataset_name, format="cogx", destination=archive_dir)
114
- assert export_result.num_nodes > 0, "Export produced an empty archive (0 nodes)."
115
- assert export_result.num_edges > 0, "Export produced an archive with 0 edges."
116
- logger.info(
117
- "Exported %d nodes, %d edges to %s",
118
- export_result.num_nodes,
119
- export_result.num_edges,
120
- export_result.destination,
121
- )
122
-
123
- # --- Phase 2: target instance — restore into a fresh store. --------
124
- await _wipe_store()
125
-
126
- # run_migrations is once-per-process; on a real restore the import
127
- # runs in a brand-new process on the target machine. Reset the guard
128
- # so the import's migration gate treats the wiped store as exactly
129
- # that: a fresh instance.
130
- import cognee.modules.migrations.startup as migrations_startup
131
-
132
- migrations_startup._startup_migrations_done = False
133
-
134
- import_result = await cognee.remember(
135
- COGXArchiveSource(archive_dir), dataset_name=dataset_name
136
- )
137
- assert import_result.status == "completed", (
138
- f"Import did not complete: {import_result.status!r}"
139
- )
140
- assert import_result.items_processed > 0, "Import processed nothing."
141
-
142
- (import_summary,) = [
143
- item for item in import_result.items if item.get("kind") == "migration_import"
144
- ]
145
- assert import_summary["graph_nodes"] > 0, "Import restored no graph nodes."
146
- assert import_summary["graph_edges"] > 0, "Import restored no graph edges."
147
- logger.info("Import summary: %s", import_summary)
148
-
149
- await _assert_revision_at_head()
150
-
151
- # --- Phase 3: the restored graph answers searches. ------------------
152
- restored_results = await cognee.search(
153
- query_type=SearchType.GRAPH_COMPLETION, query_text=QUERY, datasets=[dataset_name]
154
- )
155
- assert len(restored_results) != 0, "Search on the restored instance returned nothing."
156
- logger.info("Restored search results: %s", restored_results)
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
-
170
- print("COGX roundtrip e2e passed: export -> fresh store -> import -> search.")
171
-
172
-
173
- if __name__ == "__main__":
174
- asyncio.run(main())
@@ -1,75 +0,0 @@
1
- """Drift guard between PipelineContext and the COGX import ctx adaptation.
2
-
3
- The COGX import path adapts the pipeline context before handing it to
4
- ``add_data_points`` (``_provenance_ctx`` in ``cognee/modules/migration/loader.py``).
5
- A hand-copied field list there silently drops any parameter later added to
6
- ``PipelineContext`` — exactly how ``ctx.pipeline_run_id`` was lost, crashing
7
- every preserve-mode import with AttributeError because ``add_data_points``
8
- reads it unconditionally.
9
-
10
- These tests introspect ``dataclasses.fields(PipelineContext)``, so they fail
11
- for ANY field that does not survive the adaptation — adding a new parameter
12
- to PipelineContext without carrying it through the cogx import path breaks
13
- them by construction. Pure: no databases, no LLM calls, no network.
14
- """
15
-
16
- import dataclasses
17
- from types import SimpleNamespace
18
- from uuid import uuid4
19
-
20
- from cognee.modules.migration.loader import _provenance_ctx
21
- from cognee.modules.pipelines.models.PipelineContext import PipelineContext
22
-
23
-
24
- def _full_context() -> PipelineContext:
25
- """A PipelineContext with every field set to a distinct sentinel.
26
-
27
- ``data_item`` is DataItem-shaped (has ``data_id``, no ``id``) so
28
- ``_provenance_ctx`` takes its substitution path instead of returning the
29
- context unchanged. Sentinels are per-field strings, so a swapped or
30
- defaulted field cannot pass equality by accident.
31
- """
32
- ctx = PipelineContext()
33
- for context_field in dataclasses.fields(PipelineContext):
34
- setattr(ctx, context_field.name, f"sentinel:{context_field.name}")
35
- ctx.data_item = SimpleNamespace(data_id=uuid4())
36
- return ctx
37
-
38
-
39
- def test_every_pipeline_context_field_survives_adaptation():
40
- ctx = _full_context()
41
-
42
- adapted = _provenance_ctx(ctx)
43
-
44
- assert adapted is not ctx, "expected the data_item substitution path, not the pass-through"
45
- for context_field in dataclasses.fields(PipelineContext):
46
- assert hasattr(adapted, context_field.name), (
47
- f"PipelineContext.{context_field.name} does not survive the COGX import "
48
- "ctx adaptation (_provenance_ctx in cognee/modules/migration/loader.py). "
49
- "If you added a parameter to PipelineContext, the cogx import path must "
50
- "carry it: task consumers such as add_data_points read ctx attributes "
51
- "unconditionally (this is how pipeline_run_id regressed)."
52
- )
53
- if context_field.name == "data_item":
54
- continue
55
- assert getattr(adapted, context_field.name) == getattr(ctx, context_field.name), (
56
- f"PipelineContext.{context_field.name} changed value across _provenance_ctx"
57
- )
58
-
59
-
60
- def test_data_item_id_substituted_from_data_id():
61
- ctx = _full_context()
62
-
63
- adapted = _provenance_ctx(ctx)
64
-
65
- assert adapted.data_item.id == ctx.data_item.data_id
66
-
67
-
68
- def test_passthrough_when_data_item_already_has_id():
69
- ctx = PipelineContext(data_item=SimpleNamespace(id=uuid4()))
70
-
71
- assert _provenance_ctx(ctx) is ctx
72
-
73
-
74
- def test_none_ctx_stays_none():
75
- assert _provenance_ctx(None) is None