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,391 @@
1
+ """
2
+ Intelligent Fallback Strategy for API Providers
3
+
4
+ Provides automatic provider failover:
5
+ - Define fallback chains between providers
6
+ - Map equivalent operations across providers
7
+ - Convert parameters between provider formats
8
+ - Track fallback attempts and success rates
9
+ """
10
+
11
+ import logging
12
+ from typing import Any, Callable, Dict, List
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class FallbackStrategy:
18
+ """
19
+ Manages fallback logic when primary providers fail.
20
+
21
+ Automatically retries with alternative providers when a request fails,
22
+ handling operation mapping and parameter conversion.
23
+ """
24
+
25
+ # Provider fallback chains: primary -> [fallbacks]
26
+ FALLBACK_MAP = {
27
+ "fred": ["worldbank"], # FRED -> World Bank for economic data
28
+ "worldbank": [], # World Bank has no fallback
29
+ "newsapi": [], # News API has no fallback
30
+ "census": ["worldbank"], # Census -> World Bank for demographic data
31
+ }
32
+
33
+ # Operation mappings: (provider, operation) -> [(fallback_provider,
34
+ # fallback_operation)]
35
+ OPERATION_MAP = {
36
+ ("fred", "get_series"): [("worldbank", "get_indicator")],
37
+ ("fred", "get_series_observations"): [("worldbank", "get_indicator")],
38
+ ("fred", "search_series"): [("worldbank", "search_indicators")],
39
+ ("census", "get_population"): [("worldbank", "get_indicator")],
40
+ ("census", "get_acs_data"): [("worldbank", "get_indicator")],
41
+ }
42
+
43
+ # Parameter conversion rules
44
+ PARAMETER_CONVERSIONS = {
45
+ ("fred", "worldbank"): {
46
+ "series_id": "indicator_code",
47
+ "observation_start": "date", # Note: will need special handling
48
+ "observation_end": "date",
49
+ "limit": "per_page",
50
+ },
51
+ ("census", "worldbank"): {
52
+ "variables": "indicator_code",
53
+ # Note: needs conversion (e.g., 'state:*' -> 'US')
54
+ "geography": "country_code",
55
+ },
56
+ }
57
+
58
+ def __init__(self):
59
+ """Initialize fallback strategy"""
60
+ self.fallback_stats = {} # Track fallback success rates
61
+
62
+ def execute_with_fallback(
63
+ self,
64
+ primary_provider: str,
65
+ operation: str,
66
+ params: Dict[str, Any],
67
+ provider_executor: Callable[[str, str, Dict[str, Any]], Dict[str, Any]],
68
+ providers_available: List[str],
69
+ ) -> Dict[str, Any]:
70
+ """
71
+ Execute operation with automatic fallback to alternative providers.
72
+
73
+ Args:
74
+ primary_provider: Primary provider name
75
+ operation: Operation name
76
+ params: Operation parameters
77
+ provider_executor: Function to execute provider operation:
78
+ (provider, operation, params) -> result
79
+ providers_available: List of available provider names
80
+
81
+ Returns:
82
+ Result dictionary with:
83
+ - success: bool
84
+ - data: result data if successful
85
+ - attempts: list of attempt information
86
+ - fallback_used: bool
87
+ """
88
+ result = {
89
+ "success": False,
90
+ "data": None,
91
+ "attempts": [],
92
+ "fallback_used": False,
93
+ }
94
+
95
+ # Try primary provider
96
+ try:
97
+ logger.info(f"Attempting primary provider: {primary_provider}.{operation}")
98
+ data = provider_executor(primary_provider, operation, params)
99
+
100
+ result["success"] = True
101
+ result["data"] = data
102
+ result["attempts"].append(
103
+ {
104
+ "provider": primary_provider,
105
+ "operation": operation,
106
+ "status": "success",
107
+ }
108
+ )
109
+
110
+ return result
111
+
112
+ except Exception as primary_error:
113
+ logger.warning(
114
+ f"Primary provider {primary_provider}.{operation} failed: {primary_error}"
115
+ )
116
+ result["attempts"].append(
117
+ {
118
+ "provider": primary_provider,
119
+ "operation": operation,
120
+ "status": "failed",
121
+ "error": str(primary_error),
122
+ }
123
+ )
124
+
125
+ # Get fallback providers
126
+ fallback_providers = self.FALLBACK_MAP.get(primary_provider, [])
127
+
128
+ if not fallback_providers:
129
+ logger.info(f"No fallback providers configured for {primary_provider}")
130
+ return result
131
+
132
+ # Try each fallback provider
133
+ for fallback_provider in fallback_providers:
134
+ if fallback_provider not in providers_available:
135
+ logger.debug(f"Fallback provider {fallback_provider} not available")
136
+ continue
137
+
138
+ # Find equivalent operation
139
+ fallback_operations = self._get_fallback_operations(
140
+ primary_provider, operation, fallback_provider
141
+ )
142
+
143
+ if not fallback_operations:
144
+ logger.debug(
145
+ f"No operation mapping from {primary_provider}.{operation} "
146
+ f"to {fallback_provider}"
147
+ )
148
+ continue
149
+
150
+ # Try each mapped operation
151
+ for fallback_op in fallback_operations:
152
+ try:
153
+ logger.info(f"Attempting fallback: {fallback_provider}.{fallback_op}")
154
+
155
+ # Convert parameters
156
+ converted_params = self._convert_parameters(
157
+ primary_provider, fallback_provider, params
158
+ )
159
+
160
+ # Execute fallback
161
+ data = provider_executor(fallback_provider, fallback_op, converted_params)
162
+
163
+ # Success!
164
+ result["success"] = True
165
+ result["data"] = data
166
+ result["fallback_used"] = True
167
+ result["attempts"].append(
168
+ {
169
+ "provider": fallback_provider,
170
+ "operation": fallback_op,
171
+ "status": "success",
172
+ }
173
+ )
174
+
175
+ # Add fallback warning to metadata
176
+ if isinstance(data, dict) and "metadata" in data:
177
+ data["metadata"]["fallback_warning"] = (
178
+ f"Primary provider {primary_provider} failed, "
179
+ f"using fallback {fallback_provider}"
180
+ )
181
+ data["metadata"]["original_provider"] = primary_provider
182
+ data["metadata"]["original_operation"] = operation
183
+
184
+ # Update success stats
185
+ self._update_stats(fallback_provider, fallback_op, success=True)
186
+
187
+ logger.info(f"Fallback successful: {fallback_provider}.{fallback_op}")
188
+
189
+ return result
190
+
191
+ except Exception as fallback_error:
192
+ logger.warning(
193
+ f"Fallback {fallback_provider}.{fallback_op} failed: " f"{fallback_error}"
194
+ )
195
+ result["attempts"].append(
196
+ {
197
+ "provider": fallback_provider,
198
+ "operation": fallback_op,
199
+ "status": "failed",
200
+ "error": str(fallback_error),
201
+ }
202
+ )
203
+
204
+ # Update failure stats
205
+ self._update_stats(fallback_provider, fallback_op, success=False)
206
+
207
+ # All attempts failed
208
+ logger.error(
209
+ f"All providers failed for operation {operation}. "
210
+ f"Attempts: {len(result['attempts'])}"
211
+ )
212
+
213
+ return result
214
+
215
+ def _get_fallback_operations(
216
+ self, primary_provider: str, operation: str, fallback_provider: str
217
+ ) -> List[str]:
218
+ """
219
+ Get equivalent operations in fallback provider.
220
+
221
+ Args:
222
+ primary_provider: Primary provider name
223
+ operation: Operation name
224
+ fallback_provider: Fallback provider name
225
+
226
+ Returns:
227
+ List of equivalent operation names
228
+ """
229
+ key = (primary_provider, operation)
230
+ mappings = self.OPERATION_MAP.get(key, [])
231
+
232
+ # Filter for specific fallback provider
233
+ fallback_ops = [op for provider, op in mappings if provider == fallback_provider]
234
+
235
+ return fallback_ops
236
+
237
+ def _convert_parameters(
238
+ self,
239
+ source_provider: str,
240
+ target_provider: str,
241
+ params: Dict[str, Any],
242
+ ) -> Dict[str, Any]:
243
+ """
244
+ Convert parameters from source to target provider format.
245
+
246
+ Args:
247
+ source_provider: Source provider name
248
+ target_provider: Target provider name
249
+ params: Original parameters
250
+
251
+ Returns:
252
+ Converted parameters
253
+ """
254
+ conversion_key = (source_provider, target_provider)
255
+ conversion_rules = self.PARAMETER_CONVERSIONS.get(conversion_key, {})
256
+
257
+ converted = {}
258
+
259
+ for source_param, value in params.items():
260
+ # Check if there's a conversion rule
261
+ if source_param in conversion_rules:
262
+ target_param = conversion_rules[source_param]
263
+
264
+ # Apply special conversions
265
+ converted_value = self._convert_parameter_value(
266
+ source_provider,
267
+ target_provider,
268
+ source_param,
269
+ target_param,
270
+ value,
271
+ )
272
+
273
+ converted[target_param] = converted_value
274
+ else:
275
+ # Keep parameter as-is
276
+ converted[source_param] = value
277
+
278
+ # Add default parameters for target provider
279
+ converted = self._add_default_parameters(target_provider, converted)
280
+
281
+ return converted
282
+
283
+ def _convert_parameter_value(
284
+ self,
285
+ source_provider: str,
286
+ target_provider: str,
287
+ source_param: str,
288
+ target_param: str,
289
+ value: Any,
290
+ ) -> Any:
291
+ """
292
+ Convert parameter value between provider formats.
293
+
294
+ Args:
295
+ source_provider: Source provider
296
+ target_provider: Target provider
297
+ source_param: Source parameter name
298
+ target_param: Target parameter name
299
+ value: Original value
300
+
301
+ Returns:
302
+ Converted value
303
+ """
304
+ # FRED -> World Bank conversions
305
+ if source_provider == "fred" and target_provider == "worldbank":
306
+ if source_param == "series_id":
307
+ # Try to map common FRED series to World Bank indicators
308
+ series_map = {
309
+ "GDP": "NY.GDP.MKTP.CD",
310
+ "GDPC1": "NY.GDP.MKTP.KD",
311
+ "UNRATE": "SL.UEM.TOTL.NE.ZS",
312
+ "CPIAUCSL": "FP.CPI.TOTL",
313
+ "CPILFESL": "FP.CPI.TOTL",
314
+ }
315
+ return series_map.get(value, value)
316
+
317
+ elif source_param in ["observation_start", "observation_end"]:
318
+ # Convert to World Bank date format (year or year:year)
319
+ # Extract year from date string
320
+ if isinstance(value, str) and len(value) >= 4:
321
+ return value[:4]
322
+
323
+ # Census -> World Bank conversions
324
+ elif source_provider == "census" and target_provider == "worldbank":
325
+ if source_param == "geography":
326
+ # Convert census geography to country code
327
+ if "state" in str(value).lower():
328
+ return "US" # US country code
329
+
330
+ return value
331
+
332
+ def _add_default_parameters(self, provider: str, params: Dict[str, Any]) -> Dict[str, Any]:
333
+ """
334
+ Add default parameters required by provider.
335
+
336
+ Args:
337
+ provider: Provider name
338
+ params: Current parameters
339
+
340
+ Returns:
341
+ Parameters with defaults added
342
+ """
343
+ if provider == "worldbank":
344
+ # World Bank needs country code
345
+ if "country_code" not in params:
346
+ params["country_code"] = "US" # Default to US
347
+
348
+ return params
349
+
350
+ def _update_stats(self, provider: str, operation: str, success: bool):
351
+ """
352
+ Update fallback success statistics.
353
+
354
+ Args:
355
+ provider: Provider name
356
+ operation: Operation name
357
+ success: Whether attempt was successful
358
+ """
359
+ key = f"{provider}.{operation}"
360
+
361
+ if key not in self.fallback_stats:
362
+ self.fallback_stats[key] = {
363
+ "attempts": 0,
364
+ "successes": 0,
365
+ "failures": 0,
366
+ }
367
+
368
+ self.fallback_stats[key]["attempts"] += 1
369
+ if success:
370
+ self.fallback_stats[key]["successes"] += 1
371
+ else:
372
+ self.fallback_stats[key]["failures"] += 1
373
+
374
+ def get_fallback_stats(self) -> Dict[str, Any]:
375
+ """
376
+ Get fallback statistics.
377
+
378
+ Returns:
379
+ Dictionary with fallback statistics
380
+ """
381
+ stats = {}
382
+ for key, data in self.fallback_stats.items():
383
+ success_rate = data["successes"] / data["attempts"] if data["attempts"] > 0 else 0.0
384
+ stats[key] = {
385
+ "attempts": data["attempts"],
386
+ "successes": data["successes"],
387
+ "failures": data["failures"],
388
+ "success_rate": round(success_rate, 3),
389
+ }
390
+
391
+ return stats