cognee 0.5.1.dev0__py3-none-any.whl → 0.5.2__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.
Files changed (241) hide show
  1. cognee/__init__.py +2 -0
  2. cognee/alembic/README +1 -0
  3. cognee/alembic/env.py +107 -0
  4. cognee/alembic/script.py.mako +26 -0
  5. cognee/alembic/versions/1a58b986e6e1_enable_delete_for_old_tutorial_notebooks.py +52 -0
  6. cognee/alembic/versions/1d0bb7fede17_add_pipeline_run_status.py +33 -0
  7. cognee/alembic/versions/1daae0df1866_incremental_loading.py +48 -0
  8. cognee/alembic/versions/211ab850ef3d_add_sync_operations_table.py +118 -0
  9. cognee/alembic/versions/45957f0a9849_add_notebook_table.py +46 -0
  10. cognee/alembic/versions/46a6ce2bd2b2_expand_dataset_database_with_json_.py +333 -0
  11. cognee/alembic/versions/482cd6517ce4_add_default_user.py +30 -0
  12. cognee/alembic/versions/76625596c5c3_expand_dataset_database_for_multi_user.py +98 -0
  13. cognee/alembic/versions/8057ae7329c2_initial_migration.py +25 -0
  14. cognee/alembic/versions/9e7a3cb85175_loader_separation.py +104 -0
  15. cognee/alembic/versions/a1b2c3d4e5f6_add_label_column_to_data.py +38 -0
  16. cognee/alembic/versions/ab7e313804ae_permission_system_rework.py +236 -0
  17. cognee/alembic/versions/b9274c27a25a_kuzu_11_migration.py +75 -0
  18. cognee/alembic/versions/c946955da633_multi_tenant_support.py +137 -0
  19. cognee/alembic/versions/e1ec1dcb50b6_add_last_accessed_to_data.py +51 -0
  20. cognee/alembic/versions/e4ebee1091e7_expand_data_model_info.py +140 -0
  21. cognee/alembic.ini +117 -0
  22. cognee/api/v1/add/routers/get_add_router.py +2 -0
  23. cognee/api/v1/cognify/cognify.py +11 -6
  24. cognee/api/v1/cognify/routers/get_cognify_router.py +8 -0
  25. cognee/api/v1/config/config.py +60 -0
  26. cognee/api/v1/datasets/routers/get_datasets_router.py +45 -3
  27. cognee/api/v1/memify/routers/get_memify_router.py +2 -0
  28. cognee/api/v1/search/routers/get_search_router.py +21 -6
  29. cognee/api/v1/search/search.py +25 -5
  30. cognee/api/v1/sync/routers/get_sync_router.py +3 -3
  31. cognee/cli/commands/add_command.py +1 -1
  32. cognee/cli/commands/cognify_command.py +6 -0
  33. cognee/cli/commands/config_command.py +1 -1
  34. cognee/context_global_variables.py +5 -1
  35. cognee/eval_framework/answer_generation/answer_generation_executor.py +7 -8
  36. cognee/infrastructure/databases/cache/cache_db_interface.py +38 -1
  37. cognee/infrastructure/databases/cache/config.py +6 -0
  38. cognee/infrastructure/databases/cache/fscache/FsCacheAdapter.py +21 -0
  39. cognee/infrastructure/databases/cache/get_cache_engine.py +9 -3
  40. cognee/infrastructure/databases/cache/redis/RedisAdapter.py +60 -1
  41. cognee/infrastructure/databases/dataset_database_handler/supported_dataset_database_handlers.py +7 -0
  42. cognee/infrastructure/databases/graph/get_graph_engine.py +29 -1
  43. cognee/infrastructure/databases/graph/neo4j_driver/Neo4jAuraDevDatasetDatabaseHandler.py +62 -27
  44. cognee/infrastructure/databases/hybrid/neptune_analytics/NeptuneAnalyticsAdapter.py +17 -4
  45. cognee/infrastructure/databases/relational/sqlalchemy/SqlAlchemyAdapter.py +2 -1
  46. cognee/infrastructure/databases/vector/chromadb/ChromaDBAdapter.py +2 -0
  47. cognee/infrastructure/databases/vector/config.py +6 -0
  48. cognee/infrastructure/databases/vector/create_vector_engine.py +69 -22
  49. cognee/infrastructure/databases/vector/embeddings/LiteLLMEmbeddingEngine.py +64 -9
  50. cognee/infrastructure/databases/vector/embeddings/OllamaEmbeddingEngine.py +13 -2
  51. cognee/infrastructure/databases/vector/lancedb/LanceDBAdapter.py +16 -3
  52. cognee/infrastructure/databases/vector/models/ScoredResult.py +3 -3
  53. cognee/infrastructure/databases/vector/pgvector/PGVectorAdapter.py +16 -3
  54. cognee/infrastructure/databases/vector/pgvector/PGVectorDatasetDatabaseHandler.py +86 -0
  55. cognee/infrastructure/databases/vector/pgvector/create_db_and_tables.py +81 -2
  56. cognee/infrastructure/databases/vector/vector_db_interface.py +8 -0
  57. cognee/infrastructure/files/utils/get_data_file_path.py +33 -27
  58. cognee/infrastructure/llm/prompts/extract_query_time.txt +1 -1
  59. cognee/infrastructure/llm/prompts/generate_event_entity_prompt.txt +1 -1
  60. cognee/infrastructure/llm/prompts/generate_event_graph_prompt.txt +1 -1
  61. cognee/infrastructure/llm/prompts/generate_graph_prompt.txt +2 -2
  62. cognee/infrastructure/llm/prompts/generate_graph_prompt_guided.txt +1 -1
  63. cognee/infrastructure/llm/prompts/generate_graph_prompt_oneshot.txt +2 -2
  64. cognee/infrastructure/llm/prompts/generate_graph_prompt_simple.txt +1 -1
  65. cognee/infrastructure/llm/prompts/generate_graph_prompt_strict.txt +1 -1
  66. cognee/infrastructure/llm/prompts/search_type_selector_prompt.txt +6 -6
  67. cognee/infrastructure/llm/prompts/test.txt +1 -1
  68. cognee/infrastructure/llm/prompts/translate_content.txt +19 -0
  69. cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/get_llm_client.py +24 -0
  70. cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/llama_cpp/adapter.py +191 -0
  71. cognee/modules/chunking/models/DocumentChunk.py +0 -1
  72. cognee/modules/cognify/config.py +2 -0
  73. cognee/modules/data/models/Data.py +1 -0
  74. cognee/modules/engine/models/Entity.py +0 -1
  75. cognee/modules/engine/operations/setup.py +6 -0
  76. cognee/modules/graph/cognee_graph/CogneeGraph.py +150 -37
  77. cognee/modules/graph/cognee_graph/CogneeGraphElements.py +48 -2
  78. cognee/modules/graph/utils/__init__.py +1 -0
  79. cognee/modules/graph/utils/get_entity_nodes_from_triplets.py +12 -0
  80. cognee/modules/notebooks/methods/__init__.py +1 -0
  81. cognee/modules/notebooks/methods/create_notebook.py +0 -34
  82. cognee/modules/notebooks/methods/create_tutorial_notebooks.py +191 -0
  83. cognee/modules/notebooks/methods/get_notebooks.py +12 -8
  84. cognee/modules/notebooks/tutorials/cognee-basics/cell-1.md +3 -0
  85. cognee/modules/notebooks/tutorials/cognee-basics/cell-2.md +10 -0
  86. cognee/modules/notebooks/tutorials/cognee-basics/cell-3.md +7 -0
  87. cognee/modules/notebooks/tutorials/cognee-basics/cell-4.py +28 -0
  88. cognee/modules/notebooks/tutorials/cognee-basics/cell-5.py +3 -0
  89. cognee/modules/notebooks/tutorials/cognee-basics/cell-6.py +9 -0
  90. cognee/modules/notebooks/tutorials/cognee-basics/cell-7.py +17 -0
  91. cognee/modules/notebooks/tutorials/cognee-basics/config.json +4 -0
  92. cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-1.md +3 -0
  93. cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-10.md +3 -0
  94. cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-11.md +3 -0
  95. cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-12.py +3 -0
  96. cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-13.md +7 -0
  97. cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-14.py +6 -0
  98. cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-15.md +3 -0
  99. cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-16.py +7 -0
  100. cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-2.md +9 -0
  101. cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-3.md +7 -0
  102. cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-4.md +9 -0
  103. cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-5.md +5 -0
  104. cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-6.py +13 -0
  105. cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-7.md +3 -0
  106. cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-8.md +3 -0
  107. cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-9.py +31 -0
  108. cognee/modules/notebooks/tutorials/python-development-with-cognee/config.json +4 -0
  109. cognee/modules/notebooks/tutorials/python-development-with-cognee/data/copilot_conversations.json +107 -0
  110. cognee/modules/notebooks/tutorials/python-development-with-cognee/data/guido_contributions.json +976 -0
  111. cognee/modules/notebooks/tutorials/python-development-with-cognee/data/my_developer_rules.md +79 -0
  112. cognee/modules/notebooks/tutorials/python-development-with-cognee/data/pep_style_guide.md +74 -0
  113. cognee/modules/notebooks/tutorials/python-development-with-cognee/data/zen_principles.md +74 -0
  114. cognee/modules/retrieval/EntityCompletionRetriever.py +51 -38
  115. cognee/modules/retrieval/__init__.py +0 -1
  116. cognee/modules/retrieval/base_retriever.py +66 -10
  117. cognee/modules/retrieval/chunks_retriever.py +57 -49
  118. cognee/modules/retrieval/coding_rules_retriever.py +12 -5
  119. cognee/modules/retrieval/completion_retriever.py +29 -28
  120. cognee/modules/retrieval/cypher_search_retriever.py +25 -20
  121. cognee/modules/retrieval/graph_completion_context_extension_retriever.py +42 -46
  122. cognee/modules/retrieval/graph_completion_cot_retriever.py +68 -51
  123. cognee/modules/retrieval/graph_completion_retriever.py +78 -63
  124. cognee/modules/retrieval/graph_summary_completion_retriever.py +2 -0
  125. cognee/modules/retrieval/lexical_retriever.py +34 -12
  126. cognee/modules/retrieval/natural_language_retriever.py +18 -15
  127. cognee/modules/retrieval/summaries_retriever.py +51 -34
  128. cognee/modules/retrieval/temporal_retriever.py +59 -49
  129. cognee/modules/retrieval/triplet_retriever.py +31 -32
  130. cognee/modules/retrieval/utils/access_tracking.py +88 -0
  131. cognee/modules/retrieval/utils/brute_force_triplet_search.py +99 -85
  132. cognee/modules/retrieval/utils/node_edge_vector_search.py +174 -0
  133. cognee/modules/search/methods/__init__.py +1 -0
  134. cognee/modules/search/methods/get_retriever_output.py +53 -0
  135. cognee/modules/search/methods/get_search_type_retriever_instance.py +252 -0
  136. cognee/modules/search/methods/search.py +90 -215
  137. cognee/modules/search/models/SearchResultPayload.py +67 -0
  138. cognee/modules/search/types/SearchResult.py +1 -8
  139. cognee/modules/search/types/SearchType.py +1 -2
  140. cognee/modules/search/types/__init__.py +1 -1
  141. cognee/modules/search/utils/__init__.py +1 -2
  142. cognee/modules/search/utils/transform_insights_to_graph.py +2 -2
  143. cognee/modules/search/utils/{transform_context_to_graph.py → transform_triplets_to_graph.py} +2 -2
  144. cognee/modules/users/authentication/default/default_transport.py +11 -1
  145. cognee/modules/users/authentication/get_api_auth_backend.py +2 -1
  146. cognee/modules/users/authentication/get_client_auth_backend.py +2 -1
  147. cognee/modules/users/methods/create_user.py +0 -9
  148. cognee/modules/users/permissions/methods/has_user_management_permission.py +29 -0
  149. cognee/modules/visualization/cognee_network_visualization.py +1 -1
  150. cognee/run_migrations.py +48 -0
  151. cognee/shared/exceptions/__init__.py +1 -3
  152. cognee/shared/exceptions/exceptions.py +11 -1
  153. cognee/shared/usage_logger.py +332 -0
  154. cognee/shared/utils.py +12 -5
  155. cognee/tasks/chunks/__init__.py +9 -0
  156. cognee/tasks/cleanup/cleanup_unused_data.py +172 -0
  157. cognee/tasks/graph/__init__.py +7 -0
  158. cognee/tasks/memify/__init__.py +8 -0
  159. cognee/tasks/memify/extract_usage_frequency.py +613 -0
  160. cognee/tasks/summarization/models.py +0 -2
  161. cognee/tasks/temporal_graph/__init__.py +0 -1
  162. cognee/tasks/translation/__init__.py +96 -0
  163. cognee/tasks/translation/config.py +110 -0
  164. cognee/tasks/translation/detect_language.py +190 -0
  165. cognee/tasks/translation/exceptions.py +62 -0
  166. cognee/tasks/translation/models.py +72 -0
  167. cognee/tasks/translation/providers/__init__.py +44 -0
  168. cognee/tasks/translation/providers/azure_provider.py +192 -0
  169. cognee/tasks/translation/providers/base.py +85 -0
  170. cognee/tasks/translation/providers/google_provider.py +158 -0
  171. cognee/tasks/translation/providers/llm_provider.py +143 -0
  172. cognee/tasks/translation/translate_content.py +282 -0
  173. cognee/tasks/web_scraper/default_url_crawler.py +6 -2
  174. cognee/tests/cli_tests/cli_unit_tests/test_cli_commands.py +1 -0
  175. cognee/tests/cli_tests/cli_unit_tests/test_cli_edge_cases.py +3 -0
  176. cognee/tests/integration/retrieval/test_brute_force_triplet_search_with_cognify.py +62 -0
  177. cognee/tests/integration/retrieval/test_chunks_retriever.py +115 -16
  178. cognee/tests/integration/retrieval/test_graph_completion_retriever.py +13 -5
  179. cognee/tests/integration/retrieval/test_graph_completion_retriever_context_extension.py +22 -20
  180. cognee/tests/integration/retrieval/test_graph_completion_retriever_cot.py +23 -24
  181. cognee/tests/integration/retrieval/test_rag_completion_retriever.py +70 -5
  182. cognee/tests/integration/retrieval/test_structured_output.py +62 -18
  183. cognee/tests/integration/retrieval/test_summaries_retriever.py +20 -9
  184. cognee/tests/integration/retrieval/test_temporal_retriever.py +38 -8
  185. cognee/tests/integration/retrieval/test_triplet_retriever.py +13 -4
  186. cognee/tests/integration/shared/test_usage_logger_integration.py +255 -0
  187. cognee/tests/tasks/translation/README.md +147 -0
  188. cognee/tests/tasks/translation/__init__.py +1 -0
  189. cognee/tests/tasks/translation/config_test.py +93 -0
  190. cognee/tests/tasks/translation/detect_language_test.py +118 -0
  191. cognee/tests/tasks/translation/providers_test.py +151 -0
  192. cognee/tests/tasks/translation/translate_content_test.py +213 -0
  193. cognee/tests/test_chromadb.py +1 -1
  194. cognee/tests/test_cleanup_unused_data.py +165 -0
  195. cognee/tests/test_delete_by_id.py +6 -6
  196. cognee/tests/test_extract_usage_frequency.py +308 -0
  197. cognee/tests/test_kuzu.py +17 -7
  198. cognee/tests/test_lancedb.py +3 -1
  199. cognee/tests/test_library.py +1 -1
  200. cognee/tests/test_neo4j.py +17 -7
  201. cognee/tests/test_neptune_analytics_vector.py +3 -1
  202. cognee/tests/test_permissions.py +172 -187
  203. cognee/tests/test_pgvector.py +3 -1
  204. cognee/tests/test_relational_db_migration.py +15 -1
  205. cognee/tests/test_remote_kuzu.py +3 -1
  206. cognee/tests/test_s3_file_storage.py +1 -1
  207. cognee/tests/test_search_db.py +97 -110
  208. cognee/tests/test_usage_logger_e2e.py +268 -0
  209. cognee/tests/unit/api/test_get_raw_data_endpoint.py +206 -0
  210. cognee/tests/unit/eval_framework/answer_generation_test.py +4 -3
  211. cognee/tests/unit/infrastructure/databases/cache/test_cache_config.py +2 -0
  212. cognee/tests/unit/modules/graph/cognee_graph_elements_test.py +42 -2
  213. cognee/tests/unit/modules/graph/cognee_graph_test.py +329 -31
  214. cognee/tests/unit/modules/retrieval/chunks_retriever_test.py +31 -59
  215. cognee/tests/unit/modules/retrieval/graph_completion_retriever_context_extension_test.py +70 -33
  216. cognee/tests/unit/modules/retrieval/graph_completion_retriever_cot_test.py +72 -52
  217. cognee/tests/unit/modules/retrieval/graph_completion_retriever_test.py +27 -33
  218. cognee/tests/unit/modules/retrieval/rag_completion_retriever_test.py +28 -15
  219. cognee/tests/unit/modules/retrieval/summaries_retriever_test.py +37 -42
  220. cognee/tests/unit/modules/retrieval/temporal_retriever_test.py +48 -64
  221. cognee/tests/unit/modules/retrieval/test_brute_force_triplet_search.py +263 -24
  222. cognee/tests/unit/modules/retrieval/test_node_edge_vector_search.py +273 -0
  223. cognee/tests/unit/modules/retrieval/triplet_retriever_test.py +30 -16
  224. cognee/tests/unit/modules/search/test_get_search_type_retriever_instance.py +125 -0
  225. cognee/tests/unit/modules/search/test_search.py +176 -0
  226. cognee/tests/unit/modules/search/test_search_prepare_search_result_contract.py +190 -0
  227. cognee/tests/unit/modules/users/test_tutorial_notebook_creation.py +511 -297
  228. cognee/tests/unit/shared/test_usage_logger.py +241 -0
  229. cognee/tests/unit/users/permissions/test_has_user_management_permission.py +46 -0
  230. {cognee-0.5.1.dev0.dist-info → cognee-0.5.2.dist-info}/METADATA +22 -17
  231. {cognee-0.5.1.dev0.dist-info → cognee-0.5.2.dist-info}/RECORD +235 -147
  232. cognee/api/.env.example +0 -5
  233. cognee/modules/retrieval/base_graph_retriever.py +0 -24
  234. cognee/modules/search/methods/get_search_type_tools.py +0 -223
  235. cognee/modules/search/methods/no_access_control_search.py +0 -62
  236. cognee/modules/search/utils/prepare_search_result.py +0 -63
  237. cognee/tests/test_feedback_enrichment.py +0 -174
  238. {cognee-0.5.1.dev0.dist-info → cognee-0.5.2.dist-info}/WHEEL +0 -0
  239. {cognee-0.5.1.dev0.dist-info → cognee-0.5.2.dist-info}/entry_points.txt +0 -0
  240. {cognee-0.5.1.dev0.dist-info → cognee-0.5.2.dist-info}/licenses/LICENSE +0 -0
  241. {cognee-0.5.1.dev0.dist-info → cognee-0.5.2.dist-info}/licenses/NOTICE.md +0 -0
