aiecs 1.5.1__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.
- aiecs/__init__.py +72 -0
- aiecs/__main__.py +41 -0
- aiecs/aiecs_client.py +469 -0
- aiecs/application/__init__.py +10 -0
- aiecs/application/executors/__init__.py +10 -0
- aiecs/application/executors/operation_executor.py +363 -0
- aiecs/application/knowledge_graph/__init__.py +7 -0
- aiecs/application/knowledge_graph/builder/__init__.py +37 -0
- aiecs/application/knowledge_graph/builder/document_builder.py +375 -0
- aiecs/application/knowledge_graph/builder/graph_builder.py +356 -0
- aiecs/application/knowledge_graph/builder/schema_mapping.py +531 -0
- aiecs/application/knowledge_graph/builder/structured_pipeline.py +443 -0
- aiecs/application/knowledge_graph/builder/text_chunker.py +319 -0
- aiecs/application/knowledge_graph/extractors/__init__.py +27 -0
- aiecs/application/knowledge_graph/extractors/base.py +100 -0
- aiecs/application/knowledge_graph/extractors/llm_entity_extractor.py +327 -0
- aiecs/application/knowledge_graph/extractors/llm_relation_extractor.py +349 -0
- aiecs/application/knowledge_graph/extractors/ner_entity_extractor.py +244 -0
- aiecs/application/knowledge_graph/fusion/__init__.py +23 -0
- aiecs/application/knowledge_graph/fusion/entity_deduplicator.py +387 -0
- aiecs/application/knowledge_graph/fusion/entity_linker.py +343 -0
- aiecs/application/knowledge_graph/fusion/knowledge_fusion.py +580 -0
- aiecs/application/knowledge_graph/fusion/relation_deduplicator.py +189 -0
- aiecs/application/knowledge_graph/pattern_matching/__init__.py +21 -0
- aiecs/application/knowledge_graph/pattern_matching/pattern_matcher.py +344 -0
- aiecs/application/knowledge_graph/pattern_matching/query_executor.py +378 -0
- aiecs/application/knowledge_graph/profiling/__init__.py +12 -0
- aiecs/application/knowledge_graph/profiling/query_plan_visualizer.py +199 -0
- aiecs/application/knowledge_graph/profiling/query_profiler.py +223 -0
- aiecs/application/knowledge_graph/reasoning/__init__.py +27 -0
- aiecs/application/knowledge_graph/reasoning/evidence_synthesis.py +347 -0
- aiecs/application/knowledge_graph/reasoning/inference_engine.py +504 -0
- aiecs/application/knowledge_graph/reasoning/logic_form_parser.py +167 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/__init__.py +79 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/ast_builder.py +513 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/ast_nodes.py +630 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/ast_validator.py +654 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/error_handler.py +477 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/parser.py +390 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/query_context.py +217 -0
- aiecs/application/knowledge_graph/reasoning/logic_query_integration.py +169 -0
- aiecs/application/knowledge_graph/reasoning/query_planner.py +872 -0
- aiecs/application/knowledge_graph/reasoning/reasoning_engine.py +554 -0
- aiecs/application/knowledge_graph/retrieval/__init__.py +19 -0
- aiecs/application/knowledge_graph/retrieval/retrieval_strategies.py +596 -0
- aiecs/application/knowledge_graph/search/__init__.py +59 -0
- aiecs/application/knowledge_graph/search/hybrid_search.py +423 -0
- aiecs/application/knowledge_graph/search/reranker.py +295 -0
- aiecs/application/knowledge_graph/search/reranker_strategies.py +553 -0
- aiecs/application/knowledge_graph/search/text_similarity.py +398 -0
- aiecs/application/knowledge_graph/traversal/__init__.py +15 -0
- aiecs/application/knowledge_graph/traversal/enhanced_traversal.py +329 -0
- aiecs/application/knowledge_graph/traversal/path_scorer.py +269 -0
- aiecs/application/knowledge_graph/validators/__init__.py +13 -0
- aiecs/application/knowledge_graph/validators/relation_validator.py +189 -0
- aiecs/application/knowledge_graph/visualization/__init__.py +11 -0
- aiecs/application/knowledge_graph/visualization/graph_visualizer.py +321 -0
- aiecs/common/__init__.py +9 -0
- aiecs/common/knowledge_graph/__init__.py +17 -0
- aiecs/common/knowledge_graph/runnable.py +484 -0
- aiecs/config/__init__.py +16 -0
- aiecs/config/config.py +498 -0
- aiecs/config/graph_config.py +137 -0
- aiecs/config/registry.py +23 -0
- aiecs/core/__init__.py +46 -0
- aiecs/core/interface/__init__.py +34 -0
- aiecs/core/interface/execution_interface.py +152 -0
- aiecs/core/interface/storage_interface.py +171 -0
- aiecs/domain/__init__.py +289 -0
- aiecs/domain/agent/__init__.py +189 -0
- aiecs/domain/agent/base_agent.py +697 -0
- aiecs/domain/agent/exceptions.py +103 -0
- aiecs/domain/agent/graph_aware_mixin.py +559 -0
- aiecs/domain/agent/hybrid_agent.py +490 -0
- aiecs/domain/agent/integration/__init__.py +26 -0
- aiecs/domain/agent/integration/context_compressor.py +222 -0
- aiecs/domain/agent/integration/context_engine_adapter.py +252 -0
- aiecs/domain/agent/integration/retry_policy.py +219 -0
- aiecs/domain/agent/integration/role_config.py +213 -0
- aiecs/domain/agent/knowledge_aware_agent.py +646 -0
- aiecs/domain/agent/lifecycle.py +296 -0
- aiecs/domain/agent/llm_agent.py +300 -0
- aiecs/domain/agent/memory/__init__.py +12 -0
- aiecs/domain/agent/memory/conversation.py +197 -0
- aiecs/domain/agent/migration/__init__.py +14 -0
- aiecs/domain/agent/migration/conversion.py +160 -0
- aiecs/domain/agent/migration/legacy_wrapper.py +90 -0
- aiecs/domain/agent/models.py +317 -0
- aiecs/domain/agent/observability.py +407 -0
- aiecs/domain/agent/persistence.py +289 -0
- aiecs/domain/agent/prompts/__init__.py +29 -0
- aiecs/domain/agent/prompts/builder.py +161 -0
- aiecs/domain/agent/prompts/formatters.py +189 -0
- aiecs/domain/agent/prompts/template.py +255 -0
- aiecs/domain/agent/registry.py +260 -0
- aiecs/domain/agent/tool_agent.py +257 -0
- aiecs/domain/agent/tools/__init__.py +12 -0
- aiecs/domain/agent/tools/schema_generator.py +221 -0
- aiecs/domain/community/__init__.py +155 -0
- aiecs/domain/community/agent_adapter.py +477 -0
- aiecs/domain/community/analytics.py +481 -0
- aiecs/domain/community/collaborative_workflow.py +642 -0
- aiecs/domain/community/communication_hub.py +645 -0
- aiecs/domain/community/community_builder.py +320 -0
- aiecs/domain/community/community_integration.py +800 -0
- aiecs/domain/community/community_manager.py +813 -0
- aiecs/domain/community/decision_engine.py +879 -0
- aiecs/domain/community/exceptions.py +225 -0
- aiecs/domain/community/models/__init__.py +33 -0
- aiecs/domain/community/models/community_models.py +268 -0
- aiecs/domain/community/resource_manager.py +457 -0
- aiecs/domain/community/shared_context_manager.py +603 -0
- aiecs/domain/context/__init__.py +58 -0
- aiecs/domain/context/context_engine.py +989 -0
- aiecs/domain/context/conversation_models.py +354 -0
- aiecs/domain/context/graph_memory.py +467 -0
- aiecs/domain/execution/__init__.py +12 -0
- aiecs/domain/execution/model.py +57 -0
- aiecs/domain/knowledge_graph/__init__.py +19 -0
- aiecs/domain/knowledge_graph/models/__init__.py +52 -0
- aiecs/domain/knowledge_graph/models/entity.py +130 -0
- aiecs/domain/knowledge_graph/models/evidence.py +194 -0
- aiecs/domain/knowledge_graph/models/inference_rule.py +186 -0
- aiecs/domain/knowledge_graph/models/path.py +179 -0
- aiecs/domain/knowledge_graph/models/path_pattern.py +173 -0
- aiecs/domain/knowledge_graph/models/query.py +272 -0
- aiecs/domain/knowledge_graph/models/query_plan.py +187 -0
- aiecs/domain/knowledge_graph/models/relation.py +136 -0
- aiecs/domain/knowledge_graph/schema/__init__.py +23 -0
- aiecs/domain/knowledge_graph/schema/entity_type.py +135 -0
- aiecs/domain/knowledge_graph/schema/graph_schema.py +271 -0
- aiecs/domain/knowledge_graph/schema/property_schema.py +155 -0
- aiecs/domain/knowledge_graph/schema/relation_type.py +171 -0
- aiecs/domain/knowledge_graph/schema/schema_manager.py +496 -0
- aiecs/domain/knowledge_graph/schema/type_enums.py +205 -0
- aiecs/domain/task/__init__.py +13 -0
- aiecs/domain/task/dsl_processor.py +613 -0
- aiecs/domain/task/model.py +62 -0
- aiecs/domain/task/task_context.py +268 -0
- aiecs/infrastructure/__init__.py +24 -0
- aiecs/infrastructure/graph_storage/__init__.py +11 -0
- aiecs/infrastructure/graph_storage/base.py +601 -0
- aiecs/infrastructure/graph_storage/batch_operations.py +449 -0
- aiecs/infrastructure/graph_storage/cache.py +429 -0
- aiecs/infrastructure/graph_storage/distributed.py +226 -0
- aiecs/infrastructure/graph_storage/error_handling.py +390 -0
- aiecs/infrastructure/graph_storage/graceful_degradation.py +306 -0
- aiecs/infrastructure/graph_storage/health_checks.py +378 -0
- aiecs/infrastructure/graph_storage/in_memory.py +514 -0
- aiecs/infrastructure/graph_storage/index_optimization.py +483 -0
- aiecs/infrastructure/graph_storage/lazy_loading.py +410 -0
- aiecs/infrastructure/graph_storage/metrics.py +357 -0
- aiecs/infrastructure/graph_storage/migration.py +413 -0
- aiecs/infrastructure/graph_storage/pagination.py +471 -0
- aiecs/infrastructure/graph_storage/performance_monitoring.py +466 -0
- aiecs/infrastructure/graph_storage/postgres.py +871 -0
- aiecs/infrastructure/graph_storage/query_optimizer.py +635 -0
- aiecs/infrastructure/graph_storage/schema_cache.py +290 -0
- aiecs/infrastructure/graph_storage/sqlite.py +623 -0
- aiecs/infrastructure/graph_storage/streaming.py +495 -0
- aiecs/infrastructure/messaging/__init__.py +13 -0
- aiecs/infrastructure/messaging/celery_task_manager.py +383 -0
- aiecs/infrastructure/messaging/websocket_manager.py +298 -0
- aiecs/infrastructure/monitoring/__init__.py +34 -0
- aiecs/infrastructure/monitoring/executor_metrics.py +174 -0
- aiecs/infrastructure/monitoring/global_metrics_manager.py +213 -0
- aiecs/infrastructure/monitoring/structured_logger.py +48 -0
- aiecs/infrastructure/monitoring/tracing_manager.py +410 -0
- aiecs/infrastructure/persistence/__init__.py +24 -0
- aiecs/infrastructure/persistence/context_engine_client.py +187 -0
- aiecs/infrastructure/persistence/database_manager.py +333 -0
- aiecs/infrastructure/persistence/file_storage.py +754 -0
- aiecs/infrastructure/persistence/redis_client.py +220 -0
- aiecs/llm/__init__.py +86 -0
- aiecs/llm/callbacks/__init__.py +11 -0
- aiecs/llm/callbacks/custom_callbacks.py +264 -0
- aiecs/llm/client_factory.py +420 -0
- aiecs/llm/clients/__init__.py +33 -0
- aiecs/llm/clients/base_client.py +193 -0
- aiecs/llm/clients/googleai_client.py +181 -0
- aiecs/llm/clients/openai_client.py +131 -0
- aiecs/llm/clients/vertex_client.py +437 -0
- aiecs/llm/clients/xai_client.py +184 -0
- aiecs/llm/config/__init__.py +51 -0
- aiecs/llm/config/config_loader.py +275 -0
- aiecs/llm/config/config_validator.py +236 -0
- aiecs/llm/config/model_config.py +151 -0
- aiecs/llm/utils/__init__.py +10 -0
- aiecs/llm/utils/validate_config.py +91 -0
- aiecs/main.py +363 -0
- aiecs/scripts/__init__.py +3 -0
- aiecs/scripts/aid/VERSION_MANAGEMENT.md +97 -0
- aiecs/scripts/aid/__init__.py +19 -0
- aiecs/scripts/aid/version_manager.py +215 -0
- aiecs/scripts/dependance_check/DEPENDENCY_SYSTEM_SUMMARY.md +242 -0
- aiecs/scripts/dependance_check/README_DEPENDENCY_CHECKER.md +310 -0
- aiecs/scripts/dependance_check/__init__.py +17 -0
- aiecs/scripts/dependance_check/dependency_checker.py +938 -0
- aiecs/scripts/dependance_check/dependency_fixer.py +391 -0
- aiecs/scripts/dependance_check/download_nlp_data.py +396 -0
- aiecs/scripts/dependance_check/quick_dependency_check.py +270 -0
- aiecs/scripts/dependance_check/setup_nlp_data.sh +217 -0
- aiecs/scripts/dependance_patch/__init__.py +7 -0
- aiecs/scripts/dependance_patch/fix_weasel/README_WEASEL_PATCH.md +126 -0
- aiecs/scripts/dependance_patch/fix_weasel/__init__.py +11 -0
- aiecs/scripts/dependance_patch/fix_weasel/fix_weasel_validator.py +128 -0
- aiecs/scripts/dependance_patch/fix_weasel/fix_weasel_validator.sh +82 -0
- aiecs/scripts/dependance_patch/fix_weasel/patch_weasel_library.sh +188 -0
- aiecs/scripts/dependance_patch/fix_weasel/run_weasel_patch.sh +41 -0
- aiecs/scripts/tools_develop/README.md +449 -0
- aiecs/scripts/tools_develop/TOOL_AUTO_DISCOVERY.md +234 -0
- aiecs/scripts/tools_develop/__init__.py +21 -0
- aiecs/scripts/tools_develop/check_type_annotations.py +259 -0
- aiecs/scripts/tools_develop/validate_tool_schemas.py +422 -0
- aiecs/scripts/tools_develop/verify_tools.py +356 -0
- aiecs/tasks/__init__.py +1 -0
- aiecs/tasks/worker.py +172 -0
- aiecs/tools/__init__.py +299 -0
- aiecs/tools/apisource/__init__.py +99 -0
- aiecs/tools/apisource/intelligence/__init__.py +19 -0
- aiecs/tools/apisource/intelligence/data_fusion.py +381 -0
- aiecs/tools/apisource/intelligence/query_analyzer.py +413 -0
- aiecs/tools/apisource/intelligence/search_enhancer.py +388 -0
- aiecs/tools/apisource/monitoring/__init__.py +9 -0
- aiecs/tools/apisource/monitoring/metrics.py +303 -0
- aiecs/tools/apisource/providers/__init__.py +115 -0
- aiecs/tools/apisource/providers/base.py +664 -0
- aiecs/tools/apisource/providers/census.py +401 -0
- aiecs/tools/apisource/providers/fred.py +564 -0
- aiecs/tools/apisource/providers/newsapi.py +412 -0
- aiecs/tools/apisource/providers/worldbank.py +357 -0
- aiecs/tools/apisource/reliability/__init__.py +12 -0
- aiecs/tools/apisource/reliability/error_handler.py +375 -0
- aiecs/tools/apisource/reliability/fallback_strategy.py +391 -0
- aiecs/tools/apisource/tool.py +850 -0
- aiecs/tools/apisource/utils/__init__.py +9 -0
- aiecs/tools/apisource/utils/validators.py +338 -0
- aiecs/tools/base_tool.py +201 -0
- aiecs/tools/docs/__init__.py +121 -0
- aiecs/tools/docs/ai_document_orchestrator.py +599 -0
- aiecs/tools/docs/ai_document_writer_orchestrator.py +2403 -0
- aiecs/tools/docs/content_insertion_tool.py +1333 -0
- aiecs/tools/docs/document_creator_tool.py +1317 -0
- aiecs/tools/docs/document_layout_tool.py +1166 -0
- aiecs/tools/docs/document_parser_tool.py +994 -0
- aiecs/tools/docs/document_writer_tool.py +1818 -0
- aiecs/tools/knowledge_graph/__init__.py +17 -0
- aiecs/tools/knowledge_graph/graph_reasoning_tool.py +734 -0
- aiecs/tools/knowledge_graph/graph_search_tool.py +923 -0
- aiecs/tools/knowledge_graph/kg_builder_tool.py +476 -0
- aiecs/tools/langchain_adapter.py +542 -0
- aiecs/tools/schema_generator.py +275 -0
- aiecs/tools/search_tool/__init__.py +100 -0
- aiecs/tools/search_tool/analyzers.py +589 -0
- aiecs/tools/search_tool/cache.py +260 -0
- aiecs/tools/search_tool/constants.py +128 -0
- aiecs/tools/search_tool/context.py +216 -0
- aiecs/tools/search_tool/core.py +749 -0
- aiecs/tools/search_tool/deduplicator.py +123 -0
- aiecs/tools/search_tool/error_handler.py +271 -0
- aiecs/tools/search_tool/metrics.py +371 -0
- aiecs/tools/search_tool/rate_limiter.py +178 -0
- aiecs/tools/search_tool/schemas.py +277 -0
- aiecs/tools/statistics/__init__.py +80 -0
- aiecs/tools/statistics/ai_data_analysis_orchestrator.py +643 -0
- aiecs/tools/statistics/ai_insight_generator_tool.py +505 -0
- aiecs/tools/statistics/ai_report_orchestrator_tool.py +694 -0
- aiecs/tools/statistics/data_loader_tool.py +564 -0
- aiecs/tools/statistics/data_profiler_tool.py +658 -0
- aiecs/tools/statistics/data_transformer_tool.py +573 -0
- aiecs/tools/statistics/data_visualizer_tool.py +495 -0
- aiecs/tools/statistics/model_trainer_tool.py +487 -0
- aiecs/tools/statistics/statistical_analyzer_tool.py +459 -0
- aiecs/tools/task_tools/__init__.py +86 -0
- aiecs/tools/task_tools/chart_tool.py +732 -0
- aiecs/tools/task_tools/classfire_tool.py +922 -0
- aiecs/tools/task_tools/image_tool.py +447 -0
- aiecs/tools/task_tools/office_tool.py +684 -0
- aiecs/tools/task_tools/pandas_tool.py +635 -0
- aiecs/tools/task_tools/report_tool.py +635 -0
- aiecs/tools/task_tools/research_tool.py +392 -0
- aiecs/tools/task_tools/scraper_tool.py +715 -0
- aiecs/tools/task_tools/stats_tool.py +688 -0
- aiecs/tools/temp_file_manager.py +130 -0
- aiecs/tools/tool_executor/__init__.py +37 -0
- aiecs/tools/tool_executor/tool_executor.py +881 -0
- aiecs/utils/LLM_output_structor.py +445 -0
- aiecs/utils/__init__.py +34 -0
- aiecs/utils/base_callback.py +47 -0
- aiecs/utils/cache_provider.py +695 -0
- aiecs/utils/execution_utils.py +184 -0
- aiecs/utils/logging.py +1 -0
- aiecs/utils/prompt_loader.py +14 -0
- aiecs/utils/token_usage_repository.py +323 -0
- aiecs/ws/__init__.py +0 -0
- aiecs/ws/socket_server.py +52 -0
- aiecs-1.5.1.dist-info/METADATA +608 -0
- aiecs-1.5.1.dist-info/RECORD +302 -0
- aiecs-1.5.1.dist-info/WHEEL +5 -0
- aiecs-1.5.1.dist-info/entry_points.txt +10 -0
- aiecs-1.5.1.dist-info/licenses/LICENSE +225 -0
- aiecs-1.5.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Intelligent Caching with Redis
|
|
3
|
+
|
|
4
|
+
This module implements intelligent caching with intent-aware TTL strategies
|
|
5
|
+
using Redis as the backend.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import hashlib
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from typing import Any, Dict, List, Optional
|
|
13
|
+
|
|
14
|
+
from .constants import QueryIntentType
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class IntelligentCache:
|
|
20
|
+
"""Redis-based intelligent cache with intent-aware TTL"""
|
|
21
|
+
|
|
22
|
+
# TTL strategies by intent type (in seconds)
|
|
23
|
+
TTL_STRATEGIES = {
|
|
24
|
+
# 30 days (rarely changes)
|
|
25
|
+
QueryIntentType.DEFINITION.value: 86400 * 30,
|
|
26
|
+
QueryIntentType.HOW_TO.value: 86400 * 7, # 7 days
|
|
27
|
+
QueryIntentType.FACTUAL.value: 86400 * 7, # 7 days
|
|
28
|
+
# 30 days (papers don't change)
|
|
29
|
+
QueryIntentType.ACADEMIC.value: 86400 * 30,
|
|
30
|
+
# 1 hour (fast-changing)
|
|
31
|
+
QueryIntentType.RECENT_NEWS.value: 3600,
|
|
32
|
+
QueryIntentType.PRODUCT.value: 86400, # 1 day
|
|
33
|
+
QueryIntentType.COMPARISON.value: 86400 * 3, # 3 days
|
|
34
|
+
QueryIntentType.GENERAL.value: 3600, # 1 hour default
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
def __init__(self, redis_client: Optional[Any] = None, enabled: bool = True):
|
|
38
|
+
"""
|
|
39
|
+
Initialize intelligent cache.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
redis_client: Redis client instance (optional)
|
|
43
|
+
enabled: Whether caching is enabled
|
|
44
|
+
"""
|
|
45
|
+
self.redis_client = redis_client
|
|
46
|
+
self.enabled = enabled and redis_client is not None
|
|
47
|
+
self.cache_prefix = "search_tool:"
|
|
48
|
+
|
|
49
|
+
if not self.enabled:
|
|
50
|
+
logger.info("Intelligent cache is disabled (no Redis client)")
|
|
51
|
+
|
|
52
|
+
async def get(self, query: str, params: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
53
|
+
"""
|
|
54
|
+
Get cached search results.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
query: Search query
|
|
58
|
+
params: Search parameters
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Cached results dictionary or None if not found
|
|
62
|
+
"""
|
|
63
|
+
if not self.enabled:
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
cache_key = self._generate_cache_key(query, params)
|
|
68
|
+
redis = await self.redis_client.get_client()
|
|
69
|
+
cached_data = await redis.get(cache_key)
|
|
70
|
+
|
|
71
|
+
if cached_data:
|
|
72
|
+
logger.debug(f"Cache hit for query: {query}")
|
|
73
|
+
return json.loads(cached_data)
|
|
74
|
+
|
|
75
|
+
logger.debug(f"Cache miss for query: {query}")
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
except Exception as e:
|
|
79
|
+
logger.warning(f"Cache get error: {e}")
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
async def set(
|
|
83
|
+
self,
|
|
84
|
+
query: str,
|
|
85
|
+
params: Dict[str, Any],
|
|
86
|
+
results: List[Dict[str, Any]],
|
|
87
|
+
intent_type: str = QueryIntentType.GENERAL.value,
|
|
88
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
89
|
+
):
|
|
90
|
+
"""
|
|
91
|
+
Cache search results with intelligent TTL.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
query: Search query
|
|
95
|
+
params: Search parameters
|
|
96
|
+
results: Search results to cache
|
|
97
|
+
intent_type: Query intent type for TTL calculation
|
|
98
|
+
metadata: Optional metadata about the search
|
|
99
|
+
"""
|
|
100
|
+
if not self.enabled:
|
|
101
|
+
return
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
cache_key = self._generate_cache_key(query, params)
|
|
105
|
+
|
|
106
|
+
# Calculate intelligent TTL
|
|
107
|
+
ttl = self.calculate_ttl(query, intent_type, results)
|
|
108
|
+
|
|
109
|
+
# Prepare cache data
|
|
110
|
+
cache_data = {
|
|
111
|
+
"query": query,
|
|
112
|
+
"params": params,
|
|
113
|
+
"results": results,
|
|
114
|
+
"intent_type": intent_type,
|
|
115
|
+
"metadata": metadata or {},
|
|
116
|
+
"cached_at": datetime.utcnow().isoformat(),
|
|
117
|
+
"ttl": ttl,
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
# Store in Redis
|
|
121
|
+
redis = await self.redis_client.get_client()
|
|
122
|
+
await redis.set(cache_key, json.dumps(cache_data), ex=ttl)
|
|
123
|
+
|
|
124
|
+
logger.debug(f"Cached results for query: {query} (TTL: {ttl}s)")
|
|
125
|
+
|
|
126
|
+
except Exception as e:
|
|
127
|
+
logger.warning(f"Cache set error: {e}")
|
|
128
|
+
|
|
129
|
+
def calculate_ttl(self, query: str, intent_type: str, results: List[Dict[str, Any]]) -> int:
|
|
130
|
+
"""
|
|
131
|
+
Calculate intelligent TTL based on intent and result quality.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
query: Search query
|
|
135
|
+
intent_type: Query intent type
|
|
136
|
+
results: Search results
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
TTL in seconds
|
|
140
|
+
"""
|
|
141
|
+
# Base TTL from intent type
|
|
142
|
+
base_ttl = self.TTL_STRATEGIES.get(
|
|
143
|
+
intent_type, self.TTL_STRATEGIES[QueryIntentType.GENERAL.value]
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
if not results:
|
|
147
|
+
# No results: shorter cache time
|
|
148
|
+
return base_ttl // 2
|
|
149
|
+
|
|
150
|
+
# Adjust based on result freshness
|
|
151
|
+
try:
|
|
152
|
+
avg_freshness = sum(
|
|
153
|
+
r.get("_quality", {}).get("freshness_score", 0.5) for r in results
|
|
154
|
+
) / len(results)
|
|
155
|
+
|
|
156
|
+
# Very fresh results can be cached longer
|
|
157
|
+
if avg_freshness > 0.9:
|
|
158
|
+
base_ttl = int(base_ttl * 2)
|
|
159
|
+
# Old results should have shorter cache
|
|
160
|
+
elif avg_freshness < 0.3:
|
|
161
|
+
base_ttl = base_ttl // 2
|
|
162
|
+
except Exception:
|
|
163
|
+
pass
|
|
164
|
+
|
|
165
|
+
# Adjust based on result quality
|
|
166
|
+
try:
|
|
167
|
+
avg_quality = sum(
|
|
168
|
+
r.get("_quality", {}).get("quality_score", 0.5) for r in results
|
|
169
|
+
) / len(results)
|
|
170
|
+
|
|
171
|
+
# High quality results can be cached longer
|
|
172
|
+
if avg_quality > 0.8:
|
|
173
|
+
base_ttl = int(base_ttl * 1.5)
|
|
174
|
+
except Exception:
|
|
175
|
+
pass
|
|
176
|
+
|
|
177
|
+
return base_ttl
|
|
178
|
+
|
|
179
|
+
async def invalidate(self, query: str, params: Dict[str, Any]):
|
|
180
|
+
"""
|
|
181
|
+
Invalidate cached results.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
query: Search query
|
|
185
|
+
params: Search parameters
|
|
186
|
+
"""
|
|
187
|
+
if not self.enabled:
|
|
188
|
+
return
|
|
189
|
+
|
|
190
|
+
try:
|
|
191
|
+
cache_key = self._generate_cache_key(query, params)
|
|
192
|
+
redis = await self.redis_client.get_client()
|
|
193
|
+
await redis.delete(cache_key)
|
|
194
|
+
logger.debug(f"Invalidated cache for query: {query}")
|
|
195
|
+
except Exception as e:
|
|
196
|
+
logger.warning(f"Cache invalidate error: {e}")
|
|
197
|
+
|
|
198
|
+
async def clear_all(self):
|
|
199
|
+
"""Clear all cached search results"""
|
|
200
|
+
if not self.enabled:
|
|
201
|
+
return
|
|
202
|
+
|
|
203
|
+
try:
|
|
204
|
+
redis = await self.redis_client.get_client()
|
|
205
|
+
# Find all search_tool cache keys
|
|
206
|
+
pattern = f"{self.cache_prefix}*"
|
|
207
|
+
keys = []
|
|
208
|
+
async for key in redis.scan_iter(match=pattern):
|
|
209
|
+
keys.append(key)
|
|
210
|
+
|
|
211
|
+
if keys:
|
|
212
|
+
await redis.delete(*keys)
|
|
213
|
+
logger.info(f"Cleared {len(keys)} cached entries")
|
|
214
|
+
except Exception as e:
|
|
215
|
+
logger.warning(f"Cache clear error: {e}")
|
|
216
|
+
|
|
217
|
+
def _generate_cache_key(self, query: str, params: Dict[str, Any]) -> str:
|
|
218
|
+
"""
|
|
219
|
+
Generate unique cache key from query and parameters.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
query: Search query
|
|
223
|
+
params: Search parameters
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
Cache key string
|
|
227
|
+
"""
|
|
228
|
+
# Create deterministic string from query and params
|
|
229
|
+
param_str = json.dumps(params, sort_keys=True)
|
|
230
|
+
key_data = f"{query}:{param_str}"
|
|
231
|
+
key_hash = hashlib.sha256(key_data.encode()).hexdigest()[:16]
|
|
232
|
+
|
|
233
|
+
return f"{self.cache_prefix}{key_hash}"
|
|
234
|
+
|
|
235
|
+
async def get_stats(self) -> Dict[str, Any]:
|
|
236
|
+
"""
|
|
237
|
+
Get cache statistics.
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
Cache statistics dictionary
|
|
241
|
+
"""
|
|
242
|
+
if not self.enabled:
|
|
243
|
+
return {"enabled": False, "total_keys": 0}
|
|
244
|
+
|
|
245
|
+
try:
|
|
246
|
+
redis = await self.redis_client.get_client()
|
|
247
|
+
# Count cache keys
|
|
248
|
+
pattern = f"{self.cache_prefix}*"
|
|
249
|
+
key_count = 0
|
|
250
|
+
async for _ in redis.scan_iter(match=pattern):
|
|
251
|
+
key_count += 1
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
"enabled": True,
|
|
255
|
+
"total_keys": key_count,
|
|
256
|
+
"prefix": self.cache_prefix,
|
|
257
|
+
}
|
|
258
|
+
except Exception as e:
|
|
259
|
+
logger.warning(f"Cache stats error: {e}")
|
|
260
|
+
return {"enabled": True, "error": str(e)}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Constants, Enums, and Exception Classes for Search Tool
|
|
3
|
+
|
|
4
|
+
This module contains all the shared constants, enumeration types, and
|
|
5
|
+
custom exception classes used across the search tool package.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from enum import Enum
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# ============================================================================
|
|
12
|
+
# Enums
|
|
13
|
+
# ============================================================================
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SearchType(str, Enum):
|
|
17
|
+
"""Supported search types"""
|
|
18
|
+
|
|
19
|
+
WEB = "web"
|
|
20
|
+
IMAGE = "image"
|
|
21
|
+
NEWS = "news"
|
|
22
|
+
VIDEO = "video"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class SafeSearch(str, Enum):
|
|
26
|
+
"""Safe search levels"""
|
|
27
|
+
|
|
28
|
+
OFF = "off"
|
|
29
|
+
MEDIUM = "medium"
|
|
30
|
+
HIGH = "high"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ImageSize(str, Enum):
|
|
34
|
+
"""Image size filters"""
|
|
35
|
+
|
|
36
|
+
ICON = "icon"
|
|
37
|
+
SMALL = "small"
|
|
38
|
+
MEDIUM = "medium"
|
|
39
|
+
LARGE = "large"
|
|
40
|
+
XLARGE = "xlarge"
|
|
41
|
+
XXLARGE = "xxlarge"
|
|
42
|
+
HUGE = "huge"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ImageType(str, Enum):
|
|
46
|
+
"""Image type filters"""
|
|
47
|
+
|
|
48
|
+
CLIPART = "clipart"
|
|
49
|
+
FACE = "face"
|
|
50
|
+
LINEART = "lineart"
|
|
51
|
+
STOCK = "stock"
|
|
52
|
+
PHOTO = "photo"
|
|
53
|
+
ANIMATED = "animated"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class ImageColorType(str, Enum):
|
|
57
|
+
"""Image color type filters"""
|
|
58
|
+
|
|
59
|
+
COLOR = "color"
|
|
60
|
+
GRAY = "gray"
|
|
61
|
+
MONO = "mono"
|
|
62
|
+
TRANS = "trans"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class CircuitState(str, Enum):
|
|
66
|
+
"""Circuit breaker states"""
|
|
67
|
+
|
|
68
|
+
CLOSED = "closed"
|
|
69
|
+
OPEN = "open"
|
|
70
|
+
HALF_OPEN = "half_open"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class QueryIntentType(str, Enum):
|
|
74
|
+
"""Query intent types"""
|
|
75
|
+
|
|
76
|
+
DEFINITION = "definition"
|
|
77
|
+
HOW_TO = "how_to"
|
|
78
|
+
COMPARISON = "comparison"
|
|
79
|
+
FACTUAL = "factual"
|
|
80
|
+
RECENT_NEWS = "recent_news"
|
|
81
|
+
ACADEMIC = "academic"
|
|
82
|
+
PRODUCT = "product"
|
|
83
|
+
GENERAL = "general"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class CredibilityLevel(str, Enum):
|
|
87
|
+
"""Result credibility levels"""
|
|
88
|
+
|
|
89
|
+
HIGH = "high"
|
|
90
|
+
MEDIUM = "medium"
|
|
91
|
+
LOW = "low"
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# ============================================================================
|
|
95
|
+
# Exception Hierarchy
|
|
96
|
+
# ============================================================================
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class SearchToolError(Exception):
|
|
100
|
+
"""Base exception for SearchTool errors"""
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class AuthenticationError(SearchToolError):
|
|
104
|
+
"""Authentication-related errors"""
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class QuotaExceededError(SearchToolError):
|
|
108
|
+
"""API quota exceeded"""
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class RateLimitError(SearchToolError):
|
|
112
|
+
"""Rate limit exceeded"""
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class CircuitBreakerOpenError(SearchToolError):
|
|
116
|
+
"""Circuit breaker is open"""
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class SearchAPIError(SearchToolError):
|
|
120
|
+
"""Search API errors"""
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class ValidationError(SearchToolError):
|
|
124
|
+
"""Input validation errors"""
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class CacheError(SearchToolError):
|
|
128
|
+
"""Cache-related errors"""
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Search Context Management
|
|
3
|
+
|
|
4
|
+
This module tracks search history, learns user preferences, and provides
|
|
5
|
+
contextual suggestions for better search results.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from typing import Any, Dict, List, Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SearchContext:
|
|
13
|
+
"""Manages search history and context for improved results"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, max_history: int = 10):
|
|
16
|
+
"""
|
|
17
|
+
Initialize search context.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
max_history: Maximum number of searches to keep in history
|
|
21
|
+
"""
|
|
22
|
+
self.search_history: List[Dict[str, Any]] = []
|
|
23
|
+
self.max_history = max_history
|
|
24
|
+
self.topic_context: Optional[List[str]] = None
|
|
25
|
+
self.user_preferences = {
|
|
26
|
+
"preferred_domains": set(),
|
|
27
|
+
"avoided_domains": set(),
|
|
28
|
+
"preferred_content_types": [],
|
|
29
|
+
"language": "en",
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
def add_search(
|
|
33
|
+
self,
|
|
34
|
+
query: str,
|
|
35
|
+
results: List[Dict[str, Any]],
|
|
36
|
+
user_feedback: Optional[Dict[str, Any]] = None,
|
|
37
|
+
):
|
|
38
|
+
"""
|
|
39
|
+
Add search to history and update context.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
query: Search query
|
|
43
|
+
results: Search results
|
|
44
|
+
user_feedback: Optional user feedback for learning
|
|
45
|
+
"""
|
|
46
|
+
search_record = {
|
|
47
|
+
"timestamp": datetime.utcnow().isoformat(),
|
|
48
|
+
"query": query,
|
|
49
|
+
"result_count": len(results),
|
|
50
|
+
"clicked_results": [],
|
|
51
|
+
"feedback": user_feedback,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
self.search_history.append(search_record)
|
|
55
|
+
|
|
56
|
+
# Maintain history size limit
|
|
57
|
+
if len(self.search_history) > self.max_history:
|
|
58
|
+
self.search_history.pop(0)
|
|
59
|
+
|
|
60
|
+
# Update topic context
|
|
61
|
+
self._update_topic_context(query, results)
|
|
62
|
+
|
|
63
|
+
# Learn from feedback if provided
|
|
64
|
+
if user_feedback:
|
|
65
|
+
self._learn_preferences(results, user_feedback)
|
|
66
|
+
|
|
67
|
+
def get_contextual_suggestions(self, current_query: str) -> Dict[str, Any]:
|
|
68
|
+
"""
|
|
69
|
+
Generate context-aware suggestions for the current query.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
current_query: Current search query
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Suggestions dictionary with related queries and parameters
|
|
76
|
+
"""
|
|
77
|
+
suggestions = {
|
|
78
|
+
"related_queries": [],
|
|
79
|
+
"refinement_suggestions": [],
|
|
80
|
+
"context_aware_params": {},
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if not self.search_history:
|
|
84
|
+
return suggestions
|
|
85
|
+
|
|
86
|
+
# Find related historical queries
|
|
87
|
+
for record in reversed(self.search_history[-5:]):
|
|
88
|
+
prev_query = record["query"]
|
|
89
|
+
similarity = self._calculate_query_similarity(current_query, prev_query)
|
|
90
|
+
|
|
91
|
+
if similarity > 0.5:
|
|
92
|
+
suggestions["related_queries"].append(
|
|
93
|
+
{
|
|
94
|
+
"query": prev_query,
|
|
95
|
+
"similarity": similarity,
|
|
96
|
+
"timestamp": record["timestamp"],
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Suggest preferred sites if available
|
|
101
|
+
if self.user_preferences["preferred_domains"]:
|
|
102
|
+
suggestions["context_aware_params"]["preferred_sites"] = list(
|
|
103
|
+
self.user_preferences["preferred_domains"]
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
return suggestions
|
|
107
|
+
|
|
108
|
+
def get_history(self, limit: Optional[int] = None) -> List[Dict[str, Any]]:
|
|
109
|
+
"""
|
|
110
|
+
Get search history.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
limit: Maximum number of records to return
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
List of search history records
|
|
117
|
+
"""
|
|
118
|
+
if limit:
|
|
119
|
+
return self.search_history[-limit:]
|
|
120
|
+
return self.search_history.copy()
|
|
121
|
+
|
|
122
|
+
def clear_history(self):
|
|
123
|
+
"""Clear search history"""
|
|
124
|
+
self.search_history.clear()
|
|
125
|
+
self.topic_context = None
|
|
126
|
+
|
|
127
|
+
def get_preferences(self) -> Dict[str, Any]:
|
|
128
|
+
"""
|
|
129
|
+
Get current user preferences.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
User preferences dictionary
|
|
133
|
+
"""
|
|
134
|
+
return {
|
|
135
|
+
"preferred_domains": list(self.user_preferences["preferred_domains"]),
|
|
136
|
+
"avoided_domains": list(self.user_preferences["avoided_domains"]),
|
|
137
|
+
"preferred_content_types": self.user_preferences["preferred_content_types"].copy(),
|
|
138
|
+
"language": self.user_preferences["language"],
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
def set_preference(self, key: str, value: Any):
|
|
142
|
+
"""
|
|
143
|
+
Set a user preference.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
key: Preference key
|
|
147
|
+
value: Preference value
|
|
148
|
+
"""
|
|
149
|
+
if key in self.user_preferences:
|
|
150
|
+
if isinstance(self.user_preferences[key], set):
|
|
151
|
+
if isinstance(value, (list, set)):
|
|
152
|
+
self.user_preferences[key] = set(value)
|
|
153
|
+
else:
|
|
154
|
+
self.user_preferences[key].add(value)
|
|
155
|
+
else:
|
|
156
|
+
self.user_preferences[key] = value
|
|
157
|
+
|
|
158
|
+
def _update_topic_context(self, query: str, results: List[Dict[str, Any]]):
|
|
159
|
+
"""
|
|
160
|
+
Update topic context from query and results.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
query: Search query
|
|
164
|
+
results: Search results
|
|
165
|
+
"""
|
|
166
|
+
# Simple implementation: extract common words
|
|
167
|
+
words = query.lower().split()
|
|
168
|
+
self.topic_context = words
|
|
169
|
+
|
|
170
|
+
def _learn_preferences(self, results: List[Dict[str, Any]], feedback: Dict[str, Any]):
|
|
171
|
+
"""
|
|
172
|
+
Learn user preferences from feedback.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
results: Search results
|
|
176
|
+
feedback: User feedback
|
|
177
|
+
"""
|
|
178
|
+
# Learn from clicked/used results
|
|
179
|
+
if "clicked_indices" in feedback:
|
|
180
|
+
for idx in feedback["clicked_indices"]:
|
|
181
|
+
if 0 <= idx < len(results):
|
|
182
|
+
result = results[idx]
|
|
183
|
+
domain = result.get("displayLink", "")
|
|
184
|
+
if domain:
|
|
185
|
+
self.user_preferences["preferred_domains"].add(domain)
|
|
186
|
+
|
|
187
|
+
# Learn from disliked results
|
|
188
|
+
if "disliked_indices" in feedback:
|
|
189
|
+
for idx in feedback["disliked_indices"]:
|
|
190
|
+
if 0 <= idx < len(results):
|
|
191
|
+
result = results[idx]
|
|
192
|
+
domain = result.get("displayLink", "")
|
|
193
|
+
if domain:
|
|
194
|
+
self.user_preferences["avoided_domains"].add(domain)
|
|
195
|
+
|
|
196
|
+
def _calculate_query_similarity(self, query1: str, query2: str) -> float:
|
|
197
|
+
"""
|
|
198
|
+
Calculate similarity between two queries using Jaccard index.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
query1: First query
|
|
202
|
+
query2: Second query
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
Similarity score (0-1)
|
|
206
|
+
"""
|
|
207
|
+
words1 = set(query1.lower().split())
|
|
208
|
+
words2 = set(query2.lower().split())
|
|
209
|
+
|
|
210
|
+
if not words1 or not words2:
|
|
211
|
+
return 0.0
|
|
212
|
+
|
|
213
|
+
intersection = words1 & words2
|
|
214
|
+
union = words1 | words2
|
|
215
|
+
|
|
216
|
+
return len(intersection) / len(union) if union else 0.0
|