cognee 0.5.1.dev0__py3-none-any.whl → 0.5.2.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cognee/__init__.py +2 -0
- cognee/alembic/README +1 -0
- cognee/alembic/env.py +107 -0
- cognee/alembic/script.py.mako +26 -0
- cognee/alembic/versions/1a58b986e6e1_enable_delete_for_old_tutorial_notebooks.py +52 -0
- cognee/alembic/versions/1d0bb7fede17_add_pipeline_run_status.py +33 -0
- cognee/alembic/versions/1daae0df1866_incremental_loading.py +48 -0
- cognee/alembic/versions/211ab850ef3d_add_sync_operations_table.py +118 -0
- cognee/alembic/versions/45957f0a9849_add_notebook_table.py +46 -0
- cognee/alembic/versions/46a6ce2bd2b2_expand_dataset_database_with_json_.py +333 -0
- cognee/alembic/versions/482cd6517ce4_add_default_user.py +30 -0
- cognee/alembic/versions/76625596c5c3_expand_dataset_database_for_multi_user.py +98 -0
- cognee/alembic/versions/8057ae7329c2_initial_migration.py +25 -0
- cognee/alembic/versions/9e7a3cb85175_loader_separation.py +104 -0
- cognee/alembic/versions/a1b2c3d4e5f6_add_label_column_to_data.py +38 -0
- cognee/alembic/versions/ab7e313804ae_permission_system_rework.py +236 -0
- cognee/alembic/versions/b9274c27a25a_kuzu_11_migration.py +75 -0
- cognee/alembic/versions/c946955da633_multi_tenant_support.py +137 -0
- cognee/alembic/versions/e1ec1dcb50b6_add_last_accessed_to_data.py +51 -0
- cognee/alembic/versions/e4ebee1091e7_expand_data_model_info.py +140 -0
- cognee/alembic.ini +117 -0
- cognee/api/v1/add/routers/get_add_router.py +2 -0
- cognee/api/v1/cognify/cognify.py +11 -6
- cognee/api/v1/cognify/routers/get_cognify_router.py +8 -0
- cognee/api/v1/config/config.py +60 -0
- cognee/api/v1/datasets/routers/get_datasets_router.py +45 -3
- cognee/api/v1/memify/routers/get_memify_router.py +2 -0
- cognee/api/v1/search/routers/get_search_router.py +21 -6
- cognee/api/v1/search/search.py +25 -5
- cognee/api/v1/sync/routers/get_sync_router.py +3 -3
- cognee/cli/commands/add_command.py +1 -1
- cognee/cli/commands/cognify_command.py +6 -0
- cognee/cli/commands/config_command.py +1 -1
- cognee/context_global_variables.py +5 -1
- cognee/eval_framework/answer_generation/answer_generation_executor.py +7 -8
- cognee/infrastructure/databases/cache/cache_db_interface.py +38 -1
- cognee/infrastructure/databases/cache/config.py +6 -0
- cognee/infrastructure/databases/cache/fscache/FsCacheAdapter.py +21 -0
- cognee/infrastructure/databases/cache/get_cache_engine.py +9 -3
- cognee/infrastructure/databases/cache/redis/RedisAdapter.py +60 -1
- cognee/infrastructure/databases/dataset_database_handler/supported_dataset_database_handlers.py +7 -0
- cognee/infrastructure/databases/graph/get_graph_engine.py +29 -1
- cognee/infrastructure/databases/graph/neo4j_driver/Neo4jAuraDevDatasetDatabaseHandler.py +62 -27
- cognee/infrastructure/databases/hybrid/neptune_analytics/NeptuneAnalyticsAdapter.py +17 -4
- cognee/infrastructure/databases/relational/sqlalchemy/SqlAlchemyAdapter.py +2 -1
- cognee/infrastructure/databases/vector/chromadb/ChromaDBAdapter.py +2 -0
- cognee/infrastructure/databases/vector/config.py +6 -0
- cognee/infrastructure/databases/vector/create_vector_engine.py +69 -22
- cognee/infrastructure/databases/vector/embeddings/LiteLLMEmbeddingEngine.py +64 -9
- cognee/infrastructure/databases/vector/embeddings/OllamaEmbeddingEngine.py +13 -2
- cognee/infrastructure/databases/vector/lancedb/LanceDBAdapter.py +16 -3
- cognee/infrastructure/databases/vector/models/ScoredResult.py +3 -3
- cognee/infrastructure/databases/vector/pgvector/PGVectorAdapter.py +16 -3
- cognee/infrastructure/databases/vector/pgvector/PGVectorDatasetDatabaseHandler.py +86 -0
- cognee/infrastructure/databases/vector/pgvector/create_db_and_tables.py +81 -2
- cognee/infrastructure/databases/vector/vector_db_interface.py +8 -0
- cognee/infrastructure/files/utils/get_data_file_path.py +33 -27
- cognee/infrastructure/llm/prompts/extract_query_time.txt +1 -1
- cognee/infrastructure/llm/prompts/generate_event_entity_prompt.txt +1 -1
- cognee/infrastructure/llm/prompts/generate_event_graph_prompt.txt +1 -1
- cognee/infrastructure/llm/prompts/generate_graph_prompt.txt +2 -2
- cognee/infrastructure/llm/prompts/generate_graph_prompt_guided.txt +1 -1
- cognee/infrastructure/llm/prompts/generate_graph_prompt_oneshot.txt +2 -2
- cognee/infrastructure/llm/prompts/generate_graph_prompt_simple.txt +1 -1
- cognee/infrastructure/llm/prompts/generate_graph_prompt_strict.txt +1 -1
- cognee/infrastructure/llm/prompts/search_type_selector_prompt.txt +6 -6
- cognee/infrastructure/llm/prompts/test.txt +1 -1
- cognee/infrastructure/llm/prompts/translate_content.txt +19 -0
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/get_llm_client.py +24 -0
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/llama_cpp/adapter.py +191 -0
- cognee/modules/chunking/models/DocumentChunk.py +0 -1
- cognee/modules/cognify/config.py +2 -0
- cognee/modules/data/models/Data.py +1 -0
- cognee/modules/engine/models/Entity.py +0 -1
- cognee/modules/engine/operations/setup.py +6 -0
- cognee/modules/graph/cognee_graph/CogneeGraph.py +150 -37
- cognee/modules/graph/cognee_graph/CogneeGraphElements.py +48 -2
- cognee/modules/graph/utils/__init__.py +1 -0
- cognee/modules/graph/utils/get_entity_nodes_from_triplets.py +12 -0
- cognee/modules/notebooks/methods/__init__.py +1 -0
- cognee/modules/notebooks/methods/create_notebook.py +0 -34
- cognee/modules/notebooks/methods/create_tutorial_notebooks.py +191 -0
- cognee/modules/notebooks/methods/get_notebooks.py +12 -8
- cognee/modules/notebooks/tutorials/cognee-basics/cell-1.md +3 -0
- cognee/modules/notebooks/tutorials/cognee-basics/cell-2.md +10 -0
- cognee/modules/notebooks/tutorials/cognee-basics/cell-3.md +7 -0
- cognee/modules/notebooks/tutorials/cognee-basics/cell-4.py +28 -0
- cognee/modules/notebooks/tutorials/cognee-basics/cell-5.py +3 -0
- cognee/modules/notebooks/tutorials/cognee-basics/cell-6.py +9 -0
- cognee/modules/notebooks/tutorials/cognee-basics/cell-7.py +17 -0
- cognee/modules/notebooks/tutorials/cognee-basics/config.json +4 -0
- cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-1.md +3 -0
- cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-10.md +3 -0
- cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-11.md +3 -0
- cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-12.py +3 -0
- cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-13.md +7 -0
- cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-14.py +6 -0
- cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-15.md +3 -0
- cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-16.py +7 -0
- cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-2.md +9 -0
- cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-3.md +7 -0
- cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-4.md +9 -0
- cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-5.md +5 -0
- cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-6.py +13 -0
- cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-7.md +3 -0
- cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-8.md +3 -0
- cognee/modules/notebooks/tutorials/python-development-with-cognee/cell-9.py +31 -0
- cognee/modules/notebooks/tutorials/python-development-with-cognee/config.json +4 -0
- cognee/modules/notebooks/tutorials/python-development-with-cognee/data/copilot_conversations.json +107 -0
- cognee/modules/notebooks/tutorials/python-development-with-cognee/data/guido_contributions.json +976 -0
- cognee/modules/notebooks/tutorials/python-development-with-cognee/data/my_developer_rules.md +79 -0
- cognee/modules/notebooks/tutorials/python-development-with-cognee/data/pep_style_guide.md +74 -0
- cognee/modules/notebooks/tutorials/python-development-with-cognee/data/zen_principles.md +74 -0
- cognee/modules/retrieval/EntityCompletionRetriever.py +51 -38
- cognee/modules/retrieval/__init__.py +0 -1
- cognee/modules/retrieval/base_retriever.py +66 -10
- cognee/modules/retrieval/chunks_retriever.py +57 -49
- cognee/modules/retrieval/coding_rules_retriever.py +12 -5
- cognee/modules/retrieval/completion_retriever.py +29 -28
- cognee/modules/retrieval/cypher_search_retriever.py +25 -20
- cognee/modules/retrieval/graph_completion_context_extension_retriever.py +42 -46
- cognee/modules/retrieval/graph_completion_cot_retriever.py +68 -51
- cognee/modules/retrieval/graph_completion_retriever.py +78 -63
- cognee/modules/retrieval/graph_summary_completion_retriever.py +2 -0
- cognee/modules/retrieval/lexical_retriever.py +34 -12
- cognee/modules/retrieval/natural_language_retriever.py +18 -15
- cognee/modules/retrieval/summaries_retriever.py +51 -34
- cognee/modules/retrieval/temporal_retriever.py +59 -49
- cognee/modules/retrieval/triplet_retriever.py +31 -32
- cognee/modules/retrieval/utils/access_tracking.py +88 -0
- cognee/modules/retrieval/utils/brute_force_triplet_search.py +99 -85
- cognee/modules/retrieval/utils/node_edge_vector_search.py +174 -0
- cognee/modules/search/methods/__init__.py +1 -0
- cognee/modules/search/methods/get_retriever_output.py +53 -0
- cognee/modules/search/methods/get_search_type_retriever_instance.py +252 -0
- cognee/modules/search/methods/search.py +90 -215
- cognee/modules/search/models/SearchResultPayload.py +67 -0
- cognee/modules/search/types/SearchResult.py +1 -8
- cognee/modules/search/types/SearchType.py +1 -2
- cognee/modules/search/types/__init__.py +1 -1
- cognee/modules/search/utils/__init__.py +1 -2
- cognee/modules/search/utils/transform_insights_to_graph.py +2 -2
- cognee/modules/search/utils/{transform_context_to_graph.py → transform_triplets_to_graph.py} +2 -2
- cognee/modules/users/authentication/default/default_transport.py +11 -1
- cognee/modules/users/authentication/get_api_auth_backend.py +2 -1
- cognee/modules/users/authentication/get_client_auth_backend.py +2 -1
- cognee/modules/users/methods/create_user.py +0 -9
- cognee/modules/users/permissions/methods/has_user_management_permission.py +29 -0
- cognee/modules/visualization/cognee_network_visualization.py +1 -1
- cognee/run_migrations.py +48 -0
- cognee/shared/exceptions/__init__.py +1 -3
- cognee/shared/exceptions/exceptions.py +11 -1
- cognee/shared/usage_logger.py +332 -0
- cognee/shared/utils.py +12 -5
- cognee/tasks/cleanup/cleanup_unused_data.py +172 -0
- cognee/tasks/memify/extract_usage_frequency.py +613 -0
- cognee/tasks/summarization/models.py +0 -2
- cognee/tasks/temporal_graph/__init__.py +0 -1
- cognee/tasks/translation/__init__.py +96 -0
- cognee/tasks/translation/config.py +110 -0
- cognee/tasks/translation/detect_language.py +190 -0
- cognee/tasks/translation/exceptions.py +62 -0
- cognee/tasks/translation/models.py +72 -0
- cognee/tasks/translation/providers/__init__.py +44 -0
- cognee/tasks/translation/providers/azure_provider.py +192 -0
- cognee/tasks/translation/providers/base.py +85 -0
- cognee/tasks/translation/providers/google_provider.py +158 -0
- cognee/tasks/translation/providers/llm_provider.py +143 -0
- cognee/tasks/translation/translate_content.py +282 -0
- cognee/tasks/web_scraper/default_url_crawler.py +6 -2
- cognee/tests/cli_tests/cli_unit_tests/test_cli_commands.py +1 -0
- cognee/tests/cli_tests/cli_unit_tests/test_cli_edge_cases.py +3 -0
- cognee/tests/integration/retrieval/test_brute_force_triplet_search_with_cognify.py +62 -0
- cognee/tests/integration/retrieval/test_chunks_retriever.py +115 -16
- cognee/tests/integration/retrieval/test_graph_completion_retriever.py +13 -5
- cognee/tests/integration/retrieval/test_graph_completion_retriever_context_extension.py +22 -20
- cognee/tests/integration/retrieval/test_graph_completion_retriever_cot.py +23 -24
- cognee/tests/integration/retrieval/test_rag_completion_retriever.py +70 -5
- cognee/tests/integration/retrieval/test_structured_output.py +62 -18
- cognee/tests/integration/retrieval/test_summaries_retriever.py +20 -9
- cognee/tests/integration/retrieval/test_temporal_retriever.py +38 -8
- cognee/tests/integration/retrieval/test_triplet_retriever.py +13 -4
- cognee/tests/integration/shared/test_usage_logger_integration.py +255 -0
- cognee/tests/tasks/translation/README.md +147 -0
- cognee/tests/tasks/translation/__init__.py +1 -0
- cognee/tests/tasks/translation/config_test.py +93 -0
- cognee/tests/tasks/translation/detect_language_test.py +118 -0
- cognee/tests/tasks/translation/providers_test.py +151 -0
- cognee/tests/tasks/translation/translate_content_test.py +213 -0
- cognee/tests/test_chromadb.py +1 -1
- cognee/tests/test_cleanup_unused_data.py +165 -0
- cognee/tests/test_delete_by_id.py +6 -6
- cognee/tests/test_extract_usage_frequency.py +308 -0
- cognee/tests/test_kuzu.py +17 -7
- cognee/tests/test_lancedb.py +3 -1
- cognee/tests/test_library.py +1 -1
- cognee/tests/test_neo4j.py +17 -7
- cognee/tests/test_neptune_analytics_vector.py +3 -1
- cognee/tests/test_permissions.py +172 -187
- cognee/tests/test_pgvector.py +3 -1
- cognee/tests/test_relational_db_migration.py +15 -1
- cognee/tests/test_remote_kuzu.py +3 -1
- cognee/tests/test_s3_file_storage.py +1 -1
- cognee/tests/test_search_db.py +97 -110
- cognee/tests/test_usage_logger_e2e.py +268 -0
- cognee/tests/unit/api/test_get_raw_data_endpoint.py +206 -0
- cognee/tests/unit/eval_framework/answer_generation_test.py +4 -3
- cognee/tests/unit/infrastructure/databases/cache/test_cache_config.py +2 -0
- cognee/tests/unit/modules/graph/cognee_graph_elements_test.py +42 -2
- cognee/tests/unit/modules/graph/cognee_graph_test.py +329 -31
- cognee/tests/unit/modules/retrieval/chunks_retriever_test.py +31 -59
- cognee/tests/unit/modules/retrieval/graph_completion_retriever_context_extension_test.py +70 -33
- cognee/tests/unit/modules/retrieval/graph_completion_retriever_cot_test.py +72 -52
- cognee/tests/unit/modules/retrieval/graph_completion_retriever_test.py +27 -33
- cognee/tests/unit/modules/retrieval/rag_completion_retriever_test.py +28 -15
- cognee/tests/unit/modules/retrieval/summaries_retriever_test.py +37 -42
- cognee/tests/unit/modules/retrieval/temporal_retriever_test.py +48 -64
- cognee/tests/unit/modules/retrieval/test_brute_force_triplet_search.py +263 -24
- cognee/tests/unit/modules/retrieval/test_node_edge_vector_search.py +273 -0
- cognee/tests/unit/modules/retrieval/triplet_retriever_test.py +30 -16
- cognee/tests/unit/modules/search/test_get_search_type_retriever_instance.py +125 -0
- cognee/tests/unit/modules/search/test_search.py +176 -0
- cognee/tests/unit/modules/search/test_search_prepare_search_result_contract.py +190 -0
- cognee/tests/unit/modules/users/test_tutorial_notebook_creation.py +511 -297
- cognee/tests/unit/shared/test_usage_logger.py +241 -0
- cognee/tests/unit/users/permissions/test_has_user_management_permission.py +46 -0
- {cognee-0.5.1.dev0.dist-info → cognee-0.5.2.dev0.dist-info}/METADATA +17 -10
- {cognee-0.5.1.dev0.dist-info → cognee-0.5.2.dev0.dist-info}/RECORD +232 -144
- cognee/api/.env.example +0 -5
- cognee/modules/retrieval/base_graph_retriever.py +0 -24
- cognee/modules/search/methods/get_search_type_tools.py +0 -223
- cognee/modules/search/methods/no_access_control_search.py +0 -62
- cognee/modules/search/utils/prepare_search_result.py +0 -63
- cognee/tests/test_feedback_enrichment.py +0 -174
- {cognee-0.5.1.dev0.dist-info → cognee-0.5.2.dev0.dist-info}/WHEEL +0 -0
- {cognee-0.5.1.dev0.dist-info → cognee-0.5.2.dev0.dist-info}/entry_points.txt +0 -0
- {cognee-0.5.1.dev0.dist-info → cognee-0.5.2.dev0.dist-info}/licenses/LICENSE +0 -0
- {cognee-0.5.1.dev0.dist-info → cognee-0.5.2.dev0.dist-info}/licenses/NOTICE.md +0 -0
cognee/tests/test_search_db.py
CHANGED
|
@@ -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
|
|
52
|
-
from cognee.infrastructure.databases.vector.create_vector_engine import
|
|
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
|
-
|
|
58
|
-
|
|
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
|
|
132
|
-
"""
|
|
133
|
-
|
|
134
|
-
await
|
|
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",
|
|
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()
|
|
160
|
-
"graph_completion_cot": await
|
|
161
|
-
|
|
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
|
-
"
|
|
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)
|
|
168
|
-
"summaries": await SummariesRetriever(top_k=5)
|
|
169
|
-
"rag_completion": await CompletionRetriever(top_k=3)
|
|
170
|
-
"temporal": await TemporalRetriever(top_k=5)
|
|
171
|
-
"triplet": await TripletRetriever()
|
|
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,
|
|
314
|
-
assert ctx, f"{name}: Context should not be empty"
|
|
315
|
-
|
|
316
|
-
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,
|
|
327
|
-
assert chunks_ctx, "chunks: Context should not be empty"
|
|
328
|
-
chunks_text =
|
|
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,
|
|
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
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
-
|
|
391
|
-
text = wrapper["search_result"][0]
|
|
391
|
+
result_payload = wrapper.get("text_result")
|
|
392
392
|
else:
|
|
393
|
-
|
|
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
|
-
|
|
396
|
-
assert
|
|
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
|
-
|
|
405
|
-
assert isinstance(
|
|
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
|
-
|
|
408
|
-
|
|
409
|
-
|
|
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(
|
|
412
|
-
|
|
413
|
-
assert
|
|
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("
|
|
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("
|
|
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
|