@@ -9,7 +9,6 @@ import cognee
9
9
  from cognee.infrastructure.databases.graph import get_graph_engine
10
10
  from cognee.infrastructure.databases.vector import get_vector_engine
11
11
  from cognee.modules.graph.cognee_graph.CogneeGraphElements import Edge
12
- from cognee.modules.graph.utils import resolve_edges_to_text
13
12
  from cognee.modules.retrieval.graph_completion_retriever import GraphCompletionRetriever
14
13
  from cognee.modules.retrieval.graph_completion_context_extension_retriever import (
15
14
  GraphCompletionContextExtensionRetriever,
@@ -48,14 +47,14 @@ async def _reset_engines_and_prune() -> None:
48
47
  # Engine might not exist yet
49
48
  pass
50
49
 
51
- from cognee.infrastructure.databases.graph.get_graph_engine import create_graph_engine
52
- from cognee.infrastructure.databases.vector.create_vector_engine import create_vector_engine
50
+ from cognee.infrastructure.databases.graph.get_graph_engine import _create_graph_engine
51
+ from cognee.infrastructure.databases.vector.create_vector_engine import _create_vector_engine
53
52
  from cognee.infrastructure.databases.relational.create_relational_engine import (
54
53
  create_relational_engine,
55
54
  )
56
55
 
57
- create_graph_engine.cache_clear()
58
- create_vector_engine.cache_clear()
56
+ _create_graph_engine.cache_clear()
57
+ _create_vector_engine.cache_clear()
59
58
  create_relational_engine.cache_clear()
60
59
 
61
60
  await cognee.prune.prune_data()
@@ -128,11 +127,10 @@ async def setup_test_environment():
128
127
  return state
129
128
 
130
129
 
131
- async def setup_test_environment_for_feedback():
132
- """Helper function to set up test environment for feedback weight calculation test."""
133
- dataset_name = "test_dataset"
134
- await _reset_engines_and_prune()
135
- return await _seed_default_dataset(dataset_name=dataset_name)
130
+ async def _get_retriever_context(retriever, query: str):
131
+ """Retrieve objects and resolve context via the retriever API."""
132
+ retrieved_objects = await retriever.get_retrieved_objects(query)
133
+ return await retriever.get_context_from_objects(query, retrieved_objects)
136
134
 
137
135
 
138
136
  @pytest_asyncio.fixture(scope="session")
@@ -149,26 +147,30 @@ async def e2e_state():
149
147
 
150
148
  vector_engine = get_vector_engine()
151
149
  collection = await vector_engine.search(
152
- collection_name="Triplet_text", query_text="Test", limit=None
150
+ collection_name="Triplet_text",
151
+ query_text="Test",
152
+ limit=None,
153
153
  )
154
154
 
155
155
  # --- Retriever contexts ---
156
156
  query = "Next to which country is Germany located?"
157
157
 
158
158
  contexts = {
159
- "graph_completion": await GraphCompletionRetriever().get_context(query=query),
160
- "graph_completion_cot": await GraphCompletionCotRetriever().get_context(query=query),
161
- "graph_completion_context_extension": await GraphCompletionContextExtensionRetriever().get_context(
162
- query=query
159
+ "graph_completion": await _get_retriever_context(GraphCompletionRetriever(), query=query),
160
+ "graph_completion_cot": await _get_retriever_context(
161
+ GraphCompletionCotRetriever(), query=query
163
162
  ),
164
- "graph_summary_completion": await GraphSummaryCompletionRetriever().get_context(
165
- query=query
163
+ "graph_completion_context_extension": await _get_retriever_context(
164
+ GraphCompletionContextExtensionRetriever(), query=query
165
+ ),
166
+ "graph_summary_completion": await _get_retriever_context(
167
+ GraphSummaryCompletionRetriever(), query=query
166
168
  ),
167
- "chunks": await ChunksRetriever(top_k=5).get_context(query=query),
168
- "summaries": await SummariesRetriever(top_k=5).get_context(query=query),
169
- "rag_completion": await CompletionRetriever(top_k=3).get_context(query=query),
170
- "temporal": await TemporalRetriever(top_k=5).get_context(query=query),
171
- "triplet": await TripletRetriever().get_context(query=query),
169
+ "chunks": await _get_retriever_context(ChunksRetriever(top_k=5), query=query),
170
+ "summaries": await _get_retriever_context(SummariesRetriever(top_k=5), query=query),
171
+ "rag_completion": await _get_retriever_context(CompletionRetriever(top_k=3), query=query),
172
+ "temporal": await _get_retriever_context(TemporalRetriever(top_k=5), query=query),
173
+ "triplet": await _get_retriever_context(TripletRetriever(), query=query),
172
174
  }
173
175
 
174
176
  # --- Retriever triplets + vector distance validation ---
@@ -188,57 +190,56 @@ async def e2e_state():
188
190
  query_type=SearchType.GRAPH_COMPLETION,
189
191
  query_text="Where is germany located, next to which country?",
190
192
  save_interaction=True,
193
+ verbose=True,
191
194
  )
192
195
  completion_cot = await cognee.search(
193
196
  query_type=SearchType.GRAPH_COMPLETION_COT,
194
197
  query_text="What is the country next to germany??",
195
198
  save_interaction=True,
199
+ verbose=True,
196
200
  )
197
201
  completion_ext = await cognee.search(
198
202
  query_type=SearchType.GRAPH_COMPLETION_CONTEXT_EXTENSION,
199
203
  query_text="What is the name of the country next to germany",
200
204
  save_interaction=True,
201
- )
202
-
203
- await cognee.search(
204
- query_type=SearchType.FEEDBACK, query_text="This was not the best answer", last_k=1
205
+ verbose=True,
205
206
  )
206
207
 
207
208
  completion_sum = await cognee.search(
208
209
  query_type=SearchType.GRAPH_SUMMARY_COMPLETION,
209
210
  query_text="Next to which country is Germany located?",
210
211
  save_interaction=True,
212
+ verbose=True,
211
213
  )
212
214
  completion_triplet = await cognee.search(
213
215
  query_type=SearchType.TRIPLET_COMPLETION,
214
216
  query_text="Next to which country is Germany located?",
215
217
  save_interaction=True,
218
+ verbose=True,
216
219
  )
217
220
  completion_chunks = await cognee.search(
218
221
  query_type=SearchType.CHUNKS,
219
222
  query_text="Germany",
220
223
  save_interaction=False,
224
+ verbose=True,
221
225
  )
222
226
  completion_summaries = await cognee.search(
223
227
  query_type=SearchType.SUMMARIES,
224
228
  query_text="Germany",
225
229
  save_interaction=False,
230
+ verbose=True,
226
231
  )
227
232
  completion_rag = await cognee.search(
228
233
  query_type=SearchType.RAG_COMPLETION,
229
234
  query_text="Next to which country is Germany located?",
230
235
  save_interaction=False,
236
+ verbose=True,
231
237
  )
232
238
  completion_temporal = await cognee.search(
233
239
  query_type=SearchType.TEMPORAL,
234
240
  query_text="Next to which country is Germany located?",
235
241
  save_interaction=False,
236
- )
237
-
238
- await cognee.search(
239
- query_type=SearchType.FEEDBACK,
240
- query_text="This answer was great",
241
- last_k=1,
242
+ verbose=True,
242
243
  )
243
244
 
244
245
  # Snapshot after all E2E operations above (used by assertion-only tests).
@@ -266,32 +267,6 @@ async def e2e_state():
266
267
  }
267
268
 
268
269
 
269
- @pytest_asyncio.fixture(scope="session")
270
- async def feedback_state():
271
- """Feedback-weight scenario computed once (fresh environment)."""
272
- await setup_test_environment_for_feedback()
273
-
274
- await cognee.search(
275
- query_type=SearchType.GRAPH_COMPLETION,
276
- query_text="Next to which country is Germany located?",
277
- save_interaction=True,
278
- )
279
- await cognee.search(
280
- query_type=SearchType.FEEDBACK,
281
- query_text="This was the best answer I've ever seen",
282
- last_k=1,
283
- )
284
- await cognee.search(
285
- query_type=SearchType.FEEDBACK,
286
- query_text="Wow the correctness of this answer blows my mind",
287
- last_k=1,
288
- )
289
-
290
- graph_engine = await get_graph_engine()
291
- graph = await graph_engine.get_graph_data()
292
- return {"graph_snapshot": graph}
293
-
294
-
295
270
  @pytest.mark.asyncio
296
271
  async def test_e2e_graph_vector_consistency(e2e_state):
297
272
  """Graph and vector stores contain the same triplet edges."""
@@ -310,28 +285,24 @@ async def test_e2e_retriever_contexts(e2e_state):
310
285
  "graph_summary_completion",
311
286
  ]:
312
287
  ctx = contexts[name]
313
- assert isinstance(ctx, list), f"{name}: Context should be a list"
314
- assert ctx, f"{name}: Context should not be empty"
315
- ctx_text = await resolve_edges_to_text(ctx)
316
- lower = ctx_text.lower()
317
- assert "germany" in lower or "netherlands" in lower, (
318
- f"{name}: Context did not contain 'germany' or 'netherlands'; got: {ctx!r}"
319
- )
288
+ assert isinstance(ctx, str), f"{name}: Context should be a string"
289
+ assert ctx.strip(), f"{name}: Context should not be empty"
290
+ lower = ctx.lower()
291
+ assert "germany" in lower or "netherlands" in lower
320
292
 
321
293
  triplet_ctx = contexts["triplet"]
322
294
  assert isinstance(triplet_ctx, str), "triplet: Context should be a string"
323
295
  assert triplet_ctx.strip(), "triplet: Context should not be empty"
324
296
 
325
297
  chunks_ctx = contexts["chunks"]
326
- assert isinstance(chunks_ctx, list), "chunks: Context should be a list"
327
- assert chunks_ctx, "chunks: Context should not be empty"
328
- chunks_text = "\n".join(str(item.get("text", "")) for item in chunks_ctx).lower()
298
+ assert isinstance(chunks_ctx, str), "chunks: Context should be a string"
299
+ assert chunks_ctx.strip(), "chunks: Context should not be empty"
300
+ chunks_text = chunks_ctx.lower()
329
301
  assert "germany" in chunks_text or "netherlands" in chunks_text
