cognee 0.2.3.dev0__py3-none-any.whl → 0.2.4__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 (179) hide show
  1. cognee/__main__.py +4 -0
  2. cognee/api/v1/add/add.py +18 -11
  3. cognee/api/v1/cognify/code_graph_pipeline.py +7 -1
  4. cognee/api/v1/cognify/cognify.py +22 -115
  5. cognee/api/v1/cognify/routers/get_cognify_router.py +11 -3
  6. cognee/api/v1/config/config.py +5 -13
  7. cognee/api/v1/datasets/routers/get_datasets_router.py +2 -2
  8. cognee/api/v1/delete/delete.py +1 -1
  9. cognee/api/v1/exceptions/__init__.py +13 -0
  10. cognee/api/v1/{delete → exceptions}/exceptions.py +15 -12
  11. cognee/api/v1/responses/default_tools.py +4 -0
  12. cognee/api/v1/responses/dispatch_function.py +6 -1
  13. cognee/api/v1/responses/models.py +1 -1
  14. cognee/api/v1/search/search.py +6 -7
  15. cognee/cli/__init__.py +10 -0
  16. cognee/cli/_cognee.py +180 -0
  17. cognee/cli/commands/__init__.py +1 -0
  18. cognee/cli/commands/add_command.py +80 -0
  19. cognee/cli/commands/cognify_command.py +128 -0
  20. cognee/cli/commands/config_command.py +225 -0
  21. cognee/cli/commands/delete_command.py +80 -0
  22. cognee/cli/commands/search_command.py +149 -0
  23. cognee/cli/config.py +33 -0
  24. cognee/cli/debug.py +21 -0
  25. cognee/cli/echo.py +45 -0
  26. cognee/cli/exceptions.py +23 -0
  27. cognee/cli/minimal_cli.py +97 -0
  28. cognee/cli/reference.py +26 -0
  29. cognee/cli/suppress_logging.py +12 -0
  30. cognee/eval_framework/corpus_builder/corpus_builder_executor.py +2 -2
  31. cognee/eval_framework/eval_config.py +1 -1
  32. cognee/exceptions/__init__.py +5 -5
  33. cognee/exceptions/exceptions.py +37 -17
  34. cognee/infrastructure/data/exceptions/__init__.py +7 -0
  35. cognee/infrastructure/data/exceptions/exceptions.py +22 -0
  36. cognee/infrastructure/data/utils/extract_keywords.py +3 -3
  37. cognee/infrastructure/databases/exceptions/__init__.py +3 -0
  38. cognee/infrastructure/databases/exceptions/exceptions.py +57 -9
  39. cognee/infrastructure/databases/graph/get_graph_engine.py +4 -9
  40. cognee/infrastructure/databases/graph/kuzu/adapter.py +64 -2
  41. cognee/infrastructure/databases/graph/neo4j_driver/adapter.py +49 -0
  42. cognee/infrastructure/databases/graph/neptune_driver/exceptions.py +15 -10
  43. cognee/infrastructure/databases/hybrid/falkordb/FalkorDBAdapter.py +2 -2
  44. cognee/infrastructure/databases/hybrid/neptune_analytics/NeptuneAnalyticsAdapter.py +4 -5
  45. cognee/infrastructure/databases/vector/chromadb/ChromaDBAdapter.py +2 -2
  46. cognee/infrastructure/databases/vector/embeddings/FastembedEmbeddingEngine.py +5 -3
  47. cognee/infrastructure/databases/vector/embeddings/LiteLLMEmbeddingEngine.py +17 -8
  48. cognee/infrastructure/databases/vector/embeddings/OllamaEmbeddingEngine.py +5 -5
  49. cognee/infrastructure/databases/vector/embeddings/config.py +2 -2
  50. cognee/infrastructure/databases/vector/embeddings/get_embedding_engine.py +6 -6
  51. cognee/infrastructure/databases/vector/exceptions/exceptions.py +3 -3
  52. cognee/infrastructure/databases/vector/lancedb/LanceDBAdapter.py +2 -2
  53. cognee/infrastructure/databases/vector/pgvector/PGVectorAdapter.py +4 -3
  54. cognee/infrastructure/files/utils/get_data_file_path.py +14 -9
  55. cognee/infrastructure/files/utils/get_file_metadata.py +2 -1
  56. cognee/infrastructure/llm/LLMGateway.py +14 -5
  57. cognee/infrastructure/llm/config.py +5 -5
  58. cognee/infrastructure/llm/exceptions.py +30 -2
  59. cognee/infrastructure/llm/structured_output_framework/baml/baml_src/extraction/knowledge_graph/extract_content_graph.py +16 -5
  60. cognee/infrastructure/llm/structured_output_framework/litellm_instructor/extraction/knowledge_graph/extract_content_graph.py +19 -15
  61. cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/anthropic/adapter.py +5 -5
  62. cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/gemini/adapter.py +6 -6
  63. cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/generic_llm_api/adapter.py +2 -2
  64. cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/get_llm_client.py +24 -15
  65. cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/ollama/adapter.py +6 -4
  66. cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/openai/adapter.py +9 -7
  67. cognee/infrastructure/llm/tokenizer/Gemini/adapter.py +2 -2
  68. cognee/infrastructure/llm/tokenizer/HuggingFace/adapter.py +3 -3
  69. cognee/infrastructure/llm/tokenizer/Mistral/adapter.py +3 -3
  70. cognee/infrastructure/llm/tokenizer/TikToken/adapter.py +6 -6
  71. cognee/infrastructure/llm/utils.py +7 -7
  72. cognee/modules/data/exceptions/exceptions.py +18 -5
  73. cognee/modules/data/methods/__init__.py +2 -0
  74. cognee/modules/data/methods/create_authorized_dataset.py +19 -0
  75. cognee/modules/data/methods/delete_data.py +2 -4
  76. cognee/modules/data/methods/get_authorized_dataset.py +11 -5
  77. cognee/modules/data/methods/get_authorized_dataset_by_name.py +16 -0
  78. cognee/modules/data/methods/load_or_create_datasets.py +2 -20
  79. cognee/modules/data/processing/document_types/exceptions/exceptions.py +2 -2
  80. cognee/modules/graph/cognee_graph/CogneeGraph.py +6 -4
  81. cognee/modules/graph/cognee_graph/CogneeGraphElements.py +5 -10
  82. cognee/modules/graph/exceptions/__init__.py +2 -0
  83. cognee/modules/graph/exceptions/exceptions.py +25 -3
  84. cognee/modules/graph/methods/get_formatted_graph_data.py +3 -2
  85. cognee/modules/ingestion/exceptions/exceptions.py +2 -2
  86. cognee/modules/ontology/exceptions/exceptions.py +4 -4
  87. cognee/modules/pipelines/__init__.py +1 -1
  88. cognee/modules/pipelines/exceptions/exceptions.py +2 -2
  89. cognee/modules/pipelines/exceptions/tasks.py +18 -0
  90. cognee/modules/pipelines/layers/__init__.py +1 -0
  91. cognee/modules/pipelines/layers/check_pipeline_run_qualification.py +59 -0
  92. cognee/modules/pipelines/layers/pipeline_execution_mode.py +127 -0
  93. cognee/modules/pipelines/layers/reset_dataset_pipeline_run_status.py +12 -0
  94. cognee/modules/pipelines/layers/resolve_authorized_user_dataset.py +34 -0
  95. cognee/modules/pipelines/layers/resolve_authorized_user_datasets.py +55 -0
  96. cognee/modules/pipelines/layers/setup_and_check_environment.py +41 -0
  97. cognee/modules/pipelines/layers/validate_pipeline_tasks.py +20 -0
  98. cognee/modules/pipelines/methods/__init__.py +2 -0
  99. cognee/modules/pipelines/methods/get_pipeline_runs_by_dataset.py +34 -0
  100. cognee/modules/pipelines/methods/reset_pipeline_run_status.py +16 -0
  101. cognee/modules/pipelines/operations/__init__.py +0 -1
  102. cognee/modules/pipelines/operations/log_pipeline_run_initiated.py +1 -1
  103. cognee/modules/pipelines/operations/pipeline.py +23 -138
  104. cognee/modules/retrieval/base_feedback.py +11 -0
  105. cognee/modules/retrieval/cypher_search_retriever.py +1 -9
  106. cognee/modules/retrieval/exceptions/exceptions.py +12 -6
  107. cognee/modules/retrieval/graph_completion_context_extension_retriever.py +9 -2
  108. cognee/modules/retrieval/graph_completion_cot_retriever.py +13 -6
  109. cognee/modules/retrieval/graph_completion_retriever.py +89 -5
  110. cognee/modules/retrieval/graph_summary_completion_retriever.py +2 -0
  111. cognee/modules/retrieval/natural_language_retriever.py +0 -4
  112. cognee/modules/retrieval/user_qa_feedback.py +83 -0
  113. cognee/modules/retrieval/utils/extract_uuid_from_node.py +18 -0
  114. cognee/modules/retrieval/utils/models.py +40 -0
  115. cognee/modules/search/exceptions/__init__.py +7 -0
  116. cognee/modules/search/exceptions/exceptions.py +15 -0
  117. cognee/modules/search/methods/search.py +47 -7
  118. cognee/modules/search/types/SearchType.py +1 -0
  119. cognee/modules/settings/get_settings.py +2 -2
  120. cognee/modules/users/exceptions/exceptions.py +6 -6
  121. cognee/shared/CodeGraphEntities.py +1 -0
  122. cognee/shared/exceptions/exceptions.py +2 -2
  123. cognee/shared/logging_utils.py +142 -31
  124. cognee/shared/utils.py +0 -1
  125. cognee/tasks/completion/exceptions/exceptions.py +3 -3
  126. cognee/tasks/documents/classify_documents.py +4 -0
  127. cognee/tasks/documents/exceptions/__init__.py +11 -0
  128. cognee/tasks/documents/exceptions/exceptions.py +36 -0
  129. cognee/tasks/documents/extract_chunks_from_documents.py +8 -2
  130. cognee/tasks/graph/exceptions/__init__.py +12 -0
  131. cognee/tasks/graph/exceptions/exceptions.py +41 -0
  132. cognee/tasks/graph/extract_graph_from_data.py +34 -2
  133. cognee/tasks/ingestion/exceptions/__init__.py +8 -0
  134. cognee/tasks/ingestion/exceptions/exceptions.py +12 -0
  135. cognee/tasks/ingestion/resolve_data_directories.py +5 -0
  136. cognee/tasks/repo_processor/get_local_dependencies.py +2 -0
  137. cognee/tasks/repo_processor/get_repo_file_dependencies.py +120 -48
  138. cognee/tasks/storage/add_data_points.py +41 -3
  139. cognee/tasks/storage/exceptions/__init__.py +9 -0
  140. cognee/tasks/storage/exceptions/exceptions.py +13 -0
  141. cognee/tasks/storage/index_data_points.py +1 -1
  142. cognee/tasks/summarization/exceptions/__init__.py +9 -0
  143. cognee/tasks/summarization/exceptions/exceptions.py +14 -0
  144. cognee/tasks/summarization/summarize_text.py +8 -1
  145. cognee/tests/integration/cli/__init__.py +3 -0
  146. cognee/tests/integration/cli/test_cli_integration.py +331 -0
  147. cognee/tests/integration/documents/PdfDocument_test.py +2 -2
  148. cognee/tests/integration/documents/TextDocument_test.py +2 -4
  149. cognee/tests/integration/documents/UnstructuredDocument_test.py +5 -8
  150. cognee/tests/test_delete_by_id.py +1 -1
  151. cognee/tests/{test_deletion.py → test_delete_hard.py} +0 -37
  152. cognee/tests/test_delete_soft.py +85 -0
  153. cognee/tests/test_kuzu.py +2 -2
  154. cognee/tests/test_neo4j.py +2 -2
  155. cognee/tests/test_search_db.py +126 -7
  156. cognee/tests/unit/cli/__init__.py +3 -0
  157. cognee/tests/unit/cli/test_cli_commands.py +483 -0
  158. cognee/tests/unit/cli/test_cli_edge_cases.py +625 -0
  159. cognee/tests/unit/cli/test_cli_main.py +173 -0
  160. cognee/tests/unit/cli/test_cli_runner.py +62 -0
  161. cognee/tests/unit/cli/test_cli_utils.py +127 -0
  162. cognee/tests/unit/modules/graph/cognee_graph_elements_test.py +5 -5
  163. cognee/tests/unit/modules/retrieval/graph_completion_retriever_context_extension_test.py +3 -3
  164. cognee/tests/unit/modules/retrieval/graph_completion_retriever_cot_test.py +3 -3
  165. cognee/tests/unit/modules/retrieval/graph_completion_retriever_test.py +3 -3
  166. cognee/tests/unit/modules/search/search_methods_test.py +4 -2
  167. {cognee-0.2.3.dev0.dist-info → cognee-0.2.4.dist-info}/METADATA +7 -5
  168. {cognee-0.2.3.dev0.dist-info → cognee-0.2.4.dist-info}/RECORD +172 -121
  169. cognee-0.2.4.dist-info/entry_points.txt +2 -0
  170. cognee/infrastructure/databases/exceptions/EmbeddingException.py +0 -20
  171. cognee/infrastructure/databases/graph/networkx/__init__.py +0 -0
  172. cognee/infrastructure/databases/graph/networkx/adapter.py +0 -1017
  173. cognee/infrastructure/pipeline/models/Operation.py +0 -60
  174. cognee/infrastructure/pipeline/models/__init__.py +0 -0
  175. cognee/notebooks/github_analysis_step_by_step.ipynb +0 -37
  176. cognee/tests/tasks/descriptive_metrics/networkx_metrics_test.py +0 -7
  177. {cognee-0.2.3.dev0.dist-info → cognee-0.2.4.dist-info}/WHEEL +0 -0
  178. {cognee-0.2.3.dev0.dist-info → cognee-0.2.4.dist-info}/licenses/LICENSE +0 -0
  179. {cognee-0.2.3.dev0.dist-info → cognee-0.2.4.dist-info}/licenses/NOTICE.md +0 -0
