cognee 0.4.1__py3-none-any.whl → 0.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cognee/__init__.py +1 -0
- cognee/api/client.py +9 -5
- cognee/api/v1/add/add.py +2 -1
- cognee/api/v1/add/routers/get_add_router.py +3 -1
- cognee/api/v1/cognify/cognify.py +24 -16
- cognee/api/v1/cognify/routers/__init__.py +0 -1
- cognee/api/v1/cognify/routers/get_cognify_router.py +30 -1
- cognee/api/v1/datasets/routers/get_datasets_router.py +3 -3
- cognee/api/v1/ontologies/__init__.py +4 -0
- cognee/api/v1/ontologies/ontologies.py +158 -0
- cognee/api/v1/ontologies/routers/__init__.py +0 -0
- cognee/api/v1/ontologies/routers/get_ontology_router.py +109 -0
- cognee/api/v1/permissions/routers/get_permissions_router.py +41 -1
- cognee/api/v1/search/search.py +4 -0
- cognee/api/v1/ui/node_setup.py +360 -0
- cognee/api/v1/ui/npm_utils.py +50 -0
- cognee/api/v1/ui/ui.py +38 -68
- cognee/cli/commands/cognify_command.py +8 -1
- cognee/cli/config.py +1 -1
- cognee/context_global_variables.py +86 -9
- cognee/eval_framework/Dockerfile +29 -0
- cognee/eval_framework/answer_generation/answer_generation_executor.py +10 -0
- cognee/eval_framework/answer_generation/run_question_answering_module.py +1 -1
- cognee/eval_framework/corpus_builder/task_getters/get_cascade_graph_tasks.py +0 -2
- cognee/eval_framework/corpus_builder/task_getters/get_default_tasks_by_indices.py +4 -4
- cognee/eval_framework/eval_config.py +2 -2
- cognee/eval_framework/modal_run_eval.py +16 -28
- cognee/infrastructure/databases/cache/config.py +3 -1
- cognee/infrastructure/databases/cache/fscache/FsCacheAdapter.py +151 -0
- cognee/infrastructure/databases/cache/get_cache_engine.py +20 -10
- cognee/infrastructure/databases/dataset_database_handler/__init__.py +3 -0
- cognee/infrastructure/databases/dataset_database_handler/dataset_database_handler_interface.py +80 -0
- cognee/infrastructure/databases/dataset_database_handler/supported_dataset_database_handlers.py +18 -0
- cognee/infrastructure/databases/dataset_database_handler/use_dataset_database_handler.py +10 -0
- cognee/infrastructure/databases/exceptions/exceptions.py +16 -0
- cognee/infrastructure/databases/graph/config.py +7 -0
- cognee/infrastructure/databases/graph/get_graph_engine.py +3 -0
- cognee/infrastructure/databases/graph/graph_db_interface.py +15 -0
- cognee/infrastructure/databases/graph/kuzu/KuzuDatasetDatabaseHandler.py +81 -0
- cognee/infrastructure/databases/graph/kuzu/adapter.py +228 -0
- cognee/infrastructure/databases/graph/neo4j_driver/Neo4jAuraDevDatasetDatabaseHandler.py +168 -0
- cognee/infrastructure/databases/graph/neo4j_driver/adapter.py +80 -1
- cognee/infrastructure/databases/hybrid/neptune_analytics/NeptuneAnalyticsAdapter.py +9 -0
- cognee/infrastructure/databases/utils/__init__.py +3 -0
- cognee/infrastructure/databases/utils/get_graph_dataset_database_handler.py +10 -0
- cognee/infrastructure/databases/utils/get_or_create_dataset_database.py +66 -18
- cognee/infrastructure/databases/utils/get_vector_dataset_database_handler.py +10 -0
- cognee/infrastructure/databases/utils/resolve_dataset_database_connection_info.py +30 -0
- cognee/infrastructure/databases/vector/config.py +5 -0
- cognee/infrastructure/databases/vector/create_vector_engine.py +6 -1
- cognee/infrastructure/databases/vector/embeddings/FastembedEmbeddingEngine.py +8 -6
- cognee/infrastructure/databases/vector/embeddings/LiteLLMEmbeddingEngine.py +9 -7
- cognee/infrastructure/databases/vector/embeddings/OllamaEmbeddingEngine.py +11 -13
- cognee/infrastructure/databases/vector/lancedb/LanceDBAdapter.py +2 -0
- cognee/infrastructure/databases/vector/lancedb/LanceDBDatasetDatabaseHandler.py +50 -0
- cognee/infrastructure/databases/vector/vector_db_interface.py +35 -0
- cognee/infrastructure/engine/models/Edge.py +13 -1
- cognee/infrastructure/files/storage/s3_config.py +2 -0
- cognee/infrastructure/files/utils/guess_file_type.py +4 -0
- cognee/infrastructure/llm/LLMGateway.py +5 -2
- cognee/infrastructure/llm/config.py +37 -0
- cognee/infrastructure/llm/extraction/knowledge_graph/extract_content_graph.py +2 -2
- cognee/infrastructure/llm/structured_output_framework/baml/baml_src/extraction/acreate_structured_output.py +23 -8
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/anthropic/adapter.py +22 -18
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/bedrock/__init__.py +5 -0
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/bedrock/adapter.py +153 -0
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/gemini/adapter.py +47 -38
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/generic_llm_api/adapter.py +46 -37
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/get_llm_client.py +20 -10
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/mistral/adapter.py +23 -11
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/ollama/adapter.py +36 -23
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/openai/adapter.py +47 -36
- cognee/infrastructure/loaders/LoaderEngine.py +1 -0
- cognee/infrastructure/loaders/core/__init__.py +2 -1
- cognee/infrastructure/loaders/core/csv_loader.py +93 -0
- cognee/infrastructure/loaders/core/text_loader.py +1 -2
- cognee/infrastructure/loaders/external/advanced_pdf_loader.py +0 -9
- cognee/infrastructure/loaders/supported_loaders.py +2 -1
- cognee/memify_pipelines/create_triplet_embeddings.py +53 -0
- cognee/memify_pipelines/persist_sessions_in_knowledge_graph.py +55 -0
- cognee/modules/chunking/CsvChunker.py +35 -0
- cognee/modules/chunking/models/DocumentChunk.py +2 -1
- cognee/modules/chunking/text_chunker_with_overlap.py +124 -0
- cognee/modules/cognify/config.py +2 -0
- cognee/modules/data/deletion/prune_system.py +52 -2
- cognee/modules/data/methods/__init__.py +1 -0
- cognee/modules/data/methods/create_dataset.py +4 -2
- cognee/modules/data/methods/delete_dataset.py +26 -0
- cognee/modules/data/methods/get_dataset_ids.py +5 -1
- cognee/modules/data/methods/get_unique_data_id.py +68 -0
- cognee/modules/data/methods/get_unique_dataset_id.py +66 -4
- cognee/modules/data/models/Dataset.py +2 -0
- cognee/modules/data/processing/document_types/CsvDocument.py +33 -0
- cognee/modules/data/processing/document_types/__init__.py +1 -0
- cognee/modules/engine/models/Triplet.py +9 -0
- cognee/modules/engine/models/__init__.py +1 -0
- cognee/modules/graph/cognee_graph/CogneeGraph.py +89 -39
- cognee/modules/graph/cognee_graph/CogneeGraphElements.py +8 -3
- cognee/modules/graph/utils/expand_with_nodes_and_edges.py +19 -2
- cognee/modules/graph/utils/resolve_edges_to_text.py +48 -49
- cognee/modules/ingestion/identify.py +4 -4
- cognee/modules/memify/memify.py +1 -7
- cognee/modules/notebooks/operations/run_in_local_sandbox.py +3 -0
- cognee/modules/ontology/rdf_xml/RDFLibOntologyResolver.py +55 -23
- cognee/modules/pipelines/operations/pipeline.py +18 -2
- cognee/modules/pipelines/operations/run_tasks_data_item.py +1 -1
- cognee/modules/retrieval/EntityCompletionRetriever.py +10 -3
- cognee/modules/retrieval/__init__.py +1 -1
- cognee/modules/retrieval/base_graph_retriever.py +7 -3
- cognee/modules/retrieval/base_retriever.py +7 -3
- cognee/modules/retrieval/completion_retriever.py +11 -4
- cognee/modules/retrieval/graph_completion_context_extension_retriever.py +10 -2
- cognee/modules/retrieval/graph_completion_cot_retriever.py +18 -51
- cognee/modules/retrieval/graph_completion_retriever.py +14 -1
- cognee/modules/retrieval/graph_summary_completion_retriever.py +4 -0
- cognee/modules/retrieval/register_retriever.py +10 -0
- cognee/modules/retrieval/registered_community_retrievers.py +1 -0
- cognee/modules/retrieval/temporal_retriever.py +13 -2
- cognee/modules/retrieval/triplet_retriever.py +182 -0
- cognee/modules/retrieval/utils/brute_force_triplet_search.py +43 -11
- cognee/modules/retrieval/utils/completion.py +2 -22
- cognee/modules/run_custom_pipeline/__init__.py +1 -0
- cognee/modules/run_custom_pipeline/run_custom_pipeline.py +76 -0
- cognee/modules/search/methods/get_search_type_tools.py +54 -8
- cognee/modules/search/methods/no_access_control_search.py +4 -0
- cognee/modules/search/methods/search.py +26 -3
- cognee/modules/search/types/SearchType.py +1 -1
- cognee/modules/settings/get_settings.py +19 -0
- cognee/modules/users/methods/create_user.py +12 -27
- cognee/modules/users/methods/get_authenticated_user.py +3 -2
- cognee/modules/users/methods/get_default_user.py +4 -2
- cognee/modules/users/methods/get_user.py +1 -1
- cognee/modules/users/methods/get_user_by_email.py +1 -1
- cognee/modules/users/models/DatasetDatabase.py +24 -3
- cognee/modules/users/models/Tenant.py +6 -7
- cognee/modules/users/models/User.py +6 -5
- cognee/modules/users/models/UserTenant.py +12 -0
- cognee/modules/users/models/__init__.py +1 -0
- cognee/modules/users/permissions/methods/get_all_user_permission_datasets.py +13 -13
- cognee/modules/users/roles/methods/add_user_to_role.py +3 -1
- cognee/modules/users/tenants/methods/__init__.py +1 -0
- cognee/modules/users/tenants/methods/add_user_to_tenant.py +21 -12
- cognee/modules/users/tenants/methods/create_tenant.py +22 -8
- cognee/modules/users/tenants/methods/select_tenant.py +62 -0
- cognee/shared/logging_utils.py +6 -0
- cognee/shared/rate_limiting.py +30 -0
- cognee/tasks/chunks/__init__.py +1 -0
- cognee/tasks/chunks/chunk_by_row.py +94 -0
- cognee/tasks/documents/__init__.py +0 -1
- cognee/tasks/documents/classify_documents.py +2 -0
- cognee/tasks/feedback/generate_improved_answers.py +3 -3
- cognee/tasks/graph/extract_graph_from_data.py +9 -10
- cognee/tasks/ingestion/ingest_data.py +1 -1
- cognee/tasks/memify/__init__.py +2 -0
- cognee/tasks/memify/cognify_session.py +41 -0
- cognee/tasks/memify/extract_user_sessions.py +73 -0
- cognee/tasks/memify/get_triplet_datapoints.py +289 -0
- cognee/tasks/storage/add_data_points.py +142 -2
- cognee/tasks/storage/index_data_points.py +33 -22
- cognee/tasks/storage/index_graph_edges.py +37 -57
- cognee/tests/integration/documents/CsvDocument_test.py +70 -0
- cognee/tests/integration/retrieval/test_triplet_retriever.py +84 -0
- cognee/tests/integration/tasks/test_add_data_points.py +139 -0
- cognee/tests/integration/tasks/test_get_triplet_datapoints.py +69 -0
- cognee/tests/tasks/entity_extraction/entity_extraction_test.py +1 -1
- cognee/tests/test_add_docling_document.py +2 -2
- cognee/tests/test_cognee_server_start.py +84 -3
- cognee/tests/test_conversation_history.py +68 -5
- cognee/tests/test_data/example_with_header.csv +3 -0
- cognee/tests/test_dataset_database_handler.py +137 -0
- cognee/tests/test_dataset_delete.py +76 -0
- cognee/tests/test_edge_centered_payload.py +170 -0
- cognee/tests/test_edge_ingestion.py +27 -0
- cognee/tests/test_feedback_enrichment.py +1 -1
- cognee/tests/test_library.py +6 -4
- cognee/tests/test_load.py +62 -0
- cognee/tests/test_multi_tenancy.py +165 -0
- cognee/tests/test_parallel_databases.py +2 -0
- cognee/tests/test_pipeline_cache.py +164 -0
- cognee/tests/test_relational_db_migration.py +54 -2
- cognee/tests/test_search_db.py +44 -2
- cognee/tests/unit/api/test_conditional_authentication_endpoints.py +12 -3
- cognee/tests/unit/api/test_ontology_endpoint.py +252 -0
- cognee/tests/unit/infrastructure/databases/cache/test_cache_config.py +5 -0
- cognee/tests/unit/infrastructure/databases/test_index_data_points.py +27 -0
- cognee/tests/unit/infrastructure/databases/test_index_graph_edges.py +14 -16
- cognee/tests/unit/infrastructure/llm/test_llm_config.py +46 -0
- cognee/tests/unit/infrastructure/mock_embedding_engine.py +3 -7
- cognee/tests/unit/infrastructure/test_embedding_rate_limiting_realistic.py +0 -5
- cognee/tests/unit/modules/chunking/test_text_chunker.py +248 -0
- cognee/tests/unit/modules/chunking/test_text_chunker_with_overlap.py +324 -0
- cognee/tests/unit/modules/graph/cognee_graph_elements_test.py +2 -2
- cognee/tests/unit/modules/graph/cognee_graph_test.py +406 -0
- cognee/tests/unit/modules/memify_tasks/test_cognify_session.py +111 -0
- cognee/tests/unit/modules/memify_tasks/test_extract_user_sessions.py +175 -0
- cognee/tests/unit/modules/memify_tasks/test_get_triplet_datapoints.py +214 -0
- cognee/tests/unit/modules/retrieval/graph_completion_retriever_cot_test.py +0 -51
- cognee/tests/unit/modules/retrieval/rag_completion_retriever_test.py +1 -0
- cognee/tests/unit/modules/retrieval/structured_output_test.py +204 -0
- cognee/tests/unit/modules/retrieval/summaries_retriever_test.py +1 -1
- cognee/tests/unit/modules/retrieval/temporal_retriever_test.py +0 -1
- cognee/tests/unit/modules/retrieval/test_brute_force_triplet_search.py +608 -0
- cognee/tests/unit/modules/retrieval/triplet_retriever_test.py +83 -0
- cognee/tests/unit/modules/users/test_conditional_authentication.py +0 -63
- cognee/tests/unit/processing/chunks/chunk_by_row_test.py +52 -0
- cognee/tests/unit/tasks/storage/test_add_data_points.py +288 -0
- {cognee-0.4.1.dist-info → cognee-0.5.0.dist-info}/METADATA +11 -7
- {cognee-0.4.1.dist-info → cognee-0.5.0.dist-info}/RECORD +212 -160
- {cognee-0.4.1.dist-info → cognee-0.5.0.dist-info}/entry_points.txt +0 -1
- cognee/api/v1/cognify/code_graph_pipeline.py +0 -119
- cognee/api/v1/cognify/routers/get_code_pipeline_router.py +0 -90
- cognee/infrastructure/databases/vector/embeddings/embedding_rate_limiter.py +0 -544
- cognee/modules/retrieval/code_retriever.py +0 -232
- cognee/tasks/code/enrich_dependency_graph_checker.py +0 -35
- cognee/tasks/code/get_local_dependencies_checker.py +0 -20
- cognee/tasks/code/get_repo_dependency_graph_checker.py +0 -35
- cognee/tasks/documents/check_permissions_on_dataset.py +0 -26
- cognee/tasks/repo_processor/__init__.py +0 -2
- cognee/tasks/repo_processor/get_local_dependencies.py +0 -335
- cognee/tasks/repo_processor/get_non_code_files.py +0 -158
- cognee/tasks/repo_processor/get_repo_file_dependencies.py +0 -243
- {cognee-0.4.1.dist-info → cognee-0.5.0.dist-info}/WHEEL +0 -0
- {cognee-0.4.1.dist-info → cognee-0.5.0.dist-info}/licenses/LICENSE +0 -0
- {cognee-0.4.1.dist-info → cognee-0.5.0.dist-info}/licenses/NOTICE.md +0 -0
|
@@ -1,544 +0,0 @@
|
|
|
1
|
-
import threading
|
|
2
|
-
import logging
|
|
3
|
-
import functools
|
|
4
|
-
import os
|
|
5
|
-
import time
|
|
6
|
-
import asyncio
|
|
7
|
-
import random
|
|
8
|
-
from cognee.shared.logging_utils import get_logger
|
|
9
|
-
from cognee.infrastructure.llm.config import get_llm_config
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
logger = get_logger()
|
|
13
|
-
|
|
14
|
-
# Common error patterns that indicate rate limiting
|
|
15
|
-
RATE_LIMIT_ERROR_PATTERNS = [
|
|
16
|
-
"rate limit",
|
|
17
|
-
"rate_limit",
|
|
18
|
-
"ratelimit",
|
|
19
|
-
"too many requests",
|
|
20
|
-
"retry after",
|
|
21
|
-
"capacity",
|
|
22
|
-
"quota",
|
|
23
|
-
"limit exceeded",
|
|
24
|
-
"tps limit exceeded",
|
|
25
|
-
"request limit exceeded",
|
|
26
|
-
"maximum requests",
|
|
27
|
-
"exceeded your current quota",
|
|
28
|
-
"throttled",
|
|
29
|
-
"throttling",
|
|
30
|
-
]
|
|
31
|
-
|
|
32
|
-
# Default retry settings
|
|
33
|
-
DEFAULT_MAX_RETRIES = 5
|
|
34
|
-
DEFAULT_INITIAL_BACKOFF = 1.0 # seconds
|
|
35
|
-
DEFAULT_BACKOFF_FACTOR = 2.0 # exponential backoff multiplier
|
|
36
|
-
DEFAULT_JITTER = 0.1 # 10% jitter to avoid thundering herd
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
class EmbeddingRateLimiter:
|
|
40
|
-
"""
|
|
41
|
-
Rate limiter for embedding API calls.
|
|
42
|
-
|
|
43
|
-
This class implements a singleton pattern to ensure that rate limiting
|
|
44
|
-
is consistent across all embedding requests. It uses the limits
|
|
45
|
-
library with a moving window strategy to control request rates.
|
|
46
|
-
|
|
47
|
-
The rate limiter uses the same configuration as the LLM API rate limiter
|
|
48
|
-
but uses a separate key to track embedding API calls independently.
|
|
49
|
-
|
|
50
|
-
Public Methods:
|
|
51
|
-
- get_instance
|
|
52
|
-
- reset_instance
|
|
53
|
-
- hit_limit
|
|
54
|
-
- wait_if_needed
|
|
55
|
-
- async_wait_if_needed
|
|
56
|
-
|
|
57
|
-
Instance Variables:
|
|
58
|
-
- enabled
|
|
59
|
-
- requests_limit
|
|
60
|
-
- interval_seconds
|
|
61
|
-
- request_times
|
|
62
|
-
- lock
|
|
63
|
-
"""
|
|
64
|
-
|
|
65
|
-
_instance = None
|
|
66
|
-
lock = threading.Lock()
|
|
67
|
-
|
|
68
|
-
@classmethod
|
|
69
|
-
def get_instance(cls):
|
|
70
|
-
"""
|
|
71
|
-
Retrieve the singleton instance of the EmbeddingRateLimiter.
|
|
72
|
-
|
|
73
|
-
This method ensures that only one instance of the class exists and
|
|
74
|
-
is thread-safe. It lazily initializes the instance if it doesn't
|
|
75
|
-
already exist.
|
|
76
|
-
|
|
77
|
-
Returns:
|
|
78
|
-
--------
|
|
79
|
-
|
|
80
|
-
The singleton instance of the EmbeddingRateLimiter class.
|
|
81
|
-
"""
|
|
82
|
-
if cls._instance is None:
|
|
83
|
-
with cls.lock:
|
|
84
|
-
if cls._instance is None:
|
|
85
|
-
cls._instance = cls()
|
|
86
|
-
return cls._instance
|
|
87
|
-
|
|
88
|
-
@classmethod
|
|
89
|
-
def reset_instance(cls):
|
|
90
|
-
"""
|
|
91
|
-
Reset the singleton instance of the EmbeddingRateLimiter.
|
|
92
|
-
|
|
93
|
-
This method is thread-safe and sets the instance to None, allowing
|
|
94
|
-
for a new instance to be created when requested again.
|
|
95
|
-
"""
|
|
96
|
-
with cls.lock:
|
|
97
|
-
cls._instance = None
|
|
98
|
-
|
|
99
|
-
def __init__(self):
|
|
100
|
-
config = get_llm_config()
|
|
101
|
-
self.enabled = config.embedding_rate_limit_enabled
|
|
102
|
-
self.requests_limit = config.embedding_rate_limit_requests
|
|
103
|
-
self.interval_seconds = config.embedding_rate_limit_interval
|
|
104
|
-
self.request_times = []
|
|
105
|
-
self.lock = threading.Lock()
|
|
106
|
-
|
|
107
|
-
logging.info(
|
|
108
|
-
f"EmbeddingRateLimiter initialized: enabled={self.enabled}, "
|
|
109
|
-
f"requests_limit={self.requests_limit}, interval_seconds={self.interval_seconds}"
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
def hit_limit(self) -> bool:
|
|
113
|
-
"""
|
|
114
|
-
Check if the current request would exceed the rate limit.
|
|
115
|
-
|
|
116
|
-
This method checks if the rate limiter is enabled and evaluates
|
|
117
|
-
the number of requests made in the elapsed interval.
|
|
118
|
-
|
|
119
|
-
Returns:
|
|
120
|
-
- bool: True if the rate limit would be exceeded, False otherwise.
|
|
121
|
-
|
|
122
|
-
Returns:
|
|
123
|
-
--------
|
|
124
|
-
|
|
125
|
-
- bool: True if the rate limit would be exceeded, otherwise False.
|
|
126
|
-
"""
|
|
127
|
-
if not self.enabled:
|
|
128
|
-
return False
|
|
129
|
-
|
|
130
|
-
current_time = time.time()
|
|
131
|
-
|
|
132
|
-
with self.lock:
|
|
133
|
-
# Remove expired request times
|
|
134
|
-
cutoff_time = current_time - self.interval_seconds
|
|
135
|
-
self.request_times = [t for t in self.request_times if t > cutoff_time]
|
|
136
|
-
|
|
137
|
-
# Check if adding a new request would exceed the limit
|
|
138
|
-
if len(self.request_times) >= self.requests_limit:
|
|
139
|
-
logger.info(
|
|
140
|
-
f"Rate limit hit: {len(self.request_times)} requests in the last {self.interval_seconds} seconds"
|
|
141
|
-
)
|
|
142
|
-
return True
|
|
143
|
-
|
|
144
|
-
# Otherwise, we're under the limit
|
|
145
|
-
return False
|
|
146
|
-
|
|
147
|
-
def wait_if_needed(self) -> float:
|
|
148
|
-
"""
|
|
149
|
-
Block until a request can be made without exceeding the rate limit.
|
|
150
|
-
|
|
151
|
-
This method will wait if the current request would exceed the
|
|
152
|
-
rate limit and returns the time waited in seconds.
|
|
153
|
-
|
|
154
|
-
Returns:
|
|
155
|
-
- float: Time waited in seconds before a request is allowed.
|
|
156
|
-
|
|
157
|
-
Returns:
|
|
158
|
-
--------
|
|
159
|
-
|
|
160
|
-
- float: Time waited in seconds before proceeding.
|
|
161
|
-
"""
|
|
162
|
-
if not self.enabled:
|
|
163
|
-
return 0
|
|
164
|
-
|
|
165
|
-
wait_time = 0
|
|
166
|
-
start_time = time.time()
|
|
167
|
-
|
|
168
|
-
while self.hit_limit():
|
|
169
|
-
time.sleep(0.5) # Poll every 0.5 seconds
|
|
170
|
-
wait_time = time.time() - start_time
|
|
171
|
-
|
|
172
|
-
# Record this request
|
|
173
|
-
with self.lock:
|
|
174
|
-
self.request_times.append(time.time())
|
|
175
|
-
|
|
176
|
-
return wait_time
|
|
177
|
-
|
|
178
|
-
async def async_wait_if_needed(self) -> float:
|
|
179
|
-
"""
|
|
180
|
-
Asynchronously wait until a request can be made without exceeding the rate limit.
|
|
181
|
-
|
|
182
|
-
This method will wait if the current request would exceed the
|
|
183
|
-
rate limit and returns the time waited in seconds.
|
|
184
|
-
|
|
185
|
-
Returns:
|
|
186
|
-
- float: Time waited in seconds before a request is allowed.
|
|
187
|
-
|
|
188
|
-
Returns:
|
|
189
|
-
--------
|
|
190
|
-
|
|
191
|
-
- float: Time waited in seconds before proceeding.
|
|
192
|
-
"""
|
|
193
|
-
if not self.enabled:
|
|
194
|
-
return 0
|
|
195
|
-
|
|
196
|
-
wait_time = 0
|
|
197
|
-
start_time = time.time()
|
|
198
|
-
|
|
199
|
-
while self.hit_limit():
|
|
200
|
-
await asyncio.sleep(0.5) # Poll every 0.5 seconds
|
|
201
|
-
wait_time = time.time() - start_time
|
|
202
|
-
|
|
203
|
-
# Record this request
|
|
204
|
-
with self.lock:
|
|
205
|
-
self.request_times.append(time.time())
|
|
206
|
-
|
|
207
|
-
return wait_time
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
def embedding_rate_limit_sync(func):
|
|
211
|
-
"""
|
|
212
|
-
Apply rate limiting to a synchronous embedding function.
|
|
213
|
-
|
|
214
|
-
Parameters:
|
|
215
|
-
-----------
|
|
216
|
-
|
|
217
|
-
- func: Function to decorate with rate limiting logic.
|
|
218
|
-
|
|
219
|
-
Returns:
|
|
220
|
-
--------
|
|
221
|
-
|
|
222
|
-
Returns the decorated function that applies rate limiting.
|
|
223
|
-
"""
|
|
224
|
-
|
|
225
|
-
@functools.wraps(func)
|
|
226
|
-
def wrapper(*args, **kwargs):
|
|
227
|
-
"""
|
|
228
|
-
Wrap the given function with rate limiting logic to control the embedding API usage.
|
|
229
|
-
|
|
230
|
-
Checks if the rate limit has been exceeded before allowing the function to execute. If
|
|
231
|
-
the limit is hit, it logs a warning and raises an EmbeddingException. Otherwise, it
|
|
232
|
-
updates the request count and proceeds to call the original function.
|
|
233
|
-
|
|
234
|
-
Parameters:
|
|
235
|
-
-----------
|
|
236
|
-
|
|
237
|
-
- *args: Variable length argument list for the wrapped function.
|
|
238
|
-
- **kwargs: Keyword arguments for the wrapped function.
|
|
239
|
-
|
|
240
|
-
Returns:
|
|
241
|
-
--------
|
|
242
|
-
|
|
243
|
-
Returns the result of the wrapped function if rate limiting conditions are met.
|
|
244
|
-
"""
|
|
245
|
-
limiter = EmbeddingRateLimiter.get_instance()
|
|
246
|
-
|
|
247
|
-
# Check if rate limiting is enabled and if we're at the limit
|
|
248
|
-
if limiter.hit_limit():
|
|
249
|
-
error_msg = "Embedding API rate limit exceeded"
|
|
250
|
-
logger.warning(error_msg)
|
|
251
|
-
|
|
252
|
-
# Create a custom embedding rate limit exception
|
|
253
|
-
from cognee.infrastructure.databases.exceptions import EmbeddingException
|
|
254
|
-
|
|
255
|
-
raise EmbeddingException(error_msg)
|
|
256
|
-
|
|
257
|
-
# Add this request to the counter and proceed
|
|
258
|
-
limiter.wait_if_needed()
|
|
259
|
-
return func(*args, **kwargs)
|
|
260
|
-
|
|
261
|
-
return wrapper
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
def embedding_rate_limit_async(func):
|
|
265
|
-
"""
|
|
266
|
-
Decorator that applies rate limiting to an asynchronous embedding function.
|
|
267
|
-
|
|
268
|
-
Parameters:
|
|
269
|
-
-----------
|
|
270
|
-
|
|
271
|
-
- func: Async function to decorate.
|
|
272
|
-
|
|
273
|
-
Returns:
|
|
274
|
-
--------
|
|
275
|
-
|
|
276
|
-
Returns the decorated async function that applies rate limiting.
|
|
277
|
-
"""
|
|
278
|
-
|
|
279
|
-
@functools.wraps(func)
|
|
280
|
-
async def wrapper(*args, **kwargs):
|
|
281
|
-
"""
|
|
282
|
-
Handle function calls with embedding rate limiting.
|
|
283
|
-
|
|
284
|
-
This asynchronous wrapper checks if the embedding API rate limit is exceeded before
|
|
285
|
-
allowing the function to execute. If the limit is exceeded, it logs a warning and raises
|
|
286
|
-
an EmbeddingException. If not, it waits as necessary and proceeds with the function
|
|
287
|
-
call.
|
|
288
|
-
|
|
289
|
-
Parameters:
|
|
290
|
-
-----------
|
|
291
|
-
|
|
292
|
-
- *args: Positional arguments passed to the wrapped function.
|
|
293
|
-
- **kwargs: Keyword arguments passed to the wrapped function.
|
|
294
|
-
|
|
295
|
-
Returns:
|
|
296
|
-
--------
|
|
297
|
-
|
|
298
|
-
Returns the result of the wrapped function after handling rate limiting.
|
|
299
|
-
"""
|
|
300
|
-
limiter = EmbeddingRateLimiter.get_instance()
|
|
301
|
-
|
|
302
|
-
# Check if rate limiting is enabled and if we're at the limit
|
|
303
|
-
if limiter.hit_limit():
|
|
304
|
-
error_msg = "Embedding API rate limit exceeded"
|
|
305
|
-
logger.warning(error_msg)
|
|
306
|
-
|
|
307
|
-
# Create a custom embedding rate limit exception
|
|
308
|
-
from cognee.infrastructure.databases.exceptions import EmbeddingException
|
|
309
|
-
|
|
310
|
-
raise EmbeddingException(error_msg)
|
|
311
|
-
|
|
312
|
-
# Add this request to the counter and proceed
|
|
313
|
-
await limiter.async_wait_if_needed()
|
|
314
|
-
return await func(*args, **kwargs)
|
|
315
|
-
|
|
316
|
-
return wrapper
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
def embedding_sleep_and_retry_sync(max_retries=5, base_backoff=1.0, jitter=0.5):
|
|
320
|
-
"""
|
|
321
|
-
Add retry with exponential backoff for synchronous embedding functions.
|
|
322
|
-
|
|
323
|
-
Parameters:
|
|
324
|
-
-----------
|
|
325
|
-
|
|
326
|
-
- max_retries: Maximum number of retries before giving up. (default 5)
|
|
327
|
-
- base_backoff: Base backoff time in seconds for retry intervals. (default 1.0)
|
|
328
|
-
- jitter: Jitter factor to randomize the backoff time to avoid collision. (default
|
|
329
|
-
0.5)
|
|
330
|
-
|
|
331
|
-
Returns:
|
|
332
|
-
--------
|
|
333
|
-
|
|
334
|
-
A decorator that retries the wrapped function on rate limit errors, applying
|
|
335
|
-
exponential backoff with jitter.
|
|
336
|
-
"""
|
|
337
|
-
|
|
338
|
-
def decorator(func):
|
|
339
|
-
"""
|
|
340
|
-
Wraps a function to apply retry logic on rate limit errors.
|
|
341
|
-
|
|
342
|
-
Parameters:
|
|
343
|
-
-----------
|
|
344
|
-
|
|
345
|
-
- func: The function to be wrapped with retry logic.
|
|
346
|
-
|
|
347
|
-
Returns:
|
|
348
|
-
--------
|
|
349
|
-
|
|
350
|
-
Returns the wrapped function with retry logic applied.
|
|
351
|
-
"""
|
|
352
|
-
|
|
353
|
-
@functools.wraps(func)
|
|
354
|
-
def wrapper(*args, **kwargs):
|
|
355
|
-
"""
|
|
356
|
-
Retry the execution of a function with backoff on failure due to rate limit errors.
|
|
357
|
-
|
|
358
|
-
This wrapper function will call the specified function and if it raises an exception, it
|
|
359
|
-
will handle retries according to defined conditions. It will check the environment for a
|
|
360
|
-
DISABLE_RETRIES flag to determine whether to retry or propagate errors immediately
|
|
361
|
-
during tests. If the error is identified as a rate limit error, it will apply an
|
|
362
|
-
exponential backoff strategy with jitter before retrying, up to a maximum number of
|
|
363
|
-
retries. If the retries are exhausted, it raises the last encountered error.
|
|
364
|
-
|
|
365
|
-
Parameters:
|
|
366
|
-
-----------
|
|
367
|
-
|
|
368
|
-
- *args: Positional arguments passed to the wrapped function.
|
|
369
|
-
- **kwargs: Keyword arguments passed to the wrapped function.
|
|
370
|
-
|
|
371
|
-
Returns:
|
|
372
|
-
--------
|
|
373
|
-
|
|
374
|
-
Returns the result of the wrapped function if successful; otherwise, raises the last
|
|
375
|
-
error encountered after maximum retries are exhausted.
|
|
376
|
-
"""
|
|
377
|
-
# If DISABLE_RETRIES is set, don't retry for testing purposes
|
|
378
|
-
disable_retries = os.environ.get("DISABLE_RETRIES", "false").lower() in (
|
|
379
|
-
"true",
|
|
380
|
-
"1",
|
|
381
|
-
"yes",
|
|
382
|
-
)
|
|
383
|
-
|
|
384
|
-
retries = 0
|
|
385
|
-
last_error = None
|
|
386
|
-
|
|
387
|
-
while retries <= max_retries:
|
|
388
|
-
try:
|
|
389
|
-
return func(*args, **kwargs)
|
|
390
|
-
except Exception as e:
|
|
391
|
-
# Check if this is a rate limit error
|
|
392
|
-
error_str = str(e).lower()
|
|
393
|
-
error_type = type(e).__name__
|
|
394
|
-
is_rate_limit = any(
|
|
395
|
-
pattern in error_str.lower() for pattern in RATE_LIMIT_ERROR_PATTERNS
|
|
396
|
-
)
|
|
397
|
-
|
|
398
|
-
if disable_retries:
|
|
399
|
-
# For testing, propagate the exception immediately
|
|
400
|
-
raise
|
|
401
|
-
|
|
402
|
-
if is_rate_limit and retries < max_retries:
|
|
403
|
-
# Calculate backoff with jitter
|
|
404
|
-
backoff = (
|
|
405
|
-
base_backoff * (2**retries) * (1 + random.uniform(-jitter, jitter))
|
|
406
|
-
)
|
|
407
|
-
|
|
408
|
-
logger.warning(
|
|
409
|
-
f"Embedding rate limit hit, retrying in {backoff:.2f}s "
|
|
410
|
-
f"(attempt {retries + 1}/{max_retries}): "
|
|
411
|
-
f"({error_str!r}, {error_type!r})"
|
|
412
|
-
)
|
|
413
|
-
|
|
414
|
-
time.sleep(backoff)
|
|
415
|
-
retries += 1
|
|
416
|
-
last_error = e
|
|
417
|
-
else:
|
|
418
|
-
# Not a rate limit error or max retries reached, raise
|
|
419
|
-
raise
|
|
420
|
-
|
|
421
|
-
# If we exit the loop due to max retries, raise the last error
|
|
422
|
-
if last_error:
|
|
423
|
-
raise last_error
|
|
424
|
-
|
|
425
|
-
return wrapper
|
|
426
|
-
|
|
427
|
-
return decorator
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
def embedding_sleep_and_retry_async(max_retries=5, base_backoff=1.0, jitter=0.5):
|
|
431
|
-
"""
|
|
432
|
-
Add retry logic with exponential backoff for asynchronous embedding functions.
|
|
433
|
-
|
|
434
|
-
This decorator retries the wrapped asynchronous function upon encountering rate limit
|
|
435
|
-
errors, utilizing exponential backoff with optional jitter to space out retry attempts.
|
|
436
|
-
It allows for a maximum number of retries before giving up and raising the last error
|
|
437
|
-
encountered.
|
|
438
|
-
|
|
439
|
-
Parameters:
|
|
440
|
-
-----------
|
|
441
|
-
|
|
442
|
-
- max_retries: Maximum number of retries allowed before giving up. (default 5)
|
|
443
|
-
- base_backoff: Base amount of time in seconds to wait before retrying after a rate
|
|
444
|
-
limit error. (default 1.0)
|
|
445
|
-
- jitter: Amount of randomness to add to the backoff duration to help mitigate burst
|
|
446
|
-
issues on retries. (default 0.5)
|
|
447
|
-
|
|
448
|
-
Returns:
|
|
449
|
-
--------
|
|
450
|
-
|
|
451
|
-
Returns a decorated asynchronous function that implements the retry logic on rate
|
|
452
|
-
limit errors.
|
|
453
|
-
"""
|
|
454
|
-
|
|
455
|
-
def decorator(func):
|
|
456
|
-
"""
|
|
457
|
-
Handle retries for an async function with exponential backoff and jitter.
|
|
458
|
-
|
|
459
|
-
Parameters:
|
|
460
|
-
-----------
|
|
461
|
-
|
|
462
|
-
- func: An asynchronous function to be wrapped with retry logic.
|
|
463
|
-
|
|
464
|
-
Returns:
|
|
465
|
-
--------
|
|
466
|
-
|
|
467
|
-
Returns the wrapper function that manages the retry behavior for the wrapped async
|
|
468
|
-
function.
|
|
469
|
-
"""
|
|
470
|
-
|
|
471
|
-
@functools.wraps(func)
|
|
472
|
-
async def wrapper(*args, **kwargs):
|
|
473
|
-
"""
|
|
474
|
-
Handle retries for an async function with exponential backoff and jitter.
|
|
475
|
-
|
|
476
|
-
If the environment variable DISABLE_RETRIES is set to true, 1, or yes, the function will
|
|
477
|
-
not retry on errors.
|
|
478
|
-
It attempts to call the wrapped function until it succeeds or the maximum number of
|
|
479
|
-
retries is reached. If an exception occurs, it checks if it's a rate limit error to
|
|
480
|
-
determine if a retry is needed.
|
|
481
|
-
|
|
482
|
-
Parameters:
|
|
483
|
-
-----------
|
|
484
|
-
|
|
485
|
-
- *args: Positional arguments passed to the wrapped function.
|
|
486
|
-
- **kwargs: Keyword arguments passed to the wrapped function.
|
|
487
|
-
|
|
488
|
-
Returns:
|
|
489
|
-
--------
|
|
490
|
-
|
|
491
|
-
Returns the result of the wrapped async function if successful; raises the last
|
|
492
|
-
encountered error if all retries fail.
|
|
493
|
-
"""
|
|
494
|
-
# If DISABLE_RETRIES is set, don't retry for testing purposes
|
|
495
|
-
disable_retries = os.environ.get("DISABLE_RETRIES", "false").lower() in (
|
|
496
|
-
"true",
|
|
497
|
-
"1",
|
|
498
|
-
"yes",
|
|
499
|
-
)
|
|
500
|
-
|
|
501
|
-
retries = 0
|
|
502
|
-
last_error = None
|
|
503
|
-
|
|
504
|
-
while retries <= max_retries:
|
|
505
|
-
try:
|
|
506
|
-
return await func(*args, **kwargs)
|
|
507
|
-
except Exception as e:
|
|
508
|
-
# Check if this is a rate limit error
|
|
509
|
-
error_str = str(e).lower()
|
|
510
|
-
error_type = type(e).__name__
|
|
511
|
-
is_rate_limit = any(
|
|
512
|
-
pattern in error_str.lower() for pattern in RATE_LIMIT_ERROR_PATTERNS
|
|
513
|
-
)
|
|
514
|
-
|
|
515
|
-
if disable_retries:
|
|
516
|
-
# For testing, propagate the exception immediately
|
|
517
|
-
raise
|
|
518
|
-
|
|
519
|
-
if is_rate_limit and retries < max_retries:
|
|
520
|
-
# Calculate backoff with jitter
|
|
521
|
-
backoff = (
|
|
522
|
-
base_backoff * (2**retries) * (1 + random.uniform(-jitter, jitter))
|
|
523
|
-
)
|
|
524
|
-
|
|
525
|
-
logger.warning(
|
|
526
|
-
f"Embedding rate limit hit, retrying in {backoff:.2f}s "
|
|
527
|
-
f"(attempt {retries + 1}/{max_retries}): "
|
|
528
|
-
f"({error_str!r}, {error_type!r})"
|
|
529
|
-
)
|
|
530
|
-
|
|
531
|
-
await asyncio.sleep(backoff)
|
|
532
|
-
retries += 1
|
|
533
|
-
last_error = e
|
|
534
|
-
else:
|
|
535
|
-
# Not a rate limit error or max retries reached, raise
|
|
536
|
-
raise
|
|
537
|
-
|
|
538
|
-
# If we exit the loop due to max retries, raise the last error
|
|
539
|
-
if last_error:
|
|
540
|
-
raise last_error
|
|
541
|
-
|
|
542
|
-
return wrapper
|
|
543
|
-
|
|
544
|
-
return decorator
|