330
302
 
331
303
  summaries_ctx = contexts["summaries"]
332
- assert isinstance(summaries_ctx, list), "summaries: Context should be a list"
333
- assert summaries_ctx, "summaries: Context should not be empty"
334
- assert any(str(item.get("text", "")).strip() for item in summaries_ctx)
304
+ assert isinstance(summaries_ctx, str), "summaries: Context should be a string"
305
+ assert summaries_ctx.strip(), "summaries: Context should not be empty"
335
306
 
336
307
  rag_ctx = contexts["rag_completion"]
337
308
  assert isinstance(rag_ctx, str), "rag_completion: Context should be a string"
@@ -350,11 +321,41 @@ async def test_e2e_retriever_triplets_have_vector_distances(e2e_state):
350
321
  assert triplets, f"{name}: Triplets list should not be empty"
351
322
  for edge in triplets:
352
323
  assert isinstance(edge, Edge), f"{name}: Elements should be Edge instances"
353
- distance = edge.attributes.get("vector_distance")
354
- node1_distance = edge.node1.attributes.get("vector_distance")
355
- node2_distance = edge.node2.attributes.get("vector_distance")
356
- assert isinstance(distance, float), f"{name}: vector_distance should be float"
324
+ vector_distances = edge.attributes.get("vector_distance")
325
+ assert vector_distances is not None, (
326
+ f"{name}: vector_distance should be set when retrievers return results"
327
+ )
328
+ assert isinstance(vector_distances, list) and vector_distances, (
329
+ f"{name}: vector_distance should be a non-empty list"
330
+ )
331
+ distance = vector_distances[0]
332
+ assert isinstance(distance, float), (
333
+ f"{name}: vector_distance[0] should be float, got {type(distance)}"
334
+ )
357
335
  assert 0 <= distance <= 1
