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.
Files changed (302) hide show
  1. aiecs/__init__.py +72 -0
  2. aiecs/__main__.py +41 -0
  3. aiecs/aiecs_client.py +469 -0
  4. aiecs/application/__init__.py +10 -0
  5. aiecs/application/executors/__init__.py +10 -0
  6. aiecs/application/executors/operation_executor.py +363 -0
  7. aiecs/application/knowledge_graph/__init__.py +7 -0
  8. aiecs/application/knowledge_graph/builder/__init__.py +37 -0
  9. aiecs/application/knowledge_graph/builder/document_builder.py +375 -0
  10. aiecs/application/knowledge_graph/builder/graph_builder.py +356 -0
  11. aiecs/application/knowledge_graph/builder/schema_mapping.py +531 -0
  12. aiecs/application/knowledge_graph/builder/structured_pipeline.py +443 -0
  13. aiecs/application/knowledge_graph/builder/text_chunker.py +319 -0
  14. aiecs/application/knowledge_graph/extractors/__init__.py +27 -0
  15. aiecs/application/knowledge_graph/extractors/base.py +100 -0
  16. aiecs/application/knowledge_graph/extractors/llm_entity_extractor.py +327 -0
  17. aiecs/application/knowledge_graph/extractors/llm_relation_extractor.py +349 -0
  18. aiecs/application/knowledge_graph/extractors/ner_entity_extractor.py +244 -0
  19. aiecs/application/knowledge_graph/fusion/__init__.py +23 -0
  20. aiecs/application/knowledge_graph/fusion/entity_deduplicator.py +387 -0
  21. aiecs/application/knowledge_graph/fusion/entity_linker.py +343 -0
  22. aiecs/application/knowledge_graph/fusion/knowledge_fusion.py +580 -0
  23. aiecs/application/knowledge_graph/fusion/relation_deduplicator.py +189 -0
  24. aiecs/application/knowledge_graph/pattern_matching/__init__.py +21 -0
  25. aiecs/application/knowledge_graph/pattern_matching/pattern_matcher.py +344 -0
  26. aiecs/application/knowledge_graph/pattern_matching/query_executor.py +378 -0
  27. aiecs/application/knowledge_graph/profiling/__init__.py +12 -0
  28. aiecs/application/knowledge_graph/profiling/query_plan_visualizer.py +199 -0
  29. aiecs/application/knowledge_graph/profiling/query_profiler.py +223 -0
  30. aiecs/application/knowledge_graph/reasoning/__init__.py +27 -0
  31. aiecs/application/knowledge_graph/reasoning/evidence_synthesis.py +347 -0
  32. aiecs/application/knowledge_graph/reasoning/inference_engine.py +504 -0
  33. aiecs/application/knowledge_graph/reasoning/logic_form_parser.py +167 -0
  34. aiecs/application/knowledge_graph/reasoning/logic_parser/__init__.py +79 -0
  35. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_builder.py +513 -0
  36. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_nodes.py +630 -0
  37. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_validator.py +654 -0
  38. aiecs/application/knowledge_graph/reasoning/logic_parser/error_handler.py +477 -0
  39. aiecs/application/knowledge_graph/reasoning/logic_parser/parser.py +390 -0
  40. aiecs/application/knowledge_graph/reasoning/logic_parser/query_context.py +217 -0
  41. aiecs/application/knowledge_graph/reasoning/logic_query_integration.py +169 -0
  42. aiecs/application/knowledge_graph/reasoning/query_planner.py +872 -0
  43. aiecs/application/knowledge_graph/reasoning/reasoning_engine.py +554 -0
  44. aiecs/application/knowledge_graph/retrieval/__init__.py +19 -0
  45. aiecs/application/knowledge_graph/retrieval/retrieval_strategies.py +596 -0
  46. aiecs/application/knowledge_graph/search/__init__.py +59 -0
  47. aiecs/application/knowledge_graph/search/hybrid_search.py +423 -0
  48. aiecs/application/knowledge_graph/search/reranker.py +295 -0
  49. aiecs/application/knowledge_graph/search/reranker_strategies.py +553 -0
  50. aiecs/application/knowledge_graph/search/text_similarity.py +398 -0
  51. aiecs/application/knowledge_graph/traversal/__init__.py +15 -0
  52. aiecs/application/knowledge_graph/traversal/enhanced_traversal.py +329 -0
  53. aiecs/application/knowledge_graph/traversal/path_scorer.py +269 -0
  54. aiecs/application/knowledge_graph/validators/__init__.py +13 -0
  55. aiecs/application/knowledge_graph/validators/relation_validator.py +189 -0
  56. aiecs/application/knowledge_graph/visualization/__init__.py +11 -0
  57. aiecs/application/knowledge_graph/visualization/graph_visualizer.py +321 -0
  58. aiecs/common/__init__.py +9 -0
  59. aiecs/common/knowledge_graph/__init__.py +17 -0
  60. aiecs/common/knowledge_graph/runnable.py +484 -0
  61. aiecs/config/__init__.py +16 -0
  62. aiecs/config/config.py +498 -0
  63. aiecs/config/graph_config.py +137 -0
  64. aiecs/config/registry.py +23 -0
  65. aiecs/core/__init__.py +46 -0
  66. aiecs/core/interface/__init__.py +34 -0
  67. aiecs/core/interface/execution_interface.py +152 -0
  68. aiecs/core/interface/storage_interface.py +171 -0
  69. aiecs/domain/__init__.py +289 -0
  70. aiecs/domain/agent/__init__.py +189 -0
  71. aiecs/domain/agent/base_agent.py +697 -0
  72. aiecs/domain/agent/exceptions.py +103 -0
  73. aiecs/domain/agent/graph_aware_mixin.py +559 -0
  74. aiecs/domain/agent/hybrid_agent.py +490 -0
  75. aiecs/domain/agent/integration/__init__.py +26 -0
  76. aiecs/domain/agent/integration/context_compressor.py +222 -0
  77. aiecs/domain/agent/integration/context_engine_adapter.py +252 -0
  78. aiecs/domain/agent/integration/retry_policy.py +219 -0
  79. aiecs/domain/agent/integration/role_config.py +213 -0
  80. aiecs/domain/agent/knowledge_aware_agent.py +646 -0
  81. aiecs/domain/agent/lifecycle.py +296 -0
  82. aiecs/domain/agent/llm_agent.py +300 -0
  83. aiecs/domain/agent/memory/__init__.py +12 -0
  84. aiecs/domain/agent/memory/conversation.py +197 -0
  85. aiecs/domain/agent/migration/__init__.py +14 -0
  86. aiecs/domain/agent/migration/conversion.py +160 -0
  87. aiecs/domain/agent/migration/legacy_wrapper.py +90 -0
  88. aiecs/domain/agent/models.py +317 -0
  89. aiecs/domain/agent/observability.py +407 -0
  90. aiecs/domain/agent/persistence.py +289 -0
  91. aiecs/domain/agent/prompts/__init__.py +29 -0
  92. aiecs/domain/agent/prompts/builder.py +161 -0
  93. aiecs/domain/agent/prompts/formatters.py +189 -0
  94. aiecs/domain/agent/prompts/template.py +255 -0
  95. aiecs/domain/agent/registry.py +260 -0
  96. aiecs/domain/agent/tool_agent.py +257 -0
  97. aiecs/domain/agent/tools/__init__.py +12 -0
  98. aiecs/domain/agent/tools/schema_generator.py +221 -0
  99. aiecs/domain/community/__init__.py +155 -0
  100. aiecs/domain/community/agent_adapter.py +477 -0
  101. aiecs/domain/community/analytics.py +481 -0
  102. aiecs/domain/community/collaborative_workflow.py +642 -0
  103. aiecs/domain/community/communication_hub.py +645 -0
  104. aiecs/domain/community/community_builder.py +320 -0
  105. aiecs/domain/community/community_integration.py +800 -0
  106. aiecs/domain/community/community_manager.py +813 -0
  107. aiecs/domain/community/decision_engine.py +879 -0
  108. aiecs/domain/community/exceptions.py +225 -0
  109. aiecs/domain/community/models/__init__.py +33 -0
  110. aiecs/domain/community/models/community_models.py +268 -0
  111. aiecs/domain/community/resource_manager.py +457 -0
  112. aiecs/domain/community/shared_context_manager.py +603 -0
  113. aiecs/domain/context/__init__.py +58 -0
  114. aiecs/domain/context/context_engine.py +989 -0
  115. aiecs/domain/context/conversation_models.py +354 -0
  116. aiecs/domain/context/graph_memory.py +467 -0
  117. aiecs/domain/execution/__init__.py +12 -0
  118. aiecs/domain/execution/model.py +57 -0
  119. aiecs/domain/knowledge_graph/__init__.py +19 -0
  120. aiecs/domain/knowledge_graph/models/__init__.py +52 -0
  121. aiecs/domain/knowledge_graph/models/entity.py +130 -0
  122. aiecs/domain/knowledge_graph/models/evidence.py +194 -0
  123. aiecs/domain/knowledge_graph/models/inference_rule.py +186 -0
  124. aiecs/domain/knowledge_graph/models/path.py +179 -0
  125. aiecs/domain/knowledge_graph/models/path_pattern.py +173 -0
  126. aiecs/domain/knowledge_graph/models/query.py +272 -0
  127. aiecs/domain/knowledge_graph/models/query_plan.py +187 -0
  128. aiecs/domain/knowledge_graph/models/relation.py +136 -0
  129. aiecs/domain/knowledge_graph/schema/__init__.py +23 -0
  130. aiecs/domain/knowledge_graph/schema/entity_type.py +135 -0
  131. aiecs/domain/knowledge_graph/schema/graph_schema.py +271 -0
  132. aiecs/domain/knowledge_graph/schema/property_schema.py +155 -0
  133. aiecs/domain/knowledge_graph/schema/relation_type.py +171 -0
  134. aiecs/domain/knowledge_graph/schema/schema_manager.py +496 -0
  135. aiecs/domain/knowledge_graph/schema/type_enums.py +205 -0
  136. aiecs/domain/task/__init__.py +13 -0
  137. aiecs/domain/task/dsl_processor.py +613 -0
  138. aiecs/domain/task/model.py +62 -0
  139. aiecs/domain/task/task_context.py +268 -0
  140. aiecs/infrastructure/__init__.py +24 -0
  141. aiecs/infrastructure/graph_storage/__init__.py +11 -0
  142. aiecs/infrastructure/graph_storage/base.py +601 -0
  143. aiecs/infrastructure/graph_storage/batch_operations.py +449 -0
  144. aiecs/infrastructure/graph_storage/cache.py +429 -0
  145. aiecs/infrastructure/graph_storage/distributed.py +226 -0
  146. aiecs/infrastructure/graph_storage/error_handling.py +390 -0
  147. aiecs/infrastructure/graph_storage/graceful_degradation.py +306 -0
  148. aiecs/infrastructure/graph_storage/health_checks.py +378 -0
  149. aiecs/infrastructure/graph_storage/in_memory.py +514 -0
  150. aiecs/infrastructure/graph_storage/index_optimization.py +483 -0
  151. aiecs/infrastructure/graph_storage/lazy_loading.py +410 -0
  152. aiecs/infrastructure/graph_storage/metrics.py +357 -0
  153. aiecs/infrastructure/graph_storage/migration.py +413 -0
  154. aiecs/infrastructure/graph_storage/pagination.py +471 -0
  155. aiecs/infrastructure/graph_storage/performance_monitoring.py +466 -0
  156. aiecs/infrastructure/graph_storage/postgres.py +871 -0
  157. aiecs/infrastructure/graph_storage/query_optimizer.py +635 -0
  158. aiecs/infrastructure/graph_storage/schema_cache.py +290 -0
  159. aiecs/infrastructure/graph_storage/sqlite.py +623 -0
  160. aiecs/infrastructure/graph_storage/streaming.py +495 -0
  161. aiecs/infrastructure/messaging/__init__.py +13 -0
  162. aiecs/infrastructure/messaging/celery_task_manager.py +383 -0
  163. aiecs/infrastructure/messaging/websocket_manager.py +298 -0
  164. aiecs/infrastructure/monitoring/__init__.py +34 -0
  165. aiecs/infrastructure/monitoring/executor_metrics.py +174 -0
  166. aiecs/infrastructure/monitoring/global_metrics_manager.py +213 -0
  167. aiecs/infrastructure/monitoring/structured_logger.py +48 -0
  168. aiecs/infrastructure/monitoring/tracing_manager.py +410 -0
  169. aiecs/infrastructure/persistence/__init__.py +24 -0
  170. aiecs/infrastructure/persistence/context_engine_client.py +187 -0
  171. aiecs/infrastructure/persistence/database_manager.py +333 -0
  172. aiecs/infrastructure/persistence/file_storage.py +754 -0
  173. aiecs/infrastructure/persistence/redis_client.py +220 -0
  174. aiecs/llm/__init__.py +86 -0
  175. aiecs/llm/callbacks/__init__.py +11 -0
  176. aiecs/llm/callbacks/custom_callbacks.py +264 -0
  177. aiecs/llm/client_factory.py +420 -0
  178. aiecs/llm/clients/__init__.py +33 -0
  179. aiecs/llm/clients/base_client.py +193 -0
  180. aiecs/llm/clients/googleai_client.py +181 -0
  181. aiecs/llm/clients/openai_client.py +131 -0
  182. aiecs/llm/clients/vertex_client.py +437 -0
  183. aiecs/llm/clients/xai_client.py +184 -0
  184. aiecs/llm/config/__init__.py +51 -0
  185. aiecs/llm/config/config_loader.py +275 -0
  186. aiecs/llm/config/config_validator.py +236 -0
  187. aiecs/llm/config/model_config.py +151 -0
  188. aiecs/llm/utils/__init__.py +10 -0
  189. aiecs/llm/utils/validate_config.py +91 -0
  190. aiecs/main.py +363 -0
  191. aiecs/scripts/__init__.py +3 -0
  192. aiecs/scripts/aid/VERSION_MANAGEMENT.md +97 -0
  193. aiecs/scripts/aid/__init__.py +19 -0
  194. aiecs/scripts/aid/version_manager.py +215 -0
  195. aiecs/scripts/dependance_check/DEPENDENCY_SYSTEM_SUMMARY.md +242 -0
  196. aiecs/scripts/dependance_check/README_DEPENDENCY_CHECKER.md +310 -0
  197. aiecs/scripts/dependance_check/__init__.py +17 -0
  198. aiecs/scripts/dependance_check/dependency_checker.py +938 -0
  199. aiecs/scripts/dependance_check/dependency_fixer.py +391 -0
  200. aiecs/scripts/dependance_check/download_nlp_data.py +396 -0
  201. aiecs/scripts/dependance_check/quick_dependency_check.py +270 -0
  202. aiecs/scripts/dependance_check/setup_nlp_data.sh +217 -0
  203. aiecs/scripts/dependance_patch/__init__.py +7 -0
  204. aiecs/scripts/dependance_patch/fix_weasel/README_WEASEL_PATCH.md +126 -0
  205. aiecs/scripts/dependance_patch/fix_weasel/__init__.py +11 -0
  206. aiecs/scripts/dependance_patch/fix_weasel/fix_weasel_validator.py +128 -0
  207. aiecs/scripts/dependance_patch/fix_weasel/fix_weasel_validator.sh +82 -0
  208. aiecs/scripts/dependance_patch/fix_weasel/patch_weasel_library.sh +188 -0
  209. aiecs/scripts/dependance_patch/fix_weasel/run_weasel_patch.sh +41 -0
  210. aiecs/scripts/tools_develop/README.md +449 -0
  211. aiecs/scripts/tools_develop/TOOL_AUTO_DISCOVERY.md +234 -0
  212. aiecs/scripts/tools_develop/__init__.py +21 -0
  213. aiecs/scripts/tools_develop/check_type_annotations.py +259 -0
  214. aiecs/scripts/tools_develop/validate_tool_schemas.py +422 -0
  215. aiecs/scripts/tools_develop/verify_tools.py +356 -0
  216. aiecs/tasks/__init__.py +1 -0
  217. aiecs/tasks/worker.py +172 -0
  218. aiecs/tools/__init__.py +299 -0
  219. aiecs/tools/apisource/__init__.py +99 -0
  220. aiecs/tools/apisource/intelligence/__init__.py +19 -0
  221. aiecs/tools/apisource/intelligence/data_fusion.py +381 -0
  222. aiecs/tools/apisource/intelligence/query_analyzer.py +413 -0
  223. aiecs/tools/apisource/intelligence/search_enhancer.py +388 -0
  224. aiecs/tools/apisource/monitoring/__init__.py +9 -0
  225. aiecs/tools/apisource/monitoring/metrics.py +303 -0
  226. aiecs/tools/apisource/providers/__init__.py +115 -0
  227. aiecs/tools/apisource/providers/base.py +664 -0
  228. aiecs/tools/apisource/providers/census.py +401 -0
  229. aiecs/tools/apisource/providers/fred.py +564 -0
  230. aiecs/tools/apisource/providers/newsapi.py +412 -0
  231. aiecs/tools/apisource/providers/worldbank.py +357 -0
  232. aiecs/tools/apisource/reliability/__init__.py +12 -0
  233. aiecs/tools/apisource/reliability/error_handler.py +375 -0
  234. aiecs/tools/apisource/reliability/fallback_strategy.py +391 -0
  235. aiecs/tools/apisource/tool.py +850 -0
  236. aiecs/tools/apisource/utils/__init__.py +9 -0
  237. aiecs/tools/apisource/utils/validators.py +338 -0
  238. aiecs/tools/base_tool.py +201 -0
  239. aiecs/tools/docs/__init__.py +121 -0
  240. aiecs/tools/docs/ai_document_orchestrator.py +599 -0
  241. aiecs/tools/docs/ai_document_writer_orchestrator.py +2403 -0
  242. aiecs/tools/docs/content_insertion_tool.py +1333 -0
  243. aiecs/tools/docs/document_creator_tool.py +1317 -0
  244. aiecs/tools/docs/document_layout_tool.py +1166 -0
  245. aiecs/tools/docs/document_parser_tool.py +994 -0
  246. aiecs/tools/docs/document_writer_tool.py +1818 -0
  247. aiecs/tools/knowledge_graph/__init__.py +17 -0
  248. aiecs/tools/knowledge_graph/graph_reasoning_tool.py +734 -0
  249. aiecs/tools/knowledge_graph/graph_search_tool.py +923 -0
  250. aiecs/tools/knowledge_graph/kg_builder_tool.py +476 -0
  251. aiecs/tools/langchain_adapter.py +542 -0
  252. aiecs/tools/schema_generator.py +275 -0
  253. aiecs/tools/search_tool/__init__.py +100 -0
  254. aiecs/tools/search_tool/analyzers.py +589 -0
  255. aiecs/tools/search_tool/cache.py +260 -0
  256. aiecs/tools/search_tool/constants.py +128 -0
  257. aiecs/tools/search_tool/context.py +216 -0
  258. aiecs/tools/search_tool/core.py +749 -0
  259. aiecs/tools/search_tool/deduplicator.py +123 -0
  260. aiecs/tools/search_tool/error_handler.py +271 -0
  261. aiecs/tools/search_tool/metrics.py +371 -0
  262. aiecs/tools/search_tool/rate_limiter.py +178 -0
  263. aiecs/tools/search_tool/schemas.py +277 -0
  264. aiecs/tools/statistics/__init__.py +80 -0
  265. aiecs/tools/statistics/ai_data_analysis_orchestrator.py +643 -0
  266. aiecs/tools/statistics/ai_insight_generator_tool.py +505 -0
  267. aiecs/tools/statistics/ai_report_orchestrator_tool.py +694 -0
  268. aiecs/tools/statistics/data_loader_tool.py +564 -0
  269. aiecs/tools/statistics/data_profiler_tool.py +658 -0
  270. aiecs/tools/statistics/data_transformer_tool.py +573 -0
  271. aiecs/tools/statistics/data_visualizer_tool.py +495 -0
  272. aiecs/tools/statistics/model_trainer_tool.py +487 -0
  273. aiecs/tools/statistics/statistical_analyzer_tool.py +459 -0
  274. aiecs/tools/task_tools/__init__.py +86 -0
  275. aiecs/tools/task_tools/chart_tool.py +732 -0
  276. aiecs/tools/task_tools/classfire_tool.py +922 -0
  277. aiecs/tools/task_tools/image_tool.py +447 -0
  278. aiecs/tools/task_tools/office_tool.py +684 -0
  279. aiecs/tools/task_tools/pandas_tool.py +635 -0
  280. aiecs/tools/task_tools/report_tool.py +635 -0
  281. aiecs/tools/task_tools/research_tool.py +392 -0
  282. aiecs/tools/task_tools/scraper_tool.py +715 -0
  283. aiecs/tools/task_tools/stats_tool.py +688 -0
  284. aiecs/tools/temp_file_manager.py +130 -0
  285. aiecs/tools/tool_executor/__init__.py +37 -0
  286. aiecs/tools/tool_executor/tool_executor.py +881 -0
  287. aiecs/utils/LLM_output_structor.py +445 -0
  288. aiecs/utils/__init__.py +34 -0
  289. aiecs/utils/base_callback.py +47 -0
  290. aiecs/utils/cache_provider.py +695 -0
  291. aiecs/utils/execution_utils.py +184 -0
  292. aiecs/utils/logging.py +1 -0
  293. aiecs/utils/prompt_loader.py +14 -0
  294. aiecs/utils/token_usage_repository.py +323 -0
  295. aiecs/ws/__init__.py +0 -0
  296. aiecs/ws/socket_server.py +52 -0
  297. aiecs-1.5.1.dist-info/METADATA +608 -0
  298. aiecs-1.5.1.dist-info/RECORD +302 -0
  299. aiecs-1.5.1.dist-info/WHEEL +5 -0
  300. aiecs-1.5.1.dist-info/entry_points.txt +10 -0
  301. aiecs-1.5.1.dist-info/licenses/LICENSE +225 -0
  302. aiecs-1.5.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,123 @@
