aiecs 1.5.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aiecs/__init__.py +72 -0
- aiecs/__main__.py +41 -0
- aiecs/aiecs_client.py +469 -0
- aiecs/application/__init__.py +10 -0
- aiecs/application/executors/__init__.py +10 -0
- aiecs/application/executors/operation_executor.py +363 -0
- aiecs/application/knowledge_graph/__init__.py +7 -0
- aiecs/application/knowledge_graph/builder/__init__.py +37 -0
- aiecs/application/knowledge_graph/builder/document_builder.py +375 -0
- aiecs/application/knowledge_graph/builder/graph_builder.py +356 -0
- aiecs/application/knowledge_graph/builder/schema_mapping.py +531 -0
- aiecs/application/knowledge_graph/builder/structured_pipeline.py +443 -0
- aiecs/application/knowledge_graph/builder/text_chunker.py +319 -0
- aiecs/application/knowledge_graph/extractors/__init__.py +27 -0
- aiecs/application/knowledge_graph/extractors/base.py +100 -0
- aiecs/application/knowledge_graph/extractors/llm_entity_extractor.py +327 -0
- aiecs/application/knowledge_graph/extractors/llm_relation_extractor.py +349 -0
- aiecs/application/knowledge_graph/extractors/ner_entity_extractor.py +244 -0
- aiecs/application/knowledge_graph/fusion/__init__.py +23 -0
- aiecs/application/knowledge_graph/fusion/entity_deduplicator.py +387 -0
- aiecs/application/knowledge_graph/fusion/entity_linker.py +343 -0
- aiecs/application/knowledge_graph/fusion/knowledge_fusion.py +580 -0
- aiecs/application/knowledge_graph/fusion/relation_deduplicator.py +189 -0
- aiecs/application/knowledge_graph/pattern_matching/__init__.py +21 -0
- aiecs/application/knowledge_graph/pattern_matching/pattern_matcher.py +344 -0
- aiecs/application/knowledge_graph/pattern_matching/query_executor.py +378 -0
- aiecs/application/knowledge_graph/profiling/__init__.py +12 -0
- aiecs/application/knowledge_graph/profiling/query_plan_visualizer.py +199 -0
- aiecs/application/knowledge_graph/profiling/query_profiler.py +223 -0
- aiecs/application/knowledge_graph/reasoning/__init__.py +27 -0
- aiecs/application/knowledge_graph/reasoning/evidence_synthesis.py +347 -0
- aiecs/application/knowledge_graph/reasoning/inference_engine.py +504 -0
- aiecs/application/knowledge_graph/reasoning/logic_form_parser.py +167 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/__init__.py +79 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/ast_builder.py +513 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/ast_nodes.py +630 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/ast_validator.py +654 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/error_handler.py +477 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/parser.py +390 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/query_context.py +217 -0
- aiecs/application/knowledge_graph/reasoning/logic_query_integration.py +169 -0
- aiecs/application/knowledge_graph/reasoning/query_planner.py +872 -0
- aiecs/application/knowledge_graph/reasoning/reasoning_engine.py +554 -0
- aiecs/application/knowledge_graph/retrieval/__init__.py +19 -0
- aiecs/application/knowledge_graph/retrieval/retrieval_strategies.py +596 -0
- aiecs/application/knowledge_graph/search/__init__.py +59 -0
- aiecs/application/knowledge_graph/search/hybrid_search.py +423 -0
- aiecs/application/knowledge_graph/search/reranker.py +295 -0
- aiecs/application/knowledge_graph/search/reranker_strategies.py +553 -0
- aiecs/application/knowledge_graph/search/text_similarity.py +398 -0
- aiecs/application/knowledge_graph/traversal/__init__.py +15 -0
- aiecs/application/knowledge_graph/traversal/enhanced_traversal.py +329 -0
- aiecs/application/knowledge_graph/traversal/path_scorer.py +269 -0
- aiecs/application/knowledge_graph/validators/__init__.py +13 -0
- aiecs/application/knowledge_graph/validators/relation_validator.py +189 -0
- aiecs/application/knowledge_graph/visualization/__init__.py +11 -0
- aiecs/application/knowledge_graph/visualization/graph_visualizer.py +321 -0
- aiecs/common/__init__.py +9 -0
- aiecs/common/knowledge_graph/__init__.py +17 -0
- aiecs/common/knowledge_graph/runnable.py +484 -0
- aiecs/config/__init__.py +16 -0
- aiecs/config/config.py +498 -0
- aiecs/config/graph_config.py +137 -0
- aiecs/config/registry.py +23 -0
- aiecs/core/__init__.py +46 -0
- aiecs/core/interface/__init__.py +34 -0
- aiecs/core/interface/execution_interface.py +152 -0
- aiecs/core/interface/storage_interface.py +171 -0
- aiecs/domain/__init__.py +289 -0
- aiecs/domain/agent/__init__.py +189 -0
- aiecs/domain/agent/base_agent.py +697 -0
- aiecs/domain/agent/exceptions.py +103 -0
- aiecs/domain/agent/graph_aware_mixin.py +559 -0
- aiecs/domain/agent/hybrid_agent.py +490 -0
- aiecs/domain/agent/integration/__init__.py +26 -0
- aiecs/domain/agent/integration/context_compressor.py +222 -0
- aiecs/domain/agent/integration/context_engine_adapter.py +252 -0
- aiecs/domain/agent/integration/retry_policy.py +219 -0
- aiecs/domain/agent/integration/role_config.py +213 -0
- aiecs/domain/agent/knowledge_aware_agent.py +646 -0
- aiecs/domain/agent/lifecycle.py +296 -0
- aiecs/domain/agent/llm_agent.py +300 -0
- aiecs/domain/agent/memory/__init__.py +12 -0
- aiecs/domain/agent/memory/conversation.py +197 -0
- aiecs/domain/agent/migration/__init__.py +14 -0
- aiecs/domain/agent/migration/conversion.py +160 -0
- aiecs/domain/agent/migration/legacy_wrapper.py +90 -0
- aiecs/domain/agent/models.py +317 -0
- aiecs/domain/agent/observability.py +407 -0
- aiecs/domain/agent/persistence.py +289 -0
- aiecs/domain/agent/prompts/__init__.py +29 -0
- aiecs/domain/agent/prompts/builder.py +161 -0
- aiecs/domain/agent/prompts/formatters.py +189 -0
- aiecs/domain/agent/prompts/template.py +255 -0
- aiecs/domain/agent/registry.py +260 -0
- aiecs/domain/agent/tool_agent.py +257 -0
- aiecs/domain/agent/tools/__init__.py +12 -0
- aiecs/domain/agent/tools/schema_generator.py +221 -0
- aiecs/domain/community/__init__.py +155 -0
- aiecs/domain/community/agent_adapter.py +477 -0
- aiecs/domain/community/analytics.py +481 -0
- aiecs/domain/community/collaborative_workflow.py +642 -0
- aiecs/domain/community/communication_hub.py +645 -0
- aiecs/domain/community/community_builder.py +320 -0
- aiecs/domain/community/community_integration.py +800 -0
- aiecs/domain/community/community_manager.py +813 -0
- aiecs/domain/community/decision_engine.py +879 -0
- aiecs/domain/community/exceptions.py +225 -0
- aiecs/domain/community/models/__init__.py +33 -0
- aiecs/domain/community/models/community_models.py +268 -0
- aiecs/domain/community/resource_manager.py +457 -0
- aiecs/domain/community/shared_context_manager.py +603 -0
- aiecs/domain/context/__init__.py +58 -0
- aiecs/domain/context/context_engine.py +989 -0
- aiecs/domain/context/conversation_models.py +354 -0
- aiecs/domain/context/graph_memory.py +467 -0
- aiecs/domain/execution/__init__.py +12 -0
- aiecs/domain/execution/model.py +57 -0
- aiecs/domain/knowledge_graph/__init__.py +19 -0
- aiecs/domain/knowledge_graph/models/__init__.py +52 -0
- aiecs/domain/knowledge_graph/models/entity.py +130 -0
- aiecs/domain/knowledge_graph/models/evidence.py +194 -0
- aiecs/domain/knowledge_graph/models/inference_rule.py +186 -0
- aiecs/domain/knowledge_graph/models/path.py +179 -0
- aiecs/domain/knowledge_graph/models/path_pattern.py +173 -0
- aiecs/domain/knowledge_graph/models/query.py +272 -0
- aiecs/domain/knowledge_graph/models/query_plan.py +187 -0
- aiecs/domain/knowledge_graph/models/relation.py +136 -0
- aiecs/domain/knowledge_graph/schema/__init__.py +23 -0
- aiecs/domain/knowledge_graph/schema/entity_type.py +135 -0
- aiecs/domain/knowledge_graph/schema/graph_schema.py +271 -0
- aiecs/domain/knowledge_graph/schema/property_schema.py +155 -0
- aiecs/domain/knowledge_graph/schema/relation_type.py +171 -0
- aiecs/domain/knowledge_graph/schema/schema_manager.py +496 -0
- aiecs/domain/knowledge_graph/schema/type_enums.py +205 -0
- aiecs/domain/task/__init__.py +13 -0
- aiecs/domain/task/dsl_processor.py +613 -0
- aiecs/domain/task/model.py +62 -0
- aiecs/domain/task/task_context.py +268 -0
- aiecs/infrastructure/__init__.py +24 -0
- aiecs/infrastructure/graph_storage/__init__.py +11 -0
- aiecs/infrastructure/graph_storage/base.py +601 -0
- aiecs/infrastructure/graph_storage/batch_operations.py +449 -0
- aiecs/infrastructure/graph_storage/cache.py +429 -0
- aiecs/infrastructure/graph_storage/distributed.py +226 -0
- aiecs/infrastructure/graph_storage/error_handling.py +390 -0
- aiecs/infrastructure/graph_storage/graceful_degradation.py +306 -0
- aiecs/infrastructure/graph_storage/health_checks.py +378 -0
- aiecs/infrastructure/graph_storage/in_memory.py +514 -0
- aiecs/infrastructure/graph_storage/index_optimization.py +483 -0
- aiecs/infrastructure/graph_storage/lazy_loading.py +410 -0
- aiecs/infrastructure/graph_storage/metrics.py +357 -0
- aiecs/infrastructure/graph_storage/migration.py +413 -0
- aiecs/infrastructure/graph_storage/pagination.py +471 -0
- aiecs/infrastructure/graph_storage/performance_monitoring.py +466 -0
- aiecs/infrastructure/graph_storage/postgres.py +871 -0
- aiecs/infrastructure/graph_storage/query_optimizer.py +635 -0
- aiecs/infrastructure/graph_storage/schema_cache.py +290 -0
- aiecs/infrastructure/graph_storage/sqlite.py +623 -0
- aiecs/infrastructure/graph_storage/streaming.py +495 -0
- aiecs/infrastructure/messaging/__init__.py +13 -0
- aiecs/infrastructure/messaging/celery_task_manager.py +383 -0
- aiecs/infrastructure/messaging/websocket_manager.py +298 -0
- aiecs/infrastructure/monitoring/__init__.py +34 -0
- aiecs/infrastructure/monitoring/executor_metrics.py +174 -0
- aiecs/infrastructure/monitoring/global_metrics_manager.py +213 -0
- aiecs/infrastructure/monitoring/structured_logger.py +48 -0
- aiecs/infrastructure/monitoring/tracing_manager.py +410 -0
- aiecs/infrastructure/persistence/__init__.py +24 -0
- aiecs/infrastructure/persistence/context_engine_client.py +187 -0
- aiecs/infrastructure/persistence/database_manager.py +333 -0
- aiecs/infrastructure/persistence/file_storage.py +754 -0
- aiecs/infrastructure/persistence/redis_client.py +220 -0
- aiecs/llm/__init__.py +86 -0
- aiecs/llm/callbacks/__init__.py +11 -0
- aiecs/llm/callbacks/custom_callbacks.py +264 -0
- aiecs/llm/client_factory.py +420 -0
- aiecs/llm/clients/__init__.py +33 -0
- aiecs/llm/clients/base_client.py +193 -0
- aiecs/llm/clients/googleai_client.py +181 -0
- aiecs/llm/clients/openai_client.py +131 -0
- aiecs/llm/clients/vertex_client.py +437 -0
- aiecs/llm/clients/xai_client.py +184 -0
- aiecs/llm/config/__init__.py +51 -0
- aiecs/llm/config/config_loader.py +275 -0
- aiecs/llm/config/config_validator.py +236 -0
- aiecs/llm/config/model_config.py +151 -0
- aiecs/llm/utils/__init__.py +10 -0
- aiecs/llm/utils/validate_config.py +91 -0
- aiecs/main.py +363 -0
- aiecs/scripts/__init__.py +3 -0
- aiecs/scripts/aid/VERSION_MANAGEMENT.md +97 -0
- aiecs/scripts/aid/__init__.py +19 -0
- aiecs/scripts/aid/version_manager.py +215 -0
- aiecs/scripts/dependance_check/DEPENDENCY_SYSTEM_SUMMARY.md +242 -0
- aiecs/scripts/dependance_check/README_DEPENDENCY_CHECKER.md +310 -0
- aiecs/scripts/dependance_check/__init__.py +17 -0
- aiecs/scripts/dependance_check/dependency_checker.py +938 -0
- aiecs/scripts/dependance_check/dependency_fixer.py +391 -0
- aiecs/scripts/dependance_check/download_nlp_data.py +396 -0
- aiecs/scripts/dependance_check/quick_dependency_check.py +270 -0
- aiecs/scripts/dependance_check/setup_nlp_data.sh +217 -0
- aiecs/scripts/dependance_patch/__init__.py +7 -0
- aiecs/scripts/dependance_patch/fix_weasel/README_WEASEL_PATCH.md +126 -0
- aiecs/scripts/dependance_patch/fix_weasel/__init__.py +11 -0
- aiecs/scripts/dependance_patch/fix_weasel/fix_weasel_validator.py +128 -0
- aiecs/scripts/dependance_patch/fix_weasel/fix_weasel_validator.sh +82 -0
- aiecs/scripts/dependance_patch/fix_weasel/patch_weasel_library.sh +188 -0
- aiecs/scripts/dependance_patch/fix_weasel/run_weasel_patch.sh +41 -0
- aiecs/scripts/tools_develop/README.md +449 -0
- aiecs/scripts/tools_develop/TOOL_AUTO_DISCOVERY.md +234 -0
- aiecs/scripts/tools_develop/__init__.py +21 -0
- aiecs/scripts/tools_develop/check_type_annotations.py +259 -0
- aiecs/scripts/tools_develop/validate_tool_schemas.py +422 -0
- aiecs/scripts/tools_develop/verify_tools.py +356 -0
- aiecs/tasks/__init__.py +1 -0
- aiecs/tasks/worker.py +172 -0
- aiecs/tools/__init__.py +299 -0
- aiecs/tools/apisource/__init__.py +99 -0
- aiecs/tools/apisource/intelligence/__init__.py +19 -0
- aiecs/tools/apisource/intelligence/data_fusion.py +381 -0
- aiecs/tools/apisource/intelligence/query_analyzer.py +413 -0
- aiecs/tools/apisource/intelligence/search_enhancer.py +388 -0
- aiecs/tools/apisource/monitoring/__init__.py +9 -0
- aiecs/tools/apisource/monitoring/metrics.py +303 -0
- aiecs/tools/apisource/providers/__init__.py +115 -0
- aiecs/tools/apisource/providers/base.py +664 -0
- aiecs/tools/apisource/providers/census.py +401 -0
- aiecs/tools/apisource/providers/fred.py +564 -0
- aiecs/tools/apisource/providers/newsapi.py +412 -0
- aiecs/tools/apisource/providers/worldbank.py +357 -0
- aiecs/tools/apisource/reliability/__init__.py +12 -0
- aiecs/tools/apisource/reliability/error_handler.py +375 -0
- aiecs/tools/apisource/reliability/fallback_strategy.py +391 -0
- aiecs/tools/apisource/tool.py +850 -0
- aiecs/tools/apisource/utils/__init__.py +9 -0
- aiecs/tools/apisource/utils/validators.py +338 -0
- aiecs/tools/base_tool.py +201 -0
- aiecs/tools/docs/__init__.py +121 -0
- aiecs/tools/docs/ai_document_orchestrator.py +599 -0
- aiecs/tools/docs/ai_document_writer_orchestrator.py +2403 -0
- aiecs/tools/docs/content_insertion_tool.py +1333 -0
- aiecs/tools/docs/document_creator_tool.py +1317 -0
- aiecs/tools/docs/document_layout_tool.py +1166 -0
- aiecs/tools/docs/document_parser_tool.py +994 -0
- aiecs/tools/docs/document_writer_tool.py +1818 -0
- aiecs/tools/knowledge_graph/__init__.py +17 -0
- aiecs/tools/knowledge_graph/graph_reasoning_tool.py +734 -0
- aiecs/tools/knowledge_graph/graph_search_tool.py +923 -0
- aiecs/tools/knowledge_graph/kg_builder_tool.py +476 -0
- aiecs/tools/langchain_adapter.py +542 -0
- aiecs/tools/schema_generator.py +275 -0
- aiecs/tools/search_tool/__init__.py +100 -0
- aiecs/tools/search_tool/analyzers.py +589 -0
- aiecs/tools/search_tool/cache.py +260 -0
- aiecs/tools/search_tool/constants.py +128 -0
- aiecs/tools/search_tool/context.py +216 -0
- aiecs/tools/search_tool/core.py +749 -0
- aiecs/tools/search_tool/deduplicator.py +123 -0
- aiecs/tools/search_tool/error_handler.py +271 -0
- aiecs/tools/search_tool/metrics.py +371 -0
- aiecs/tools/search_tool/rate_limiter.py +178 -0
- aiecs/tools/search_tool/schemas.py +277 -0
- aiecs/tools/statistics/__init__.py +80 -0
- aiecs/tools/statistics/ai_data_analysis_orchestrator.py +643 -0
- aiecs/tools/statistics/ai_insight_generator_tool.py +505 -0
- aiecs/tools/statistics/ai_report_orchestrator_tool.py +694 -0
- aiecs/tools/statistics/data_loader_tool.py +564 -0
- aiecs/tools/statistics/data_profiler_tool.py +658 -0
- aiecs/tools/statistics/data_transformer_tool.py +573 -0
- aiecs/tools/statistics/data_visualizer_tool.py +495 -0
- aiecs/tools/statistics/model_trainer_tool.py +487 -0
- aiecs/tools/statistics/statistical_analyzer_tool.py +459 -0
- aiecs/tools/task_tools/__init__.py +86 -0
- aiecs/tools/task_tools/chart_tool.py +732 -0
- aiecs/tools/task_tools/classfire_tool.py +922 -0
- aiecs/tools/task_tools/image_tool.py +447 -0
- aiecs/tools/task_tools/office_tool.py +684 -0
- aiecs/tools/task_tools/pandas_tool.py +635 -0
- aiecs/tools/task_tools/report_tool.py +635 -0
- aiecs/tools/task_tools/research_tool.py +392 -0
- aiecs/tools/task_tools/scraper_tool.py +715 -0
- aiecs/tools/task_tools/stats_tool.py +688 -0
- aiecs/tools/temp_file_manager.py +130 -0
- aiecs/tools/tool_executor/__init__.py +37 -0
- aiecs/tools/tool_executor/tool_executor.py +881 -0
- aiecs/utils/LLM_output_structor.py +445 -0
- aiecs/utils/__init__.py +34 -0
- aiecs/utils/base_callback.py +47 -0
- aiecs/utils/cache_provider.py +695 -0
- aiecs/utils/execution_utils.py +184 -0
- aiecs/utils/logging.py +1 -0
- aiecs/utils/prompt_loader.py +14 -0
- aiecs/utils/token_usage_repository.py +323 -0
- aiecs/ws/__init__.py +0 -0
- aiecs/ws/socket_server.py +52 -0
- aiecs-1.5.1.dist-info/METADATA +608 -0
- aiecs-1.5.1.dist-info/RECORD +302 -0
- aiecs-1.5.1.dist-info/WHEEL +5 -0
- aiecs-1.5.1.dist-info/entry_points.txt +10 -0
- aiecs-1.5.1.dist-info/licenses/LICENSE +225 -0
- aiecs-1.5.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,694 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AI Report Orchestrator Tool - AI-powered comprehensive report generation
|
|
3
|
+
|
|
4
|
+
This tool provides advanced report generation with:
|
|
5
|
+
- Automated analysis report creation
|
|
6
|
+
- Multiple report types and formats
|
|
7
|
+
- Integration with analysis results
|
|
8
|
+
- Visualization embedding
|
|
9
|
+
- Export to multiple formats
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import logging
|
|
14
|
+
import tempfile
|
|
15
|
+
from typing import Dict, Any, List, Optional
|
|
16
|
+
from enum import Enum
|
|
17
|
+
from datetime import datetime
|
|
18
|
+
|
|
19
|
+
from pydantic import BaseModel, Field, ConfigDict
|
|
20
|
+
|
|
21
|
+
from aiecs.tools.base_tool import BaseTool
|
|
22
|
+
from aiecs.tools import register_tool
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ReportType(str, Enum):
|
|
26
|
+
"""Types of reports"""
|
|
27
|
+
|
|
28
|
+
EXECUTIVE_SUMMARY = "executive_summary"
|
|
29
|
+
TECHNICAL_REPORT = "technical_report"
|
|
30
|
+
BUSINESS_REPORT = "business_report"
|
|
31
|
+
RESEARCH_PAPER = "research_paper"
|
|
32
|
+
DATA_QUALITY_REPORT = "data_quality_report"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ReportFormat(str, Enum):
|
|
36
|
+
"""Report output formats"""
|
|
37
|
+
|
|
38
|
+
MARKDOWN = "markdown"
|
|
39
|
+
HTML = "html"
|
|
40
|
+
PDF = "pdf"
|
|
41
|
+
WORD = "word"
|
|
42
|
+
JSON = "json"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ReportOrchestratorError(Exception):
|
|
46
|
+
"""Base exception for Report Orchestrator errors"""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ReportGenerationError(ReportOrchestratorError):
|
|
50
|
+
"""Raised when report generation fails"""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@register_tool("ai_report_orchestrator")
|
|
54
|
+
class AIReportOrchestratorTool(BaseTool):
|
|
55
|
+
"""
|
|
56
|
+
AI-powered analysis report generator that can:
|
|
57
|
+
1. Generate comprehensive analysis reports
|
|
58
|
+
2. Customize report structure and style
|
|
59
|
+
3. Include visualizations and tables
|
|
60
|
+
4. Export to multiple formats
|
|
61
|
+
5. Integrate analysis results and insights
|
|
62
|
+
|
|
63
|
+
Integrates with report_tool for document generation.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
# Configuration schema
|
|
67
|
+
class Config(BaseModel):
|
|
68
|
+
"""Configuration for the AI report orchestrator tool"""
|
|
69
|
+
|
|
70
|
+
model_config = ConfigDict(env_prefix="AI_REPORT_ORCHESTRATOR_")
|
|
71
|
+
|
|
72
|
+
default_report_type: str = Field(
|
|
73
|
+
default="business_report",
|
|
74
|
+
description="Default report type to generate",
|
|
75
|
+
)
|
|
76
|
+
default_format: str = Field(default="markdown", description="Default report output format")
|
|
77
|
+
output_directory: str = Field(
|
|
78
|
+
default=tempfile.gettempdir(),
|
|
79
|
+
description="Directory for report output files",
|
|
80
|
+
)
|
|
81
|
+
include_code: bool = Field(
|
|
82
|
+
default=False,
|
|
83
|
+
description="Whether to include code snippets in reports",
|
|
84
|
+
)
|
|
85
|
+
include_visualizations: bool = Field(
|
|
86
|
+
default=True,
|
|
87
|
+
description="Whether to include visualizations in reports",
|
|
88
|
+
)
|
|
89
|
+
max_insights_per_report: int = Field(
|
|
90
|
+
default=20,
|
|
91
|
+
description="Maximum number of insights to include per report",
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
|
95
|
+
"""Initialize AI Report Orchestrator Tool"""
|
|
96
|
+
super().__init__(config)
|
|
97
|
+
|
|
98
|
+
# Parse configuration
|
|
99
|
+
self.config = self.Config(**(config or {}))
|
|
100
|
+
|
|
101
|
+
self.logger = logging.getLogger(__name__)
|
|
102
|
+
if not self.logger.handlers:
|
|
103
|
+
handler = logging.StreamHandler()
|
|
104
|
+
handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s"))
|
|
105
|
+
self.logger.addHandler(handler)
|
|
106
|
+
self.logger.setLevel(logging.INFO)
|
|
107
|
+
|
|
108
|
+
self._init_external_tools()
|
|
109
|
+
|
|
110
|
+
# Ensure output directory exists
|
|
111
|
+
os.makedirs(self.config.output_directory, exist_ok=True)
|
|
112
|
+
|
|
113
|
+
def _init_external_tools(self):
|
|
114
|
+
"""Initialize external task tools"""
|
|
115
|
+
self.external_tools = {}
|
|
116
|
+
|
|
117
|
+
# Initialize ReportTool for document generation
|
|
118
|
+
try:
|
|
119
|
+
from aiecs.tools.task_tools.report_tool import ReportTool
|
|
120
|
+
|
|
121
|
+
self.external_tools["report"] = ReportTool()
|
|
122
|
+
self.logger.info("ReportTool initialized successfully")
|
|
123
|
+
except ImportError:
|
|
124
|
+
self.logger.warning("ReportTool not available")
|
|
125
|
+
self.external_tools["report"] = None
|
|
126
|
+
|
|
127
|
+
# Schema definitions
|
|
128
|
+
class GenerateReportSchema(BaseModel):
|
|
129
|
+
"""Schema for generate_report operation"""
|
|
130
|
+
|
|
131
|
+
analysis_results: Dict[str, Any] = Field(description="Analysis results to include")
|
|
132
|
+
insights: Optional[Dict[str, Any]] = Field(default=None, description="Generated insights")
|
|
133
|
+
report_type: ReportType = Field(
|
|
134
|
+
default=ReportType.BUSINESS_REPORT, description="Type of report"
|
|
135
|
+
)
|
|
136
|
+
output_format: ReportFormat = Field(
|
|
137
|
+
default=ReportFormat.MARKDOWN, description="Output format"
|
|
138
|
+
)
|
|
139
|
+
title: Optional[str] = Field(default=None, description="Report title")
|
|
140
|
+
include_code: bool = Field(default=False, description="Include code snippets")
|
|
141
|
+
|
|
142
|
+
class FormatReportSchema(BaseModel):
|
|
143
|
+
"""Schema for format_report operation"""
|
|
144
|
+
|
|
145
|
+
report_content: str = Field(description="Report content to format")
|
|
146
|
+
output_format: ReportFormat = Field(description="Desired output format")
|
|
147
|
+
output_path: Optional[str] = Field(default=None, description="Output file path")
|
|
148
|
+
|
|
149
|
+
class ExportReportSchema(BaseModel):
|
|
150
|
+
"""Schema for export_report operation"""
|
|
151
|
+
|
|
152
|
+
report_content: str = Field(description="Report content")
|
|
153
|
+
output_format: ReportFormat = Field(description="Export format")
|
|
154
|
+
output_path: str = Field(description="Output file path")
|
|
155
|
+
|
|
156
|
+
def generate_report(
|
|
157
|
+
self,
|
|
158
|
+
analysis_results: Dict[str, Any],
|
|
159
|
+
insights: Optional[Dict[str, Any]] = None,
|
|
160
|
+
report_type: ReportType = ReportType.BUSINESS_REPORT,
|
|
161
|
+
output_format: ReportFormat = ReportFormat.MARKDOWN,
|
|
162
|
+
title: Optional[str] = None,
|
|
163
|
+
include_code: bool = False,
|
|
164
|
+
) -> Dict[str, Any]:
|
|
165
|
+
"""
|
|
166
|
+
Generate comprehensive analysis report.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
analysis_results: Results from data analysis
|
|
170
|
+
insights: Generated insights to include
|
|
171
|
+
report_type: Type of report to generate
|
|
172
|
+
output_format: Output format
|
|
173
|
+
title: Report title
|
|
174
|
+
include_code: Whether to include code snippets
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
Dict containing:
|
|
178
|
+
- report_content: Generated report content
|
|
179
|
+
- sections: Report sections
|
|
180
|
+
- export_path: Path to exported report
|
|
181
|
+
- metadata: Report metadata
|
|
182
|
+
"""
|
|
183
|
+
try:
|
|
184
|
+
self.logger.info(
|
|
185
|
+
f"Generating {report_type.value} report in {output_format.value} format"
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
# Generate report title
|
|
189
|
+
if title is None:
|
|
190
|
+
title = self._generate_title(report_type, analysis_results)
|
|
191
|
+
|
|
192
|
+
# Build report sections
|
|
193
|
+
sections = self._build_report_sections(
|
|
194
|
+
analysis_results, insights, report_type, include_code
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
# Compile report content
|
|
198
|
+
report_content = self._compile_report(title, sections, report_type)
|
|
199
|
+
|
|
200
|
+
# Format report for output format
|
|
201
|
+
formatted_content = self._format_content(report_content, output_format)
|
|
202
|
+
|
|
203
|
+
# Export report
|
|
204
|
+
export_path = self._export_report(formatted_content, output_format, title)
|
|
205
|
+
|
|
206
|
+
# Generate metadata
|
|
207
|
+
metadata = {
|
|
208
|
+
"generated_at": datetime.now().isoformat(),
|
|
209
|
+
"report_type": report_type.value,
|
|
210
|
+
"output_format": output_format.value,
|
|
211
|
+
"sections_count": len(sections),
|
|
212
|
+
"word_count": len(report_content.split()),
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
"report_content": report_content,
|
|
217
|
+
"sections": {s["title"]: s["content"] for s in sections},
|
|
218
|
+
"export_path": export_path,
|
|
219
|
+
"metadata": metadata,
|
|
220
|
+
"title": title,
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
except Exception as e:
|
|
224
|
+
self.logger.error(f"Error generating report: {e}")
|
|
225
|
+
raise ReportGenerationError(f"Report generation failed: {e}")
|
|
226
|
+
|
|
227
|
+
def format_report(
|
|
228
|
+
self,
|
|
229
|
+
report_content: str,
|
|
230
|
+
output_format: ReportFormat,
|
|
231
|
+
output_path: Optional[str] = None,
|
|
232
|
+
) -> Dict[str, Any]:
|
|
233
|
+
"""
|
|
234
|
+
Format report content to specified format.
|
|
235
|
+
|
|
236
|
+
Args:
|
|
237
|
+
report_content: Report content to format
|
|
238
|
+
output_format: Desired output format
|
|
239
|
+
output_path: Optional output file path
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
Dict containing formatted report info
|
|
243
|
+
"""
|
|
244
|
+
try:
|
|
245
|
+
formatted_content = self._format_content(report_content, output_format)
|
|
246
|
+
|
|
247
|
+
if output_path:
|
|
248
|
+
with open(output_path, "w", encoding="utf-8") as f:
|
|
249
|
+
f.write(formatted_content)
|
|
250
|
+
export_path = output_path
|
|
251
|
+
else:
|
|
252
|
+
export_path = self._export_report(formatted_content, output_format, "report")
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
"formatted_content": formatted_content,
|
|
256
|
+
"output_format": output_format.value,
|
|
257
|
+
"export_path": export_path,
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
except Exception as e:
|
|
261
|
+
self.logger.error(f"Error formatting report: {e}")
|
|
262
|
+
raise ReportGenerationError(f"Report formatting failed: {e}")
|
|
263
|
+
|
|
264
|
+
def export_report(
|
|
265
|
+
self,
|
|
266
|
+
report_content: str,
|
|
267
|
+
output_format: ReportFormat,
|
|
268
|
+
output_path: str,
|
|
269
|
+
) -> Dict[str, Any]:
|
|
270
|
+
"""
|
|
271
|
+
Export report to file.
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
report_content: Report content
|
|
275
|
+
output_format: Export format
|
|
276
|
+
output_path: Output file path
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
Dict containing export information
|
|
280
|
+
"""
|
|
281
|
+
try:
|
|
282
|
+
formatted_content = self._format_content(report_content, output_format)
|
|
283
|
+
|
|
284
|
+
with open(output_path, "w", encoding="utf-8") as f:
|
|
285
|
+
f.write(formatted_content)
|
|
286
|
+
|
|
287
|
+
file_size = os.path.getsize(output_path)
|
|
288
|
+
|
|
289
|
+
return {
|
|
290
|
+
"export_path": output_path,
|
|
291
|
+
"format": output_format.value,
|
|
292
|
+
"file_size_bytes": file_size,
|
|
293
|
+
"success": True,
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
except Exception as e:
|
|
297
|
+
self.logger.error(f"Error exporting report: {e}")
|
|
298
|
+
raise ReportGenerationError(f"Report export failed: {e}")
|
|
299
|
+
|
|
300
|
+
# Internal report generation methods
|
|
301
|
+
|
|
302
|
+
def _generate_title(self, report_type: ReportType, analysis_results: Dict[str, Any]) -> str:
|
|
303
|
+
"""Generate appropriate report title"""
|
|
304
|
+
if report_type == ReportType.EXECUTIVE_SUMMARY:
|
|
305
|
+
return "Executive Summary: Data Analysis Report"
|
|
306
|
+
elif report_type == ReportType.TECHNICAL_REPORT:
|
|
307
|
+
return "Technical Data Analysis Report"
|
|
308
|
+
elif report_type == ReportType.BUSINESS_REPORT:
|
|
309
|
+
return "Business Intelligence Report"
|
|
310
|
+
elif report_type == ReportType.RESEARCH_PAPER:
|
|
311
|
+
return "Data Analysis Research Paper"
|
|
312
|
+
elif report_type == ReportType.DATA_QUALITY_REPORT:
|
|
313
|
+
return "Data Quality Assessment Report"
|
|
314
|
+
else:
|
|
315
|
+
return "Data Analysis Report"
|
|
316
|
+
|
|
317
|
+
def _build_report_sections(
|
|
318
|
+
self,
|
|
319
|
+
analysis_results: Dict[str, Any],
|
|
320
|
+
insights: Optional[Dict[str, Any]],
|
|
321
|
+
report_type: ReportType,
|
|
322
|
+
include_code: bool,
|
|
323
|
+
) -> List[Dict[str, str]]:
|
|
324
|
+
"""Build report sections based on type"""
|
|
325
|
+
sections = []
|
|
326
|
+
|
|
327
|
+
# Executive Summary (for all report types)
|
|
328
|
+
sections.append(
|
|
329
|
+
{
|
|
330
|
+
"title": "Executive Summary",
|
|
331
|
+
"content": self._generate_executive_summary(analysis_results, insights),
|
|
332
|
+
}
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
# Methodology section (for technical and research reports)
|
|
336
|
+
if report_type in [
|
|
337
|
+
ReportType.TECHNICAL_REPORT,
|
|
338
|
+
ReportType.RESEARCH_PAPER,
|
|
339
|
+
]:
|
|
340
|
+
sections.append(
|
|
341
|
+
{
|
|
342
|
+
"title": "Methodology",
|
|
343
|
+
"content": self._generate_methodology_section(analysis_results),
|
|
344
|
+
}
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
# Data Overview
|
|
348
|
+
sections.append(
|
|
349
|
+
{
|
|
350
|
+
"title": "Data Overview",
|
|
351
|
+
"content": self._generate_data_overview(analysis_results),
|
|
352
|
+
}
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
# Findings section
|
|
356
|
+
sections.append(
|
|
357
|
+
{
|
|
358
|
+
"title": "Key Findings",
|
|
359
|
+
"content": self._generate_findings_section(analysis_results, insights),
|
|
360
|
+
}
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
# Statistical Analysis (for technical reports)
|
|
364
|
+
if (
|
|
365
|
+
report_type == ReportType.TECHNICAL_REPORT
|
|
366
|
+
and "statistical_analysis" in analysis_results
|
|
367
|
+
):
|
|
368
|
+
sections.append(
|
|
369
|
+
{
|
|
370
|
+
"title": "Statistical Analysis",
|
|
371
|
+
"content": self._generate_statistics_section(
|
|
372
|
+
analysis_results.get("statistical_analysis", {})
|
|
373
|
+
),
|
|
374
|
+
}
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
# Insights section
|
|
378
|
+
if insights:
|
|
379
|
+
sections.append(
|
|
380
|
+
{
|
|
381
|
+
"title": "Insights and Patterns",
|
|
382
|
+
"content": self._generate_insights_section(insights),
|
|
383
|
+
}
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
# Recommendations
|
|
387
|
+
sections.append(
|
|
388
|
+
{
|
|
389
|
+
"title": "Recommendations",
|
|
390
|
+
"content": self._generate_recommendations_section(analysis_results, insights),
|
|
391
|
+
}
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
# Conclusion
|
|
395
|
+
sections.append(
|
|
396
|
+
{
|
|
397
|
+
"title": "Conclusion",
|
|
398
|
+
"content": self._generate_conclusion(analysis_results, insights),
|
|
399
|
+
}
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
# Appendix (if code included)
|
|
403
|
+
if include_code:
|
|
404
|
+
sections.append(
|
|
405
|
+
{
|
|
406
|
+
"title": "Appendix: Technical Details",
|
|
407
|
+
"content": self._generate_appendix(analysis_results),
|
|
408
|
+
}
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
return sections
|
|
412
|
+
|
|
413
|
+
def _generate_executive_summary(
|
|
414
|
+
self,
|
|
415
|
+
analysis_results: Dict[str, Any],
|
|
416
|
+
insights: Optional[Dict[str, Any]],
|
|
417
|
+
) -> str:
|
|
418
|
+
"""Generate executive summary"""
|
|
419
|
+
lines = []
|
|
420
|
+
|
|
421
|
+
# Get data profile if available
|
|
422
|
+
data_profile = analysis_results.get("data_profile", {})
|
|
423
|
+
summary = data_profile.get("summary", {})
|
|
424
|
+
|
|
425
|
+
if summary:
|
|
426
|
+
lines.append(
|
|
427
|
+
f"This report presents a comprehensive analysis of a dataset containing {summary.get('rows', 'N/A')} rows and {summary.get('columns', 'N/A')} columns."
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
missing_pct = summary.get("missing_percentage", 0)
|
|
431
|
+
if missing_pct > 0:
|
|
432
|
+
lines.append(f"The dataset has {missing_pct:.2f}% missing values.")
|
|
433
|
+
|
|
434
|
+
# Add insight summary
|
|
435
|
+
if insights and "summary" in insights:
|
|
436
|
+
lines.append(f"\n{insights['summary']}")
|
|
437
|
+
|
|
438
|
+
# Add key metrics
|
|
439
|
+
if insights and "priority_insights" in insights:
|
|
440
|
+
top_insights = insights["priority_insights"][:3]
|
|
441
|
+
if top_insights:
|
|
442
|
+
lines.append("\nKey highlights:")
|
|
443
|
+
for i, insight in enumerate(top_insights, 1):
|
|
444
|
+
lines.append(f"{i}. {insight.get('title', 'Insight')}")
|
|
445
|
+
|
|
446
|
+
return "\n".join(lines) if lines else "Analysis completed successfully."
|
|
447
|
+
|
|
448
|
+
def _generate_methodology_section(self, analysis_results: Dict[str, Any]) -> str:
|
|
449
|
+
"""Generate methodology section"""
|
|
450
|
+
lines = [
|
|
451
|
+
"The analysis was conducted using a systematic approach:",
|
|
452
|
+
"",
|
|
453
|
+
"1. **Data Loading**: Data was loaded and validated for quality",
|
|
454
|
+
"2. **Data Profiling**: Comprehensive statistical profiling was performed",
|
|
455
|
+
"3. **Data Transformation**: Necessary transformations and cleaning were applied",
|
|
456
|
+
"4. **Statistical Analysis**: Various statistical tests and analyses were conducted",
|
|
457
|
+
"5. **Insight Generation**: Patterns, trends, and anomalies were identified",
|
|
458
|
+
"6. **Visualization**: Key findings were visualized for clarity",
|
|
459
|
+
]
|
|
460
|
+
|
|
461
|
+
return "\n".join(lines)
|
|
462
|
+
|
|
463
|
+
def _generate_data_overview(self, analysis_results: Dict[str, Any]) -> str:
|
|
464
|
+
"""Generate data overview section"""
|
|
465
|
+
lines = []
|
|
466
|
+
|
|
467
|
+
data_profile = analysis_results.get("data_profile", {})
|
|
468
|
+
summary = data_profile.get("summary", {})
|
|
469
|
+
|
|
470
|
+
if summary:
|
|
471
|
+
lines.append("**Dataset Characteristics:**")
|
|
472
|
+
lines.append(f"- Total Records: {summary.get('rows', 'N/A')}")
|
|
473
|
+
lines.append(f"- Total Columns: {summary.get('columns', 'N/A')}")
|
|
474
|
+
lines.append(f"- Numeric Columns: {summary.get('numeric_columns', 'N/A')}")
|
|
475
|
+
lines.append(f"- Categorical Columns: {summary.get('categorical_columns', 'N/A')}")
|
|
476
|
+
lines.append(f"- Missing Values: {summary.get('missing_percentage', 0):.2f}%")
|
|
477
|
+
lines.append(f"- Duplicate Rows: {summary.get('duplicate_rows', 0)}")
|
|
478
|
+
else:
|
|
479
|
+
lines.append("Data overview not available.")
|
|
480
|
+
|
|
481
|
+
return "\n".join(lines)
|
|
482
|
+
|
|
483
|
+
def _generate_findings_section(
|
|
484
|
+
self,
|
|
485
|
+
analysis_results: Dict[str, Any],
|
|
486
|
+
insights: Optional[Dict[str, Any]],
|
|
487
|
+
) -> str:
|
|
488
|
+
"""Generate findings section"""
|
|
489
|
+
lines = []
|
|
490
|
+
|
|
491
|
+
# Extract findings from analysis results
|
|
492
|
+
if "findings" in analysis_results:
|
|
493
|
+
findings = analysis_results["findings"]
|
|
494
|
+
for i, finding in enumerate(findings[:10], 1):
|
|
495
|
+
lines.append(
|
|
496
|
+
f"{i}. **{finding.get('title', 'Finding')}**: {finding.get('description', 'No description')}"
|
|
497
|
+
)
|
|
498
|
+
|
|
499
|
+
# Add insights if available
|
|
500
|
+
if insights and "insights" in insights:
|
|
501
|
+
insight_list = insights["insights"][: self.config.max_insights_per_report]
|
|
502
|
+
if insight_list and not lines:
|
|
503
|
+
lines.append("**Key Insights:**")
|
|
504
|
+
for insight in insight_list:
|
|
505
|
+
lines.append(
|
|
506
|
+
f"- {insight.get('title', 'Insight')}: {insight.get('description', '')}"
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
return "\n".join(lines) if lines else "No significant findings to report."
|
|
510
|
+
|
|
511
|
+
def _generate_statistics_section(self, stats_results: Dict[str, Any]) -> str:
|
|
512
|
+
"""Generate statistics section"""
|
|
513
|
+
lines = ["**Statistical Analysis Results:**", ""]
|
|
514
|
+
|
|
515
|
+
if "correlation_matrix" in stats_results:
|
|
516
|
+
lines.append(
|
|
517
|
+
"Correlation analysis was performed to identify relationships between variables."
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
if "hypothesis_tests" in stats_results:
|
|
521
|
+
lines.append("Hypothesis testing was conducted to validate statistical significance.")
|
|
522
|
+
|
|
523
|
+
return "\n".join(lines)
|
|
524
|
+
|
|
525
|
+
def _generate_insights_section(self, insights: Dict[str, Any]) -> str:
|
|
526
|
+
"""Generate insights section"""
|
|
527
|
+
lines = []
|
|
528
|
+
|
|
529
|
+
insights.get("insights", [])
|
|
530
|
+
priority_insights = insights.get("priority_insights", [])
|
|
531
|
+
|
|
532
|
+
if priority_insights:
|
|
533
|
+
lines.append("**Priority Insights:**")
|
|
534
|
+
for i, insight in enumerate(priority_insights, 1):
|
|
535
|
+
title = insight.get("title", "Insight")
|
|
536
|
+
description = insight.get("description", "")
|
|
537
|
+
confidence = insight.get("confidence", 0)
|
|
538
|
+
impact = insight.get("impact", "medium")
|
|
539
|
+
|
|
540
|
+
lines.append(f"\n{i}. **{title}** (Confidence: {confidence:.0%}, Impact: {impact})")
|
|
541
|
+
lines.append(f" {description}")
|
|
542
|
+
|
|
543
|
+
if "recommendation" in insight:
|
|
544
|
+
lines.append(f" *Recommendation: {insight['recommendation']}*")
|
|
545
|
+
|
|
546
|
+
return "\n".join(lines) if lines else "No insights generated."
|
|
547
|
+
|
|
548
|
+
def _generate_recommendations_section(
|
|
549
|
+
self,
|
|
550
|
+
analysis_results: Dict[str, Any],
|
|
551
|
+
insights: Optional[Dict[str, Any]],
|
|
552
|
+
) -> str:
|
|
553
|
+
"""Generate recommendations section"""
|
|
554
|
+
lines = []
|
|
555
|
+
|
|
556
|
+
# Get recommendations from analysis results
|
|
557
|
+
if "recommendations" in analysis_results:
|
|
558
|
+
recs = analysis_results["recommendations"]
|
|
559
|
+
for i, rec in enumerate(recs[:10], 1):
|
|
560
|
+
action = rec.get("action", "Action")
|
|
561
|
+
reason = rec.get("reason", "")
|
|
562
|
+
priority = rec.get("priority", "medium")
|
|
563
|
+
lines.append(f"{i}. **{action}** (Priority: {priority})")
|
|
564
|
+
if reason:
|
|
565
|
+
lines.append(f" {reason}")
|
|
566
|
+
|
|
567
|
+
# Get recommendations from data profiler
|
|
568
|
+
data_profile = analysis_results.get("data_profile", {})
|
|
569
|
+
if "recommendations" in data_profile:
|
|
570
|
+
if not lines:
|
|
571
|
+
lines.append("**Data Quality Recommendations:**")
|
|
572
|
+
for rec in data_profile["recommendations"][:5]:
|
|
573
|
+
lines.append(f"- {rec.get('action', 'Action')}: {rec.get('reason', '')}")
|
|
574
|
+
|
|
575
|
+
return "\n".join(lines) if lines else "No specific recommendations at this time."
|
|
576
|
+
|
|
577
|
+
def _generate_conclusion(
|
|
578
|
+
self,
|
|
579
|
+
analysis_results: Dict[str, Any],
|
|
580
|
+
insights: Optional[Dict[str, Any]],
|
|
581
|
+
) -> str:
|
|
582
|
+
"""Generate conclusion"""
|
|
583
|
+
lines = []
|
|
584
|
+
|
|
585
|
+
lines.append("This comprehensive analysis has provided valuable insights into the dataset.")
|
|
586
|
+
|
|
587
|
+
if insights:
|
|
588
|
+
total_insights = insights.get("total_insights", 0)
|
|
589
|
+
lines.append(
|
|
590
|
+
f"A total of {total_insights} insights were generated through systematic analysis."
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
lines.append(
|
|
594
|
+
"\nThe findings and recommendations presented in this report should be carefully considered in the context of your specific business objectives and constraints."
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
return "\n".join(lines)
|
|
598
|
+
|
|
599
|
+
def _generate_appendix(self, analysis_results: Dict[str, Any]) -> str:
|
|
600
|
+
"""Generate appendix with technical details"""
|
|
601
|
+
lines = [
|
|
602
|
+
"**Technical Details:**",
|
|
603
|
+
"",
|
|
604
|
+
"This section contains technical information about the analysis process.",
|
|
605
|
+
"",
|
|
606
|
+
"Analysis was performed using the AIECS Data Analysis Orchestrator framework.",
|
|
607
|
+
]
|
|
608
|
+
|
|
609
|
+
return "\n".join(lines)
|
|
610
|
+
|
|
611
|
+
def _compile_report(
|
|
612
|
+
self,
|
|
613
|
+
title: str,
|
|
614
|
+
sections: List[Dict[str, str]],
|
|
615
|
+
report_type: ReportType,
|
|
616
|
+
) -> str:
|
|
617
|
+
"""Compile report sections into final document"""
|
|
618
|
+
lines = [
|
|
619
|
+
f"# {title}",
|
|
620
|
+
"",
|
|
621
|
+
f"*Report Type: {report_type.value.replace('_', ' ').title()}*",
|
|
622
|
+
f"*Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*",
|
|
623
|
+
"",
|
|
624
|
+
"---",
|
|
625
|
+
"",
|
|
626
|
+
]
|
|
627
|
+
|
|
628
|
+
for section in sections:
|
|
629
|
+
lines.append(f"## {section['title']}")
|
|
630
|
+
lines.append("")
|
|
631
|
+
lines.append(section["content"])
|
|
632
|
+
lines.append("")
|
|
633
|
+
lines.append("---")
|
|
634
|
+
lines.append("")
|
|
635
|
+
|
|
636
|
+
return "\n".join(lines)
|
|
637
|
+
|
|
638
|
+
def _format_content(self, content: str, output_format: ReportFormat) -> str:
|
|
639
|
+
"""Format content for specified output format"""
|
|
640
|
+
if output_format == ReportFormat.MARKDOWN:
|
|
641
|
+
return content
|
|
642
|
+
elif output_format == ReportFormat.HTML:
|
|
643
|
+
return self._markdown_to_html(content)
|
|
644
|
+
elif output_format == ReportFormat.JSON:
|
|
645
|
+
return self._content_to_json(content)
|
|
646
|
+
else:
|
|
647
|
+
# For PDF and Word, return markdown (would need additional
|
|
648
|
+
# libraries for conversion)
|
|
649
|
+
self.logger.warning(
|
|
650
|
+
f"Format {output_format.value} not fully implemented, returning markdown"
|
|
651
|
+
)
|
|
652
|
+
return content
|
|
653
|
+
|
|
654
|
+
def _markdown_to_html(self, markdown_content: str) -> str:
|
|
655
|
+
"""Convert markdown to HTML (basic implementation)"""
|
|
656
|
+
html = markdown_content
|
|
657
|
+
# Basic conversions
|
|
658
|
+
html = html.replace("# ", "<h1>").replace("\n", "</h1>\n", 1)
|
|
659
|
+
html = html.replace("## ", "<h2>").replace("\n", "</h2>\n")
|
|
660
|
+
html = html.replace("**", "<strong>").replace("**", "</strong>")
|
|
661
|
+
html = html.replace("*", "<em>").replace("*", "</em>")
|
|
662
|
+
html = f"<html><body>{html}</body></html>"
|
|
663
|
+
return html
|
|
664
|
+
|
|
665
|
+
def _content_to_json(self, content: str) -> str:
|
|
666
|
+
"""Convert content to JSON format"""
|
|
667
|
+
import json
|
|
668
|
+
|
|
669
|
+
return json.dumps({"content": content, "format": "markdown"}, indent=2)
|
|
670
|
+
|
|
671
|
+
def _export_report(self, content: str, output_format: ReportFormat, title: str) -> str:
|
|
672
|
+
"""Export report to file"""
|
|
673
|
+
# Generate filename
|
|
674
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
675
|
+
safe_title = title.replace(" ", "_").replace(":", "").lower()
|
|
676
|
+
|
|
677
|
+
extension_map = {
|
|
678
|
+
ReportFormat.MARKDOWN: ".md",
|
|
679
|
+
ReportFormat.HTML: ".html",
|
|
680
|
+
ReportFormat.PDF: ".pdf",
|
|
681
|
+
ReportFormat.WORD: ".docx",
|
|
682
|
+
ReportFormat.JSON: ".json",
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
extension = extension_map.get(output_format, ".txt")
|
|
686
|
+
filename = f"{safe_title}_{timestamp}{extension}"
|
|
687
|
+
filepath = os.path.join(self.config.output_directory, filename)
|
|
688
|
+
|
|
689
|
+
# Write file
|
|
690
|
+
with open(filepath, "w", encoding="utf-8") as f:
|
|
691
|
+
f.write(content)
|
|
692
|
+
|
|
693
|
+
self.logger.info(f"Report exported to: {filepath}")
|
|
694
|
+
return filepath
|