diff --git a/cognee-mcp/src/server.py b/cognee-mcp/src/server.py index a657225f5..3e65a5eb7 100755 --- a/cognee-mcp/src/server.py +++ b/cognee-mcp/src/server.py @@ -221,14 +221,6 @@ async def cognify(data: str, graph_model_file: str = None, graph_model_name: str - The actual cognify process may take significant time depending on text length - Use the cognify_status tool to check the progress of the operation - Raises - ------ - InvalidValueError - If LLM_API_KEY is not set - ValueError - If chunks exceed max token limits (reduce chunk_size) - DatabaseNotCreatedError - If databases are not properly initialized """ async def cognify_task( @@ -512,14 +504,6 @@ async def search(search_query: str, search_type: str) -> list: - Different search types produce different output formats - The function handles the conversion between Cognee's internal result format and MCP's output format - Raises - ------ - InvalidValueError - If LLM_API_KEY is not set (for LLM-based search types) - ValueError - If query_text is empty or search parameters are invalid - NoDataError - If no relevant data found for the search query """ async def search_task(search_query: str, search_type: str) -> str: diff --git a/cognee/api/v1/add/add.py b/cognee/api/v1/add/add.py index 7daaaf1dd..78c13449b 100644 --- a/cognee/api/v1/add/add.py +++ b/cognee/api/v1/add/add.py @@ -134,11 +134,6 @@ async def add( - VECTOR_DB_PROVIDER: "lancedb" (default), "chromadb", "pgvector" - GRAPH_DATABASE_PROVIDER: "kuzu" (default), "neo4j", "networkx" - Raises: - FileNotFoundError: If specified file paths don't exist - PermissionError: If user lacks access to files or dataset - UnsupportedFileTypeError: If file format cannot be processed - InvalidValueError: If LLM_API_KEY is not set or invalid """ tasks = [ Task(resolve_data_directories, include_subdirectories=True), diff --git a/cognee/api/v1/cognify/cognify.py b/cognee/api/v1/cognify/cognify.py index c6508f3a7..23984b9a6 100644 --- a/cognee/api/v1/cognify/cognify.py +++ b/cognee/api/v1/cognify/cognify.py @@ -177,14 +177,6 @@ async def cognify( - LLM_PROVIDER, LLM_MODEL, VECTOR_DB_PROVIDER, GRAPH_DATABASE_PROVIDER - LLM_RATE_LIMIT_ENABLED: Enable rate limiting (default: False) - LLM_RATE_LIMIT_REQUESTS: Max requests per interval (default: 60) - - Raises: - DatasetNotFoundError: If specified datasets don't exist - PermissionError: If user lacks processing rights - InvalidValueError: If LLM_API_KEY is not set - OntologyParsingError: If ontology file is malformed - ValueError: If chunks exceed max token limits (reduce chunk_size) - DatabaseNotCreatedError: If databases are not properly initialized """ tasks = await get_default_tasks(user, graph_model, chunker, chunk_size, ontology_file_path) diff --git a/cognee/api/v1/config/config.py b/cognee/api/v1/config/config.py index 9970b7471..464753438 100644 --- a/cognee/api/v1/config/config.py +++ b/cognee/api/v1/config/config.py @@ -2,7 +2,6 @@ import os from cognee.base_config import get_base_config -from cognee.exceptions import InvalidValueError, InvalidAttributeError from cognee.modules.cognify.config import get_cognify_config from cognee.infrastructure.data.chunking.config import get_chunk_config from cognee.infrastructure.databases.vector import get_vectordb_config @@ -11,6 +10,7 @@ from cognee.infrastructure.llm.config import ( get_llm_config, ) from cognee.infrastructure.databases.relational import get_relational_config, get_migration_config +from cognee.api.v1.exceptions.exceptions import InvalidConfigAttributeError class config: @@ -92,9 +92,7 @@ class config: if hasattr(llm_config, key): object.__setattr__(llm_config, key, value) else: - raise InvalidAttributeError( - message=f"'{key}' is not a valid attribute of the config." - ) + raise InvalidConfigAttributeError(attribute=key) @staticmethod def set_chunk_strategy(chunk_strategy: object): @@ -131,9 +129,7 @@ class config: if hasattr(relational_db_config, key): object.__setattr__(relational_db_config, key, value) else: - raise InvalidAttributeError( - message=f"'{key}' is not a valid attribute of the config." - ) + raise InvalidConfigAttributeError(attribute=key) @staticmethod def set_migration_db_config(config_dict: dict): @@ -145,9 +141,7 @@ class config: if hasattr(migration_db_config, key): object.__setattr__(migration_db_config, key, value) else: - raise InvalidAttributeError( - message=f"'{key}' is not a valid attribute of the config." - ) + raise InvalidConfigAttributeError(attribute=key) @staticmethod def set_graph_db_config(config_dict: dict) -> None: @@ -171,9 +165,7 @@ class config: if hasattr(vector_db_config, key): object.__setattr__(vector_db_config, key, value) else: - raise InvalidAttributeError( - message=f"'{key}' is not a valid attribute of the config." - ) + InvalidConfigAttributeError(attribute=key) @staticmethod def set_vector_db_key(db_key: str): diff --git a/cognee/api/v1/datasets/routers/get_datasets_router.py b/cognee/api/v1/datasets/routers/get_datasets_router.py index 627be226d..a6938d764 100644 --- a/cognee/api/v1/datasets/routers/get_datasets_router.py +++ b/cognee/api/v1/datasets/routers/get_datasets_router.py @@ -13,7 +13,7 @@ from cognee.infrastructure.databases.relational import get_relational_engine from cognee.modules.data.methods import get_authorized_existing_datasets from cognee.modules.data.methods import create_dataset, get_datasets_by_name from cognee.shared.logging_utils import get_logger -from cognee.api.v1.delete.exceptions import DataNotFoundError, DatasetNotFoundError +from cognee.api.v1.exceptions import DataNotFoundError, DatasetNotFoundError from cognee.modules.users.models import User from cognee.modules.users.methods import get_authenticated_user from cognee.modules.users.permissions.methods import ( diff --git a/cognee/api/v1/delete/delete.py b/cognee/api/v1/delete/delete.py index 98f6cb9fc..73f264670 100644 --- a/cognee/api/v1/delete/delete.py +++ b/cognee/api/v1/delete/delete.py @@ -16,7 +16,7 @@ from cognee.modules.users.methods import get_default_user from cognee.modules.data.methods import get_authorized_existing_datasets from cognee.context_global_variables import set_database_global_context_variables -from cognee.api.v1.delete.exceptions import ( +from cognee.api.v1.exceptions import ( DocumentNotFoundError, DatasetNotFoundError, DocumentSubgraphNotFoundError, diff --git a/cognee/api/v1/exceptions/__init__.py b/cognee/api/v1/exceptions/__init__.py new file mode 100644 index 000000000..5767a5801 --- /dev/null +++ b/cognee/api/v1/exceptions/__init__.py @@ -0,0 +1,13 @@ +""" +Custom exceptions for the Cognee API. + +This module defines a set of exceptions for handling various data errors +""" + +from .exceptions import ( + InvalidConfigAttributeError, + DocumentNotFoundError, + DatasetNotFoundError, + DataNotFoundError, + DocumentSubgraphNotFoundError, +) diff --git a/cognee/api/v1/delete/exceptions.py b/cognee/api/v1/exceptions/exceptions.py similarity index 61% rename from cognee/api/v1/delete/exceptions.py rename to cognee/api/v1/exceptions/exceptions.py index a4d2a77ed..12dfa9d37 100644 --- a/cognee/api/v1/delete/exceptions.py +++ b/cognee/api/v1/exceptions/exceptions.py @@ -1,10 +1,19 @@ -from cognee.exceptions import CogneeApiError +from cognee.exceptions import CogneeConfigurationError, CogneeValidationError from fastapi import status -class DocumentNotFoundError(CogneeApiError): - """Raised when a document cannot be found in the database.""" +class InvalidConfigAttributeError(CogneeConfigurationError): + def __init__( + self, + attribute: str, + name: str = "InvalidConfigAttributeError", + status_code: int = status.HTTP_400_BAD_REQUEST, + ): + message = f"'{attribute}' is not a valid attribute of the configuration." + super().__init__(message, name, status_code) + +class DocumentNotFoundError(CogneeValidationError): def __init__( self, message: str = "Document not found in database.", @@ -14,9 +23,7 @@ class DocumentNotFoundError(CogneeApiError): super().__init__(message, name, status_code) -class DatasetNotFoundError(CogneeApiError): - """Raised when a dataset cannot be found.""" - +class DatasetNotFoundError(CogneeValidationError): def __init__( self, message: str = "Dataset not found.", @@ -26,9 +33,7 @@ class DatasetNotFoundError(CogneeApiError): super().__init__(message, name, status_code) -class DataNotFoundError(CogneeApiError): - """Raised when a dataset cannot be found.""" - +class DataNotFoundError(CogneeValidationError): def __init__( self, message: str = "Data not found.", @@ -38,9 +43,7 @@ class DataNotFoundError(CogneeApiError): super().__init__(message, name, status_code) -class DocumentSubgraphNotFoundError(CogneeApiError): - """Raised when a document's subgraph cannot be found in the graph database.""" - +class DocumentSubgraphNotFoundError(CogneeValidationError): def __init__( self, message: str = "Document subgraph not found in graph database.", diff --git a/cognee/api/v1/search/search.py b/cognee/api/v1/search/search.py index 66ce48cc2..f4f4831c1 100644 --- a/cognee/api/v1/search/search.py +++ b/cognee/api/v1/search/search.py @@ -158,13 +158,6 @@ async def search( - VECTOR_DB_PROVIDER: Must match what was used during cognify - GRAPH_DATABASE_PROVIDER: Must match what was used during cognify - Raises: - DatasetNotFoundError: If specified datasets don't exist or aren't accessible - PermissionDeniedError: If user lacks read access to requested datasets - NoDataError: If no relevant data found for the search query - InvalidValueError: If LLM_API_KEY is not set (for LLM-based search types) - ValueError: If query_text is empty or search parameters are invalid - CollectionNotFoundError: If vector collection not found (data not processed) """ # We use lists from now on for datasets if isinstance(datasets, UUID) or isinstance(datasets, str): diff --git a/cognee/exceptions/__init__.py b/cognee/exceptions/__init__.py index d1d4ecbf5..2f1589291 100644 --- a/cognee/exceptions/__init__.py +++ b/cognee/exceptions/__init__.py @@ -2,13 +2,13 @@ Custom exceptions for the Cognee API. This module defines a set of exceptions for handling various application errors, -such as service failures, resource conflicts, and invalid operations. +such as System, Validation, Configuration or TransientErrors """ from .exceptions import ( CogneeApiError, - ServiceError, - InvalidValueError, - InvalidAttributeError, - CriticalError, + CogneeSystemError, + CogneeValidationError, + CogneeConfigurationError, + CogneeTransientError, ) diff --git a/cognee/exceptions/exceptions.py b/cognee/exceptions/exceptions.py index 46e0af1a8..d956d9cef 100644 --- a/cognee/exceptions/exceptions.py +++ b/cognee/exceptions/exceptions.py @@ -35,37 +35,57 @@ class CogneeApiError(Exception): return f"{self.name}: {self.message} (Status code: {self.status_code})" -class ServiceError(CogneeApiError): - """Failures in external services or APIs, like a database or a third-party service""" +class CogneeSystemError(CogneeApiError): + """System error""" def __init__( self, - message: str = "Service is unavailable.", - name: str = "ServiceError", - status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + message: str = "A system error occurred.", + name: str = "CogneeSystemError", + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + log=True, + log_level="ERROR", ): - super().__init__(message, name, status_code) + super().__init__(message, name, status_code, log, log_level) -class InvalidValueError(CogneeApiError): +class CogneeValidationError(CogneeApiError): + """Validation error""" + def __init__( self, - message: str = "Invalid Value.", - name: str = "InvalidValueError", + message: str = "A validation error occurred.", + name: str = "CogneeValidationError", status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + log=True, + log_level="ERROR", ): - super().__init__(message, name, status_code) + super().__init__(message, name, status_code, log, log_level) -class InvalidAttributeError(CogneeApiError): +class CogneeConfigurationError(CogneeApiError): + """SystemConfigError""" + def __init__( self, - message: str = "Invalid attribute.", - name: str = "InvalidAttributeError", - status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + message: str = "A system configuration error occurred.", + name: str = "CogneeConfigurationError", + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + log=True, + log_level="ERROR", ): - super().__init__(message, name, status_code) + super().__init__(message, name, status_code, log, log_level) -class CriticalError(CogneeApiError): - pass +class CogneeTransientError(CogneeApiError): + """TransientError""" + + def __init__( + self, + message: str = "A transient error occurred.", + name: str = "CogneeTransientError", + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, + log=True, + log_level="ERROR", + ): + super().__init__(message, name, status_code, log, log_level) diff --git a/cognee/infrastructure/data/exceptions/__init__.py b/cognee/infrastructure/data/exceptions/__init__.py new file mode 100644 index 000000000..cc5f6044f --- /dev/null +++ b/cognee/infrastructure/data/exceptions/__init__.py @@ -0,0 +1,7 @@ +""" +Custom exceptions for the Cognee API. + +This module defines a set of exceptions for handling various data errors +""" + +from .exceptions import KeywordExtractionError diff --git a/cognee/infrastructure/data/exceptions/exceptions.py b/cognee/infrastructure/data/exceptions/exceptions.py new file mode 100644 index 000000000..5c36d6754 --- /dev/null +++ b/cognee/infrastructure/data/exceptions/exceptions.py @@ -0,0 +1,22 @@ +from cognee.exceptions import ( + CogneeValidationError, +) +from fastapi import status + + +class KeywordExtractionError(CogneeValidationError): + """ + Raised when a provided value is syntactically valid but semantically unacceptable + for the given operation. + + Example: + - Passing an empty string to a keyword extraction function. + """ + + def __init__( + self, + message: str = "Extract_keywords cannot extract keywords from empty text.", + name: str = "KeywordExtractionError", + status_code: int = status.HTTP_400_BAD_REQUEST, + ): + super().__init__(message, name, status_code) diff --git a/cognee/infrastructure/data/utils/extract_keywords.py b/cognee/infrastructure/data/utils/extract_keywords.py index bd4fedd56..8085459c9 100644 --- a/cognee/infrastructure/data/utils/extract_keywords.py +++ b/cognee/infrastructure/data/utils/extract_keywords.py @@ -1,6 +1,6 @@ from sklearn.feature_extraction.text import TfidfVectorizer -from cognee.exceptions import InvalidValueError +from cognee.infrastructure.data.exceptions.exceptions import KeywordExtractionError from cognee.shared.utils import extract_pos_tags @@ -8,7 +8,7 @@ def extract_keywords(text: str) -> list[str]: """ Extract keywords from the provided text string. - This function raises an InvalidValueError if the input text is empty. It processes the + This function raises an KeyWordExtractionError if the input text is empty. It processes the text to extract parts of speech, focusing on nouns, and uses TF-IDF to identify the most relevant keywords based on their frequency. The function returns a list of up to 15 keywords, each having more than 3 characters. @@ -25,7 +25,7 @@ def extract_keywords(text: str) -> list[str]: with more than 3 characters. """ if len(text) == 0: - raise InvalidValueError(message="extract_keywords cannot extract keywords from empty text.") + raise KeywordExtractionError() tags = extract_pos_tags(text) nouns = [word for (word, tag) in tags if tag == "NN"] diff --git a/cognee/infrastructure/databases/exceptions/EmbeddingException.py b/cognee/infrastructure/databases/exceptions/EmbeddingException.py deleted file mode 100644 index 62616899c..000000000 --- a/cognee/infrastructure/databases/exceptions/EmbeddingException.py +++ /dev/null @@ -1,20 +0,0 @@ -from cognee.exceptions import CogneeApiError -from fastapi import status - - -class EmbeddingException(CogneeApiError): - """ - Custom exception for handling embedding-related errors. - - This exception class is designed to indicate issues specifically related to embeddings - within the application. It extends the base exception class CogneeApiError and allows - for customization of the error message, name, and status code. - """ - - def __init__( - self, - message: str = "Embedding Exception.", - name: str = "EmbeddingException", - status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, - ): - super().__init__(message, name, status_code) diff --git a/cognee/infrastructure/databases/exceptions/__init__.py b/cognee/infrastructure/databases/exceptions/__init__.py index 9d8d18567..2969b1c59 100644 --- a/cognee/infrastructure/databases/exceptions/__init__.py +++ b/cognee/infrastructure/databases/exceptions/__init__.py @@ -8,4 +8,7 @@ from .exceptions import ( EntityNotFoundError, EntityAlreadyExistsError, DatabaseNotCreatedError, + EmbeddingException, + MissingQueryParameterError, + MutuallyExclusiveQueryParametersError, ) diff --git a/cognee/infrastructure/databases/exceptions/exceptions.py b/cognee/infrastructure/databases/exceptions/exceptions.py index 7a4220358..c240d3133 100644 --- a/cognee/infrastructure/databases/exceptions/exceptions.py +++ b/cognee/infrastructure/databases/exceptions/exceptions.py @@ -1,13 +1,13 @@ from fastapi import status -from cognee.exceptions import CogneeApiError, CriticalError +from cognee.exceptions import CogneeSystemError, CogneeValidationError, CogneeConfigurationError -class DatabaseNotCreatedError(CriticalError): +class DatabaseNotCreatedError(CogneeSystemError): """ Represents an error indicating that the database has not been created. This error should be raised when an attempt is made to access the database before it has been initialized. - Inherits from CriticalError. Overrides the constructor to include a default message and + Inherits from CogneeSystemError. Overrides the constructor to include a default message and status code. """ @@ -20,10 +20,10 @@ class DatabaseNotCreatedError(CriticalError): super().__init__(message, name, status_code) -class EntityNotFoundError(CogneeApiError): +class EntityNotFoundError(CogneeValidationError): """ Represents an error when a requested entity is not found in the database. This class - inherits from CogneeApiError. + inherits from CogneeValidationError. Public methods: @@ -49,11 +49,11 @@ class EntityNotFoundError(CogneeApiError): # super().__init__(message, name, status_code) :TODO: This is not an error anymore with the dynamic exception handling therefore we shouldn't log error -class EntityAlreadyExistsError(CogneeApiError): +class EntityAlreadyExistsError(CogneeValidationError): """ Represents an error when an entity creation is attempted but the entity already exists. - This class is derived from CogneeApiError and is used to signal a conflict in operations + This class is derived from CogneeValidationError and is used to signal a conflict in operations involving resource creation. """ @@ -66,11 +66,11 @@ class EntityAlreadyExistsError(CogneeApiError): super().__init__(message, name, status_code) -class NodesetFilterNotSupportedError(CogneeApiError): +class NodesetFilterNotSupportedError(CogneeConfigurationError): """ Raise an exception when a nodeset filter is not supported by the current database. - This exception inherits from `CogneeApiError` and is designed to provide information + This exception inherits from `CogneeConfigurationError` and is designed to provide information about the specific issue of unsupported nodeset filters in the context of graph databases. """ @@ -84,3 +84,51 @@ class NodesetFilterNotSupportedError(CogneeApiError): self.message = message self.name = name self.status_code = status_code + + +class EmbeddingException(CogneeConfigurationError): + """ + Custom exception for handling embedding-related errors. + + This exception class is designed to indicate issues specifically related to embeddings + within the application. It extends the base exception class CogneeConfigurationError allows + for customization of the error message, name, and status code. + """ + + def __init__( + self, + message: str = "Embedding Exception.", + name: str = "EmbeddingException", + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + ): + super().__init__(message, name, status_code) + + +class MissingQueryParameterError(CogneeValidationError): + """ + Raised when neither 'query_text' nor 'query_vector' is provided, + and at least one is required to perform the operation. + """ + + def __init__( + self, + name: str = "MissingQueryParameterError", + status_code: int = status.HTTP_400_BAD_REQUEST, + ): + message = "One of query_text or query_vector must be provided!" + super().__init__(message, name, status_code) + + +class MutuallyExclusiveQueryParametersError(CogneeValidationError): + """ + Raised when both 'text' and 'embedding' are provided to the search function, + but only one type of input is allowed at a time. + """ + + def __init__( + self, + name: str = "MutuallyExclusiveQueryParametersError", + status_code: int = status.HTTP_400_BAD_REQUEST, + ): + message = "The search function accepts either text or embedding as input, but not both." + super().__init__(message, name, status_code) diff --git a/cognee/infrastructure/databases/graph/neptune_driver/exceptions.py b/cognee/infrastructure/databases/graph/neptune_driver/exceptions.py index 57d54d74d..a200aad39 100644 --- a/cognee/infrastructure/databases/graph/neptune_driver/exceptions.py +++ b/cognee/infrastructure/databases/graph/neptune_driver/exceptions.py @@ -3,11 +3,16 @@ This module defines custom exceptions for Neptune Analytics operations. """ -from cognee.exceptions import CogneeApiError +from cognee.exceptions import ( + CogneeSystemError, + CogneeTransientError, + CogneeValidationError, + CogneeConfigurationError, +) from fastapi import status -class NeptuneAnalyticsError(CogneeApiError): +class NeptuneAnalyticsError(CogneeSystemError): """Base exception for Neptune Analytics operations.""" def __init__( @@ -19,7 +24,7 @@ class NeptuneAnalyticsError(CogneeApiError): super().__init__(message, name, status_code) -class NeptuneAnalyticsConnectionError(NeptuneAnalyticsError): +class NeptuneAnalyticsConnectionError(CogneeTransientError): """Exception raised when connection to Neptune Analytics fails.""" def __init__( @@ -31,7 +36,7 @@ class NeptuneAnalyticsConnectionError(NeptuneAnalyticsError): super().__init__(message, name, status_code) -class NeptuneAnalyticsQueryError(NeptuneAnalyticsError): +class NeptuneAnalyticsQueryError(CogneeValidationError): """Exception raised when a query execution fails.""" def __init__( @@ -43,7 +48,7 @@ class NeptuneAnalyticsQueryError(NeptuneAnalyticsError): super().__init__(message, name, status_code) -class NeptuneAnalyticsAuthenticationError(NeptuneAnalyticsError): +class NeptuneAnalyticsAuthenticationError(CogneeConfigurationError): """Exception raised when authentication with Neptune Analytics fails.""" def __init__( @@ -55,7 +60,7 @@ class NeptuneAnalyticsAuthenticationError(NeptuneAnalyticsError): super().__init__(message, name, status_code) -class NeptuneAnalyticsConfigurationError(NeptuneAnalyticsError): +class NeptuneAnalyticsConfigurationError(CogneeConfigurationError): """Exception raised when Neptune Analytics configuration is invalid.""" def __init__( @@ -67,7 +72,7 @@ class NeptuneAnalyticsConfigurationError(NeptuneAnalyticsError): super().__init__(message, name, status_code) -class NeptuneAnalyticsTimeoutError(NeptuneAnalyticsError): +class NeptuneAnalyticsTimeoutError(CogneeTransientError): """Exception raised when a Neptune Analytics operation times out.""" def __init__( @@ -79,7 +84,7 @@ class NeptuneAnalyticsTimeoutError(NeptuneAnalyticsError): super().__init__(message, name, status_code) -class NeptuneAnalyticsThrottlingError(NeptuneAnalyticsError): +class NeptuneAnalyticsThrottlingError(CogneeTransientError): """Exception raised when requests are throttled by Neptune Analytics.""" def __init__( @@ -91,7 +96,7 @@ class NeptuneAnalyticsThrottlingError(NeptuneAnalyticsError): super().__init__(message, name, status_code) -class NeptuneAnalyticsResourceNotFoundError(NeptuneAnalyticsError): +class NeptuneAnalyticsResourceNotFoundError(CogneeValidationError): """Exception raised when a Neptune Analytics resource is not found.""" def __init__( @@ -103,7 +108,7 @@ class NeptuneAnalyticsResourceNotFoundError(NeptuneAnalyticsError): super().__init__(message, name, status_code) -class NeptuneAnalyticsInvalidParameterError(NeptuneAnalyticsError): +class NeptuneAnalyticsInvalidParameterError(CogneeValidationError): """Exception raised when invalid parameters are provided to Neptune Analytics.""" def __init__( diff --git a/cognee/infrastructure/databases/hybrid/falkordb/FalkorDBAdapter.py b/cognee/infrastructure/databases/hybrid/falkordb/FalkorDBAdapter.py index 35ce7c77e..cb6899925 100644 --- a/cognee/infrastructure/databases/hybrid/falkordb/FalkorDBAdapter.py +++ b/cognee/infrastructure/databases/hybrid/falkordb/FalkorDBAdapter.py @@ -9,7 +9,7 @@ from typing import List, Dict, Any, Optional, Tuple, Type, Union from falkordb import FalkorDB -from cognee.exceptions import InvalidValueError +from cognee.infrastructure.databases.exceptions import MissingQueryParameterError from cognee.infrastructure.databases.graph.graph_db_interface import ( GraphDBInterface, record_graph_changes, @@ -721,7 +721,7 @@ class FalkorDBAdapter(VectorDBInterface, GraphDBInterface): Returns the search results as a result set from the graph database. """ if query_text is None and query_vector is None: - raise InvalidValueError(message="One of query_text or query_vector must be provided!") + raise MissingQueryParameterError() if query_text and not query_vector: query_vector = (await self.embed_data([query_text]))[0] diff --git a/cognee/infrastructure/databases/hybrid/neptune_analytics/NeptuneAnalyticsAdapter.py b/cognee/infrastructure/databases/hybrid/neptune_analytics/NeptuneAnalyticsAdapter.py index a04e6f09e..4baf8ff13 100644 --- a/cognee/infrastructure/databases/hybrid/neptune_analytics/NeptuneAnalyticsAdapter.py +++ b/cognee/infrastructure/databases/hybrid/neptune_analytics/NeptuneAnalyticsAdapter.py @@ -5,7 +5,8 @@ import json from typing import List, Optional, Any, Dict, Type, Tuple from uuid import UUID -from cognee.exceptions import InvalidValueError +from cognee.infrastructure.databases.exceptions import MissingQueryParameterError +from cognee.infrastructure.databases.exceptions import MutuallyExclusiveQueryParametersError from cognee.infrastructure.databases.graph.neptune_driver.adapter import NeptuneGraphDB from cognee.infrastructure.databases.vector.vector_db_interface import VectorDBInterface from cognee.infrastructure.engine import DataPoint @@ -274,11 +275,9 @@ class NeptuneAnalyticsAdapter(NeptuneGraphDB, VectorDBInterface): limit = self._TOPK_UPPER_BOUND if query_vector and query_text: - raise InvalidValueError( - message="The search function accepts either text or embedding as input, but not both." - ) + raise MutuallyExclusiveQueryParametersError() elif query_text is None and query_vector is None: - raise InvalidValueError(message="One of query_text or query_vector must be provided!") + raise MissingQueryParameterError() elif query_vector: embedding = query_vector else: diff --git a/cognee/infrastructure/databases/vector/chromadb/ChromaDBAdapter.py b/cognee/infrastructure/databases/vector/chromadb/ChromaDBAdapter.py index 84c781db6..96e0a3bf6 100644 --- a/cognee/infrastructure/databases/vector/chromadb/ChromaDBAdapter.py +++ b/cognee/infrastructure/databases/vector/chromadb/ChromaDBAdapter.py @@ -4,13 +4,13 @@ from uuid import UUID from typing import List, Optional from chromadb import AsyncHttpClient, Settings -from cognee.exceptions import InvalidValueError from cognee.shared.logging_utils import get_logger from cognee.modules.storage.utils import get_own_properties from cognee.infrastructure.engine import DataPoint from cognee.infrastructure.engine.utils import parse_id from cognee.infrastructure.databases.vector.exceptions import CollectionNotFoundError from cognee.infrastructure.databases.vector.models.ScoredResult import ScoredResult +from cognee.infrastructure.databases.exceptions import MissingQueryParameterError from ..embeddings.EmbeddingEngine import EmbeddingEngine from ..vector_db_interface import VectorDBInterface @@ -378,7 +378,7 @@ class ChromaDBAdapter(VectorDBInterface): Returns a list of ScoredResult instances representing the search results. """ if query_text is None and query_vector is None: - raise InvalidValueError(message="One of query_text or query_vector must be provided!") + raise MissingQueryParameterError() if query_text and not query_vector: query_vector = (await self.embedding_engine.embed_text([query_text]))[0] diff --git a/cognee/infrastructure/databases/vector/embeddings/LiteLLMEmbeddingEngine.py b/cognee/infrastructure/databases/vector/embeddings/LiteLLMEmbeddingEngine.py index e3cdaea00..dae664907 100644 --- a/cognee/infrastructure/databases/vector/embeddings/LiteLLMEmbeddingEngine.py +++ b/cognee/infrastructure/databases/vector/embeddings/LiteLLMEmbeddingEngine.py @@ -6,7 +6,7 @@ import math import litellm import os from cognee.infrastructure.databases.vector.embeddings.EmbeddingEngine import EmbeddingEngine -from cognee.infrastructure.databases.exceptions.EmbeddingException import EmbeddingException +from cognee.infrastructure.databases.exceptions import EmbeddingException from cognee.infrastructure.llm.tokenizer.Gemini import ( GeminiTokenizer, ) diff --git a/cognee/infrastructure/databases/vector/exceptions/exceptions.py b/cognee/infrastructure/databases/vector/exceptions/exceptions.py index b6a8237d8..ecd106c0d 100644 --- a/cognee/infrastructure/databases/vector/exceptions/exceptions.py +++ b/cognee/infrastructure/databases/vector/exceptions/exceptions.py @@ -1,12 +1,12 @@ from fastapi import status -from cognee.exceptions import CriticalError +from cognee.exceptions import CogneeValidationError -class CollectionNotFoundError(CriticalError): +class CollectionNotFoundError(CogneeValidationError): """ Represents an error that occurs when a requested collection cannot be found. - This class extends the CriticalError to handle specific cases where a requested + This class extends the CogneeValidationError to handle specific cases where a requested collection is unavailable. It can be initialized with a custom message and allows for logging options including log level and whether to log the error. """ diff --git a/cognee/infrastructure/databases/vector/lancedb/LanceDBAdapter.py b/cognee/infrastructure/databases/vector/lancedb/LanceDBAdapter.py index f37c83113..0184ec3ee 100644 --- a/cognee/infrastructure/databases/vector/lancedb/LanceDBAdapter.py +++ b/cognee/infrastructure/databases/vector/lancedb/LanceDBAdapter.py @@ -5,7 +5,7 @@ from pydantic import BaseModel from lancedb.pydantic import LanceModel, Vector from typing import Generic, List, Optional, TypeVar, Union, get_args, get_origin, get_type_hints -from cognee.exceptions import InvalidValueError +from cognee.infrastructure.databases.exceptions import MissingQueryParameterError from cognee.infrastructure.engine import DataPoint from cognee.infrastructure.engine.utils import parse_id from cognee.infrastructure.files.storage import get_file_storage @@ -228,7 +228,7 @@ class LanceDBAdapter(VectorDBInterface): normalized: bool = True, ): if query_text is None and query_vector is None: - raise InvalidValueError(message="One of query_text or query_vector must be provided!") + raise MissingQueryParameterError() if query_text and not query_vector: query_vector = (await self.embedding_engine.embed_text([query_text]))[0] diff --git a/cognee/infrastructure/databases/vector/pgvector/PGVectorAdapter.py b/cognee/infrastructure/databases/vector/pgvector/PGVectorAdapter.py index ac40b91b8..4dfd9792f 100644 --- a/cognee/infrastructure/databases/vector/pgvector/PGVectorAdapter.py +++ b/cognee/infrastructure/databases/vector/pgvector/PGVectorAdapter.py @@ -9,7 +9,7 @@ from sqlalchemy.exc import ProgrammingError from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_exponential from asyncpg import DeadlockDetectedError, DuplicateTableError, UniqueViolationError -from cognee.exceptions import InvalidValueError + from cognee.shared.logging_utils import get_logger from cognee.infrastructure.engine import DataPoint from cognee.infrastructure.engine.utils import parse_id @@ -17,6 +17,7 @@ from cognee.infrastructure.databases.relational import get_relational_engine from distributed.utils import override_distributed from distributed.tasks.queued_add_data_points import queued_add_data_points +from cognee.infrastructure.databases.exceptions import MissingQueryParameterError from ...relational.ModelBase import Base from ...relational.sqlalchemy.SqlAlchemyAdapter import SQLAlchemyAdapter @@ -275,7 +276,7 @@ class PGVectorAdapter(SQLAlchemyAdapter, VectorDBInterface): return metadata.tables[collection_name] else: raise CollectionNotFoundError( - f"Collection '{collection_name}' not found!", log_level="DEBUG" + f"Collection '{collection_name}' not found!", ) async def retrieve(self, collection_name: str, data_point_ids: List[str]): @@ -302,7 +303,7 @@ class PGVectorAdapter(SQLAlchemyAdapter, VectorDBInterface): with_vector: bool = False, ) -> List[ScoredResult]: if query_text is None and query_vector is None: - raise InvalidValueError(message="One of query_text or query_vector must be provided!") + raise MissingQueryParameterError() if query_text and not query_vector: query_vector = (await self.embedding_engine.embed_text([query_text]))[0] diff --git a/cognee/infrastructure/llm/exceptions.py b/cognee/infrastructure/llm/exceptions.py index af3aa5832..1d390a951 100644 --- a/cognee/infrastructure/llm/exceptions.py +++ b/cognee/infrastructure/llm/exceptions.py @@ -1,5 +1,33 @@ -from cognee.exceptions.exceptions import CriticalError +from cognee.exceptions.exceptions import CogneeValidationError -class ContentPolicyFilterError(CriticalError): +class ContentPolicyFilterError(CogneeValidationError): pass + + +class LLMAPIKeyNotSetError(CogneeValidationError): + """ + Raised when the LLM API key is not set in the configuration. + """ + + def __init__(self, message: str = "LLM API key is not set."): + super().__init__(message=message, name="LLMAPIKeyNotSetError") + + +class UnsupportedLLMProviderError(CogneeValidationError): + """ + Raised when an unsupported LLM provider is specified in the configuration. + """ + + def __init__(self, provider: str): + message = f"Unsupported LLM provider: {provider}" + super().__init__(message=message, name="UnsupportedLLMProviderError") + + +class MissingSystemPromptPathError(CogneeValidationError): + def __init__( + self, + name: str = "MissingSystemPromptPathError", + ): + message = "No system prompt path provided." + super().__init__(message, name) diff --git a/cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/anthropic/adapter.py b/cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/anthropic/adapter.py index 636e6c0f2..6845fb6aa 100644 --- a/cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/anthropic/adapter.py +++ b/cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/anthropic/adapter.py @@ -2,7 +2,7 @@ from typing import Type from pydantic import BaseModel import instructor -from cognee.exceptions import InvalidValueError +from cognee.infrastructure.llm.exceptions import MissingSystemPromptPathError from cognee.infrastructure.llm.structured_output_framework.litellm_instructor.llm.llm_interface import ( LLMInterface, ) @@ -89,7 +89,7 @@ class AnthropicAdapter(LLMInterface): if not text_input: text_input = "No user input provided." if not system_prompt: - raise InvalidValueError(message="No system prompt path provided.") + raise MissingSystemPromptPathError() system_prompt = LLMGateway.read_query_prompt(system_prompt) diff --git a/cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/gemini/adapter.py b/cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/gemini/adapter.py index 61d42ff5f..3cde1fdc4 100644 --- a/cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/gemini/adapter.py +++ b/cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/gemini/adapter.py @@ -5,7 +5,7 @@ from litellm import acompletion, JSONSchemaValidationError from cognee.shared.logging_utils import get_logger from cognee.modules.observability.get_observe import get_observe -from cognee.exceptions import InvalidValueError +from cognee.infrastructure.llm.exceptions import MissingSystemPromptPathError from cognee.infrastructure.llm.structured_output_framework.litellm_instructor.llm.llm_interface import ( LLMInterface, ) @@ -118,7 +118,7 @@ class GeminiAdapter(LLMInterface): """ Format and display the prompt for a user query. - Raises an InvalidValueError if no system prompt is provided. + Raises an MissingQueryParameterError if no system prompt is provided. Parameters: ----------- @@ -135,7 +135,7 @@ class GeminiAdapter(LLMInterface): if not text_input: text_input = "No user input provided." if not system_prompt: - raise InvalidValueError(message="No system prompt path provided.") + raise MissingSystemPromptPathError() system_prompt = LLMGateway.read_query_prompt(system_prompt) formatted_prompt = ( diff --git a/cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/get_llm_client.py b/cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/get_llm_client.py index 22d101077..fd347aef3 100644 --- a/cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/get_llm_client.py +++ b/cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/get_llm_client.py @@ -2,11 +2,14 @@ from enum import Enum -from cognee.exceptions import InvalidValueError from cognee.infrastructure.llm import get_llm_config from cognee.infrastructure.llm.structured_output_framework.litellm_instructor.llm.ollama.adapter import ( OllamaAPIAdapter, ) +from cognee.infrastructure.llm.exceptions import ( + LLMAPIKeyNotSetError, + UnsupportedLLMProviderError, +) # Define an Enum for LLM Providers @@ -35,7 +38,7 @@ def get_llm_client(): This function retrieves the configuration for the LLM provider and model, and initializes the appropriate LLM client adapter accordingly. It raises an - InvalidValueError if the LLM API key is not set for certain providers or if the provider + LLMAPIKeyNotSetError if the LLM API key is not set for certain providers or if the provider is unsupported. Returns: @@ -59,7 +62,7 @@ def get_llm_client(): if provider == LLMProvider.OPENAI: if llm_config.llm_api_key is None: - raise InvalidValueError(message="LLM API key is not set.") + raise LLMAPIKeyNotSetError() from cognee.infrastructure.llm.structured_output_framework.litellm_instructor.llm.openai.adapter import ( OpenAIAdapter, @@ -80,7 +83,7 @@ def get_llm_client(): elif provider == LLMProvider.OLLAMA: if llm_config.llm_api_key is None: - raise InvalidValueError(message="LLM API key is not set.") + raise LLMAPIKeyNotSetError() from cognee.infrastructure.llm.structured_output_framework.litellm_instructor.llm.generic_llm_api.adapter import ( GenericAPIAdapter, @@ -103,7 +106,7 @@ def get_llm_client(): elif provider == LLMProvider.CUSTOM: if llm_config.llm_api_key is None: - raise InvalidValueError(message="LLM API key is not set.") + raise LLMAPIKeyNotSetError() from cognee.infrastructure.llm.structured_output_framework.litellm_instructor.llm.generic_llm_api.adapter import ( GenericAPIAdapter, @@ -122,7 +125,7 @@ def get_llm_client(): elif provider == LLMProvider.GEMINI: if llm_config.llm_api_key is None: - raise InvalidValueError(message="LLM API key is not set.") + raise LLMAPIKeyNotSetError() from cognee.infrastructure.llm.structured_output_framework.litellm_instructor.llm.gemini.adapter import ( GeminiAdapter, @@ -138,4 +141,4 @@ def get_llm_client(): ) else: - raise InvalidValueError(message=f"Unsupported LLM provider: {provider}") + raise UnsupportedLLMProviderError(provider) diff --git a/cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/openai/adapter.py b/cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/openai/adapter.py index 69c1bac1c..c3c215896 100644 --- a/cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/openai/adapter.py +++ b/cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/openai/adapter.py @@ -7,12 +7,14 @@ from openai import ContentFilterFinishReasonError from litellm.exceptions import ContentPolicyViolationError from instructor.exceptions import InstructorRetryException -from cognee.exceptions import InvalidValueError from cognee.infrastructure.llm.LLMGateway import LLMGateway from cognee.infrastructure.llm.structured_output_framework.litellm_instructor.llm.llm_interface import ( LLMInterface, ) -from cognee.infrastructure.llm.exceptions import ContentPolicyFilterError +from cognee.infrastructure.llm.exceptions import ( + ContentPolicyFilterError, + MissingSystemPromptPathError, +) from cognee.infrastructure.files.utils.open_data_file import open_data_file from cognee.infrastructure.llm.structured_output_framework.litellm_instructor.llm.rate_limiter import ( rate_limit_async, @@ -308,7 +310,7 @@ class OpenAIAdapter(LLMInterface): Format and display the prompt for a user query. This method formats the prompt using the provided user input and system prompt, - returning a string representation. Raises InvalidValueError if the system prompt is not + returning a string representation. Raises MissingSystemPromptPathError if the system prompt is not provided. Parameters: @@ -325,7 +327,7 @@ class OpenAIAdapter(LLMInterface): if not text_input: text_input = "No user input provided." if not system_prompt: - raise InvalidValueError(message="No system prompt path provided.") + raise MissingSystemPromptPathError() system_prompt = LLMGateway.read_query_prompt(system_prompt) formatted_prompt = ( diff --git a/cognee/modules/data/exceptions/exceptions.py b/cognee/modules/data/exceptions/exceptions.py index 214bf5381..ac3b68e64 100644 --- a/cognee/modules/data/exceptions/exceptions.py +++ b/cognee/modules/data/exceptions/exceptions.py @@ -1,8 +1,11 @@ -from cognee.exceptions import CogneeApiError +from cognee.exceptions import ( + CogneeValidationError, + CogneeConfigurationError, +) from fastapi import status -class UnstructuredLibraryImportError(CogneeApiError): +class UnstructuredLibraryImportError(CogneeConfigurationError): def __init__( self, message: str = "Import error. Unstructured library is not installed.", @@ -12,7 +15,7 @@ class UnstructuredLibraryImportError(CogneeApiError): super().__init__(message, name, status_code) -class UnauthorizedDataAccessError(CogneeApiError): +class UnauthorizedDataAccessError(CogneeValidationError): def __init__( self, message: str = "User does not have permission to access this data.", @@ -22,7 +25,7 @@ class UnauthorizedDataAccessError(CogneeApiError): super().__init__(message, name, status_code) -class DatasetNotFoundError(CogneeApiError): +class DatasetNotFoundError(CogneeValidationError): def __init__( self, message: str = "Dataset not found.", @@ -32,7 +35,7 @@ class DatasetNotFoundError(CogneeApiError): super().__init__(message, name, status_code) -class DatasetTypeError(CogneeApiError): +class DatasetTypeError(CogneeValidationError): def __init__( self, message: str = "Dataset type not supported.", @@ -40,3 +43,13 @@ class DatasetTypeError(CogneeApiError): status_code=status.HTTP_400_BAD_REQUEST, ): super().__init__(message, name, status_code) + + +class InvalidTableAttributeError(CogneeValidationError): + def __init__( + self, + message: str = "The provided data object is missing the required '__tablename__' attribute.", + name: str = "InvalidTableAttributeError", + status_code: int = status.HTTP_400_BAD_REQUEST, + ): + super().__init__(message, name, status_code) diff --git a/cognee/modules/data/methods/delete_data.py b/cognee/modules/data/methods/delete_data.py index 2d87d73a5..5425acac3 100644 --- a/cognee/modules/data/methods/delete_data.py +++ b/cognee/modules/data/methods/delete_data.py @@ -1,4 +1,4 @@ -from cognee.exceptions import InvalidAttributeError +from cognee.modules.data.exceptions.exceptions import InvalidTableAttributeError from cognee.modules.data.models import Data from cognee.infrastructure.databases.relational import get_relational_engine @@ -13,9 +13,7 @@ async def delete_data(data: Data): ValueError: If the data object is invalid. """ if not hasattr(data, "__tablename__"): - raise InvalidAttributeError( - message="The provided data object is missing the required '__tablename__' attribute." - ) + raise InvalidTableAttributeError() db_engine = get_relational_engine() diff --git a/cognee/modules/data/processing/document_types/exceptions/exceptions.py b/cognee/modules/data/processing/document_types/exceptions/exceptions.py index b5126a8a7..a05e64d60 100644 --- a/cognee/modules/data/processing/document_types/exceptions/exceptions.py +++ b/cognee/modules/data/processing/document_types/exceptions/exceptions.py @@ -1,8 +1,8 @@ -from cognee.exceptions import CogneeApiError +from cognee.exceptions import CogneeSystemError from fastapi import status -class PyPdfInternalError(CogneeApiError): +class PyPdfInternalError(CogneeSystemError): """Internal pypdf error""" def __init__( diff --git a/cognee/modules/graph/cognee_graph/CogneeGraph.py b/cognee/modules/graph/cognee_graph/CogneeGraph.py index ca1984dfe..ed867ae24 100644 --- a/cognee/modules/graph/cognee_graph/CogneeGraph.py +++ b/cognee/modules/graph/cognee_graph/CogneeGraph.py @@ -2,8 +2,11 @@ import time from cognee.shared.logging_utils import get_logger from typing import List, Dict, Union, Optional, Type -from cognee.exceptions import InvalidValueError -from cognee.modules.graph.exceptions import EntityNotFoundError, EntityAlreadyExistsError +from cognee.modules.graph.exceptions import ( + EntityNotFoundError, + EntityAlreadyExistsError, + InvalidDimensionsError, +) from cognee.infrastructure.databases.graph.graph_db_interface import GraphDBInterface from cognee.modules.graph.cognee_graph.CogneeGraphElements import Node, Edge from cognee.modules.graph.cognee_graph.CogneeAbstractGraph import CogneeAbstractGraph @@ -66,8 +69,7 @@ class CogneeGraph(CogneeAbstractGraph): node_name: Optional[List[str]] = None, ) -> None: if node_dimension < 1 or edge_dimension < 1: - raise InvalidValueError(message="Dimensions must be positive integers") - + raise InvalidDimensionsError() try: import time diff --git a/cognee/modules/graph/cognee_graph/CogneeGraphElements.py b/cognee/modules/graph/cognee_graph/CogneeGraphElements.py index 063248ee2..0ca9c4fb9 100644 --- a/cognee/modules/graph/cognee_graph/CogneeGraphElements.py +++ b/cognee/modules/graph/cognee_graph/CogneeGraphElements.py @@ -1,7 +1,6 @@ import numpy as np from typing import List, Dict, Optional, Any, Union - -from cognee.exceptions import InvalidValueError +from cognee.modules.graph.exceptions import InvalidDimensionsError, DimensionOutOfRangeError class Node: @@ -24,7 +23,7 @@ class Node: self, node_id: str, attributes: Optional[Dict[str, Any]] = None, dimension: int = 1 ): if dimension <= 0: - raise InvalidValueError(message="Dimension must be a positive integer") + raise InvalidDimensionsError() self.id = node_id self.attributes = attributes if attributes is not None else {} self.attributes["vector_distance"] = float("inf") @@ -58,9 +57,7 @@ class Node: def is_node_alive_in_dimension(self, dimension: int) -> bool: if dimension < 0 or dimension >= len(self.status): - raise InvalidValueError( - message=f"Dimension {dimension} is out of range. Valid range is 0 to {len(self.status) - 1}." - ) + raise DimensionOutOfRangeError(dimension=dimension, max_index=len(self.status) - 1) return self.status[dimension] == 1 def add_attribute(self, key: str, value: Any) -> None: @@ -110,7 +107,7 @@ class Edge: dimension: int = 1, ): if dimension <= 0: - raise InvalidValueError(message="Dimensions must be a positive integer.") + raise InvalidDimensionsError() self.node1 = node1 self.node2 = node2 self.attributes = attributes if attributes is not None else {} @@ -120,9 +117,7 @@ class Edge: def is_edge_alive_in_dimension(self, dimension: int) -> bool: if dimension < 0 or dimension >= len(self.status): - raise InvalidValueError( - message=f"Dimension {dimension} is out of range. Valid range is 0 to {len(self.status) - 1}." - ) + raise DimensionOutOfRangeError(dimension=dimension, max_index=len(self.status) - 1) return self.status[dimension] == 1 def add_attribute(self, key: str, value: Any) -> None: diff --git a/cognee/modules/graph/exceptions/__init__.py b/cognee/modules/graph/exceptions/__init__.py index 5cf600099..04bec74ad 100644 --- a/cognee/modules/graph/exceptions/__init__.py +++ b/cognee/modules/graph/exceptions/__init__.py @@ -7,4 +7,6 @@ This module defines a set of exceptions for handling various graph errors from .exceptions import ( EntityNotFoundError, EntityAlreadyExistsError, + InvalidDimensionsError, + DimensionOutOfRangeError, ) diff --git a/cognee/modules/graph/exceptions/exceptions.py b/cognee/modules/graph/exceptions/exceptions.py index 854e620ff..67f4200ff 100644 --- a/cognee/modules/graph/exceptions/exceptions.py +++ b/cognee/modules/graph/exceptions/exceptions.py @@ -1,8 +1,8 @@ -from cognee.exceptions import CogneeApiError +from cognee.exceptions import CogneeValidationError from fastapi import status -class EntityNotFoundError(CogneeApiError): +class EntityNotFoundError(CogneeValidationError): """Database returns nothing""" def __init__( @@ -14,7 +14,7 @@ class EntityNotFoundError(CogneeApiError): super().__init__(message, name, status_code) -class EntityAlreadyExistsError(CogneeApiError): +class EntityAlreadyExistsError(CogneeValidationError): """Conflict detected, like trying to create a resource that already exists""" def __init__( @@ -24,3 +24,25 @@ class EntityAlreadyExistsError(CogneeApiError): status_code=status.HTTP_409_CONFLICT, ): super().__init__(message, name, status_code) + + +class InvalidDimensionsError(CogneeValidationError): + def __init__( + self, + name: str = "InvalidDimensionsError", + status_code: int = status.HTTP_400_BAD_REQUEST, + ): + message = "Dimensions must be positive integers." + super().__init__(message, name, status_code) + + +class DimensionOutOfRangeError(CogneeValidationError): + def __init__( + self, + dimension: int, + max_index: int, + name: str = "DimensionOutOfRangeError", + status_code: int = status.HTTP_400_BAD_REQUEST, + ): + message = f"Dimension {dimension} is out of range. Valid range is 0 to {max_index}." + super().__init__(message, name, status_code) diff --git a/cognee/modules/ingestion/exceptions/exceptions.py b/cognee/modules/ingestion/exceptions/exceptions.py index 08991a946..d43b69d60 100644 --- a/cognee/modules/ingestion/exceptions/exceptions.py +++ b/cognee/modules/ingestion/exceptions/exceptions.py @@ -1,8 +1,8 @@ -from cognee.exceptions import CogneeApiError +from cognee.exceptions import CogneeValidationError from fastapi import status -class IngestionError(CogneeApiError): +class IngestionError(CogneeValidationError): def __init__( self, message: str = "Type of data sent to classify not supported.", diff --git a/cognee/modules/ontology/exceptions/exceptions.py b/cognee/modules/ontology/exceptions/exceptions.py index 511e41524..daa8dcdb5 100644 --- a/cognee/modules/ontology/exceptions/exceptions.py +++ b/cognee/modules/ontology/exceptions/exceptions.py @@ -1,8 +1,8 @@ -from cognee.exceptions import CogneeApiError +from cognee.exceptions import CogneeSystemError from fastapi import status -class OntologyInitializationError(CogneeApiError): +class OntologyInitializationError(CogneeSystemError): def __init__( self, message: str = "Ontology initialization failed", @@ -12,7 +12,7 @@ class OntologyInitializationError(CogneeApiError): super().__init__(message, name, status_code) -class FindClosestMatchError(CogneeApiError): +class FindClosestMatchError(CogneeSystemError): def __init__( self, message: str = "Error in find_closest_match", @@ -22,7 +22,7 @@ class FindClosestMatchError(CogneeApiError): super().__init__(message, name, status_code) -class GetSubgraphError(CogneeApiError): +class GetSubgraphError(CogneeSystemError): def __init__( self, message: str = "Failed to retrieve subgraph", diff --git a/cognee/modules/pipelines/exceptions/exceptions.py b/cognee/modules/pipelines/exceptions/exceptions.py index 0a4863075..646a51286 100644 --- a/cognee/modules/pipelines/exceptions/exceptions.py +++ b/cognee/modules/pipelines/exceptions/exceptions.py @@ -1,8 +1,8 @@ -from cognee.exceptions import CogneeApiError +from cognee.exceptions import CogneeSystemError from fastapi import status -class PipelineRunFailedError(CogneeApiError): +class PipelineRunFailedError(CogneeSystemError): def __init__( self, message: str = "Pipeline run failed.", diff --git a/cognee/modules/retrieval/exceptions/exceptions.py b/cognee/modules/retrieval/exceptions/exceptions.py index a403f3e44..3e934909b 100644 --- a/cognee/modules/retrieval/exceptions/exceptions.py +++ b/cognee/modules/retrieval/exceptions/exceptions.py @@ -1,8 +1,8 @@ from fastapi import status -from cognee.exceptions import CogneeApiError, CriticalError +from cognee.exceptions import CogneeValidationError, CogneeSystemError -class SearchTypeNotSupported(CogneeApiError): +class SearchTypeNotSupported(CogneeValidationError): def __init__( self, message: str = "CYPHER search type not supported by the adapter.", @@ -12,7 +12,7 @@ class SearchTypeNotSupported(CogneeApiError): super().__init__(message, name, status_code) -class CypherSearchError(CogneeApiError): +class CypherSearchError(CogneeSystemError): def __init__( self, message: str = "An error occurred during the execution of the Cypher query.", @@ -22,11 +22,17 @@ class CypherSearchError(CogneeApiError): super().__init__(message, name, status_code) -class NoDataError(CriticalError): - message: str = "No data found in the system, please add data first." +class NoDataError(CogneeValidationError): + def __init__( + self, + message: str = "No data found in the system, please add data first.", + name: str = "NoDataError", + status_code: int = status.HTTP_404_NOT_FOUND, + ): + super().__init__(message, name, status_code) -class CollectionDistancesNotFoundError(CogneeApiError): +class CollectionDistancesNotFoundError(CogneeValidationError): def __init__( self, message: str = "No collection distances found for the given query.", diff --git a/cognee/modules/search/exceptions/__init__.py b/cognee/modules/search/exceptions/__init__.py new file mode 100644 index 000000000..a019da249 --- /dev/null +++ b/cognee/modules/search/exceptions/__init__.py @@ -0,0 +1,7 @@ +""" +Custom exceptions for the Cognee API. + +This module defines a set of exceptions for handling various data errors +""" + +from .exceptions import UnsupportedSearchTypeError diff --git a/cognee/modules/search/exceptions/exceptions.py b/cognee/modules/search/exceptions/exceptions.py new file mode 100644 index 000000000..ddc877700 --- /dev/null +++ b/cognee/modules/search/exceptions/exceptions.py @@ -0,0 +1,15 @@ +from cognee.exceptions import ( + CogneeValidationError, +) +from fastapi import status + + +class UnsupportedSearchTypeError(CogneeValidationError): + def __init__( + self, + search_type: str, + name: str = "UnsupportedSearchTypeError", + status_code: int = status.HTTP_400_BAD_REQUEST, + ): + message = f"Unsupported search type: {search_type}" + super().__init__(message, name, status_code) diff --git a/cognee/modules/search/methods/search.py b/cognee/modules/search/methods/search.py index 365920019..f431a498e 100644 --- a/cognee/modules/search/methods/search.py +++ b/cognee/modules/search/methods/search.py @@ -3,9 +3,8 @@ import json import asyncio from uuid import UUID from typing import Callable, List, Optional, Type, Union - +from cognee.modules.search.exceptions import UnsupportedSearchTypeError from cognee.context_global_variables import set_database_global_context_variables -from cognee.exceptions import InvalidValueError from cognee.modules.retrieval.chunks_retriever import ChunksRetriever from cognee.modules.retrieval.insights_retriever import InsightsRetriever from cognee.modules.retrieval.summaries_retriever import SummariesRetriever @@ -136,7 +135,7 @@ async def specific_search( search_task = search_tasks.get(query_type) if search_task is None: - raise InvalidValueError(message=f"Unsupported search type: {query_type}") + raise UnsupportedSearchTypeError(str(query_type)) send_telemetry("cognee.search EXECUTION STARTED", user.id) diff --git a/cognee/modules/users/exceptions/exceptions.py b/cognee/modules/users/exceptions/exceptions.py index a7484c05e..85ca8ab63 100644 --- a/cognee/modules/users/exceptions/exceptions.py +++ b/cognee/modules/users/exceptions/exceptions.py @@ -1,8 +1,8 @@ -from cognee.exceptions import CogneeApiError +from cognee.exceptions import CogneeValidationError from fastapi import status -class RoleNotFoundError(CogneeApiError): +class RoleNotFoundError(CogneeValidationError): """User group not found""" def __init__( @@ -14,7 +14,7 @@ class RoleNotFoundError(CogneeApiError): super().__init__(message, name, status_code) -class TenantNotFoundError(CogneeApiError): +class TenantNotFoundError(CogneeValidationError): """User group not found""" def __init__( @@ -26,7 +26,7 @@ class TenantNotFoundError(CogneeApiError): super().__init__(message, name, status_code) -class UserNotFoundError(CogneeApiError): +class UserNotFoundError(CogneeValidationError): """User not found""" def __init__( @@ -38,7 +38,7 @@ class UserNotFoundError(CogneeApiError): super().__init__(message, name, status_code) -class PermissionDeniedError(CogneeApiError): +class PermissionDeniedError(CogneeValidationError): def __init__( self, message: str = "User does not have permission on documents.", @@ -48,7 +48,7 @@ class PermissionDeniedError(CogneeApiError): super().__init__(message, name, status_code) -class PermissionNotFoundError(CogneeApiError): +class PermissionNotFoundError(CogneeValidationError): def __init__( self, message: str = "Permission type does not exist.", diff --git a/cognee/shared/exceptions/exceptions.py b/cognee/shared/exceptions/exceptions.py index 4b4164995..43084e04b 100644 --- a/cognee/shared/exceptions/exceptions.py +++ b/cognee/shared/exceptions/exceptions.py @@ -1,8 +1,8 @@ -from cognee.exceptions import CogneeApiError +from cognee.exceptions import CogneeValidationError from fastapi import status -class IngestionError(CogneeApiError): +class IngestionError(CogneeValidationError): def __init__( self, message: str = "Failed to load data.", diff --git a/cognee/tasks/completion/exceptions/exceptions.py b/cognee/tasks/completion/exceptions/exceptions.py index ac105a966..e5ab5b67c 100644 --- a/cognee/tasks/completion/exceptions/exceptions.py +++ b/cognee/tasks/completion/exceptions/exceptions.py @@ -1,11 +1,11 @@ -from cognee.exceptions import CogneeApiError +from cognee.exceptions import CogneeValidationError from fastapi import status -class NoRelevantDataError(CogneeApiError): +class NoRelevantDataError(CogneeValidationError): """ Represents an error when no relevant data is found during a search. This class is a - subclass of CogneeApiError. + subclass of CogneeValidationError. Public methods: diff --git a/cognee/tasks/documents/classify_documents.py b/cognee/tasks/documents/classify_documents.py index 673e17c75..9fa512906 100644 --- a/cognee/tasks/documents/classify_documents.py +++ b/cognee/tasks/documents/classify_documents.py @@ -10,6 +10,7 @@ from cognee.modules.data.processing.document_types import ( ) from cognee.modules.engine.models.node_set import NodeSet from cognee.modules.engine.utils.generate_node_id import generate_node_id +from cognee.tasks.documents.exceptions import WrongDataDocumentInputError EXTENSION_TO_DOCUMENT_CLASS = { "pdf": PdfDocument, # Text documents @@ -111,6 +112,9 @@ async def classify_documents(data_documents: list[Data]) -> list[Document]: - list[Document]: A list of Document objects created based on the classified data documents. """ + if not isinstance(data_documents, list): + raise WrongDataDocumentInputError("data_documents") + documents = [] for data_item in data_documents: document = EXTENSION_TO_DOCUMENT_CLASS[data_item.extension]( diff --git a/cognee/tasks/documents/exceptions/__init__.py b/cognee/tasks/documents/exceptions/__init__.py new file mode 100644 index 000000000..a8602d6f5 --- /dev/null +++ b/cognee/tasks/documents/exceptions/__init__.py @@ -0,0 +1,11 @@ +""" +Custom exceptions for the Cognee API. + +This module defines a set of exceptions for handling various data errors +""" + +from .exceptions import ( + WrongDataDocumentInputError, + InvalidChunkSizeError, + InvalidChunkerError, +) diff --git a/cognee/tasks/documents/exceptions/exceptions.py b/cognee/tasks/documents/exceptions/exceptions.py new file mode 100644 index 000000000..737e9a3d1 --- /dev/null +++ b/cognee/tasks/documents/exceptions/exceptions.py @@ -0,0 +1,36 @@ +from cognee.exceptions import ( + CogneeValidationError, + CogneeConfigurationError, +) +from fastapi import status + + +class WrongDataDocumentInputError(CogneeValidationError): + """Raised when a wrong data document is provided.""" + + def __init__( + self, + field: str, + name: str = "WrongDataDocumentInputError", + status_code: int = status.HTTP_422_UNPROCESSABLE_ENTITY, + ): + message = f"Missing of invalid parameter: '{field}'." + super().__init__(message, name, status_code) + + +class InvalidChunkSizeError(CogneeValidationError): + def __init__(self, value): + super().__init__( + message=f"max_chunk_size must be a positive integer (got {value}).", + name="InvalidChunkSizeError", + status_code=status.HTTP_400_BAD_REQUEST, + ) + + +class InvalidChunkerError(CogneeValidationError): + def __init__(self): + super().__init__( + message="chunker must be a valid Chunker class.", + name="InvalidChunkerError", + status_code=status.HTTP_400_BAD_REQUEST, + ) diff --git a/cognee/tasks/documents/extract_chunks_from_documents.py b/cognee/tasks/documents/extract_chunks_from_documents.py index 216185495..d52380c8d 100644 --- a/cognee/tasks/documents/extract_chunks_from_documents.py +++ b/cognee/tasks/documents/extract_chunks_from_documents.py @@ -8,6 +8,7 @@ from cognee.modules.data.models import Data from cognee.infrastructure.databases.relational import get_relational_engine from cognee.modules.chunking.TextChunker import TextChunker from cognee.modules.chunking.Chunker import Chunker +from cognee.tasks.documents.exceptions import InvalidChunkSizeError, InvalidChunkerError async def update_document_token_count(document_id: UUID, token_count: int) -> None: @@ -37,6 +38,13 @@ async def extract_chunks_from_documents( - The `read` method of the `Document` class must be implemented to support the chunking operation. - The `chunker` parameter determines the chunking logic and should align with the document type. """ + if not isinstance(max_chunk_size, int) or max_chunk_size <= 0: + raise InvalidChunkSizeError(max_chunk_size) + if not isinstance(chunker, type): + raise InvalidChunkerError() + if not hasattr(chunker, "read"): + raise InvalidChunkerError() + for document in documents: document_token_count = 0 @@ -48,5 +56,3 @@ async def extract_chunks_from_documents( yield document_chunk await update_document_token_count(document.id, document_token_count) - - # todo rita diff --git a/cognee/tasks/graph/exceptions/__init__.py b/cognee/tasks/graph/exceptions/__init__.py new file mode 100644 index 000000000..d91bbbd36 --- /dev/null +++ b/cognee/tasks/graph/exceptions/__init__.py @@ -0,0 +1,12 @@ +""" +Custom exceptions for the Cognee API. + +This module defines a set of exceptions for handling various data errors +""" + +from .exceptions import ( + InvalidDataChunksError, + InvalidGraphModelError, + InvalidOntologyAdapterError, + InvalidChunkGraphInputError, +) diff --git a/cognee/tasks/graph/exceptions/exceptions.py b/cognee/tasks/graph/exceptions/exceptions.py new file mode 100644 index 000000000..c09ee1c08 --- /dev/null +++ b/cognee/tasks/graph/exceptions/exceptions.py @@ -0,0 +1,41 @@ +from cognee.exceptions import ( + CogneeValidationError, + CogneeConfigurationError, +) +from fastapi import status + + +class InvalidDataChunksError(CogneeValidationError): + def __init__(self, detail: str): + super().__init__( + message=f"Invalid data_chunks: {detail}", + name="InvalidDataChunksError", + status_code=status.HTTP_400_BAD_REQUEST, + ) + + +class InvalidGraphModelError(CogneeValidationError): + def __init__(self, got): + super().__init__( + message=f"graph_model must be a subclass of BaseModel (got {got}).", + name="InvalidGraphModelError", + status_code=status.HTTP_400_BAD_REQUEST, + ) + + +class InvalidOntologyAdapterError(CogneeConfigurationError): + def __init__(self, got): + super().__init__( + message=f"ontology_adapter lacks required interface (got {got}).", + name="InvalidOntologyAdapterError", + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + ) + + +class InvalidChunkGraphInputError(CogneeValidationError): + def __init__(self, detail: str): + super().__init__( + message=f"Invalid chunk inputs or LLM Chunkgraphs: {detail}", + name="InvalidChunkGraphInputError", + status_code=status.HTTP_400_BAD_REQUEST, + ) diff --git a/cognee/tasks/graph/extract_graph_from_data.py b/cognee/tasks/graph/extract_graph_from_data.py index aa415d504..019e9e4a1 100644 --- a/cognee/tasks/graph/extract_graph_from_data.py +++ b/cognee/tasks/graph/extract_graph_from_data.py @@ -12,6 +12,12 @@ from cognee.modules.graph.utils import ( ) from cognee.shared.data_models import KnowledgeGraph from cognee.infrastructure.llm.LLMGateway import LLMGateway +from cognee.tasks.graph.exceptions import ( + InvalidGraphModelError, + InvalidDataChunksError, + InvalidChunkGraphInputError, + InvalidOntologyAdapterError, +) async def integrate_chunk_graphs( @@ -21,6 +27,20 @@ async def integrate_chunk_graphs( ontology_adapter: OntologyResolver, ) -> List[DocumentChunk]: """Updates DocumentChunk objects, integrates data points and edges into databases.""" + + if not isinstance(data_chunks, list) or not isinstance(chunk_graphs, list): + raise InvalidChunkGraphInputError("data_chunks and chunk_graphs must be lists.") + if len(data_chunks) != len(chunk_graphs): + raise InvalidChunkGraphInputError( + f"length mismatch: {len(data_chunks)} chunks vs {len(chunk_graphs)} graphs." + ) + if not isinstance(graph_model, type) or not issubclass(graph_model, BaseModel): + raise InvalidGraphModelError(graph_model) + if ontology_adapter is None or not hasattr(ontology_adapter, "get_subgraph"): + raise InvalidOntologyAdapterError( + type(ontology_adapter).__name__ if ontology_adapter else "None" + ) + graph_engine = await get_graph_engine() if graph_model is not KnowledgeGraph: @@ -55,6 +75,14 @@ async def extract_graph_from_data( """ Extracts and integrates a knowledge graph from the text content of document chunks using a specified graph model. """ + + if not isinstance(data_chunks, list) or not data_chunks: + raise InvalidDataChunksError("must be a non-empty list of DocumentChunk.") + if not all(hasattr(c, "text") for c in data_chunks): + raise InvalidDataChunksError("each chunk must have a 'text' attribute") + if not isinstance(graph_model, type) or not issubclass(graph_model, BaseModel): + raise InvalidGraphModelError(graph_model) + chunk_graphs = await asyncio.gather( *[LLMGateway.extract_content_graph(chunk.text, graph_model) for chunk in data_chunks] ) diff --git a/cognee/tasks/ingestion/exceptions/__init__.py b/cognee/tasks/ingestion/exceptions/__init__.py new file mode 100644 index 000000000..f24792d1f --- /dev/null +++ b/cognee/tasks/ingestion/exceptions/__init__.py @@ -0,0 +1,8 @@ +""" +Custom exceptions for the Cognee API. + +This module defines a set of exceptions for handling various application errors, +such as System, Validation, Configuration or TransientErrors +""" + +from .exceptions import S3FileSystemNotFoundError diff --git a/cognee/tasks/ingestion/exceptions/exceptions.py b/cognee/tasks/ingestion/exceptions/exceptions.py new file mode 100644 index 000000000..9d07b9ab3 --- /dev/null +++ b/cognee/tasks/ingestion/exceptions/exceptions.py @@ -0,0 +1,12 @@ +from cognee.exceptions import CogneeSystemError +from fastapi import status + + +class S3FileSystemNotFoundError(CogneeSystemError): + def __init__( + self, + name: str = "S3FileSystemNotFoundError", + status_code: int = status.HTTP_500_INTERNAL_SERVER_ERROR, + ): + message = "Could not find S3FileSystem." + super().__init__(message, name, status_code) diff --git a/cognee/tasks/ingestion/resolve_data_directories.py b/cognee/tasks/ingestion/resolve_data_directories.py index 0f2f2a85f..1d3124a0c 100644 --- a/cognee/tasks/ingestion/resolve_data_directories.py +++ b/cognee/tasks/ingestion/resolve_data_directories.py @@ -1,6 +1,9 @@ import os from urllib.parse import urlparse from typing import List, Union, BinaryIO + +from cognee.tasks.ingestion.exceptions import S3FileSystemNotFoundError +from cognee.exceptions import CogneeSystemError from cognee.infrastructure.files.storage.s3_config import get_s3_config @@ -54,6 +57,8 @@ async def resolve_data_directories( else: s3_files.append(key) resolved_data.extend(s3_files) + else: + raise S3FileSystemNotFoundError() elif os.path.isdir(item): # If it's a directory if include_subdirectories: diff --git a/cognee/tasks/storage/add_data_points.py b/cognee/tasks/storage/add_data_points.py index 9b5c36c37..6cdc90ac9 100644 --- a/cognee/tasks/storage/add_data_points.py +++ b/cognee/tasks/storage/add_data_points.py @@ -5,9 +5,17 @@ from cognee.infrastructure.databases.graph import get_graph_engine from cognee.modules.graph.utils import deduplicate_nodes_and_edges, get_graph_from_model from .index_data_points import index_data_points from .index_graph_edges import index_graph_edges +from cognee.tasks.storage.exceptions import ( + InvalidDataPointsInAddDataPointsError, +) async def add_data_points(data_points: List[DataPoint]) -> List[DataPoint]: + if not isinstance(data_points, list): + raise InvalidDataPointsInAddDataPointsError("data_points must be a list.") + if not all(isinstance(dp, DataPoint) for dp in data_points): + raise InvalidDataPointsInAddDataPointsError("data_points: each item must be a DataPoint.") + nodes = [] edges = [] diff --git a/cognee/tasks/storage/exceptions/__init__.py b/cognee/tasks/storage/exceptions/__init__.py new file mode 100644 index 000000000..33dd215d6 --- /dev/null +++ b/cognee/tasks/storage/exceptions/__init__.py @@ -0,0 +1,9 @@ +""" +Custom exceptions for the Cognee API. + +This module defines a set of exceptions for handling various data errors +""" + +from .exceptions import ( + InvalidDataPointsInAddDataPointsError, +) diff --git a/cognee/tasks/storage/exceptions/exceptions.py b/cognee/tasks/storage/exceptions/exceptions.py new file mode 100644 index 000000000..9b2de9efd --- /dev/null +++ b/cognee/tasks/storage/exceptions/exceptions.py @@ -0,0 +1,13 @@ +from cognee.exceptions import ( + CogneeValidationError, +) +from fastapi import status + + +class InvalidDataPointsInAddDataPointsError(CogneeValidationError): + def __init__(self, detail: str): + super().__init__( + message=f"Invalid data_points: {detail}", + name="InvalidDataPointsInAddDataPointsError", + status_code=status.HTTP_400_BAD_REQUEST, + ) diff --git a/cognee/tasks/storage/index_data_points.py b/cognee/tasks/storage/index_data_points.py index 51b6c2d6e..9c363c04c 100644 --- a/cognee/tasks/storage/index_data_points.py +++ b/cognee/tasks/storage/index_data_points.py @@ -1,6 +1,6 @@ from cognee.shared.logging_utils import get_logger -from cognee.infrastructure.databases.exceptions.EmbeddingException import EmbeddingException +from cognee.infrastructure.databases.exceptions import EmbeddingException from cognee.infrastructure.databases.vector import get_vector_engine from cognee.infrastructure.engine import DataPoint diff --git a/cognee/tasks/summarization/exceptions/__init__.py b/cognee/tasks/summarization/exceptions/__init__.py new file mode 100644 index 000000000..502f973e7 --- /dev/null +++ b/cognee/tasks/summarization/exceptions/__init__.py @@ -0,0 +1,9 @@ +""" +Custom exceptions for the Cognee API. + +This module defines a set of exceptions for handling various data errors +""" + +from .exceptions import ( + InvalidSummaryInputsError, +) diff --git a/cognee/tasks/summarization/exceptions/exceptions.py b/cognee/tasks/summarization/exceptions/exceptions.py new file mode 100644 index 000000000..9e8e7197e --- /dev/null +++ b/cognee/tasks/summarization/exceptions/exceptions.py @@ -0,0 +1,14 @@ +from cognee.exceptions import ( + CogneeValidationError, + CogneeConfigurationError, +) +from fastapi import status + + +class InvalidSummaryInputsError(CogneeValidationError): + def __init__(self, detail: str): + super().__init__( + message=f"Invalid summarize_text inputs: {detail}", + name="InvalidSummaryInputsError", + status_code=status.HTTP_400_BAD_REQUEST, + ) diff --git a/cognee/tasks/summarization/summarize_text.py b/cognee/tasks/summarization/summarize_text.py index ca6964f83..f6dcc54a2 100644 --- a/cognee/tasks/summarization/summarize_text.py +++ b/cognee/tasks/summarization/summarize_text.py @@ -3,10 +3,11 @@ from typing import Type from uuid import uuid5 from pydantic import BaseModel +from cognee.tasks.summarization.exceptions import InvalidSummaryInputsError from cognee.modules.chunking.models.DocumentChunk import DocumentChunk from cognee.infrastructure.llm.LLMGateway import LLMGateway from cognee.modules.cognify.config import get_cognify_config -from .models import TextSummary +from cognee.tasks.summarization.models import TextSummary async def summarize_text( @@ -35,6 +36,12 @@ async def summarize_text( A list of TextSummary objects, each containing the summary of a corresponding DocumentChunk. """ + + if not isinstance(data_chunks, list): + raise InvalidSummaryInputsError("data_chunks must be a list.") + if not all(hasattr(c, "text") for c in data_chunks): + raise InvalidSummaryInputsError("each DocumentChunk must have a 'text' attribute.") + if len(data_chunks) == 0: return data_chunks diff --git a/cognee/tests/test_delete_by_id.py b/cognee/tests/test_delete_by_id.py index 36e0a57f5..8fc5395eb 100644 --- a/cognee/tests/test_delete_by_id.py +++ b/cognee/tests/test_delete_by_id.py @@ -7,7 +7,7 @@ from cognee.shared.logging_utils import get_logger from cognee.modules.users.methods import get_default_user, create_user from cognee.modules.users.permissions.methods import authorized_give_permission_on_datasets from cognee.modules.data.methods import get_dataset_data, get_datasets_by_name -from cognee.api.v1.delete.exceptions import DocumentNotFoundError, DatasetNotFoundError +from cognee.api.v1.exceptions import DocumentNotFoundError, DatasetNotFoundError logger = get_logger() diff --git a/cognee/tests/unit/modules/graph/cognee_graph_elements_test.py b/cognee/tests/unit/modules/graph/cognee_graph_elements_test.py index 000856b12..37ba113b5 100644 --- a/cognee/tests/unit/modules/graph/cognee_graph_elements_test.py +++ b/cognee/tests/unit/modules/graph/cognee_graph_elements_test.py @@ -1,8 +1,8 @@ import numpy as np import pytest -from cognee.exceptions import InvalidValueError from cognee.modules.graph.cognee_graph.CogneeGraphElements import Edge, Node +from cognee.modules.graph.exceptions import InvalidDimensionsError, DimensionOutOfRangeError def test_node_initialization(): @@ -16,7 +16,7 @@ def test_node_initialization(): def test_node_invalid_dimension(): """Test that initializing a Node with a non-positive dimension raises an error.""" - with pytest.raises(InvalidValueError, match="Dimension must be a positive integer"): + with pytest.raises(InvalidDimensionsError): Node("node1", dimension=0) @@ -69,7 +69,7 @@ def test_is_node_alive_in_dimension(): def test_node_alive_invalid_dimension(): """Test that checking alive status with an invalid dimension raises an error.""" node = Node("node1", dimension=1) - with pytest.raises(InvalidValueError, match="Dimension 1 is out of range"): + with pytest.raises(DimensionOutOfRangeError): node.is_node_alive_in_dimension(1) @@ -106,7 +106,7 @@ def test_edge_invalid_dimension(): """Test that initializing an Edge with a non-positive dimension raises an error.""" node1 = Node("node1") node2 = Node("node2") - with pytest.raises(InvalidValueError, match="Dimensions must be a positive integer."): + with pytest.raises(InvalidDimensionsError): Edge(node1, node2, dimension=0) @@ -125,7 +125,7 @@ def test_edge_alive_invalid_dimension(): node1 = Node("node1") node2 = Node("node2") edge = Edge(node1, node2, dimension=1) - with pytest.raises(InvalidValueError, match="Dimension 1 is out of range"): + with pytest.raises(DimensionOutOfRangeError): edge.is_edge_alive_in_dimension(1) diff --git a/cognee/tests/unit/modules/search/search_methods_test.py b/cognee/tests/unit/modules/search/search_methods_test.py index 14712f6d2..8e9afff1c 100644 --- a/cognee/tests/unit/modules/search/search_methods_test.py +++ b/cognee/tests/unit/modules/search/search_methods_test.py @@ -5,7 +5,7 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest from pylint.checkers.utils import node_type -from cognee.exceptions import InvalidValueError +from cognee.modules.search.exceptions import UnsupportedSearchTypeError from cognee.modules.search.methods.search import search, specific_search from cognee.modules.search.types import SearchType from cognee.modules.users.models import User @@ -217,7 +217,7 @@ async def test_specific_search_invalid_type(mock_user): query_type = "INVALID_TYPE" # Not a valid SearchType # Execute and verify - with pytest.raises(InvalidValueError) as excinfo: + with pytest.raises(UnsupportedSearchTypeError) as excinfo: await specific_search(query_type, query, mock_user) assert "Unsupported search type" in str(excinfo.value)