@@ -4,6 +4,7 @@ import pathlib
4
4
  from dns.e164 import query
5
5
 
6
6
  import cognee
7
+ from cognee.infrastructure.databases.graph import get_graph_engine
7
8
  from cognee.modules.graph.cognee_graph.CogneeGraphElements import Edge
8
9
  from cognee.modules.retrieval.graph_completion_retriever import GraphCompletionRetriever
9
10
  from cognee.modules.retrieval.graph_completion_context_extension_retriever import (
@@ -18,6 +19,7 @@ from cognee.modules.users.methods import get_default_user
18
19
  from cognee.shared.logging_utils import get_logger
19
20
  from cognee.modules.search.types import SearchType
20
21
  from cognee.modules.engine.models import NodeSet
22
+ from collections import Counter
21
23
 
22
24
  logger = get_logger()
23
25
 
@@ -44,16 +46,16 @@ async def main():
44
46
 
45
47
  await cognee.cognify([dataset_name])
46
48
 
47
- context_gk = await GraphCompletionRetriever().get_context(
49
+ context_gk, _ = await GraphCompletionRetriever().get_context(
48
50
  query="Next to which country is Germany located?"
49
51
  )
50
- context_gk_cot = await GraphCompletionCotRetriever().get_context(
52
+ context_gk_cot, _ = await GraphCompletionCotRetriever().get_context(
51
53
  query="Next to which country is Germany located?"
52
54
  )
53
- context_gk_ext = await GraphCompletionContextExtensionRetriever().get_context(
55
+ context_gk_ext, _ = await GraphCompletionContextExtensionRetriever().get_context(
54
56
  query="Next to which country is Germany located?"
55
57
  )
56
- context_gk_sum = await GraphSummaryCompletionRetriever().get_context(
58
+ context_gk_sum, _ = await GraphSummaryCompletionRetriever().get_context(
57
59
  query="Next to which country is Germany located?"
58
60
  )
59
61
 
@@ -111,19 +113,34 @@ async def main():
111
113
 
112
114
  completion_gk = await cognee.search(
113
115
  query_type=SearchType.GRAPH_COMPLETION,
114
- query_text="Next to which country is Germany located?",
116
+ query_text="Where is germany located, next to which country?",
117
+ save_interaction=True,
115
118
  )
116
119
  completion_cot = await cognee.search(
117
120
  query_type=SearchType.GRAPH_COMPLETION_COT,
118
- query_text="Next to which country is Germany located?",
121
+ query_text="What is the country next to germany??",
122
+ save_interaction=True,
119
123
  )
120
124
  completion_ext = await cognee.search(
121
125
  query_type=SearchType.GRAPH_COMPLETION_CONTEXT_EXTENSION,
122
- query_text="Next to which country is Germany located?",
126
+ query_text="What is the name of the country next to germany",
127
+ save_interaction=True,
123
128
  )
129
+
130
+ await cognee.search(
131
+ query_type=SearchType.FEEDBACK, query_text="This was not the best answer", last_k=1
132
+ )
133
+
124
134
  completion_sum = await cognee.search(
125
135
  query_type=SearchType.GRAPH_SUMMARY_COMPLETION,
126
136
  query_text="Next to which country is Germany located?",
137
+ save_interaction=True,
138
+ )
139
+
140
+ await cognee.search(
141
+ query_type=SearchType.FEEDBACK,
142
+ query_text="This answer was great",
143
+ last_k=1,
127
144
  )
128
145
 
129
146
  for name, completion in [
@@ -141,6 +158,108 @@ async def main():
141
158
  f"{name}: expected 'netherlands' in result, got: {text!r}"
142
159
  )
143
160
 
161
+ graph_engine = await get_graph_engine()
162
+ graph = await graph_engine.get_graph_data()
163
+
164
+ type_counts = Counter(node_data[1].get("type", {}) for node_data in graph[0])
165
+
166
+ edge_type_counts = Counter(edge_type[2] for edge_type in graph[1])
167
+
168
+ # Assert there are exactly 4 CogneeUserInteraction nodes.
169
+ assert type_counts.get("CogneeUserInteraction", 0) == 4, (
170
+ f"Expected exactly four DCogneeUserInteraction nodes, but found {type_counts.get('CogneeUserInteraction', 0)}"
171
+ )
172
+
173
+ # Assert there is exactly two CogneeUserFeedback nodes.
174
+ assert type_counts.get("CogneeUserFeedback", 0) == 2, (
175
+ f"Expected exactly two CogneeUserFeedback nodes, but found {type_counts.get('CogneeUserFeedback', 0)}"
176
+ )
177
+
178
+ # Assert there is exactly two NodeSet.
179
+ assert type_counts.get("NodeSet", 0) == 2, (
180
+ f"Expected exactly two NodeSet nodes, but found {type_counts.get('NodeSet', 0)}"
181
+ )
182
+
183
+ # Assert that there are at least 10 'used_graph_element_to_answer' edges.
184
+ assert edge_type_counts.get("used_graph_element_to_answer", 0) >= 10, (
185
+ f"Expected at least ten 'used_graph_element_to_answer' edges, but found {edge_type_counts.get('used_graph_element_to_answer', 0)}"
186
+ )
187
+
188
+ # Assert that there are exactly 2 'gives_feedback_to' edges.
189
+ assert edge_type_counts.get("gives_feedback_to", 0) == 2, (
190
+ f"Expected exactly two 'gives_feedback_to' edges, but found {edge_type_counts.get('gives_feedback_to', 0)}"
191
+ )
192
+
193
+ # Assert that there are at least 6 'belongs_to_set' edges.
194
+ assert edge_type_counts.get("belongs_to_set", 0) == 6, (
195
+ f"Expected at least six 'belongs_to_set' edges, but found {edge_type_counts.get('belongs_to_set', 0)}"
196
+ )
197
+
198
+ nodes = graph[0]
199
+
200
+ required_fields_user_interaction = {"question", "answer", "context"}
201
+ required_fields_feedback = {"feedback", "sentiment"}
202
+
203
+ for node_id, data in nodes:
204
+ if data.get("type") == "CogneeUserInteraction":
205
+ assert required_fields_user_interaction.issubset(data.keys()), (
206
+ f"Node {node_id} is missing fields: {required_fields_user_interaction - set(data.keys())}"
207
+ )
208
+
209
+ for field in required_fields_user_interaction:
210
+ value = data[field]
211
+ assert isinstance(value, str) and value.strip(), (
212
+ f"Node {node_id} has invalid value for '{field}': {value!r}"
213
+ )
214
+
215
+ if data.get("type") == "CogneeUserFeedback":
216
+ assert required_fields_feedback.issubset(data.keys()), (
217
+ f"Node {node_id} is missing fields: {required_fields_feedback - set(data.keys())}"
218
+ )
219
+
220
+ for field in required_fields_feedback:
221
+ value = data[field]
222
+ assert isinstance(value, str) and value.strip(), (
223
+ f"Node {node_id} has invalid value for '{field}': {value!r}"
224
+ )
225
+
226
+ await cognee.prune.prune_data()
227
+ await cognee.prune.prune_system(metadata=True)
228
+
229
+ await cognee.add(text_1, dataset_name)
230
+
231
+ await cognee.add([text], dataset_name)
232
+
233
+ await cognee.cognify([dataset_name])
234
+
235
+ await cognee.search(
236
+ query_type=SearchType.GRAPH_COMPLETION,
237
+ query_text="Next to which country is Germany located?",
238
+ save_interaction=True,
239
+ )
240
+
241
+ await cognee.search(
242
+ query_type=SearchType.FEEDBACK,
243
+ query_text="This was the best answer I've ever seen",
244
+ last_k=1,
245
+ )
246
+
247
+ await cognee.search(
248
+ query_type=SearchType.FEEDBACK,
249
+ query_text="Wow the correctness of this answer blows my mind",
250
+ last_k=1,
251
+ )
252
+
253
+ graph = await graph_engine.get_graph_data()
254
+
255
+ edges = graph[1]
256
+
257
+ for from_node, to_node, relationship_name, properties in edges:
258
+ if relationship_name == "used_graph_element_to_answer":
259
+ assert properties["feedback_weight"] >= 6, (
260
+ "Feedback weight calculation is not correct, it should be more then 6."
261
+ )
262
+
144
263
 
145
264
  if __name__ == "__main__":
146
265
  import asyncio
@@ -0,0 +1,3 @@
1
+ """
2
+ CLI unit tests package.
3
+ """
@@ -0,0 +1,483 @@
1
+ """
2
+ Tests for individual CLI commands with proper mocking and coroutine handling.
3
+ """
4
+
5
+ import pytest
6
+ import sys
7
+ import argparse
8
+ import asyncio
9
+ from unittest.mock import patch, MagicMock, AsyncMock, ANY
10
+ from cognee.cli.commands.add_command import AddCommand
11
+ from cognee.cli.commands.search_command import SearchCommand
12
+ from cognee.cli.commands.cognify_command import CognifyCommand
13
+ from cognee.cli.commands.delete_command import DeleteCommand
14
+ from cognee.cli.commands.config_command import ConfigCommand
15
+ from cognee.cli.exceptions import CliCommandException, CliCommandInnerException
16
+
17
+
18
+ # Mock asyncio.run to properly handle coroutines
19
+ def _mock_run(coro):
20
+ # Create an event loop and run the coroutine
21
+ loop = asyncio.new_event_loop()
22
+ try:
23
+ return loop.run_until_complete(coro)
24
+ finally:
25
+ loop.close()
26
+
27
+
28
+ class TestAddCommand:
29
+ """Test the AddCommand class"""
30
+
31
+ def test_command_properties(self):
32
+ """Test basic command properties"""
33
+ command = AddCommand()
34
+ assert command.command_string == "add"
35
+ assert "Add data" in command.help_string
36
+ assert command.docs_url is not None
37
+
38
+ def test_configure_parser(self):
39
+ """Test parser configuration"""
40
+ command = AddCommand()
41
+ parser = argparse.ArgumentParser()
42
+
43
+ command.configure_parser(parser)
44
+
45
+ # Check that required arguments are added
46
+ actions = {action.dest: action for action in parser._actions}
47
+ assert "data" in actions
48
+ assert "dataset_name" in actions
49
+
50
+ # Check data argument accepts multiple values
51
+ assert actions["data"].nargs == "+"
52
+
53
+ @patch("cognee.cli.commands.add_command.asyncio.run", side_effect=_mock_run)
54
+ def test_execute_single_item(self, mock_asyncio_run):
55
+ """Test execute with single data item"""
56
+ # Mock the cognee module
57
+ mock_cognee = MagicMock()
58
+ mock_cognee.add = AsyncMock()
59
+
60
+ with patch.dict(sys.modules, {"cognee": mock_cognee}):
61
+ command = AddCommand()
62
+ args = argparse.Namespace(data=["test.txt"], dataset_name="test_dataset")
63
+ command.execute(args)
64
+
65
+ mock_asyncio_run.assert_called_once()
66
+ assert asyncio.iscoroutine(mock_asyncio_run.call_args[0][0])
67
+ mock_cognee.add.assert_awaited_once_with(data="test.txt", dataset_name="test_dataset")
68
+
69
+ @patch("cognee.cli.commands.add_command.asyncio.run", side_effect=_mock_run)
70
+ def test_execute_multiple_items(self, mock_asyncio_run):
71
+ """Test execute with multiple data items"""
72
+ # Mock the cognee module
73
+ mock_cognee = MagicMock()
74
+ mock_cognee.add = AsyncMock()
75
+
76
+ with patch.dict(sys.modules, {"cognee": mock_cognee}):
77
+ command = AddCommand()
78
+ args = argparse.Namespace(data=["test1.txt", "test2.txt"], dataset_name="test_dataset")
79
+ command.execute(args)
80
+
81
+ mock_asyncio_run.assert_called_once()
82
+ assert asyncio.iscoroutine(mock_asyncio_run.call_args[0][0])
83
+ mock_cognee.add.assert_awaited_once_with(
84
+ data=["test1.txt", "test2.txt"], dataset_name="test_dataset"
85
+ )
86
+
87
+ @patch("cognee.cli.commands.add_command.asyncio.run")
88
+ def test_execute_with_exception(self, mock_asyncio_run):
89
+ """Test execute handles exceptions properly"""
90
+ command = AddCommand()
91
+ args = argparse.Namespace(data=["test.txt"], dataset_name="test_dataset")
92
+
93
+ mock_asyncio_run.side_effect = Exception("Test error")
94
+
95
+ with pytest.raises(CliCommandException):
96
+ command.execute(args)
97
+
98
+
99
+ class TestSearchCommand:
100
+ """Test the SearchCommand class"""
101
+
102
+ def test_command_properties(self):
103
+ """Test basic command properties"""
104
+ command = SearchCommand()
105
+ assert command.command_string == "search"
106
+ assert "Search and query" in command.help_string
107
+ assert command.docs_url is not None
108
+
109
+ def test_configure_parser(self):
110
+ """Test parser configuration"""
111
+ command = SearchCommand()
112
+ parser = argparse.ArgumentParser()
113
+
114
+ command.configure_parser(parser)
115
+
116
+ # Check that required arguments are added
117
+ actions = {action.dest: action for action in parser._actions}
118
+ assert "query_text" in actions
119
+ assert "query_type" in actions
120
+ assert "datasets" in actions
121
+ assert "top_k" in actions
122
+ assert "output_format" in actions
123
+
124
+ # Check default values
125
+ assert actions["query_type"].default == "GRAPH_COMPLETION"
126
+ assert actions["top_k"].default == 10
127
+ assert actions["output_format"].default == "pretty"
128
+
129
+ @patch("cognee.cli.commands.search_command.asyncio.run", side_effect=_mock_run)
130
+ def test_execute_basic_search(self, mock_asyncio_run):
131
+ """Test execute with basic search"""
132
+ # Mock the cognee module and SearchType
133
+ mock_cognee = MagicMock()
134
+ mock_cognee.search = AsyncMock(return_value=["result1", "result2"])
135
+ mock_search_type = MagicMock()
136
+ mock_search_type.__getitem__.return_value = "GRAPH_COMPLETION"
137
+
138
+ with patch.dict(sys.modules, {"cognee": mock_cognee}):
139
+ command = SearchCommand()
140
+ args = argparse.Namespace(
141
+ query_text="test query",
142
+ query_type="GRAPH_COMPLETION",
143
+ datasets=None,
144
+ top_k=10,
145
+ system_prompt=None,
146
+ output_format="pretty",
147
+ )
148
+ command.execute(args)
149
+
150
+ mock_asyncio_run.assert_called_once()
151
+ assert asyncio.iscoroutine(mock_asyncio_run.call_args[0][0])
152
+ mock_cognee.search.assert_awaited_once_with(
153
+ query_text="test query",
154
+ query_type=ANY,
155
+ datasets=None,
156
+ top_k=10,
157
+ system_prompt_path="answer_simple_question.txt",
158
+ )
159
+ # verify the enum’s name separately
160
+ called_enum = mock_cognee.search.await_args.kwargs["query_type"]
161
+ assert called_enum.name == "GRAPH_COMPLETION"
162
+
163
+ @patch("cognee.cli.commands.search_command.asyncio.run")
164
+ def test_execute_with_exception(self, mock_asyncio_run):
165
+ """Test execute handles exceptions properly"""
166
+ command = SearchCommand()
167
+ args = argparse.Namespace(
168
+ query_text="test query",
169
+ query_type="GRAPH_COMPLETION",
170
+ datasets=None,
171
+ top_k=10,
172
+ system_prompt=None,
173
+ output_format="pretty",
174
+ )
175
+
176
+ mock_asyncio_run.side_effect = Exception("Search error")
177
+
178
+ with pytest.raises(CliCommandException):
179
+ command.execute(args)
180
+
181
+
182
+ class TestCognifyCommand:
183
+ """Test the CognifyCommand class"""
184
+
185
+ def test_command_properties(self):
186
+ """Test basic command properties"""
187
+ command = CognifyCommand()
188
+ assert command.command_string == "cognify"
189
+ assert "Transform ingested data" in command.help_string
190
+ assert command.docs_url is not None
191
+
192
+ def test_configure_parser(self):
193
+ """Test parser configuration"""
194
+ command = CognifyCommand()
195
+ parser = argparse.ArgumentParser()
196
+
197
+ command.configure_parser(parser)
198
+
199
+ # Check that arguments are added
200
+ actions = {action.dest: action for action in parser._actions}
201
+ assert "datasets" in actions
202
+ assert "chunk_size" in actions
203
+ assert "ontology_file" in actions
204
+ assert "chunker" in actions
205
+ assert "background" in actions
206
+ assert "verbose" in actions
207
+
208
+ # Check default values
209
+ assert actions["chunker"].default == "TextChunker"
210
+
211
+ @patch("cognee.cli.commands.cognify_command.asyncio.run", side_effect=_mock_run)
212
+ def test_execute_basic_cognify(self, mock_asyncio_run):
213
+ """Test execute with basic cognify"""
214
+ # Mock the cognee module
215
+ mock_cognee = MagicMock()
216
+ mock_cognee.cognify = AsyncMock(return_value="success")
217
+
218
+ with patch.dict(sys.modules, {"cognee": mock_cognee}):
219
+ command = CognifyCommand()
220
+ args = argparse.Namespace(
221
+ datasets=None,
222
+ chunk_size=None,
223
+ ontology_file=None,
224
+ chunker="TextChunker",
225
+ background=False,
226
+ verbose=False,
227
+ )
228
+ command.execute(args)
229
+
230
+ mock_asyncio_run.assert_called_once()
231
+ assert asyncio.iscoroutine(mock_asyncio_run.call_args[0][0])
232
+ from cognee.modules.chunking.TextChunker import TextChunker
233
+
234
+ mock_cognee.cognify.assert_awaited_once_with(
235
+ datasets=None,
236
+ chunk_size=None,
237
+ ontology_file_path=None,
238
+ chunker=TextChunker,
239
+ run_in_background=False,
240
+ )
241
+
242
+ @patch("cognee.cli.commands.cognify_command.asyncio.run")
243
+ def test_execute_with_exception(self, mock_asyncio_run):
244
+ """Test execute handles exceptions properly"""
245
+ command = CognifyCommand()
246
+ args = argparse.Namespace(
247
+ datasets=None,
248
+ chunk_size=None,
249
+ ontology_file=None,
250
+ chunker="TextChunker",
251
+ background=False,
252
+ verbose=False,
253
+ )
254
+
255
+ mock_asyncio_run.side_effect = Exception("Cognify error")
256
+
257
+ with pytest.raises(CliCommandException):
258
+ command.execute(args)
259
+
260
+
261
+ class TestDeleteCommand:
262
+ """Test the DeleteCommand class"""
263
+
264
+ def test_command_properties(self):
265
+ """Test basic command properties"""
266
+ command = DeleteCommand()
267
+ assert command.command_string == "delete"
268
+ assert "Delete data" in command.help_string
269
+ assert command.docs_url is not None
270
+
271
+ def test_configure_parser(self):
272
+ """Test parser configuration"""
273
+ command = DeleteCommand()
274
+ parser = argparse.ArgumentParser()
275
+
276
+ command.configure_parser(parser)
277
+
278
+ # Check that arguments are added
279
+ actions = {action.dest: action for action in parser._actions}
280
+ assert "dataset_name" in actions
281
+ assert "user_id" in actions
282
+ assert "all" in actions
283
+ assert "force" in actions
284
+
285
+ @patch("cognee.cli.commands.delete_command.fmt.confirm")
286
+ @patch("cognee.cli.commands.delete_command.asyncio.run", side_effect=_mock_run)
287
+ def test_execute_delete_dataset_with_confirmation(self, mock_asyncio_run, mock_confirm):
288
+ """Test execute delete dataset with user confirmation"""
289
+ # Mock the cognee module
290
+ mock_cognee = MagicMock()
291
+ mock_cognee.delete = AsyncMock()
292
+
293
+ with patch.dict(sys.modules, {"cognee": mock_cognee}):
294
+ command = DeleteCommand()
295
+ args = argparse.Namespace(
296
+ dataset_name="test_dataset", user_id=None, all=False, force=False
297
+ )
298
+
299
+ mock_confirm.return_value = True
300
+
301
+ command.execute(args)
302
+
303
+ mock_confirm.assert_called_once_with(f"Delete dataset '{args.dataset_name}'?")
304
+ mock_asyncio_run.assert_called_once()
305
+ assert asyncio.iscoroutine(mock_asyncio_run.call_args[0][0])
306
+ mock_cognee.delete.assert_awaited_once_with(dataset_name="test_dataset", user_id=None)
307
+
308
+ @patch("cognee.cli.commands.delete_command.fmt.confirm")
309
+ def test_execute_delete_cancelled(self, mock_confirm):
310
+ """Test execute when user cancels deletion"""
311
+ command = DeleteCommand()
312
+ args = argparse.Namespace(dataset_name="test_dataset", user_id=None, all=False, force=False)
313
+
314
+ mock_confirm.return_value = False
315
+
316
+ # Should not raise exception, just return
317
+ command.execute(args)
318
+
319
+ mock_confirm.assert_called_once_with(f"Delete dataset '{args.dataset_name}'?")
320
+
321
+ @patch("cognee.cli.commands.delete_command.asyncio.run", side_effect=_mock_run)
322
+ def test_execute_delete_forced(self, mock_asyncio_run):
323
+ """Test execute delete with force flag"""
324
+ # Mock the cognee module
325
+ mock_cognee = MagicMock()
326
+ mock_cognee.delete = AsyncMock()
327
+
328
+ with patch.dict(sys.modules, {"cognee": mock_cognee}):
329
+ command = DeleteCommand()
330
+ args = argparse.Namespace(
331
+ dataset_name="test_dataset", user_id=None, all=False, force=True
332
+ )
333
+
334
+ command.execute(args)
335
+
336
+ mock_asyncio_run.assert_called_once()
337
+ assert asyncio.iscoroutine(mock_asyncio_run.call_args[0][0])
338
+ mock_cognee.delete.assert_awaited_once_with(dataset_name="test_dataset", user_id=None)
339
+
340
+ def test_execute_no_delete_target(self):
341
+ """Test execute when no delete target is specified"""
342
+ command = DeleteCommand()
343
+ args = argparse.Namespace(dataset_name=None, user_id=None, all=False, force=False)
344
+
345
+ # Should not raise exception, just return with error message
346
+ command.execute(args)
347
+
348
+ @patch("cognee.cli.commands.delete_command.asyncio.run")
349
+ def test_execute_with_exception(self, mock_asyncio_run):
350
+ """Test execute handles exceptions properly"""
351
+ command = DeleteCommand()
352
+ args = argparse.Namespace(dataset_name="test_dataset", user_id=None, all=False, force=True)
353
+
354
+ mock_asyncio_run.side_effect = Exception("Delete error")
355
+
356
+ with pytest.raises(CliCommandException):
357
+ command.execute(args)
358
+
359
+
360
+ class TestConfigCommand:
361
+ """Test the ConfigCommand class"""
362
+
363
+ def test_command_properties(self):
364
+ """Test basic command properties"""
365
+ command = ConfigCommand()
366
+ assert command.command_string == "config"
367
+ assert "Manage cognee configuration" in command.help_string
368
+ assert command.docs_url is not None
369
+
370
+ def test_configure_parser(self):
371
+ """Test parser configuration"""
372
+ command = ConfigCommand()
373
+ parser = argparse.ArgumentParser()
374
+
375
+ command.configure_parser(parser)
376
+
377
+ # Check that subparsers are created
378
+ subparsers_actions = [
379
+ action for action in parser._actions if isinstance(action, argparse._SubParsersAction)
380
+ ]
381
+ assert len(subparsers_actions) == 1
382
+
383
+ subparsers = subparsers_actions[0]
384
+ assert "get" in subparsers.choices
385
+ assert "set" in subparsers.choices
386
+ assert "list" in subparsers.choices
387
+ assert "unset" in subparsers.choices
388
+ assert "reset" in subparsers.choices
389
+
390
+ def test_execute_no_action(self):
391
+ """Test execute when no config action is provided"""
392
+ command = ConfigCommand()
393
+ args = argparse.Namespace()
394
+
395
+ # Should not raise exception, just return with error message
396
+ command.execute(args)
397
+
398
+ @patch("builtins.__import__")
399
+ def test_execute_get_action(self, mock_import):
400
+ """Test execute get action"""
401
+ # Mock the cognee module
402
+ mock_cognee = MagicMock()
403
+ mock_cognee.config.get = MagicMock(return_value="openai")
404
+ mock_import.return_value = mock_cognee
405
+
406
+ command = ConfigCommand()
407
+ args = argparse.Namespace(config_action="get", key="llm_provider")
408
+
409
+ command.execute(args)
410
+
411
+ @patch("builtins.__import__")
412
+ def test_execute_set_action(self, mock_import):
413
+ """Test execute set action"""
414
+ # Mock the cognee module
415
+ mock_cognee = MagicMock()
416
+ mock_cognee.config.set = MagicMock()
417
+ mock_import.return_value = mock_cognee
418
+
419
+ command = ConfigCommand()
420
+ args = argparse.Namespace(config_action="set", key="llm_provider", value="anthropic")
421
+
422
+ command.execute(args)
423
+
424
+ @patch("builtins.__import__")
425
+ def test_execute_set_action_json_value(self, mock_import):
426
+ """Test execute set action with JSON value"""
427
+ # Mock the cognee module
428
+ mock_cognee = MagicMock()
429
+ mock_cognee.config.set = MagicMock()
430
+ mock_import.return_value = mock_cognee
431
+
432
+ command = ConfigCommand()
433
+ args = argparse.Namespace(config_action="set", key="chunk_size", value="1024")
434
+
435
+ command.execute(args)
436
+
437
+ def test_execute_list_action(self):
438
+ """Test execute list action"""
439
+ command = ConfigCommand()
440
+ args = argparse.Namespace(config_action="list")
441
+
442
+ # Should not raise exception
443
+ command.execute(args)
444
+
445
+ @patch("cognee.cli.commands.config_command.fmt.confirm")
446
+ def test_execute_unset_action(self, mock_confirm):
447
+ """Test execute unset action"""
448
+ # Mock the cognee module
449
+ mock_cognee = MagicMock()
450
+ mock_cognee.config.set_llm_provider = MagicMock()
451
+
452
+ with patch.dict(sys.modules, {"cognee": mock_cognee}):
453
+ command = ConfigCommand()
454
+ args = argparse.Namespace(config_action="unset", key="llm_provider", force=False)
455
+
456
+ mock_confirm.return_value = True
457
+
458
+ command.execute(args)
459
+
460
+ mock_confirm.assert_called_once()
461
+
462
+ @patch("cognee.cli.commands.config_command.fmt.confirm")
463
+ def test_execute_reset_action(self, mock_confirm):
464
+ """Test execute reset action"""
465
+ command = ConfigCommand()
466
+ args = argparse.Namespace(config_action="reset", force=False)
467
+
468
+ mock_confirm.return_value = True
469
+
470
+ # Should not raise exception
471
+ command.execute(args)
472
+
473
+ mock_confirm.assert_called_once()
474
+
475
+ def test_execute_with_exception(self):
476
+ """Test execute handles exceptions properly"""
477
+ # Test with an invalid action that will cause an exception in the main execute method
478
+ command = ConfigCommand()
479
+ args = argparse.Namespace(config_action="invalid_action")
480
+
481
+ # This should not raise CliCommandException, just handle it gracefully
482
+ # The config command handles unknown actions by showing an error message
483
+ command.execute(args)