aiecs 1.0.1__py3-none-any.whl → 1.7.17__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.
Potentially problematic release.
This version of aiecs might be problematic. Click here for more details.
- aiecs/__init__.py +13 -16
- aiecs/__main__.py +7 -7
- aiecs/aiecs_client.py +269 -75
- aiecs/application/executors/operation_executor.py +79 -54
- aiecs/application/knowledge_graph/__init__.py +7 -0
- aiecs/application/knowledge_graph/builder/__init__.py +37 -0
- aiecs/application/knowledge_graph/builder/data_quality.py +302 -0
- aiecs/application/knowledge_graph/builder/data_reshaping.py +293 -0
- aiecs/application/knowledge_graph/builder/document_builder.py +369 -0
- aiecs/application/knowledge_graph/builder/graph_builder.py +490 -0
- aiecs/application/knowledge_graph/builder/import_optimizer.py +396 -0
- aiecs/application/knowledge_graph/builder/schema_inference.py +462 -0
- aiecs/application/knowledge_graph/builder/schema_mapping.py +563 -0
- aiecs/application/knowledge_graph/builder/structured_pipeline.py +1384 -0
- aiecs/application/knowledge_graph/builder/text_chunker.py +317 -0
- aiecs/application/knowledge_graph/extractors/__init__.py +27 -0
- aiecs/application/knowledge_graph/extractors/base.py +98 -0
- aiecs/application/knowledge_graph/extractors/llm_entity_extractor.py +422 -0
- aiecs/application/knowledge_graph/extractors/llm_relation_extractor.py +347 -0
- aiecs/application/knowledge_graph/extractors/ner_entity_extractor.py +241 -0
- aiecs/application/knowledge_graph/fusion/__init__.py +78 -0
- aiecs/application/knowledge_graph/fusion/ab_testing.py +395 -0
- aiecs/application/knowledge_graph/fusion/abbreviation_expander.py +327 -0
- aiecs/application/knowledge_graph/fusion/alias_index.py +597 -0
- aiecs/application/knowledge_graph/fusion/alias_matcher.py +384 -0
- aiecs/application/knowledge_graph/fusion/cache_coordinator.py +343 -0
- aiecs/application/knowledge_graph/fusion/entity_deduplicator.py +433 -0
- aiecs/application/knowledge_graph/fusion/entity_linker.py +511 -0
- aiecs/application/knowledge_graph/fusion/evaluation_dataset.py +240 -0
- aiecs/application/knowledge_graph/fusion/knowledge_fusion.py +632 -0
- aiecs/application/knowledge_graph/fusion/matching_config.py +489 -0
- aiecs/application/knowledge_graph/fusion/name_normalizer.py +352 -0
- aiecs/application/knowledge_graph/fusion/relation_deduplicator.py +183 -0
- aiecs/application/knowledge_graph/fusion/semantic_name_matcher.py +464 -0
- aiecs/application/knowledge_graph/fusion/similarity_pipeline.py +534 -0
- aiecs/application/knowledge_graph/pattern_matching/__init__.py +21 -0
- aiecs/application/knowledge_graph/pattern_matching/pattern_matcher.py +342 -0
- aiecs/application/knowledge_graph/pattern_matching/query_executor.py +366 -0
- aiecs/application/knowledge_graph/profiling/__init__.py +12 -0
- aiecs/application/knowledge_graph/profiling/query_plan_visualizer.py +195 -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 +341 -0
- aiecs/application/knowledge_graph/reasoning/inference_engine.py +500 -0
- aiecs/application/knowledge_graph/reasoning/logic_form_parser.py +163 -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 +913 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/ast_validator.py +866 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/error_handler.py +475 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/parser.py +396 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/query_context.py +208 -0
- aiecs/application/knowledge_graph/reasoning/logic_query_integration.py +170 -0
- aiecs/application/knowledge_graph/reasoning/query_planner.py +855 -0
- aiecs/application/knowledge_graph/reasoning/reasoning_engine.py +518 -0
- aiecs/application/knowledge_graph/retrieval/__init__.py +27 -0
- aiecs/application/knowledge_graph/retrieval/query_intent_classifier.py +211 -0
- aiecs/application/knowledge_graph/retrieval/retrieval_strategies.py +592 -0
- aiecs/application/knowledge_graph/retrieval/strategy_types.py +23 -0
- aiecs/application/knowledge_graph/search/__init__.py +59 -0
- aiecs/application/knowledge_graph/search/hybrid_search.py +457 -0
- aiecs/application/knowledge_graph/search/reranker.py +293 -0
- aiecs/application/knowledge_graph/search/reranker_strategies.py +535 -0
- aiecs/application/knowledge_graph/search/text_similarity.py +392 -0
- aiecs/application/knowledge_graph/traversal/__init__.py +15 -0
- aiecs/application/knowledge_graph/traversal/enhanced_traversal.py +305 -0
- aiecs/application/knowledge_graph/traversal/path_scorer.py +271 -0
- aiecs/application/knowledge_graph/validators/__init__.py +13 -0
- aiecs/application/knowledge_graph/validators/relation_validator.py +239 -0
- aiecs/application/knowledge_graph/visualization/__init__.py +11 -0
- aiecs/application/knowledge_graph/visualization/graph_visualizer.py +313 -0
- aiecs/common/__init__.py +9 -0
- aiecs/common/knowledge_graph/__init__.py +17 -0
- aiecs/common/knowledge_graph/runnable.py +471 -0
- aiecs/config/__init__.py +20 -5
- aiecs/config/config.py +762 -31
- aiecs/config/graph_config.py +131 -0
- aiecs/config/tool_config.py +435 -0
- aiecs/core/__init__.py +29 -13
- aiecs/core/interface/__init__.py +2 -2
- aiecs/core/interface/execution_interface.py +22 -22
- aiecs/core/interface/storage_interface.py +37 -88
- aiecs/core/registry/__init__.py +31 -0
- aiecs/core/registry/service_registry.py +92 -0
- aiecs/domain/__init__.py +270 -1
- aiecs/domain/agent/__init__.py +191 -0
- aiecs/domain/agent/base_agent.py +3949 -0
- aiecs/domain/agent/exceptions.py +99 -0
- aiecs/domain/agent/graph_aware_mixin.py +569 -0
- aiecs/domain/agent/hybrid_agent.py +1731 -0
- aiecs/domain/agent/integration/__init__.py +29 -0
- aiecs/domain/agent/integration/context_compressor.py +216 -0
- aiecs/domain/agent/integration/context_engine_adapter.py +587 -0
- aiecs/domain/agent/integration/protocols.py +281 -0
- aiecs/domain/agent/integration/retry_policy.py +218 -0
- aiecs/domain/agent/integration/role_config.py +213 -0
- aiecs/domain/agent/knowledge_aware_agent.py +1892 -0
- aiecs/domain/agent/lifecycle.py +291 -0
- aiecs/domain/agent/llm_agent.py +692 -0
- aiecs/domain/agent/memory/__init__.py +12 -0
- aiecs/domain/agent/memory/conversation.py +1124 -0
- aiecs/domain/agent/migration/__init__.py +14 -0
- aiecs/domain/agent/migration/conversion.py +163 -0
- aiecs/domain/agent/migration/legacy_wrapper.py +86 -0
- aiecs/domain/agent/models.py +894 -0
- aiecs/domain/agent/observability.py +479 -0
- aiecs/domain/agent/persistence.py +449 -0
- aiecs/domain/agent/prompts/__init__.py +29 -0
- aiecs/domain/agent/prompts/builder.py +159 -0
- aiecs/domain/agent/prompts/formatters.py +187 -0
- aiecs/domain/agent/prompts/template.py +255 -0
- aiecs/domain/agent/registry.py +253 -0
- aiecs/domain/agent/tool_agent.py +444 -0
- aiecs/domain/agent/tools/__init__.py +15 -0
- aiecs/domain/agent/tools/schema_generator.py +377 -0
- aiecs/domain/community/__init__.py +155 -0
- aiecs/domain/community/agent_adapter.py +469 -0
- aiecs/domain/community/analytics.py +432 -0
- aiecs/domain/community/collaborative_workflow.py +648 -0
- aiecs/domain/community/communication_hub.py +634 -0
- aiecs/domain/community/community_builder.py +320 -0
- aiecs/domain/community/community_integration.py +796 -0
- aiecs/domain/community/community_manager.py +803 -0
- aiecs/domain/community/decision_engine.py +849 -0
- aiecs/domain/community/exceptions.py +231 -0
- aiecs/domain/community/models/__init__.py +33 -0
- aiecs/domain/community/models/community_models.py +234 -0
- aiecs/domain/community/resource_manager.py +461 -0
- aiecs/domain/community/shared_context_manager.py +589 -0
- aiecs/domain/context/__init__.py +40 -10
- aiecs/domain/context/context_engine.py +1910 -0
- aiecs/domain/context/conversation_models.py +87 -53
- aiecs/domain/context/graph_memory.py +582 -0
- aiecs/domain/execution/model.py +12 -4
- aiecs/domain/knowledge_graph/__init__.py +19 -0
- aiecs/domain/knowledge_graph/models/__init__.py +52 -0
- aiecs/domain/knowledge_graph/models/entity.py +148 -0
- aiecs/domain/knowledge_graph/models/evidence.py +178 -0
- aiecs/domain/knowledge_graph/models/inference_rule.py +184 -0
- aiecs/domain/knowledge_graph/models/path.py +171 -0
- aiecs/domain/knowledge_graph/models/path_pattern.py +171 -0
- aiecs/domain/knowledge_graph/models/query.py +261 -0
- aiecs/domain/knowledge_graph/models/query_plan.py +181 -0
- aiecs/domain/knowledge_graph/models/relation.py +202 -0
- aiecs/domain/knowledge_graph/schema/__init__.py +23 -0
- aiecs/domain/knowledge_graph/schema/entity_type.py +131 -0
- aiecs/domain/knowledge_graph/schema/graph_schema.py +253 -0
- aiecs/domain/knowledge_graph/schema/property_schema.py +143 -0
- aiecs/domain/knowledge_graph/schema/relation_type.py +163 -0
- aiecs/domain/knowledge_graph/schema/schema_manager.py +691 -0
- aiecs/domain/knowledge_graph/schema/type_enums.py +209 -0
- aiecs/domain/task/dsl_processor.py +172 -56
- aiecs/domain/task/model.py +20 -8
- aiecs/domain/task/task_context.py +27 -24
- aiecs/infrastructure/__init__.py +0 -2
- aiecs/infrastructure/graph_storage/__init__.py +11 -0
- aiecs/infrastructure/graph_storage/base.py +837 -0
- aiecs/infrastructure/graph_storage/batch_operations.py +458 -0
- aiecs/infrastructure/graph_storage/cache.py +424 -0
- aiecs/infrastructure/graph_storage/distributed.py +223 -0
- aiecs/infrastructure/graph_storage/error_handling.py +380 -0
- aiecs/infrastructure/graph_storage/graceful_degradation.py +294 -0
- aiecs/infrastructure/graph_storage/health_checks.py +378 -0
- aiecs/infrastructure/graph_storage/in_memory.py +1197 -0
- aiecs/infrastructure/graph_storage/index_optimization.py +446 -0
- aiecs/infrastructure/graph_storage/lazy_loading.py +431 -0
- aiecs/infrastructure/graph_storage/metrics.py +344 -0
- aiecs/infrastructure/graph_storage/migration.py +400 -0
- aiecs/infrastructure/graph_storage/pagination.py +483 -0
- aiecs/infrastructure/graph_storage/performance_monitoring.py +456 -0
- aiecs/infrastructure/graph_storage/postgres.py +1563 -0
- aiecs/infrastructure/graph_storage/property_storage.py +353 -0
- aiecs/infrastructure/graph_storage/protocols.py +76 -0
- aiecs/infrastructure/graph_storage/query_optimizer.py +642 -0
- aiecs/infrastructure/graph_storage/schema_cache.py +290 -0
- aiecs/infrastructure/graph_storage/sqlite.py +1373 -0
- aiecs/infrastructure/graph_storage/streaming.py +487 -0
- aiecs/infrastructure/graph_storage/tenant.py +412 -0
- aiecs/infrastructure/messaging/celery_task_manager.py +92 -54
- aiecs/infrastructure/messaging/websocket_manager.py +51 -35
- aiecs/infrastructure/monitoring/__init__.py +22 -0
- aiecs/infrastructure/monitoring/executor_metrics.py +45 -11
- aiecs/infrastructure/monitoring/global_metrics_manager.py +212 -0
- aiecs/infrastructure/monitoring/structured_logger.py +3 -7
- aiecs/infrastructure/monitoring/tracing_manager.py +63 -35
- aiecs/infrastructure/persistence/__init__.py +14 -1
- aiecs/infrastructure/persistence/context_engine_client.py +184 -0
- aiecs/infrastructure/persistence/database_manager.py +67 -43
- aiecs/infrastructure/persistence/file_storage.py +180 -103
- aiecs/infrastructure/persistence/redis_client.py +74 -21
- aiecs/llm/__init__.py +73 -25
- aiecs/llm/callbacks/__init__.py +11 -0
- aiecs/llm/{custom_callbacks.py → callbacks/custom_callbacks.py} +26 -19
- aiecs/llm/client_factory.py +230 -37
- aiecs/llm/client_resolver.py +155 -0
- aiecs/llm/clients/__init__.py +38 -0
- aiecs/llm/clients/base_client.py +328 -0
- aiecs/llm/clients/google_function_calling_mixin.py +415 -0
- aiecs/llm/clients/googleai_client.py +314 -0
- aiecs/llm/clients/openai_client.py +158 -0
- aiecs/llm/clients/openai_compatible_mixin.py +367 -0
- aiecs/llm/clients/vertex_client.py +1186 -0
- aiecs/llm/clients/xai_client.py +201 -0
- aiecs/llm/config/__init__.py +51 -0
- aiecs/llm/config/config_loader.py +272 -0
- aiecs/llm/config/config_validator.py +206 -0
- aiecs/llm/config/model_config.py +143 -0
- aiecs/llm/protocols.py +149 -0
- aiecs/llm/utils/__init__.py +10 -0
- aiecs/llm/utils/validate_config.py +89 -0
- aiecs/main.py +140 -121
- aiecs/scripts/aid/VERSION_MANAGEMENT.md +138 -0
- aiecs/scripts/aid/__init__.py +19 -0
- aiecs/scripts/aid/module_checker.py +499 -0
- aiecs/scripts/aid/version_manager.py +235 -0
- aiecs/scripts/{DEPENDENCY_SYSTEM_SUMMARY.md → dependance_check/DEPENDENCY_SYSTEM_SUMMARY.md} +1 -0
- aiecs/scripts/{README_DEPENDENCY_CHECKER.md → dependance_check/README_DEPENDENCY_CHECKER.md} +1 -0
- aiecs/scripts/dependance_check/__init__.py +15 -0
- aiecs/scripts/dependance_check/dependency_checker.py +1835 -0
- aiecs/scripts/{dependency_fixer.py → dependance_check/dependency_fixer.py} +192 -90
- aiecs/scripts/{download_nlp_data.py → dependance_check/download_nlp_data.py} +203 -71
- aiecs/scripts/dependance_patch/__init__.py +7 -0
- aiecs/scripts/dependance_patch/fix_weasel/__init__.py +11 -0
- aiecs/scripts/{fix_weasel_validator.py → dependance_patch/fix_weasel/fix_weasel_validator.py} +21 -14
- aiecs/scripts/{patch_weasel_library.sh → dependance_patch/fix_weasel/patch_weasel_library.sh} +1 -1
- aiecs/scripts/knowledge_graph/__init__.py +3 -0
- aiecs/scripts/knowledge_graph/run_threshold_experiments.py +212 -0
- aiecs/scripts/migrations/multi_tenancy/README.md +142 -0
- aiecs/scripts/tools_develop/README.md +671 -0
- aiecs/scripts/tools_develop/README_CONFIG_CHECKER.md +273 -0
- aiecs/scripts/tools_develop/TOOLS_CONFIG_GUIDE.md +1287 -0
- aiecs/scripts/tools_develop/TOOL_AUTO_DISCOVERY.md +234 -0
- aiecs/scripts/tools_develop/__init__.py +21 -0
- aiecs/scripts/tools_develop/check_all_tools_config.py +548 -0
- aiecs/scripts/tools_develop/check_type_annotations.py +257 -0
- aiecs/scripts/tools_develop/pre-commit-schema-coverage.sh +66 -0
- aiecs/scripts/tools_develop/schema_coverage.py +511 -0
- aiecs/scripts/tools_develop/validate_tool_schemas.py +475 -0
- aiecs/scripts/tools_develop/verify_executor_config_fix.py +98 -0
- aiecs/scripts/tools_develop/verify_tools.py +352 -0
- aiecs/tasks/__init__.py +0 -1
- aiecs/tasks/worker.py +115 -47
- aiecs/tools/__init__.py +194 -72
- aiecs/tools/apisource/__init__.py +99 -0
- aiecs/tools/apisource/intelligence/__init__.py +19 -0
- aiecs/tools/apisource/intelligence/data_fusion.py +632 -0
- aiecs/tools/apisource/intelligence/query_analyzer.py +417 -0
- aiecs/tools/apisource/intelligence/search_enhancer.py +385 -0
- aiecs/tools/apisource/monitoring/__init__.py +9 -0
- aiecs/tools/apisource/monitoring/metrics.py +330 -0
- aiecs/tools/apisource/providers/__init__.py +112 -0
- aiecs/tools/apisource/providers/base.py +671 -0
- aiecs/tools/apisource/providers/census.py +397 -0
- aiecs/tools/apisource/providers/fred.py +535 -0
- aiecs/tools/apisource/providers/newsapi.py +409 -0
- aiecs/tools/apisource/providers/worldbank.py +352 -0
- aiecs/tools/apisource/reliability/__init__.py +12 -0
- aiecs/tools/apisource/reliability/error_handler.py +363 -0
- aiecs/tools/apisource/reliability/fallback_strategy.py +376 -0
- aiecs/tools/apisource/tool.py +832 -0
- aiecs/tools/apisource/utils/__init__.py +9 -0
- aiecs/tools/apisource/utils/validators.py +334 -0
- aiecs/tools/base_tool.py +415 -21
- aiecs/tools/docs/__init__.py +121 -0
- aiecs/tools/docs/ai_document_orchestrator.py +607 -0
- aiecs/tools/docs/ai_document_writer_orchestrator.py +2350 -0
- aiecs/tools/docs/content_insertion_tool.py +1320 -0
- aiecs/tools/docs/document_creator_tool.py +1464 -0
- aiecs/tools/docs/document_layout_tool.py +1160 -0
- aiecs/tools/docs/document_parser_tool.py +1016 -0
- aiecs/tools/docs/document_writer_tool.py +2008 -0
- aiecs/tools/knowledge_graph/__init__.py +17 -0
- aiecs/tools/knowledge_graph/graph_reasoning_tool.py +807 -0
- aiecs/tools/knowledge_graph/graph_search_tool.py +944 -0
- aiecs/tools/knowledge_graph/kg_builder_tool.py +524 -0
- aiecs/tools/langchain_adapter.py +300 -138
- aiecs/tools/schema_generator.py +455 -0
- aiecs/tools/search_tool/__init__.py +100 -0
- aiecs/tools/search_tool/analyzers.py +581 -0
- aiecs/tools/search_tool/cache.py +264 -0
- aiecs/tools/search_tool/constants.py +128 -0
- aiecs/tools/search_tool/context.py +224 -0
- aiecs/tools/search_tool/core.py +778 -0
- aiecs/tools/search_tool/deduplicator.py +119 -0
- aiecs/tools/search_tool/error_handler.py +242 -0
- aiecs/tools/search_tool/metrics.py +343 -0
- aiecs/tools/search_tool/rate_limiter.py +172 -0
- aiecs/tools/search_tool/schemas.py +275 -0
- aiecs/tools/statistics/__init__.py +80 -0
- aiecs/tools/statistics/ai_data_analysis_orchestrator.py +646 -0
- aiecs/tools/statistics/ai_insight_generator_tool.py +508 -0
- aiecs/tools/statistics/ai_report_orchestrator_tool.py +684 -0
- aiecs/tools/statistics/data_loader_tool.py +555 -0
- aiecs/tools/statistics/data_profiler_tool.py +638 -0
- aiecs/tools/statistics/data_transformer_tool.py +580 -0
- aiecs/tools/statistics/data_visualizer_tool.py +498 -0
- aiecs/tools/statistics/model_trainer_tool.py +507 -0
- aiecs/tools/statistics/statistical_analyzer_tool.py +472 -0
- aiecs/tools/task_tools/__init__.py +49 -36
- aiecs/tools/task_tools/chart_tool.py +200 -184
- aiecs/tools/task_tools/classfire_tool.py +268 -267
- aiecs/tools/task_tools/image_tool.py +220 -141
- aiecs/tools/task_tools/office_tool.py +226 -146
- aiecs/tools/task_tools/pandas_tool.py +477 -121
- aiecs/tools/task_tools/report_tool.py +390 -142
- aiecs/tools/task_tools/research_tool.py +149 -79
- aiecs/tools/task_tools/scraper_tool.py +339 -145
- aiecs/tools/task_tools/stats_tool.py +448 -209
- aiecs/tools/temp_file_manager.py +26 -24
- aiecs/tools/tool_executor/__init__.py +18 -16
- aiecs/tools/tool_executor/tool_executor.py +364 -52
- aiecs/utils/LLM_output_structor.py +74 -48
- aiecs/utils/__init__.py +14 -3
- aiecs/utils/base_callback.py +0 -3
- aiecs/utils/cache_provider.py +696 -0
- aiecs/utils/execution_utils.py +50 -31
- aiecs/utils/prompt_loader.py +1 -0
- aiecs/utils/token_usage_repository.py +37 -11
- aiecs/ws/socket_server.py +14 -4
- {aiecs-1.0.1.dist-info → aiecs-1.7.17.dist-info}/METADATA +52 -15
- aiecs-1.7.17.dist-info/RECORD +337 -0
- aiecs-1.7.17.dist-info/entry_points.txt +13 -0
- aiecs/config/registry.py +0 -19
- aiecs/domain/context/content_engine.py +0 -982
- aiecs/llm/base_client.py +0 -99
- aiecs/llm/openai_client.py +0 -125
- aiecs/llm/vertex_client.py +0 -186
- aiecs/llm/xai_client.py +0 -184
- aiecs/scripts/dependency_checker.py +0 -857
- aiecs/scripts/quick_dependency_check.py +0 -269
- aiecs/tools/task_tools/search_api.py +0 -7
- aiecs-1.0.1.dist-info/RECORD +0 -90
- aiecs-1.0.1.dist-info/entry_points.txt +0 -7
- /aiecs/scripts/{setup_nlp_data.sh → dependance_check/setup_nlp_data.sh} +0 -0
- /aiecs/scripts/{README_WEASEL_PATCH.md → dependance_patch/fix_weasel/README_WEASEL_PATCH.md} +0 -0
- /aiecs/scripts/{fix_weasel_validator.sh → dependance_patch/fix_weasel/fix_weasel_validator.sh} +0 -0
- /aiecs/scripts/{run_weasel_patch.sh → dependance_patch/fix_weasel/run_weasel_patch.sh} +0 -0
- {aiecs-1.0.1.dist-info → aiecs-1.7.17.dist-info}/WHEEL +0 -0
- {aiecs-1.0.1.dist-info → aiecs-1.7.17.dist-info}/licenses/LICENSE +0 -0
- {aiecs-1.0.1.dist-info → aiecs-1.7.17.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Federal Reserve Economic Data (FRED) API Provider
|
|
3
|
+
|
|
4
|
+
Provides access to Federal Reserve Economic Data through the FRED API.
|
|
5
|
+
Supports time series data retrieval, search, and metadata operations.
|
|
6
|
+
|
|
7
|
+
API Documentation: https://fred.stlouisfed.org/docs/api/fred/
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
12
|
+
|
|
13
|
+
from aiecs.tools.apisource.providers.base import (
|
|
14
|
+
BaseAPIProvider,
|
|
15
|
+
expose_operation,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
# Optional HTTP client - graceful degradation
|
|
21
|
+
try:
|
|
22
|
+
import requests
|
|
23
|
+
|
|
24
|
+
REQUESTS_AVAILABLE = True
|
|
25
|
+
except ImportError:
|
|
26
|
+
REQUESTS_AVAILABLE = False
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class FREDProvider(BaseAPIProvider):
|
|
30
|
+
"""
|
|
31
|
+
Federal Reserve Economic Data (FRED) API provider.
|
|
32
|
+
|
|
33
|
+
Provides access to economic indicators including:
|
|
34
|
+
- GDP, unemployment, inflation data
|
|
35
|
+
- Interest rates and monetary indicators
|
|
36
|
+
- Regional economic data
|
|
37
|
+
- International statistics
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
BASE_URL = "https://api.stlouisfed.org/fred"
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def name(self) -> str:
|
|
44
|
+
return "fred"
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def description(self) -> str:
|
|
48
|
+
return "Federal Reserve Economic Data API for US economic indicators and time series"
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def supported_operations(self) -> List[str]:
|
|
52
|
+
return [
|
|
53
|
+
"get_series",
|
|
54
|
+
"search_series",
|
|
55
|
+
"get_series_observations",
|
|
56
|
+
"get_series_info",
|
|
57
|
+
"get_categories",
|
|
58
|
+
"get_releases",
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
def validate_params(self, operation: str, params: Dict[str, Any]) -> Tuple[bool, Optional[str]]:
|
|
62
|
+
"""Validate parameters for FRED operations with detailed guidance"""
|
|
63
|
+
|
|
64
|
+
if operation == "get_series" or operation == "get_series_info":
|
|
65
|
+
if "series_id" not in params:
|
|
66
|
+
return False, ("Missing required parameter: series_id\n" "Example: {'series_id': 'GDP'}\n" "Use search_series operation to find valid series IDs")
|
|
67
|
+
|
|
68
|
+
elif operation == "get_series_observations":
|
|
69
|
+
if "series_id" not in params:
|
|
70
|
+
return False, ("Missing required parameter: series_id\n" "Example: {'series_id': 'GDP', 'observation_start': '2020-01-01'}\n" "Use search_series to find valid series IDs")
|
|
71
|
+
|
|
72
|
+
elif operation == "search_series":
|
|
73
|
+
if "search_text" not in params:
|
|
74
|
+
return False, ("Missing required parameter: search_text\n" "Example: {'search_text': 'gdp', 'limit': 10}")
|
|
75
|
+
|
|
76
|
+
return True, None
|
|
77
|
+
|
|
78
|
+
# Exposed operations for AI agent visibility
|
|
79
|
+
|
|
80
|
+
@expose_operation(
|
|
81
|
+
operation_name="get_series_observations",
|
|
82
|
+
description="Get FRED economic time series observation data with optional date range filtering",
|
|
83
|
+
)
|
|
84
|
+
def get_series_observations(
|
|
85
|
+
self,
|
|
86
|
+
series_id: str,
|
|
87
|
+
observation_start: Optional[str] = None,
|
|
88
|
+
observation_end: Optional[str] = None,
|
|
89
|
+
limit: Optional[int] = None,
|
|
90
|
+
offset: Optional[int] = None,
|
|
91
|
+
sort_order: Optional[str] = None,
|
|
92
|
+
) -> Dict[str, Any]:
|
|
93
|
+
"""
|
|
94
|
+
Get time series observation data from FRED.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
series_id: FRED series ID (e.g., 'GDP', 'UNRATE', 'CPIAUCSL')
|
|
98
|
+
observation_start: Start date in YYYY-MM-DD format
|
|
99
|
+
observation_end: End date in YYYY-MM-DD format
|
|
100
|
+
limit: Maximum number of observations to return
|
|
101
|
+
offset: Offset for pagination
|
|
102
|
+
sort_order: Sort order ('asc' or 'desc')
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
Dictionary containing observations and metadata
|
|
106
|
+
"""
|
|
107
|
+
params: Dict[str, Any] = {"series_id": series_id}
|
|
108
|
+
if observation_start:
|
|
109
|
+
params["observation_start"] = observation_start
|
|
110
|
+
if observation_end:
|
|
111
|
+
params["observation_end"] = observation_end
|
|
112
|
+
if limit:
|
|
113
|
+
params["limit"] = limit
|
|
114
|
+
if offset:
|
|
115
|
+
params["offset"] = offset
|
|
116
|
+
if sort_order:
|
|
117
|
+
params["sort_order"] = sort_order
|
|
118
|
+
|
|
119
|
+
return self.execute("get_series_observations", params)
|
|
120
|
+
|
|
121
|
+
@expose_operation(
|
|
122
|
+
operation_name="search_series",
|
|
123
|
+
description="Search for FRED economic data series by keywords",
|
|
124
|
+
)
|
|
125
|
+
def search_series(
|
|
126
|
+
self,
|
|
127
|
+
search_text: str,
|
|
128
|
+
limit: Optional[int] = None,
|
|
129
|
+
offset: Optional[int] = None,
|
|
130
|
+
) -> Dict[str, Any]:
|
|
131
|
+
"""
|
|
132
|
+
Search for FRED series by keywords.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
search_text: Search keywords (e.g., 'unemployment', 'GDP growth')
|
|
136
|
+
limit: Maximum number of results to return
|
|
137
|
+
offset: Offset for pagination
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
Dictionary containing search results and metadata
|
|
141
|
+
"""
|
|
142
|
+
params: Dict[str, Any] = {"search_text": search_text}
|
|
143
|
+
if limit:
|
|
144
|
+
params["limit"] = limit
|
|
145
|
+
if offset:
|
|
146
|
+
params["offset"] = offset
|
|
147
|
+
|
|
148
|
+
return self.execute("search_series", params)
|
|
149
|
+
|
|
150
|
+
@expose_operation(
|
|
151
|
+
operation_name="get_series_info",
|
|
152
|
+
description="Get detailed metadata and information about a specific FRED series",
|
|
153
|
+
)
|
|
154
|
+
def get_series_info(self, series_id: str) -> Dict[str, Any]:
|
|
155
|
+
"""
|
|
156
|
+
Get metadata about a FRED series.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
series_id: FRED series ID (e.g., 'GDP', 'UNRATE')
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
Dictionary containing series metadata
|
|
163
|
+
"""
|
|
164
|
+
return self.execute("get_series_info", {"series_id": series_id})
|
|
165
|
+
|
|
166
|
+
@expose_operation(
|
|
167
|
+
operation_name="get_categories",
|
|
168
|
+
description="Get FRED data categories for browsing available datasets",
|
|
169
|
+
)
|
|
170
|
+
def get_categories(self, category_id: Optional[int] = None) -> Dict[str, Any]:
|
|
171
|
+
"""
|
|
172
|
+
Get FRED categories.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
category_id: Optional category ID to get subcategories
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
Dictionary containing category information
|
|
179
|
+
"""
|
|
180
|
+
params = {}
|
|
181
|
+
if category_id:
|
|
182
|
+
params["category_id"] = category_id
|
|
183
|
+
|
|
184
|
+
return self.execute("get_categories", params)
|
|
185
|
+
|
|
186
|
+
@expose_operation(
|
|
187
|
+
operation_name="get_releases",
|
|
188
|
+
description="Get FRED data release information and schedules",
|
|
189
|
+
)
|
|
190
|
+
def get_releases(self, limit: Optional[int] = None) -> Dict[str, Any]:
|
|
191
|
+
"""
|
|
192
|
+
Get FRED releases.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
limit: Maximum number of releases to return
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
Dictionary containing release information
|
|
199
|
+
"""
|
|
200
|
+
params = {}
|
|
201
|
+
if limit:
|
|
202
|
+
params["limit"] = limit
|
|
203
|
+
|
|
204
|
+
return self.execute("get_releases", params)
|
|
205
|
+
|
|
206
|
+
def fetch(self, operation: str, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
207
|
+
"""Fetch data from FRED API"""
|
|
208
|
+
|
|
209
|
+
if not REQUESTS_AVAILABLE:
|
|
210
|
+
raise ImportError("requests library is required for FRED provider. Install with: pip install requests")
|
|
211
|
+
|
|
212
|
+
# Get API key
|
|
213
|
+
api_key = self._get_api_key("FRED_API_KEY")
|
|
214
|
+
if not api_key:
|
|
215
|
+
raise ValueError("FRED API key not found. Set FRED_API_KEY environment variable or " "provide 'api_key' in config")
|
|
216
|
+
|
|
217
|
+
# Build endpoint based on operation
|
|
218
|
+
if operation == "get_series" or operation == "get_series_observations":
|
|
219
|
+
endpoint = f"{self.BASE_URL}/series/observations"
|
|
220
|
+
query_params = {
|
|
221
|
+
"series_id": params["series_id"],
|
|
222
|
+
"api_key": api_key,
|
|
223
|
+
"file_type": "json",
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
# Optional parameters
|
|
227
|
+
if "limit" in params:
|
|
228
|
+
query_params["limit"] = params["limit"]
|
|
229
|
+
if "offset" in params:
|
|
230
|
+
query_params["offset"] = params["offset"]
|
|
231
|
+
if "sort_order" in params:
|
|
232
|
+
query_params["sort_order"] = params["sort_order"]
|
|
233
|
+
if "observation_start" in params:
|
|
234
|
+
query_params["observation_start"] = params["observation_start"]
|
|
235
|
+
if "observation_end" in params:
|
|
236
|
+
query_params["observation_end"] = params["observation_end"]
|
|
237
|
+
|
|
238
|
+
elif operation == "get_series_info":
|
|
239
|
+
endpoint = f"{self.BASE_URL}/series"
|
|
240
|
+
query_params = {
|
|
241
|
+
"series_id": params["series_id"],
|
|
242
|
+
"api_key": api_key,
|
|
243
|
+
"file_type": "json",
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
elif operation == "search_series":
|
|
247
|
+
endpoint = f"{self.BASE_URL}/series/search"
|
|
248
|
+
query_params = {
|
|
249
|
+
"search_text": params["search_text"],
|
|
250
|
+
"api_key": api_key,
|
|
251
|
+
"file_type": "json",
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if "limit" in params:
|
|
255
|
+
query_params["limit"] = params["limit"]
|
|
256
|
+
if "offset" in params:
|
|
257
|
+
query_params["offset"] = params["offset"]
|
|
258
|
+
|
|
259
|
+
elif operation == "get_categories":
|
|
260
|
+
endpoint = f"{self.BASE_URL}/category"
|
|
261
|
+
query_params = {"api_key": api_key, "file_type": "json"}
|
|
262
|
+
|
|
263
|
+
if "category_id" in params:
|
|
264
|
+
query_params["category_id"] = params["category_id"]
|
|
265
|
+
|
|
266
|
+
elif operation == "get_releases":
|
|
267
|
+
endpoint = f"{self.BASE_URL}/releases"
|
|
268
|
+
query_params = {"api_key": api_key, "file_type": "json"}
|
|
269
|
+
|
|
270
|
+
if "limit" in params:
|
|
271
|
+
query_params["limit"] = params["limit"]
|
|
272
|
+
|
|
273
|
+
else:
|
|
274
|
+
raise ValueError(f"Unknown operation: {operation}")
|
|
275
|
+
|
|
276
|
+
# Make API request
|
|
277
|
+
timeout = self.config.get("timeout", 30)
|
|
278
|
+
try:
|
|
279
|
+
response = requests.get(endpoint, params=query_params, timeout=timeout)
|
|
280
|
+
response.raise_for_status()
|
|
281
|
+
|
|
282
|
+
data = response.json()
|
|
283
|
+
|
|
284
|
+
# Extract relevant data based on operation
|
|
285
|
+
if operation in ["get_series", "get_series_observations"]:
|
|
286
|
+
result_data = data.get("observations", [])
|
|
287
|
+
elif operation == "search_series":
|
|
288
|
+
result_data = data.get("seriess", [])
|
|
289
|
+
elif operation == "get_series_info":
|
|
290
|
+
result_data = data.get("seriess", [])
|
|
291
|
+
elif operation == "get_categories":
|
|
292
|
+
result_data = data.get("categories", [])
|
|
293
|
+
elif operation == "get_releases":
|
|
294
|
+
result_data = data.get("releases", [])
|
|
295
|
+
else:
|
|
296
|
+
result_data = data
|
|
297
|
+
|
|
298
|
+
return self._format_response(
|
|
299
|
+
operation=operation,
|
|
300
|
+
data=result_data,
|
|
301
|
+
source=f"FRED API - {endpoint}",
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
except requests.exceptions.RequestException as e:
|
|
305
|
+
self.logger.error(f"FRED API request failed: {e}")
|
|
306
|
+
raise Exception(f"FRED API request failed: {str(e)}")
|
|
307
|
+
|
|
308
|
+
def get_operation_schema(self, operation: str) -> Optional[Dict[str, Any]]:
|
|
309
|
+
"""Get detailed schema for FRED operations"""
|
|
310
|
+
|
|
311
|
+
schemas = {
|
|
312
|
+
"get_series_observations": {
|
|
313
|
+
"description": "Get time series observation data from FRED",
|
|
314
|
+
"parameters": {
|
|
315
|
+
"series_id": {
|
|
316
|
+
"type": "string",
|
|
317
|
+
"required": True,
|
|
318
|
+
"description": "FRED series ID (e.g., GDP, UNRATE, CPIAUCSL)",
|
|
319
|
+
"examples": ["GDP", "UNRATE", "CPIAUCSL", "DGS10"],
|
|
320
|
+
"validation": {
|
|
321
|
+
"pattern": r"^[A-Z0-9]+$",
|
|
322
|
+
"max_length": 50,
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
"observation_start": {
|
|
326
|
+
"type": "string",
|
|
327
|
+
"required": False,
|
|
328
|
+
"description": "Start date for observations (YYYY-MM-DD)",
|
|
329
|
+
"examples": ["2020-01-01", "2015-06-15"],
|
|
330
|
+
"default": "earliest available",
|
|
331
|
+
},
|
|
332
|
+
"observation_end": {
|
|
333
|
+
"type": "string",
|
|
334
|
+
"required": False,
|
|
335
|
+
"description": "End date for observations (YYYY-MM-DD)",
|
|
336
|
+
"examples": ["2025-10-15", "2023-12-31"],
|
|
337
|
+
"default": "latest available",
|
|
338
|
+
},
|
|
339
|
+
"limit": {
|
|
340
|
+
"type": "integer",
|
|
341
|
+
"required": False,
|
|
342
|
+
"description": "Maximum number of observations",
|
|
343
|
+
"examples": [100, 1000],
|
|
344
|
+
"default": 100000,
|
|
345
|
+
},
|
|
346
|
+
"sort_order": {
|
|
347
|
+
"type": "string",
|
|
348
|
+
"required": False,
|
|
349
|
+
"description": "Sort order (asc/desc)",
|
|
350
|
+
"examples": ["desc", "asc"],
|
|
351
|
+
"default": "asc",
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
"examples": [
|
|
355
|
+
{
|
|
356
|
+
"description": "Get GDP data for last 5 years",
|
|
357
|
+
"params": {
|
|
358
|
+
"series_id": "GDP",
|
|
359
|
+
"observation_start": "2020-01-01",
|
|
360
|
+
"limit": 100,
|
|
361
|
+
},
|
|
362
|
+
}
|
|
363
|
+
],
|
|
364
|
+
},
|
|
365
|
+
"search_series": {
|
|
366
|
+
"description": "Search for FRED series by text query",
|
|
367
|
+
"parameters": {
|
|
368
|
+
"search_text": {
|
|
369
|
+
"type": "string",
|
|
370
|
+
"required": True,
|
|
371
|
+
"description": "Text to search for in series",
|
|
372
|
+
"examples": ["gdp", "unemployment", "inflation"],
|
|
373
|
+
},
|
|
374
|
+
"limit": {
|
|
375
|
+
"type": "integer",
|
|
376
|
+
"required": False,
|
|
377
|
+
"description": "Maximum results to return",
|
|
378
|
+
"examples": [10, 50],
|
|
379
|
+
"default": 1000,
|
|
380
|
+
},
|
|
381
|
+
},
|
|
382
|
+
"examples": [
|
|
383
|
+
{
|
|
384
|
+
"description": "Search for GDP series",
|
|
385
|
+
"params": {"search_text": "gdp", "limit": 10},
|
|
386
|
+
}
|
|
387
|
+
],
|
|
388
|
+
},
|
|
389
|
+
"get_series_info": {
|
|
390
|
+
"description": "Get metadata about a FRED series",
|
|
391
|
+
"parameters": {
|
|
392
|
+
"series_id": {
|
|
393
|
+
"type": "string",
|
|
394
|
+
"required": True,
|
|
395
|
+
"description": "FRED series ID to get information about",
|
|
396
|
+
"examples": ["GDP", "UNRATE", "CPIAUCSL"],
|
|
397
|
+
}
|
|
398
|
+
},
|
|
399
|
+
"examples": [
|
|
400
|
+
{
|
|
401
|
+
"description": "Get info about GDP series",
|
|
402
|
+
"params": {"series_id": "GDP"},
|
|
403
|
+
}
|
|
404
|
+
],
|
|
405
|
+
},
|
|
406
|
+
"get_categories": {
|
|
407
|
+
"description": "Get FRED data categories",
|
|
408
|
+
"parameters": {
|
|
409
|
+
"category_id": {
|
|
410
|
+
"type": "integer",
|
|
411
|
+
"required": False,
|
|
412
|
+
"description": "Category ID to get subcategories (omit for root categories)",
|
|
413
|
+
"examples": [125, 32991],
|
|
414
|
+
}
|
|
415
|
+
},
|
|
416
|
+
"examples": [
|
|
417
|
+
{"description": "Get root categories", "params": {}},
|
|
418
|
+
{
|
|
419
|
+
"description": "Get subcategories of category 125",
|
|
420
|
+
"params": {"category_id": 125},
|
|
421
|
+
},
|
|
422
|
+
],
|
|
423
|
+
},
|
|
424
|
+
"get_releases": {
|
|
425
|
+
"description": "Get FRED data releases",
|
|
426
|
+
"parameters": {
|
|
427
|
+
"limit": {
|
|
428
|
+
"type": "integer",
|
|
429
|
+
"required": False,
|
|
430
|
+
"description": "Maximum number of releases to return",
|
|
431
|
+
"examples": [10, 50],
|
|
432
|
+
"default": 1000,
|
|
433
|
+
}
|
|
434
|
+
},
|
|
435
|
+
"examples": [
|
|
436
|
+
{
|
|
437
|
+
"description": "Get recent releases",
|
|
438
|
+
"params": {"limit": 20},
|
|
439
|
+
}
|
|
440
|
+
],
|
|
441
|
+
},
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return schemas.get(operation)
|
|
445
|
+
|
|
446
|
+
def validate_and_clean_data(self, operation: str, raw_data: Any) -> Dict[str, Any]:
|
|
447
|
+
"""Validate and clean FRED data"""
|
|
448
|
+
|
|
449
|
+
result = {
|
|
450
|
+
"data": raw_data,
|
|
451
|
+
"validation_warnings": [],
|
|
452
|
+
"statistics": {},
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if operation in ["get_series", "get_series_observations"]:
|
|
456
|
+
if isinstance(raw_data, list) and len(raw_data) > 0:
|
|
457
|
+
# Check for missing values (FRED uses '.')
|
|
458
|
+
completeness_info = self.validator.check_data_completeness(raw_data, "value", [".", "NA"])
|
|
459
|
+
|
|
460
|
+
result["statistics"]["completeness"] = completeness_info
|
|
461
|
+
|
|
462
|
+
if completeness_info["missing_count"] > 0:
|
|
463
|
+
result["validation_warnings"].append(f"{completeness_info['missing_count']} missing values detected " f"({completeness_info['completeness']:.1%} complete)")
|
|
464
|
+
|
|
465
|
+
# Extract numeric values for outlier detection
|
|
466
|
+
numeric_values = []
|
|
467
|
+
for item in raw_data:
|
|
468
|
+
value = item.get("value")
|
|
469
|
+
if value not in [".", "NA", None]:
|
|
470
|
+
try:
|
|
471
|
+
numeric_values.append(float(value))
|
|
472
|
+
except (ValueError, TypeError):
|
|
473
|
+
pass
|
|
474
|
+
|
|
475
|
+
if len(numeric_values) >= 4:
|
|
476
|
+
# Detect outliers
|
|
477
|
+
outlier_indices = self.validator.detect_outliers(numeric_values, method="iqr", threshold=3.0)
|
|
478
|
+
|
|
479
|
+
if outlier_indices:
|
|
480
|
+
result["validation_warnings"].append(f"{len(outlier_indices)} potential outliers detected")
|
|
481
|
+
result["statistics"]["outliers_count"] = len(outlier_indices)
|
|
482
|
+
|
|
483
|
+
# Calculate value range
|
|
484
|
+
value_range = self.validator.calculate_value_range(raw_data, "value")
|
|
485
|
+
if value_range:
|
|
486
|
+
result["statistics"]["value_range"] = value_range
|
|
487
|
+
|
|
488
|
+
# Detect time gaps
|
|
489
|
+
time_gaps = self.validator.detect_time_gaps(raw_data, "date")
|
|
490
|
+
if time_gaps:
|
|
491
|
+
result["validation_warnings"].append(f"{len(time_gaps)} time gaps detected in series")
|
|
492
|
+
result["statistics"]["time_gaps"] = len(time_gaps)
|
|
493
|
+
|
|
494
|
+
return result
|
|
495
|
+
|
|
496
|
+
def calculate_data_quality(self, operation: str, data: Any, response_time_ms: float) -> Dict[str, Any]:
|
|
497
|
+
"""Calculate quality metadata specific to FRED data"""
|
|
498
|
+
|
|
499
|
+
# Get base quality from parent
|
|
500
|
+
quality = super().calculate_data_quality(operation, data, response_time_ms)
|
|
501
|
+
|
|
502
|
+
# FRED-specific quality enhancements
|
|
503
|
+
# FRED is official government data
|
|
504
|
+
quality["authority_level"] = "official"
|
|
505
|
+
quality["confidence"] = 0.95 # High confidence in FRED data
|
|
506
|
+
|
|
507
|
+
# For time series, assess freshness
|
|
508
|
+
if operation in ["get_series", "get_series_observations"]:
|
|
509
|
+
if isinstance(data, list) and len(data) > 0:
|
|
510
|
+
# Check the date of most recent observation
|
|
511
|
+
latest_date = None
|
|
512
|
+
for item in data:
|
|
513
|
+
if "date" in item:
|
|
514
|
+
try:
|
|
515
|
+
from datetime import datetime
|
|
516
|
+
|
|
517
|
+
date_obj = datetime.strptime(item["date"], "%Y-%m-%d")
|
|
518
|
+
if latest_date is None or date_obj > latest_date:
|
|
519
|
+
latest_date = date_obj
|
|
520
|
+
except Exception:
|
|
521
|
+
pass
|
|
522
|
+
|
|
523
|
+
if latest_date:
|
|
524
|
+
from datetime import datetime
|
|
525
|
+
|
|
526
|
+
age_days = (datetime.now() - latest_date).days
|
|
527
|
+
quality["freshness_hours"] = age_days * 24
|
|
528
|
+
|
|
529
|
+
# Adjust quality score based on data freshness
|
|
530
|
+
if age_days < 30:
|
|
531
|
+
quality["score"] = min(quality["score"] + 0.1, 1.0)
|
|
532
|
+
elif age_days > 365:
|
|
533
|
+
quality["score"] = max(quality["score"] - 0.1, 0.0)
|
|
534
|
+
|
|
535
|
+
return quality
|