1
+ """
2
+ Result Deduplication
3
+
4
+ This module handles detection and removal of duplicate and highly similar
5
+ search results.
6
+ """
7
+
8
+ import hashlib
9
+ from typing import Any, Dict, List
10
+ from urllib.parse import urlparse, urlunparse
11
+
12
+
13
+ class ResultDeduplicator:
14
+ """Removes duplicate and similar search results"""
15
+
16
+ def deduplicate_results(
17
+ self, results: List[Dict[str, Any]], similarity_threshold: float = 0.85
18
+ ) -> List[Dict[str, Any]]:
19
+ """
20
+ Remove duplicate and highly similar results.
21
+
22
+ Args:
23
+ results: List of search results
24
+ similarity_threshold: Similarity threshold (0-1) for considering results as duplicates
25
+
26
+ Returns:
27
+ Deduplicated list of results
28
+ """
29
+ if not results:
30
+ return []
31
+
32
+ unique_results = []
33
+ seen_urls = set()
34
+ seen_content_hashes = set()
35
+
36
+ for result in results:
37
+ url = result.get("link", "")
38
+
39
+ # 1. URL deduplication (normalized)
40
+ normalized_url = self._normalize_url(url)
41
+ if normalized_url in seen_urls:
42
+ continue
43
+
44
+ # 2. Content similarity deduplication
45
+ content_hash = self._calculate_content_hash(
46
+ result.get("title", ""), result.get("snippet", "")
47
+ )
48
+
49
+ # Check for high similarity with existing results
50
+ is_duplicate = False
51
+ for seen_hash in seen_content_hashes:
52
+ similarity = self._calculate_similarity(content_hash, seen_hash)
53
+ if similarity > similarity_threshold:
54
+ is_duplicate = True
55
+ break
56
+
57
+ if is_duplicate:
58
+ continue
59
+
60
+ # Add to unique results
61
+ unique_results.append(result)
62
+ seen_urls.add(normalized_url)
63
+ seen_content_hashes.add(content_hash)
64
+
65
+ return unique_results
66
+
67
+ def _normalize_url(self, url: str) -> str:
68
+ """
69
+ Normalize URL by removing query parameters and fragments.
70
+
71
+ Args:
72
+ url: URL to normalize
73
+
74
+ Returns:
75
+ Normalized URL
76
+ """
77
+ try:
78
+ parsed = urlparse(url)
79
+ # Keep only scheme, netloc, and path
80
+ normalized = urlunparse(
81
+ (
82
+ parsed.scheme,
83
+ parsed.netloc.lower(),
84
+ parsed.path.rstrip("/"),
85
+ "",
86
+ "",
87
+ "", # Remove params, query, fragment
88
+ )
89
+ )
90
+ return normalized
91
+ except Exception:
92
+ return url.lower()
93
+
94
+ def _calculate_content_hash(self, title: str, snippet: str) -> str:
95
+ """
96
+ Calculate content hash for similarity detection.
97
+
98
+ Args:
99
+ title: Result title
100
+ snippet: Result snippet
101
+
102
+ Returns:
103
+ Content hash string
104
+ """
105
+ content = f"{title.lower()} {snippet.lower()}"
106
+ # Remove punctuation and normalize whitespace
107
+ content = "".join(c for c in content if c.isalnum() or c.isspace())
108
+ content = " ".join(content.split())
109
+ return hashlib.md5(content.encode()).hexdigest()
110
+
111
+ def _calculate_similarity(self, hash1: str, hash2: str) -> float:
112
+ """
113
+ Calculate similarity between two content hashes.
114
+
115
+ Args:
116
+ hash1: First content hash
117
+ hash2: Second content hash
118
+
119
+ Returns:
120
+ Similarity score (0-1)
121
+ """
122
+ # Exact hash match
123
+ return 1.0 if hash1 == hash2 else 0.0
@@ -0,0 +1,271 @@
1
+ """
2
+ Agent-Friendly Error Handling
3
+
4
+ This module formats errors in an agent-friendly way with clear messages,
5
+ suggested actions, and alternative approaches.
6
+ """
7
+
8
+ from typing import Any, Dict
9
+
10
+ from .constants import (
11
+ QuotaExceededError,
12
+ AuthenticationError,
13
+ RateLimitError,
14
+ CircuitBreakerOpenError,
15
+ ValidationError,
16
+ )
17
+
18
+
19
+ class AgentFriendlyErrorHandler:
20
+ """Formats errors for agent consumption with actionable suggestions"""
21
+
22
+ def format_error_for_agent(self, error: Exception, context: Dict[str, Any]) -> Dict[str, Any]:
23
+ """
24
+ Format error for agent-friendly consumption.
25
+
26
+ Args:
27
+ error: The exception that occurred
28
+ context: Context information (circuit breaker timeout, etc.)
29
+
30
+ Returns:
31
+ Structured error information dictionary
32
+ """
33
+ error_response = {
34
+ "error_type": "unknown",
35
+ "severity": "medium",
36
+ "user_message": "",
37
+ "technical_details": str(error),
38
+ "suggested_actions": [],
39
+ "alternative_approaches": [],
40
+ "can_retry": False,
41
+ "estimated_recovery_time": None,
42
+ }
43
+
44
+ error_str = str(error).lower()
45
+ error_type = type(error).__name__
46
+
47
+ # Handle specific error types
48
+ if (
49
+ isinstance(error, QuotaExceededError)
50
+ or "quota" in error_str
51
+ or "rate limit" in error_str
52
+ ):
53
+ self._handle_quota_exceeded(error_response)
54
+
55
+ elif (
56
+ isinstance(error, AuthenticationError)
57
+ or "auth" in error_str
58
+ or "credential" in error_str
59
+ ):
60
+ self._handle_authentication_error(error_response)
61
+
62
+ elif isinstance(error, RateLimitError):
63
+ self._handle_rate_limit_error(error_response)
64
+
65
+ elif isinstance(error, CircuitBreakerOpenError) or "circuit breaker" in error_str:
66
+ self._handle_circuit_breaker_error(error_response, context)
67
+
68
+ elif (
69
+ isinstance(error, ValidationError)
70
+ or "invalid" in error_str
71
+ or "validation" in error_str
72
+ ):
73
+ self._handle_validation_error(error_response)
74
+
75
+ elif "timeout" in error_str or "connection" in error_str or "network" in error_str:
76
+ self._handle_network_error(error_response)
77
+
78
+ elif "no results" in error_str or "not found" in error_str:
79
+ self._handle_no_results(error_response)
80
+
81
+ else:
82
+ # Generic error handling
83
+ error_response.update(
84
+ {
85
+ "error_type": error_type,
86
+ "severity": "medium",
87
+ "user_message": f"An unexpected error occurred: {str(error)}",
88
+ "suggested_actions": [
89
+ "Check your query parameters",
90
+ "Try simplifying the query",
91
+ "Retry the operation",
92
+ ],
93
+ "can_retry": True,
94
+ }
95
+ )
96
+
97
+ return error_response
98
+
99
+ def _handle_quota_exceeded(self, response: Dict[str, Any]):
100
+ """Handle quota exceeded errors"""
101
+ response.update(
102
+ {
103
+ "error_type": "quota_exceeded",
104
+ "severity": "high",
105
+ "user_message": (
106
+ "Search API quota has been exceeded. "
107
+ "The service has temporarily reached its usage limit."
108
+ ),
109
+ "suggested_actions": [
110
+ "Wait 60-120 seconds before retrying",
111
+ "Reduce the number of results requested",
112
+ "Use more specific queries to get better results with fewer searches",
113
+ "Check if cached results are available",
114
+ ],
115
+ "alternative_approaches": [
116
+ "Use the scraper tool to extract information from known URLs",
117
+ "Query specific authoritative domains using site: operator",
118
+ "Defer non-urgent searches to later",
119
+ ],
120
+ "can_retry": True,
121
+ "estimated_recovery_time": "1-2 minutes",
122
+ }
123
+ )
124
+
125
+ def _handle_authentication_error(self, response: Dict[str, Any]):
126
+ """Handle authentication errors"""
127
+ response.update(
128
+ {
129
+ "error_type": "authentication_failed",
130
+ "severity": "high",
131
+ "user_message": (
132
+ "Search API authentication failed. "
133
+ "The API credentials may be invalid or expired."
134
+ ),
135
+ "suggested_actions": [
136
+ "Verify that GOOGLE_API_KEY is set correctly in environment",
137
+ "Check that GOOGLE_CSE_ID is valid",
138
+ "Ensure API key has not expired",
139
+ "Verify API key has Custom Search API enabled",
140
+ ],
141
+ "alternative_approaches": [
142
+ "Use alternative data sources (apisource_tool)",
143
+ "Request manual search from user",
144
+ ],
145
+ "can_retry": False,
146
+ "estimated_recovery_time": None,
147
+ }
148
+ )
149
+
150
+ def _handle_rate_limit_error(self, response: Dict[str, Any]):
151
+ """Handle rate limit errors"""
152
+ response.update(
153
+ {
154
+ "error_type": "rate_limit_exceeded",
155
+ "severity": "medium",
156
+ "user_message": (
157
+ "Rate limit has been exceeded. " "Too many requests in a short time period."
158
+ ),
159
+ "suggested_actions": [
160
+ "Wait for the suggested time before retrying",
161
+ "Reduce request frequency",
162
+ "Use cached results when available",
163
+ "Batch similar queries together",
164
+ ],
165
+ "alternative_approaches": [
166
+ "Use cached or historical data",
167
+ "Prioritize critical searches",
168
+ ],
169
+ "can_retry": True,
170
+ "estimated_recovery_time": "As indicated in error message",
171
+ }
172
+ )
173
+
174
+ def _handle_circuit_breaker_error(self, response: Dict[str, Any], context: Dict[str, Any]):
175
+ """Handle circuit breaker open errors"""
176
+ timeout = context.get("circuit_breaker_timeout", 60)
177
+
178
+ response.update(
179
+ {
180
+ "error_type": "circuit_breaker_open",
181
+ "severity": "high",
182
+ "user_message": (
183
+ "Search service is temporarily unavailable due to repeated failures. "
184
+ "The circuit breaker has been triggered for protection."
185
+ ),
186
+ "suggested_actions": [
187
+ f"Wait {timeout} seconds for circuit to reset",
188
+ "Check search service status",
189
+ "Review recent error logs",
190
+ ],
191
+ "alternative_approaches": [
192
+ "Use alternative data sources",
193
+ "Defer search to later",
194
+ "Use cached or historical data",
195
+ ],
196
+ "can_retry": True,
197
+ "estimated_recovery_time": f"{timeout} seconds",
198
+ }
199
+ )
200
+
201
+ def _handle_validation_error(self, response: Dict[str, Any]):
202
+ """Handle validation errors"""
203
+ response.update(
204
+ {
205
+ "error_type": "invalid_query",
206
+ "severity": "low",
207
+ "user_message": (
208
+ "The search query or parameters are invalid. " "Please check the query format."
209
+ ),
210
+ "suggested_actions": [
211
+ "Simplify the query - remove special characters",
212
+ "Check that all parameters are within valid ranges",
213
+ "Ensure query is not empty",
214
+ "Review query syntax for search operators",
215
+ ],
216
+ "alternative_approaches": [
217
+ "Break complex query into simpler parts",
218
+ "Use basic search without advanced operators",
219
+ ],
220
+ "can_retry": True,
221
+ "estimated_recovery_time": "immediate (after fixing query)",
222
+ }
223
+ )
224
+
225
+ def _handle_network_error(self, response: Dict[str, Any]):
226
+ """Handle network-related errors"""
227
+ response.update(
228
+ {
229
+ "error_type": "network_error",
230
+ "severity": "medium",
231
+ "user_message": (
232
+ "Network connection to search API failed. " "This is usually a temporary issue."
233
+ ),
234
+ "suggested_actions": [
235
+ "Retry the search in 5-10 seconds",
236
+ "Check internet connectivity",
237
+ "Try with a shorter timeout if query is complex",
238
+ ],
239
+ "alternative_approaches": [
240
+ "Use cached results if available",
241
+ "Try alternative search parameters",
242
+ ],
243
+ "can_retry": True,
244
+ "estimated_recovery_time": "10-30 seconds",
245
+ }
246
+ )
247
+
248
+ def _handle_no_results(self, response: Dict[str, Any]):
249
+ """Handle no results found"""
250
+ response.update(
251
+ {
252
+ "error_type": "no_results",
253
+ "severity": "low",
254
+ "user_message": (
255
+ "No search results found for the query. " "Try broadening your search terms."
256
+ ),
257
+ "suggested_actions": [
258
+ "Remove overly specific terms",
259
+ "Try synonyms or related terms",
260
+ "Remove date restrictions",
261
+ "Broaden the search scope",
262
+ ],
263
+ "alternative_approaches": [
264
+ "Search for related topics",
265
+ "Try different search engines or sources",
266
+ "Break down into sub-queries",
267
+ ],
268
+ "can_retry": True,
269
+ "estimated_recovery_time": "immediate (with modified query)",
270
+ }
271
+ )