336
+
337
+ node1_distances = edge.node1.attributes.get("vector_distance")
338
+ node2_distances = edge.node2.attributes.get("vector_distance")
339
+ assert node1_distances is not None, (
340
+ f"{name}: node1 vector_distance should be set when retrievers return results"
341
+ )
342
+ assert node2_distances is not None, (
343
+ f"{name}: node2 vector_distance should be set when retrievers return results"
344
+ )
345
+ assert isinstance(node1_distances, list) and node1_distances, (
346
+ f"{name}: node1 vector_distance should be a non-empty list"
347
+ )
348
+ assert isinstance(node2_distances, list) and node2_distances, (
349
+ f"{name}: node2 vector_distance should be a non-empty list"
350
+ )
351
+ node1_distance = node1_distances[0]
352
+ node2_distance = node2_distances[0]
353
+ assert isinstance(node1_distance, float), (
354
+ f"{name}: node1 vector_distance[0] should be float, got {type(node1_distance)}"
355
+ )
356
+ assert isinstance(node2_distance, float), (
357
+ f"{name}: node2 vector_distance[0] should be float, got {type(node2_distance)}"
358
+ )
358
359
  assert 0 <= node1_distance <= 1
359
360
  assert 0 <= node2_distance <= 1
360
361
 
@@ -387,13 +388,15 @@ async def test_e2e_search_results_and_wrappers(e2e_state):
387
388
  )
