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.
- cognee/__main__.py +4 -0
- cognee/api/v1/add/add.py +18 -11
- cognee/api/v1/cognify/code_graph_pipeline.py +7 -1
- cognee/api/v1/cognify/cognify.py +22 -115
- cognee/api/v1/cognify/routers/get_cognify_router.py +11 -3
- cognee/api/v1/config/config.py +5 -13
- cognee/api/v1/datasets/routers/get_datasets_router.py +2 -2
- cognee/api/v1/delete/delete.py +1 -1
- cognee/api/v1/exceptions/__init__.py +13 -0
- cognee/api/v1/{delete → exceptions}/exceptions.py +15 -12
- cognee/api/v1/responses/default_tools.py +4 -0
- cognee/api/v1/responses/dispatch_function.py +6 -1
- cognee/api/v1/responses/models.py +1 -1
- cognee/api/v1/search/search.py +6 -7
- cognee/cli/__init__.py +10 -0
- cognee/cli/_cognee.py +180 -0
- cognee/cli/commands/__init__.py +1 -0
- cognee/cli/commands/add_command.py +80 -0
- cognee/cli/commands/cognify_command.py +128 -0
- cognee/cli/commands/config_command.py +225 -0
- cognee/cli/commands/delete_command.py +80 -0
- cognee/cli/commands/search_command.py +149 -0
- cognee/cli/config.py +33 -0
- cognee/cli/debug.py +21 -0
- cognee/cli/echo.py +45 -0
- cognee/cli/exceptions.py +23 -0
- cognee/cli/minimal_cli.py +97 -0
- cognee/cli/reference.py +26 -0
- cognee/cli/suppress_logging.py +12 -0
- cognee/eval_framework/corpus_builder/corpus_builder_executor.py +2 -2
- cognee/eval_framework/eval_config.py +1 -1
- cognee/exceptions/__init__.py +5 -5
- cognee/exceptions/exceptions.py +37 -17
- cognee/infrastructure/data/exceptions/__init__.py +7 -0
- cognee/infrastructure/data/exceptions/exceptions.py +22 -0
- cognee/infrastructure/data/utils/extract_keywords.py +3 -3
- cognee/infrastructure/databases/exceptions/__init__.py +3 -0
- cognee/infrastructure/databases/exceptions/exceptions.py +57 -9
- cognee/infrastructure/databases/graph/get_graph_engine.py +4 -9
- cognee/infrastructure/databases/graph/kuzu/adapter.py +64 -2
- cognee/infrastructure/databases/graph/neo4j_driver/adapter.py +49 -0
- cognee/infrastructure/databases/graph/neptune_driver/exceptions.py +15 -10
- cognee/infrastructure/databases/hybrid/falkordb/FalkorDBAdapter.py +2 -2
- cognee/infrastructure/databases/hybrid/neptune_analytics/NeptuneAnalyticsAdapter.py +4 -5
- cognee/infrastructure/databases/vector/chromadb/ChromaDBAdapter.py +2 -2
- cognee/infrastructure/databases/vector/embeddings/FastembedEmbeddingEngine.py +5 -3
- cognee/infrastructure/databases/vector/embeddings/LiteLLMEmbeddingEngine.py +17 -8
- cognee/infrastructure/databases/vector/embeddings/OllamaEmbeddingEngine.py +5 -5
- cognee/infrastructure/databases/vector/embeddings/config.py +2 -2
- cognee/infrastructure/databases/vector/embeddings/get_embedding_engine.py +6 -6
- cognee/infrastructure/databases/vector/exceptions/exceptions.py +3 -3
- cognee/infrastructure/databases/vector/lancedb/LanceDBAdapter.py +2 -2
- cognee/infrastructure/databases/vector/pgvector/PGVectorAdapter.py +4 -3
- cognee/infrastructure/files/utils/get_data_file_path.py +14 -9
- cognee/infrastructure/files/utils/get_file_metadata.py +2 -1
- cognee/infrastructure/llm/LLMGateway.py +14 -5
- cognee/infrastructure/llm/config.py +5 -5
- cognee/infrastructure/llm/exceptions.py +30 -2
- cognee/infrastructure/llm/structured_output_framework/baml/baml_src/extraction/knowledge_graph/extract_content_graph.py +16 -5
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/extraction/knowledge_graph/extract_content_graph.py +19 -15
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/anthropic/adapter.py +5 -5
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/gemini/adapter.py +6 -6
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/generic_llm_api/adapter.py +2 -2
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/get_llm_client.py +24 -15
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/ollama/adapter.py +6 -4
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/openai/adapter.py +9 -7
- cognee/infrastructure/llm/tokenizer/Gemini/adapter.py +2 -2
- cognee/infrastructure/llm/tokenizer/HuggingFace/adapter.py +3 -3
- cognee/infrastructure/llm/tokenizer/Mistral/adapter.py +3 -3
- cognee/infrastructure/llm/tokenizer/TikToken/adapter.py +6 -6
- cognee/infrastructure/llm/utils.py +7 -7
- cognee/modules/data/exceptions/exceptions.py +18 -5
- cognee/modules/data/methods/__init__.py +2 -0
- cognee/modules/data/methods/create_authorized_dataset.py +19 -0
- cognee/modules/data/methods/delete_data.py +2 -4
- cognee/modules/data/methods/get_authorized_dataset.py +11 -5
- cognee/modules/data/methods/get_authorized_dataset_by_name.py +16 -0
- cognee/modules/data/methods/load_or_create_datasets.py +2 -20
- cognee/modules/data/processing/document_types/exceptions/exceptions.py +2 -2
- cognee/modules/graph/cognee_graph/CogneeGraph.py +6 -4
- cognee/modules/graph/cognee_graph/CogneeGraphElements.py +5 -10
- cognee/modules/graph/exceptions/__init__.py +2 -0
- cognee/modules/graph/exceptions/exceptions.py +25 -3
- cognee/modules/graph/methods/get_formatted_graph_data.py +3 -2
- cognee/modules/ingestion/exceptions/exceptions.py +2 -2
- cognee/modules/ontology/exceptions/exceptions.py +4 -4
- cognee/modules/pipelines/__init__.py +1 -1
- cognee/modules/pipelines/exceptions/exceptions.py +2 -2
- cognee/modules/pipelines/exceptions/tasks.py +18 -0
- cognee/modules/pipelines/layers/__init__.py +1 -0
- cognee/modules/pipelines/layers/check_pipeline_run_qualification.py +59 -0
- cognee/modules/pipelines/layers/pipeline_execution_mode.py +127 -0
- cognee/modules/pipelines/layers/reset_dataset_pipeline_run_status.py +12 -0
- cognee/modules/pipelines/layers/resolve_authorized_user_dataset.py +34 -0
- cognee/modules/pipelines/layers/resolve_authorized_user_datasets.py +55 -0
- cognee/modules/pipelines/layers/setup_and_check_environment.py +41 -0
- cognee/modules/pipelines/layers/validate_pipeline_tasks.py +20 -0
- cognee/modules/pipelines/methods/__init__.py +2 -0
- cognee/modules/pipelines/methods/get_pipeline_runs_by_dataset.py +34 -0
- cognee/modules/pipelines/methods/reset_pipeline_run_status.py +16 -0
- cognee/modules/pipelines/operations/__init__.py +0 -1
- cognee/modules/pipelines/operations/log_pipeline_run_initiated.py +1 -1
- cognee/modules/pipelines/operations/pipeline.py +23 -138
- cognee/modules/retrieval/base_feedback.py +11 -0
- cognee/modules/retrieval/cypher_search_retriever.py +1 -9
- cognee/modules/retrieval/exceptions/exceptions.py +12 -6
- cognee/modules/retrieval/graph_completion_context_extension_retriever.py +9 -2
- cognee/modules/retrieval/graph_completion_cot_retriever.py +13 -6
- cognee/modules/retrieval/graph_completion_retriever.py +89 -5
- cognee/modules/retrieval/graph_summary_completion_retriever.py +2 -0
- cognee/modules/retrieval/natural_language_retriever.py +0 -4
- cognee/modules/retrieval/user_qa_feedback.py +83 -0
- cognee/modules/retrieval/utils/extract_uuid_from_node.py +18 -0
- cognee/modules/retrieval/utils/models.py +40 -0
- cognee/modules/search/exceptions/__init__.py +7 -0
- cognee/modules/search/exceptions/exceptions.py +15 -0
- cognee/modules/search/methods/search.py +47 -7
- cognee/modules/search/types/SearchType.py +1 -0
- cognee/modules/settings/get_settings.py +2 -2
- cognee/modules/users/exceptions/exceptions.py +6 -6
- cognee/shared/CodeGraphEntities.py +1 -0
- cognee/shared/exceptions/exceptions.py +2 -2
- cognee/shared/logging_utils.py +142 -31
- cognee/shared/utils.py +0 -1
- cognee/tasks/completion/exceptions/exceptions.py +3 -3
- cognee/tasks/documents/classify_documents.py +4 -0
- cognee/tasks/documents/exceptions/__init__.py +11 -0
- cognee/tasks/documents/exceptions/exceptions.py +36 -0
- cognee/tasks/documents/extract_chunks_from_documents.py +8 -2
- cognee/tasks/graph/exceptions/__init__.py +12 -0
- cognee/tasks/graph/exceptions/exceptions.py +41 -0
- cognee/tasks/graph/extract_graph_from_data.py +34 -2
- cognee/tasks/ingestion/exceptions/__init__.py +8 -0
- cognee/tasks/ingestion/exceptions/exceptions.py +12 -0
- cognee/tasks/ingestion/resolve_data_directories.py +5 -0
- cognee/tasks/repo_processor/get_local_dependencies.py +2 -0
- cognee/tasks/repo_processor/get_repo_file_dependencies.py +120 -48
- cognee/tasks/storage/add_data_points.py +41 -3
- cognee/tasks/storage/exceptions/__init__.py +9 -0
- cognee/tasks/storage/exceptions/exceptions.py +13 -0
- cognee/tasks/storage/index_data_points.py +1 -1
- cognee/tasks/summarization/exceptions/__init__.py +9 -0
- cognee/tasks/summarization/exceptions/exceptions.py +14 -0
- cognee/tasks/summarization/summarize_text.py +8 -1
- cognee/tests/integration/cli/__init__.py +3 -0
- cognee/tests/integration/cli/test_cli_integration.py +331 -0
- cognee/tests/integration/documents/PdfDocument_test.py +2 -2
- cognee/tests/integration/documents/TextDocument_test.py +2 -4
- cognee/tests/integration/documents/UnstructuredDocument_test.py +5 -8
- cognee/tests/test_delete_by_id.py +1 -1
- cognee/tests/{test_deletion.py → test_delete_hard.py} +0 -37
- cognee/tests/test_delete_soft.py +85 -0
- cognee/tests/test_kuzu.py +2 -2
- cognee/tests/test_neo4j.py +2 -2
- cognee/tests/test_search_db.py +126 -7
- cognee/tests/unit/cli/__init__.py +3 -0
- cognee/tests/unit/cli/test_cli_commands.py +483 -0
- cognee/tests/unit/cli/test_cli_edge_cases.py +625 -0
- cognee/tests/unit/cli/test_cli_main.py +173 -0
- cognee/tests/unit/cli/test_cli_runner.py +62 -0
- cognee/tests/unit/cli/test_cli_utils.py +127 -0
- cognee/tests/unit/modules/graph/cognee_graph_elements_test.py +5 -5
- cognee/tests/unit/modules/retrieval/graph_completion_retriever_context_extension_test.py +3 -3
- cognee/tests/unit/modules/retrieval/graph_completion_retriever_cot_test.py +3 -3
- cognee/tests/unit/modules/retrieval/graph_completion_retriever_test.py +3 -3
- cognee/tests/unit/modules/search/search_methods_test.py +4 -2
- {cognee-0.2.3.dev0.dist-info → cognee-0.2.4.dist-info}/METADATA +7 -5
- {cognee-0.2.3.dev0.dist-info → cognee-0.2.4.dist-info}/RECORD +172 -121
- cognee-0.2.4.dist-info/entry_points.txt +2 -0
- cognee/infrastructure/databases/exceptions/EmbeddingException.py +0 -20
- cognee/infrastructure/databases/graph/networkx/__init__.py +0 -0
- cognee/infrastructure/databases/graph/networkx/adapter.py +0 -1017
- cognee/infrastructure/pipeline/models/Operation.py +0 -60
- cognee/infrastructure/pipeline/models/__init__.py +0 -0
- cognee/notebooks/github_analysis_step_by_step.ipynb +0 -37
- cognee/tests/tasks/descriptive_metrics/networkx_metrics_test.py +0 -7
- {cognee-0.2.3.dev0.dist-info → cognee-0.2.4.dist-info}/WHEEL +0 -0
- {cognee-0.2.3.dev0.dist-info → cognee-0.2.4.dist-info}/licenses/LICENSE +0 -0
- {cognee-0.2.3.dev0.dist-info → cognee-0.2.4.dist-info}/licenses/NOTICE.md +0 -0
|
@@ -29,6 +29,7 @@ class GraphCompletionContextExtensionRetriever(GraphCompletionRetriever):
|
|
|
29
29
|
top_k: Optional[int] = 5,
|
|
30
30
|
node_type: Optional[Type] = None,
|
|
31
31
|
node_name: Optional[List[str]] = None,
|
|
32
|
+
save_interaction: bool = False,
|
|
32
33
|
):
|
|
33
34
|
super().__init__(
|
|
34
35
|
user_prompt_path=user_prompt_path,
|
|
@@ -36,6 +37,7 @@ class GraphCompletionContextExtensionRetriever(GraphCompletionRetriever):
|
|
|
36
37
|
top_k=top_k,
|
|
37
38
|
node_type=node_type,
|
|
38
39
|
node_name=node_name,
|
|
40
|
+
save_interaction=save_interaction,
|
|
39
41
|
)
|
|
40
42
|
|
|
41
43
|
async def get_completion(
|
|
@@ -105,11 +107,16 @@ class GraphCompletionContextExtensionRetriever(GraphCompletionRetriever):
|
|
|
105
107
|
|
|
106
108
|
round_idx += 1
|
|
107
109
|
|
|
108
|
-
|
|
110
|
+
completion = await generate_completion(
|
|
109
111
|
query=query,
|
|
110
112
|
context=context,
|
|
111
113
|
user_prompt_path=self.user_prompt_path,
|
|
112
114
|
system_prompt_path=self.system_prompt_path,
|
|
113
115
|
)
|
|
114
116
|
|
|
115
|
-
|
|
117
|
+
if self.save_interaction and context and triplets and completion:
|
|
118
|
+
await self.save_qa(
|
|
119
|
+
question=query, answer=completion, context=context, triplets=triplets
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
return [completion]
|
|
@@ -35,6 +35,7 @@ class GraphCompletionCotRetriever(GraphCompletionRetriever):
|
|
|
35
35
|
top_k: Optional[int] = 5,
|
|
36
36
|
node_type: Optional[Type] = None,
|
|
37
37
|
node_name: Optional[List[str]] = None,
|
|
38
|
+
save_interaction: bool = False,
|
|
38
39
|
):
|
|
39
40
|
super().__init__(
|
|
40
41
|
user_prompt_path=user_prompt_path,
|
|
@@ -42,6 +43,7 @@ class GraphCompletionCotRetriever(GraphCompletionRetriever):
|
|
|
42
43
|
top_k=top_k,
|
|
43
44
|
node_type=node_type,
|
|
44
45
|
node_name=node_name,
|
|
46
|
+
save_interaction=save_interaction,
|
|
45
47
|
)
|
|
46
48
|
self.validation_system_prompt_path = validation_system_prompt_path
|
|
47
49
|
self.validation_user_prompt_path = validation_user_prompt_path
|
|
@@ -75,7 +77,7 @@ class GraphCompletionCotRetriever(GraphCompletionRetriever):
|
|
|
75
77
|
"""
|
|
76
78
|
followup_question = ""
|
|
77
79
|
triplets = []
|
|
78
|
-
|
|
80
|
+
completion = [""]
|
|
79
81
|
|
|
80
82
|
for round_idx in range(max_iter + 1):
|
|
81
83
|
if round_idx == 0:
|
|
@@ -85,15 +87,15 @@ class GraphCompletionCotRetriever(GraphCompletionRetriever):
|
|
|
85
87
|
triplets += await self.get_triplets(followup_question)
|
|
86
88
|
context = await self.resolve_edges_to_text(list(set(triplets)))
|
|
87
89
|
|
|
88
|
-
|
|
90
|
+
completion = await generate_completion(
|
|
89
91
|
query=query,
|
|
90
92
|
context=context,
|
|
91
93
|
user_prompt_path=self.user_prompt_path,
|
|
92
94
|
system_prompt_path=self.system_prompt_path,
|
|
93
95
|
)
|
|
94
|
-
logger.info(f"Chain-of-thought: round {round_idx} - answer: {
|
|
96
|
+
logger.info(f"Chain-of-thought: round {round_idx} - answer: {completion}")
|
|
95
97
|
if round_idx < max_iter:
|
|
96
|
-
valid_args = {"query": query, "answer":
|
|
98
|
+
valid_args = {"query": query, "answer": completion, "context": context}
|
|
97
99
|
valid_user_prompt = LLMGateway.render_prompt(
|
|
98
100
|
filename=self.validation_user_prompt_path, context=valid_args
|
|
99
101
|
)
|
|
@@ -106,7 +108,7 @@ class GraphCompletionCotRetriever(GraphCompletionRetriever):
|
|
|
106
108
|
system_prompt=valid_system_prompt,
|
|
107
109
|
response_model=str,
|
|
108
110
|
)
|
|
109
|
-
followup_args = {"query": query, "answer":
|
|
111
|
+
followup_args = {"query": query, "answer": completion, "reasoning": reasoning}
|
|
110
112
|
followup_prompt = LLMGateway.render_prompt(
|
|
111
113
|
filename=self.followup_user_prompt_path, context=followup_args
|
|
112
114
|
)
|
|
@@ -121,4 +123,9 @@ class GraphCompletionCotRetriever(GraphCompletionRetriever):
|
|
|
121
123
|
f"Chain-of-thought: round {round_idx} - follow-up question: {followup_question}"
|
|
122
124
|
)
|
|
123
125
|
|
|
124
|
-
|
|
126
|
+
if self.save_interaction and context and triplets and completion:
|
|
127
|
+
await self.save_qa(
|
|
128
|
+
question=query, answer=completion, context=context, triplets=triplets
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
return [completion]
|
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
from typing import Any, Optional, Type, List
|
|
1
|
+
from typing import Any, Optional, Type, List, Coroutine
|
|
2
2
|
from collections import Counter
|
|
3
|
+
from uuid import NAMESPACE_OID, uuid5
|
|
3
4
|
import string
|
|
4
5
|
|
|
5
6
|
from cognee.infrastructure.engine import DataPoint
|
|
7
|
+
from cognee.tasks.storage import add_data_points
|
|
6
8
|
from cognee.modules.graph.utils.convert_node_to_data_point import get_all_subclasses
|
|
7
9
|
from cognee.modules.retrieval.base_retriever import BaseRetriever
|
|
8
10
|
from cognee.modules.retrieval.utils.brute_force_triplet_search import brute_force_triplet_search
|
|
9
11
|
from cognee.modules.retrieval.utils.completion import generate_completion
|
|
10
12
|
from cognee.modules.retrieval.utils.stop_words import DEFAULT_STOP_WORDS
|
|
11
13
|
from cognee.shared.logging_utils import get_logger
|
|
14
|
+
from cognee.modules.retrieval.utils.extract_uuid_from_node import extract_uuid_from_node
|
|
15
|
+
from cognee.modules.retrieval.utils.models import CogneeUserInteraction
|
|
16
|
+
from cognee.modules.engine.models.node_set import NodeSet
|
|
17
|
+
from cognee.infrastructure.databases.graph import get_graph_engine
|
|
12
18
|
|
|
13
19
|
logger = get_logger("GraphCompletionRetriever")
|
|
14
20
|
|
|
@@ -33,8 +39,10 @@ class GraphCompletionRetriever(BaseRetriever):
|
|
|
33
39
|
top_k: Optional[int] = 5,
|
|
34
40
|
node_type: Optional[Type] = None,
|
|
35
41
|
node_name: Optional[List[str]] = None,
|
|
42
|
+
save_interaction: bool = False,
|
|
36
43
|
):
|
|
37
44
|
"""Initialize retriever with prompt paths and search parameters."""
|
|
45
|
+
self.save_interaction = save_interaction
|
|
38
46
|
self.user_prompt_path = user_prompt_path
|
|
39
47
|
self.system_prompt_path = system_prompt_path
|
|
40
48
|
self.top_k = top_k if top_k is not None else 5
|
|
@@ -118,7 +126,7 @@ class GraphCompletionRetriever(BaseRetriever):
|
|
|
118
126
|
|
|
119
127
|
return found_triplets
|
|
120
128
|
|
|
121
|
-
async def get_context(self, query: str) -> str:
|
|
129
|
+
async def get_context(self, query: str) -> str | tuple[str, list]:
|
|
122
130
|
"""
|
|
123
131
|
Retrieves and resolves graph triplets into context based on a query.
|
|
124
132
|
|
|
@@ -137,9 +145,11 @@ class GraphCompletionRetriever(BaseRetriever):
|
|
|
137
145
|
|
|
138
146
|
if len(triplets) == 0:
|
|
139
147
|
logger.warning("Empty context was provided to the completion")
|
|
140
|
-
return ""
|
|
148
|
+
return "", triplets
|
|
141
149
|
|
|
142
|
-
|
|
150
|
+
context = await self.resolve_edges_to_text(triplets)
|
|
151
|
+
|
|
152
|
+
return context, triplets
|
|
143
153
|
|
|
144
154
|
async def get_completion(self, query: str, context: Optional[Any] = None) -> Any:
|
|
145
155
|
"""
|
|
@@ -157,8 +167,10 @@ class GraphCompletionRetriever(BaseRetriever):
|
|
|
157
167
|
|
|
158
168
|
- Any: A generated completion based on the query and context provided.
|
|
159
169
|
"""
|
|
170
|
+
triplets = None
|
|
171
|
+
|
|
160
172
|
if context is None:
|
|
161
|
-
context = await self.get_context(query)
|
|
173
|
+
context, triplets = await self.get_context(query)
|
|
162
174
|
|
|
163
175
|
completion = await generate_completion(
|
|
164
176
|
query=query,
|
|
@@ -166,6 +178,12 @@ class GraphCompletionRetriever(BaseRetriever):
|
|
|
166
178
|
user_prompt_path=self.user_prompt_path,
|
|
167
179
|
system_prompt_path=self.system_prompt_path,
|
|
168
180
|
)
|
|
181
|
+
|
|
182
|
+
if self.save_interaction and context and triplets and completion:
|
|
183
|
+
await self.save_qa(
|
|
184
|
+
question=query, answer=completion, context=context, triplets=triplets
|
|
185
|
+
)
|
|
186
|
+
|
|
169
187
|
return [completion]
|
|
170
188
|
|
|
171
189
|
def _top_n_words(self, text, stop_words=None, top_n=3, separator=", "):
|
|
@@ -187,3 +205,69 @@ class GraphCompletionRetriever(BaseRetriever):
|
|
|
187
205
|
first_n_words = text.split()[:first_n_words]
|
|
188
206
|
top_n_words = self._top_n_words(text, top_n=top_n_words)
|
|
189
207
|
return f"{' '.join(first_n_words)}... [{top_n_words}]"
|
|
208
|
+
|
|
209
|
+
async def save_qa(self, question: str, answer: str, context: str, triplets: List) -> None:
|
|
210
|
+
"""
|
|
211
|
+
Saves a question and answer pair for later analysis or storage.
|
|
212
|
+
Parameters:
|
|
213
|
+
-----------
|
|
214
|
+
- question (str): The question text.
|
|
215
|
+
- answer (str): The answer text.
|
|
216
|
+
- context (str): The context text.
|
|
217
|
+
- triplets (List): A list of triples retrieved from the graph.
|
|
218
|
+
"""
|
|
219
|
+
nodeset_name = "Interactions"
|
|
220
|
+
interactions_node_set = NodeSet(
|
|
221
|
+
id=uuid5(NAMESPACE_OID, name=nodeset_name), name=nodeset_name
|
|
222
|
+
)
|
|
223
|
+
source_id = uuid5(NAMESPACE_OID, name=(question + answer + context))
|
|
224
|
+
|
|
225
|
+
cognee_user_interaction = CogneeUserInteraction(
|
|
226
|
+
id=source_id,
|
|
227
|
+
question=question,
|
|
228
|
+
answer=answer,
|
|
229
|
+
context=context,
|
|
230
|
+
belongs_to_set=interactions_node_set,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
await add_data_points(data_points=[cognee_user_interaction], update_edge_collection=False)
|
|
234
|
+
|
|
235
|
+
relationships = []
|
|
236
|
+
relationship_name = "used_graph_element_to_answer"
|
|
237
|
+
for triplet in triplets:
|
|
238
|
+
target_id_1 = extract_uuid_from_node(triplet.node1)
|
|
239
|
+
target_id_2 = extract_uuid_from_node(triplet.node2)
|
|
240
|
+
if target_id_1 and target_id_2:
|
|
241
|
+
relationships.append(
|
|
242
|
+
(
|
|
243
|
+
source_id,
|
|
244
|
+
target_id_1,
|
|
245
|
+
relationship_name,
|
|
246
|
+
{
|
|
247
|
+
"relationship_name": relationship_name,
|
|
248
|
+
"source_node_id": source_id,
|
|
249
|
+
"target_node_id": target_id_1,
|
|
250
|
+
"ontology_valid": False,
|
|
251
|
+
"feedback_weight": 0,
|
|
252
|
+
},
|
|
253
|
+
)
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
relationships.append(
|
|
257
|
+
(
|
|
258
|
+
source_id,
|
|
259
|
+
target_id_2,
|
|
260
|
+
relationship_name,
|
|
261
|
+
{
|
|
262
|
+
"relationship_name": relationship_name,
|
|
263
|
+
"source_node_id": source_id,
|
|
264
|
+
"target_node_id": target_id_2,
|
|
265
|
+
"ontology_valid": False,
|
|
266
|
+
"feedback_weight": 0,
|
|
267
|
+
},
|
|
268
|
+
)
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
if len(relationships) > 0:
|
|
272
|
+
graph_engine = await get_graph_engine()
|
|
273
|
+
await graph_engine.add_edges(relationships)
|
|
@@ -24,6 +24,7 @@ class GraphSummaryCompletionRetriever(GraphCompletionRetriever):
|
|
|
24
24
|
top_k: Optional[int] = 5,
|
|
25
25
|
node_type: Optional[Type] = None,
|
|
26
26
|
node_name: Optional[List[str]] = None,
|
|
27
|
+
save_interaction: bool = False,
|
|
27
28
|
):
|
|
28
29
|
"""Initialize retriever with default prompt paths and search parameters."""
|
|
29
30
|
super().__init__(
|
|
@@ -32,6 +33,7 @@ class GraphSummaryCompletionRetriever(GraphCompletionRetriever):
|
|
|
32
33
|
top_k=top_k,
|
|
33
34
|
node_type=node_type,
|
|
34
35
|
node_name=node_name,
|
|
36
|
+
save_interaction=save_interaction,
|
|
35
37
|
)
|
|
36
38
|
self.summarize_prompt_path = summarize_prompt_path
|
|
37
39
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from typing import Any, Optional
|
|
2
2
|
from cognee.shared.logging_utils import get_logger
|
|
3
3
|
from cognee.infrastructure.databases.graph import get_graph_engine
|
|
4
|
-
from cognee.infrastructure.databases.graph.networkx.adapter import NetworkXAdapter
|
|
5
4
|
from cognee.infrastructure.llm.LLMGateway import LLMGateway
|
|
6
5
|
from cognee.modules.retrieval.base_retriever import BaseRetriever
|
|
7
6
|
from cognee.modules.retrieval.exceptions import SearchTypeNotSupported
|
|
@@ -123,9 +122,6 @@ class NaturalLanguageRetriever(BaseRetriever):
|
|
|
123
122
|
"""
|
|
124
123
|
graph_engine = await get_graph_engine()
|
|
125
124
|
|
|
126
|
-
if isinstance(graph_engine, (NetworkXAdapter)):
|
|
127
|
-
raise SearchTypeNotSupported("Natural language search type not supported.")
|
|
128
|
-
|
|
129
125
|
return await self._execute_cypher_query(query, graph_engine)
|
|
130
126
|
|
|
131
127
|
async def get_completion(self, query: str, context: Optional[Any] = None) -> Any:
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
from typing import Any, Optional, List
|
|
2
|
+
|
|
3
|
+
from uuid import NAMESPACE_OID, uuid5, UUID
|
|
4
|
+
from cognee.infrastructure.databases.graph import get_graph_engine
|
|
5
|
+
from cognee.infrastructure.llm import LLMGateway
|
|
6
|
+
from cognee.modules.engine.models import NodeSet
|
|
7
|
+
from cognee.shared.logging_utils import get_logger
|
|
8
|
+
from cognee.modules.retrieval.base_feedback import BaseFeedback
|
|
9
|
+
from cognee.modules.retrieval.utils.models import CogneeUserFeedback
|
|
10
|
+
from cognee.modules.retrieval.utils.models import UserFeedbackEvaluation
|
|
11
|
+
from cognee.tasks.storage import add_data_points
|
|
12
|
+
|
|
13
|
+
logger = get_logger("CompletionRetriever")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class UserQAFeedback(BaseFeedback):
|
|
17
|
+
"""
|
|
18
|
+
Interface for handling user feedback queries.
|
|
19
|
+
Public methods:
|
|
20
|
+
- get_context(query: str) -> str
|
|
21
|
+
- get_completion(query: str, context: Optional[Any] = None) -> Any
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, last_k: Optional[int] = 1) -> None:
|
|
25
|
+
"""Initialize retriever with optional custom prompt paths."""
|
|
26
|
+
self.last_k = last_k
|
|
27
|
+
|
|
28
|
+
async def add_feedback(self, feedback_text: str) -> List[str]:
|
|
29
|
+
feedback_sentiment = await LLMGateway.acreate_structured_output(
|
|
30
|
+
text_input=feedback_text,
|
|
31
|
+
system_prompt="You are a sentiment analysis assistant. For each piece of user feedback you receive, return exactly one of: Positive, Negative, or Neutral classification and a corresponding score from -5 (worst negative) to 5 (best positive)",
|
|
32
|
+
response_model=UserFeedbackEvaluation,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
graph_engine = await get_graph_engine()
|
|
36
|
+
last_interaction_ids = await graph_engine.get_last_user_interaction_ids(limit=self.last_k)
|
|
37
|
+
|
|
38
|
+
nodeset_name = "UserQAFeedbacks"
|
|
39
|
+
feedbacks_node_set = NodeSet(id=uuid5(NAMESPACE_OID, name=nodeset_name), name=nodeset_name)
|
|
40
|
+
feedback_id = uuid5(NAMESPACE_OID, name=feedback_text)
|
|
41
|
+
|
|
42
|
+
cognee_user_feedback = CogneeUserFeedback(
|
|
43
|
+
id=feedback_id,
|
|
44
|
+
feedback=feedback_text,
|
|
45
|
+
sentiment=feedback_sentiment.evaluation.value,
|
|
46
|
+
score=feedback_sentiment.score,
|
|
47
|
+
belongs_to_set=feedbacks_node_set,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
await add_data_points(data_points=[cognee_user_feedback], update_edge_collection=False)
|
|
51
|
+
|
|
52
|
+
relationships = []
|
|
53
|
+
relationship_name = "gives_feedback_to"
|
|
54
|
+
to_node_ids = []
|
|
55
|
+
|
|
56
|
+
for interaction_id in last_interaction_ids:
|
|
57
|
+
target_id_1 = feedback_id
|
|
58
|
+
target_id_2 = UUID(interaction_id)
|
|
59
|
+
|
|
60
|
+
if target_id_1 and target_id_2:
|
|
61
|
+
relationships.append(
|
|
62
|
+
(
|
|
63
|
+
target_id_1,
|
|
64
|
+
target_id_2,
|
|
65
|
+
relationship_name,
|
|
66
|
+
{
|
|
67
|
+
"relationship_name": relationship_name,
|
|
68
|
+
"source_node_id": target_id_1,
|
|
69
|
+
"target_node_id": target_id_2,
|
|
70
|
+
"ontology_valid": False,
|
|
71
|
+
},
|
|
72
|
+
)
|
|
73
|
+
)
|
|
74
|
+
to_node_ids.append(str(target_id_2))
|
|
75
|
+
|
|
76
|
+
if len(relationships) > 0:
|
|
77
|
+
graph_engine = await get_graph_engine()
|
|
78
|
+
await graph_engine.add_edges(relationships)
|
|
79
|
+
await graph_engine.apply_feedback_weight(
|
|
80
|
+
node_ids=to_node_ids, weight=feedback_sentiment.score
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
return [feedback_text]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from typing import Any, Optional
|
|
2
|
+
from uuid import UUID
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def extract_uuid_from_node(node: Any) -> Optional[UUID]:
|
|
6
|
+
"""
|
|
7
|
+
Try to pull a UUID string out of node.id or node.properties['id'],
|
|
8
|
+
then return a UUID instance (or None if neither exists).
|
|
9
|
+
"""
|
|
10
|
+
id_str = None
|
|
11
|
+
if not id_str:
|
|
12
|
+
id_str = getattr(node, "id", None)
|
|
13
|
+
|
|
14
|
+
if hasattr(node, "attributes") and not id_str:
|
|
15
|
+
id_str = node.attributes.get("id", None)
|
|
16
|
+
|
|
17
|
+
id = UUID(id_str) if isinstance(id_str, str) else None
|
|
18
|
+
return id
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from cognee.infrastructure.engine.models.DataPoint import DataPoint
|
|
3
|
+
from cognee.modules.engine.models.node_set import NodeSet
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from pydantic import BaseModel, Field, confloat
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CogneeUserInteraction(DataPoint):
|
|
9
|
+
"""User - Cognee interaction"""
|
|
10
|
+
|
|
11
|
+
question: str
|
|
12
|
+
answer: str
|
|
13
|
+
context: str
|
|
14
|
+
belongs_to_set: Optional[NodeSet] = None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CogneeUserFeedback(DataPoint):
|
|
18
|
+
"""User - Cognee Feedback"""
|
|
19
|
+
|
|
20
|
+
feedback: str
|
|
21
|
+
sentiment: str
|
|
22
|
+
score: float
|
|
23
|
+
belongs_to_set: Optional[NodeSet] = None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class UserFeedbackSentiment(str, Enum):
|
|
27
|
+
"""User - User feedback sentiment"""
|
|
28
|
+
|
|
29
|
+
positive = "positive"
|
|
30
|
+
negative = "negative"
|
|
31
|
+
neutral = "neutral"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class UserFeedbackEvaluation(BaseModel):
|
|
35
|
+
"""User - User feedback evaluation"""
|
|
36
|
+
|
|
37
|
+
score: confloat(ge=-5, le=5) = Field(
|
|
38
|
+
..., description="Sentiment score from -5 (negative) to +5 (positive)"
|
|
39
|
+
)
|
|
40
|
+
evaluation: UserFeedbackSentiment
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from cognee.exceptions import (
|
|
2
|
+
CogneeValidationError,
|
|
3
|
+
)
|
|
4
|
+
from fastapi import status
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class UnsupportedSearchTypeError(CogneeValidationError):
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
search_type: str,
|
|
11
|
+
name: str = "UnsupportedSearchTypeError",
|
|
12
|
+
status_code: int = status.HTTP_400_BAD_REQUEST,
|
|
13
|
+
):
|
|
14
|
+
message = f"Unsupported search type: {search_type}"
|
|
15
|
+
super().__init__(message, name, status_code)
|
|
@@ -4,8 +4,9 @@ import asyncio
|
|
|
4
4
|
from uuid import UUID
|
|
5
5
|
from typing import Callable, List, Optional, Type, Union
|
|
6
6
|
|
|
7
|
+
from cognee.modules.retrieval.user_qa_feedback import UserQAFeedback
|
|
8
|
+
from cognee.modules.search.exceptions import UnsupportedSearchTypeError
|
|
7
9
|
from cognee.context_global_variables import set_database_global_context_variables
|
|
8
|
-
from cognee.exceptions import InvalidValueError
|
|
9
10
|
from cognee.modules.retrieval.chunks_retriever import ChunksRetriever
|
|
10
11
|
from cognee.modules.retrieval.insights_retriever import InsightsRetriever
|
|
11
12
|
from cognee.modules.retrieval.summaries_retriever import SummariesRetriever
|
|
@@ -39,6 +40,8 @@ async def search(
|
|
|
39
40
|
top_k: int = 10,
|
|
40
41
|
node_type: Optional[Type] = None,
|
|
41
42
|
node_name: Optional[List[str]] = None,
|
|
43
|
+
save_interaction: Optional[bool] = False,
|
|
44
|
+
last_k: Optional[int] = None,
|
|
42
45
|
):
|
|
43
46
|
"""
|
|
44
47
|
|
|
@@ -58,7 +61,14 @@ async def search(
|
|
|
58
61
|
# Use search function filtered by permissions if access control is enabled
|
|
59
62
|
if os.getenv("ENABLE_BACKEND_ACCESS_CONTROL", "false").lower() == "true":
|
|
60
63
|
return await authorized_search(
|
|
61
|
-
query_text,
|
|
64
|
+
query_text=query_text,
|
|
65
|
+
query_type=query_type,
|
|
66
|
+
user=user,
|
|
67
|
+
dataset_ids=dataset_ids,
|
|
68
|
+
system_prompt_path=system_prompt_path,
|
|
69
|
+
top_k=top_k,
|
|
70
|
+
save_interaction=save_interaction,
|
|
71
|
+
last_k=last_k,
|
|
62
72
|
)
|
|
63
73
|
|
|
64
74
|
query = await log_query(query_text, query_type.value, user.id)
|
|
@@ -71,6 +81,8 @@ async def search(
|
|
|
71
81
|
top_k=top_k,
|
|
72
82
|
node_type=node_type,
|
|
73
83
|
node_name=node_name,
|
|
84
|
+
save_interaction=save_interaction,
|
|
85
|
+
last_k=last_k,
|
|
74
86
|
)
|
|
75
87
|
|
|
76
88
|
await log_result(
|
|
@@ -92,6 +104,8 @@ async def specific_search(
|
|
|
92
104
|
top_k: int = 10,
|
|
93
105
|
node_type: Optional[Type] = None,
|
|
94
106
|
node_name: Optional[List[str]] = None,
|
|
107
|
+
save_interaction: Optional[bool] = False,
|
|
108
|
+
last_k: Optional[int] = None,
|
|
95
109
|
) -> list:
|
|
96
110
|
search_tasks: dict[SearchType, Callable] = {
|
|
97
111
|
SearchType.SUMMARIES: SummariesRetriever(top_k=top_k).get_completion,
|
|
@@ -105,28 +119,33 @@ async def specific_search(
|
|
|
105
119
|
top_k=top_k,
|
|
106
120
|
node_type=node_type,
|
|
107
121
|
node_name=node_name,
|
|
122
|
+
save_interaction=save_interaction,
|
|
108
123
|
).get_completion,
|
|
109
124
|
SearchType.GRAPH_COMPLETION_COT: GraphCompletionCotRetriever(
|
|
110
125
|
system_prompt_path=system_prompt_path,
|
|
111
126
|
top_k=top_k,
|
|
112
127
|
node_type=node_type,
|
|
113
128
|
node_name=node_name,
|
|
129
|
+
save_interaction=save_interaction,
|
|
114
130
|
).get_completion,
|
|
115
131
|
SearchType.GRAPH_COMPLETION_CONTEXT_EXTENSION: GraphCompletionContextExtensionRetriever(
|
|
116
132
|
system_prompt_path=system_prompt_path,
|
|
117
133
|
top_k=top_k,
|
|
118
134
|
node_type=node_type,
|
|
119
135
|
node_name=node_name,
|
|
136
|
+
save_interaction=save_interaction,
|
|
120
137
|
).get_completion,
|
|
121
138
|
SearchType.GRAPH_SUMMARY_COMPLETION: GraphSummaryCompletionRetriever(
|
|
122
139
|
system_prompt_path=system_prompt_path,
|
|
123
140
|
top_k=top_k,
|
|
124
141
|
node_type=node_type,
|
|
125
142
|
node_name=node_name,
|
|
143
|
+
save_interaction=save_interaction,
|
|
126
144
|
).get_completion,
|
|
127
145
|
SearchType.CODE: CodeRetriever(top_k=top_k).get_completion,
|
|
128
146
|
SearchType.CYPHER: CypherSearchRetriever().get_completion,
|
|
129
147
|
SearchType.NATURAL_LANGUAGE: NaturalLanguageRetriever().get_completion,
|
|
148
|
+
SearchType.FEEDBACK: UserQAFeedback(last_k=last_k).add_feedback,
|
|
130
149
|
}
|
|
131
150
|
|
|
132
151
|
# If the query type is FEELING_LUCKY, select the search type intelligently
|
|
@@ -136,7 +155,7 @@ async def specific_search(
|
|
|
136
155
|
search_task = search_tasks.get(query_type)
|
|
137
156
|
|
|
138
157
|
if search_task is None:
|
|
139
|
-
raise
|
|
158
|
+
raise UnsupportedSearchTypeError(str(query_type))
|
|
140
159
|
|
|
141
160
|
send_telemetry("cognee.search EXECUTION STARTED", user.id)
|
|
142
161
|
|
|
@@ -154,6 +173,8 @@ async def authorized_search(
|
|
|
154
173
|
dataset_ids: Optional[list[UUID]] = None,
|
|
155
174
|
system_prompt_path: str = "answer_simple_question.txt",
|
|
156
175
|
top_k: int = 10,
|
|
176
|
+
save_interaction: bool = False,
|
|
177
|
+
last_k: Optional[int] = None,
|
|
157
178
|
) -> list:
|
|
158
179
|
"""
|
|
159
180
|
Verifies access for provided datasets or uses all datasets user has read access for and performs search per dataset.
|
|
@@ -167,7 +188,14 @@ async def authorized_search(
|
|
|
167
188
|
|
|
168
189
|
# Searches all provided datasets and handles setting up of appropriate database context based on permissions
|
|
169
190
|
search_results = await specific_search_by_context(
|
|
170
|
-
search_datasets,
|
|
191
|
+
search_datasets,
|
|
192
|
+
query_text,
|
|
193
|
+
query_type,
|
|
194
|
+
user,
|
|
195
|
+
system_prompt_path,
|
|
196
|
+
top_k,
|
|
197
|
+
save_interaction,
|
|
198
|
+
last_k=last_k,
|
|
171
199
|
)
|
|
172
200
|
|
|
173
201
|
await log_result(query.id, json.dumps(search_results, cls=JSONEncoder), user.id)
|
|
@@ -182,17 +210,27 @@ async def specific_search_by_context(
|
|
|
182
210
|
user: User,
|
|
183
211
|
system_prompt_path: str,
|
|
184
212
|
top_k: int,
|
|
213
|
+
save_interaction: bool = False,
|
|
214
|
+
last_k: Optional[int] = None,
|
|
185
215
|
):
|
|
186
216
|
"""
|
|
187
217
|
Searches all provided datasets and handles setting up of appropriate database context based on permissions.
|
|
188
218
|
Not to be used outside of active access control mode.
|
|
189
219
|
"""
|
|
190
220
|
|
|
191
|
-
async def _search_by_context(
|
|
221
|
+
async def _search_by_context(
|
|
222
|
+
dataset, user, query_type, query_text, system_prompt_path, top_k, last_k
|
|
223
|
+
):
|
|
192
224
|
# Set database configuration in async context for each dataset user has access for
|
|
193
225
|
await set_database_global_context_variables(dataset.id, dataset.owner_id)
|
|
194
226
|
search_results = await specific_search(
|
|
195
|
-
query_type,
|
|
227
|
+
query_type,
|
|
228
|
+
query_text,
|
|
229
|
+
user,
|
|
230
|
+
system_prompt_path=system_prompt_path,
|
|
231
|
+
top_k=top_k,
|
|
232
|
+
save_interaction=save_interaction,
|
|
233
|
+
last_k=last_k,
|
|
196
234
|
)
|
|
197
235
|
return {
|
|
198
236
|
"search_result": search_results,
|
|
@@ -204,7 +242,9 @@ async def specific_search_by_context(
|
|
|
204
242
|
tasks = []
|
|
205
243
|
for dataset in search_datasets:
|
|
206
244
|
tasks.append(
|
|
207
|
-
_search_by_context(
|
|
245
|
+
_search_by_context(
|
|
246
|
+
dataset, user, query_type, query_text, system_prompt_path, top_k, last_k
|
|
247
|
+
)
|
|
208
248
|
)
|
|
209
249
|
|
|
210
250
|
return await asyncio.gather(*tasks)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
from cognee.exceptions import
|
|
1
|
+
from cognee.exceptions import CogneeValidationError
|
|
2
2
|
from fastapi import status
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
class RoleNotFoundError(
|
|
5
|
+
class RoleNotFoundError(CogneeValidationError):
|
|
6
6
|
"""User group not found"""
|
|
7
7
|
|
|
8
8
|
def __init__(
|
|
@@ -14,7 +14,7 @@ class RoleNotFoundError(CogneeApiError):
|
|
|
14
14
|
super().__init__(message, name, status_code)
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class TenantNotFoundError(
|
|
17
|
+
class TenantNotFoundError(CogneeValidationError):
|
|
18
18
|
"""User group not found"""
|
|
19
19
|
|
|
20
20
|
def __init__(
|
|
@@ -26,7 +26,7 @@ class TenantNotFoundError(CogneeApiError):
|
|
|
26
26
|
super().__init__(message, name, status_code)
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
class UserNotFoundError(
|
|
29
|
+
class UserNotFoundError(CogneeValidationError):
|
|
30
30
|
"""User not found"""
|
|
31
31
|
|
|
32
32
|
def __init__(
|
|
@@ -38,7 +38,7 @@ class UserNotFoundError(CogneeApiError):
|
|
|
38
38
|
super().__init__(message, name, status_code)
|
|
39
39
|
|
|
40
40
|
|
|
41
|
-
class PermissionDeniedError(
|
|
41
|
+
class PermissionDeniedError(CogneeValidationError):
|
|
42
42
|
def __init__(
|
|
43
43
|
self,
|
|
44
44
|
message: str = "User does not have permission on documents.",
|
|
@@ -48,7 +48,7 @@ class PermissionDeniedError(CogneeApiError):
|
|
|
48
48
|
super().__init__(message, name, status_code)
|
|
49
49
|
|
|
50
50
|
|
|
51
|
-
class PermissionNotFoundError(
|
|
51
|
+
class PermissionNotFoundError(CogneeValidationError):
|
|
52
52
|
def __init__(
|
|
53
53
|
self,
|
|
54
54
|
message: str = "Permission type does not exist.",
|