388
389
  assert wrapper.get("dataset_id"), f"{name}: missing dataset_id in wrapper"
389
390
  assert wrapper.get("dataset_name") == "test_dataset"
390
- assert "graphs" in wrapper
391
- text = wrapper["search_result"][0]
391
+ result_payload = wrapper.get("text_result")
392
392
  else:
393
- text = search_results[0]
393
+ entry = search_results[0]
394
+ assert isinstance(entry, dict), f"{name}: expected dict entries"
395
+ result_payload = entry.get("text_result")
394
396
 
395
- assert isinstance(text, str) and text.strip()
396
- assert "netherlands" in text.lower()
397
+ text_blob = str(result_payload)
398
+ assert text_blob.strip()
399
+ assert "netherlands" in text_blob.lower()
397
400
 
398
401
  # Non-LLM search types: CHUNKS / SUMMARIES validate payload list + text
399
402
  for name in ["chunks", "summaries"]:
@@ -401,16 +404,20 @@ async def test_e2e_search_results_and_wrappers(e2e_state):
401
404
  assert isinstance(search_results, list), f"{name}: should return a list"
402
405
  assert search_results, f"{name}: should not be empty"
403
406
 
404
- first = search_results[0]
405
- assert isinstance(first, dict), f"{name}: expected dict entries"
407
+ entry = search_results[0]
408
+ assert isinstance(entry, dict), f"{name}: expected dict entries"
409
+
410
+ context_result = entry.get("context_result")
411
+ text_result = entry.get("text_result")
406
412
 
407
- payloads = search_results
408
- if "search_result" in first and "text" not in first:
409
- payloads = (first.get("search_result") or [None])[0]
413
+ assert isinstance(context_result, str) and context_result.strip()
414
+ lower_context = context_result.lower()
415
+ assert "germany" in lower_context or "netherlands" in lower_context
410
416
 
411
- assert isinstance(payloads, list) and payloads
412
- assert isinstance(payloads[0], dict)
413
- assert str(payloads[0].get("text", "")).strip()
417
+ assert isinstance(text_result, list) and text_result
418
+ first_text = text_result[0]
419
+ assert isinstance(first_text, dict)
420
+ assert str(first_text.get("text", "")).strip()
414
421
 
415
422
 
416
423
  @pytest.mark.asyncio
@@ -423,14 +430,11 @@ async def test_e2e_graph_side_effects_and_node_fields(e2e_state):
423
430
  edge_type_counts = Counter(edge_type[2] for edge_type in edges)
424
431
 
425
432
  assert type_counts.get("CogneeUserInteraction", 0) == 4
426
- assert type_counts.get("CogneeUserFeedback", 0) == 2
427
- assert type_counts.get("NodeSet", 0) == 2
433
+ assert type_counts.get("NodeSet", 0) == 1
428
434
  assert edge_type_counts.get("used_graph_element_to_answer", 0) >= 10
429
- assert edge_type_counts.get("gives_feedback_to", 0) == 2
430
- assert edge_type_counts.get("belongs_to_set", 0) >= 6
435
+ assert edge_type_counts.get("belongs_to_set", 0) >= 4
431
436
 
432
437
  required_fields_user_interaction = {"question", "answer", "context"}
433
- required_fields_feedback = {"feedback", "sentiment"}
434
438
 
435
439
  for node_id, data in nodes:
436
440
  if data.get("type") == "CogneeUserInteraction":
@@ -438,20 +442,3 @@ async def test_e2e_graph_side_effects_and_node_fields(e2e_state):
438
442
  for field in required_fields_user_interaction:
439
443
  value = data[field]
440
444
  assert isinstance(value, str) and value.strip()
441
-
442
- if data.get("type") == "CogneeUserFeedback":
443
- assert required_fields_feedback.issubset(data.keys())
444
- for field in required_fields_feedback:
445
- value = data[field]
446
- assert isinstance(value, str) and value.strip()
447
-
448
-
449
- @pytest.mark.asyncio
450
- async def test_e2e_feedback_weight_calculation(feedback_state):
451
- """Positive feedback increases used_graph_element_to_answer feedback_weight."""
452
- _nodes, edges = feedback_state["graph_snapshot"]
453
- for _from_node, _to_node, relationship_name, properties in edges:
454
- if relationship_name == "used_graph_element_to_answer":
455
- assert properties["feedback_weight"] >= 6, (
456
- "Feedback weight calculation is not correct, it should be more then 6."
457
- )
@@ -0,0 +1,268 @@
1
+ import os
2
+ import pytest
3
+ import pytest_asyncio
4
+ import asyncio
5
+ from fastapi.testclient import TestClient
6
+
7
+ import cognee
8
+ from cognee.api.client import app
9
+ from cognee.modules.users.methods import get_default_user, get_authenticated_user
10
+
11
+
12
+ async def _reset_engines_and_prune():
13
+ """Reset db engine caches and prune data/system."""
14
+ try:
15
+ from cognee.infrastructure.databases.vector import get_vector_engine
16
+
17
+ vector_engine = get_vector_engine()
18
+ if hasattr(vector_engine, "engine") and hasattr(vector_engine.engine, "dispose"):
19
+ await vector_engine.engine.dispose(close=True)
20
+ except Exception:
21
+ pass
22
+
23
+ await cognee.prune.prune_data()
24
+ await cognee.prune.prune_system(metadata=True)
25
+
26
+
27
+ @pytest.fixture(scope="session")
28
+ def event_loop():
29
+ """Use a single asyncio event loop for this test module."""
30
+ loop = asyncio.new_event_loop()
31
+ try:
32
+ yield loop
33
+ finally:
34
+ loop.close()
35
+
36
+
37
+ @pytest.fixture(scope="session")
38
+ def e2e_config():
39
+ """Configure environment for E2E tests."""
40
+ original_env = os.environ.copy()
41
+ os.environ["USAGE_LOGGING"] = "true"
42
+ os.environ["CACHE_BACKEND"] = "redis"
43
+ os.environ["CACHE_HOST"] = "localhost"
44
+ os.environ["CACHE_PORT"] = "6379"
45
+ yield
46
+ os.environ.clear()
47
+ os.environ.update(original_env)
48
+
49
+
50
+ @pytest.fixture(scope="session")
51
+ def authenticated_client(test_client):
52
+ """Override authentication to use default user."""
53
+
54
+ async def override_get_authenticated_user():
55
+ return await get_default_user()
56
+
57
+ app.dependency_overrides[get_authenticated_user] = override_get_authenticated_user
58
+ yield test_client
59
+ app.dependency_overrides.pop(get_authenticated_user, None)
60
+
61
+
62
+ @pytest_asyncio.fixture(scope="session")
63
+ async def test_data_setup():
64
+ """Set up test data: prune first, then add file and cognify."""
65
+ await _reset_engines_and_prune()
66
+
67
+ dataset_name = "test_e2e_dataset"
68
+ test_text = "Germany is located in Europe right next to the Netherlands."
69
+
70
+ await cognee.add(test_text, dataset_name)
71
+ await cognee.cognify([dataset_name])
72
+
73
+ yield dataset_name
74
+
75
+ await _reset_engines_and_prune()
76
+
77
+
78
+ @pytest_asyncio.fixture
79
+ async def mcp_data_setup():
80
+ """Set up test data for MCP tests: prune first, then add file and cognify."""
81
+ await _reset_engines_and_prune()
82
+
83
+ dataset_name = "test_mcp_dataset"
84
+ test_text = "Germany is located in Europe right next to the Netherlands."
85
+
86
+ await cognee.add(test_text, dataset_name)
87
+ await cognee.cognify([dataset_name])
88
+
89
+ yield dataset_name
90
+
91
+ await _reset_engines_and_prune()
92
+
93
+
94
+ @pytest.fixture(scope="session")
95
+ def test_client():
96
+ """TestClient instance for API calls."""
97
+ with TestClient(app) as client:
98
+ yield client
99
+
100
+
101
+ @pytest_asyncio.fixture
102
+ async def cache_engine(e2e_config):
103
+ """Get cache engine for log verification in test's event loop."""
104
+ from cognee.infrastructure.databases.cache.redis.RedisAdapter import RedisAdapter
105
+ from cognee.infrastructure.databases.cache.config import get_cache_config
106
+
107
+ config = get_cache_config()
108
+ if not config.usage_logging or config.cache_backend != "redis":
109
+ pytest.skip("Redis usage logging not configured")
110
+
111
+ engine = RedisAdapter(
112
+ host=config.cache_host,
113
+ port=config.cache_port,
114
+ username=config.cache_username,
115
+ password=config.cache_password,
116
+ log_key="usage_logs",
117
+ )
118
+ return engine
119
+
120
+
121
+ @pytest.mark.asyncio
122
+ async def test_api_endpoint_logging(e2e_config, authenticated_client, cache_engine):
123
+ """Test that API endpoints succeed and log to Redis."""
124
+ user = await get_default_user()
125
+ dataset_name = "test_e2e_api_dataset"
126
+
127
+ add_response = authenticated_client.post(
128
+ "/api/v1/add",
129
+ data={"datasetName": dataset_name},
130
+ files=[
131
+ (
132
+ "data",
133
+ (
134
+ "test.txt",
135
+ b"Germany is located in Europe right next to the Netherlands.",
136
+ "text/plain",
137
+ ),
138
+ )
139
+ ],
140
+ )
141
+ assert add_response.status_code in [200, 201], f"Add endpoint failed: {add_response.text}"
142
+
143
+ cognify_response = authenticated_client.post(
144
+ "/api/v1/cognify",
145
+ json={"datasets": [dataset_name], "run_in_background": False},
146
+ )
147
+ assert cognify_response.status_code in [200, 201], (
148
+ f"Cognify endpoint failed: {cognify_response.text}"
149
+ )
150
+
151
+ search_response = authenticated_client.post(
152
+ "/api/v1/search",
153
+ json={"query": "Germany", "search_type": "GRAPH_COMPLETION", "datasets": [dataset_name]},
154
+ )
155
+ assert search_response.status_code == 200, f"Search endpoint failed: {search_response.text}"
156
+
157
+ logs = await cache_engine.get_usage_logs(str(user.id), limit=20)
158
+
159
+ add_logs = [log for log in logs if log.get("function_name") == "POST /v1/add"]
160
+ assert len(add_logs) > 0
161
+ assert add_logs[0]["type"] == "api_endpoint"
162
+ assert add_logs[0]["user_id"] == str(user.id)
163
+ assert add_logs[0]["success"] is True
164
+
165
+ cognify_logs = [log for log in logs if log.get("function_name") == "POST /v1/cognify"]
166
+ assert len(cognify_logs) > 0
167
+ assert cognify_logs[0]["type"] == "api_endpoint"
168
+ assert cognify_logs[0]["user_id"] == str(user.id)
169
+ assert cognify_logs[0]["success"] is True
170
+
171
+ search_logs = [log for log in logs if log.get("function_name") == "POST /v1/search"]
172
+ assert len(search_logs) > 0
173
+ assert search_logs[0]["type"] == "api_endpoint"
174
+ assert search_logs[0]["user_id"] == str(user.id)
175
+ assert search_logs[0]["success"] is True
176
+
177
+
178
+ @pytest.mark.asyncio
179
+ async def test_mcp_tool_logging(e2e_config, cache_engine):
180
+ """Test that MCP tools succeed and log to Redis."""
181
+ import sys
182
+ import importlib.util
183
+ from pathlib import Path
184
+
185
+ await _reset_engines_and_prune()
186
+
187
+ repo_root = Path(__file__).parent.parent.parent
188
+ mcp_src_path = repo_root / "cognee-mcp" / "src"
189
+ mcp_server_path = mcp_src_path / "server.py"
190
+
191
+ if not mcp_server_path.exists():
192
+ pytest.skip(f"MCP server not found at {mcp_server_path}")
193
+
194
+ if str(mcp_src_path) not in sys.path:
195
+ sys.path.insert(0, str(mcp_src_path))
196
+
197
+ spec = importlib.util.spec_from_file_location("mcp_server_module", mcp_server_path)
198
+ mcp_server_module = importlib.util.module_from_spec(spec)
199
+
200
+ import os
201
+
202
+ original_cwd = os.getcwd()
203
+ try:
204
+ os.chdir(str(mcp_src_path))
205
+ spec.loader.exec_module(mcp_server_module)
206
+ finally:
207
+ os.chdir(original_cwd)
208
+
209
+ if mcp_server_module.cognee_client is None:
210
+ cognee_client_path = mcp_src_path / "cognee_client.py"
211
+ if cognee_client_path.exists():
212
+ spec_client = importlib.util.spec_from_file_location(
213
+ "cognee_client", cognee_client_path
214
+ )
215
+ cognee_client_module = importlib.util.module_from_spec(spec_client)
216
+ spec_client.loader.exec_module(cognee_client_module)
217
+ CogneeClient = cognee_client_module.CogneeClient
218
+ mcp_server_module.cognee_client = CogneeClient()
219
+ else:
220
+ pytest.skip(f"CogneeClient not found at {cognee_client_path}")
221
+
222
+ test_text = "Germany is located in Europe right next to the Netherlands."
223
+ await mcp_server_module.cognify(data=test_text)
224
+ await asyncio.sleep(30.0)
225
+
226
+ list_result = await mcp_server_module.list_data()
227
+ assert list_result is not None, "List data should return results"
228
+
229
+ search_result = await mcp_server_module.search(
230
+ search_query="Germany", search_type="GRAPH_COMPLETION", top_k=5
231
+ )
232
+ assert search_result is not None, "Search should return results"
233
+
234
+ interaction_data = "User: What is Germany?\nAgent: Germany is a country in Europe."
235
+ await mcp_server_module.save_interaction(data=interaction_data)
236
+ await asyncio.sleep(30.0)
237
+
238
+ status_result = await mcp_server_module.cognify_status()
239
+ assert status_result is not None, "Cognify status should return results"
240
+
241
+ await mcp_server_module.prune()
242
+ await asyncio.sleep(0.5)
243
+
244
+ logs = await cache_engine.get_usage_logs("unknown", limit=50)
245
+ mcp_logs = [log for log in logs if log.get("type") == "mcp_tool"]
246
+ assert len(mcp_logs) > 0, (
247
+ f"Should have MCP tool logs with user_id='unknown'. Found logs: {[log.get('function_name') for log in logs[:5]]}"
248
+ )
249
+ assert len(mcp_logs) == 6
250
+ function_names = [log.get("function_name") for log in mcp_logs]
251
+ expected_tools = [
252
+ "MCP cognify",
253
+ "MCP list_data",
254
+ "MCP search",
255
+ "MCP save_interaction",
256
+ "MCP cognify_status",
257
+ "MCP prune",
258
+ ]
259
+
260
+ for expected_tool in expected_tools:
261
+ assert expected_tool in function_names, (
262
+ f"Should have {expected_tool} log. Found: {function_names}"
263
+ )
264
+
265
+ for log in mcp_logs:
266
+ assert log["type"] == "mcp_tool"
267
+ assert log["user_id"] == "unknown"
268
+ assert log["success"] is True