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,635 @@
1
+ """
2
+ Report Tool - A multi-format report generation tool supporting HTML, PDF, Excel, PowerPoint, Markdown, Word, and image-based reports.
3
+
4
+ This module provides a comprehensive report generation tool that can be used to create various types of reports
5
+ in different formats. It supports template-based rendering, data visualization, and batch processing.
6
+
7
+ Author: Your Organization
8
+ Version: 1.0.0
9
+ """
10
+
11
+ import os
12
+ import bleach
13
+ from typing import Dict, Any, List, Optional, Union, Tuple, Set
14
+ from jinja2 import FileSystemLoader, sandbox
15
+
16
+ # from weasyprint import HTML # TODO: Re-enable when deployment issues
17
+ # are resolved
18
+ import pandas as pd
19
+ from pptx import Presentation
20
+ from pptx.util import Pt
21
+ from docx import Document
22
+ from docx.shared import Pt as DocxPt
23
+ import matplotlib.pyplot as plt
24
+ from pydantic import BaseModel, ConfigDict, Field
25
+ import tempfile
26
+ import logging
27
+
28
+ from aiecs.tools.base_tool import BaseTool
29
+ from aiecs.tools import register_tool
30
+ from aiecs.tools.temp_file_manager import TempFileManager
31
+
32
+
33
+ # Exceptions
34
+ class ReportToolError(Exception):
35
+ """Base exception for ReportTool errors."""
36
+
37
+
38
+ class FileOperationError(ReportToolError):
39
+ """Raised when file operations fail."""
40
+
41
+
42
+ # Helper function for HTML sanitization
43
+
44
+
45
+ def sanitize_html(
46
+ html_content: str,
47
+ allowed_tags: Set[str],
48
+ allowed_attributes: Dict[str, List[str]],
49
+ ) -> str:
50
+ """
51
+ Sanitize HTML content to prevent XSS attacks.
52
+
53
+ Args:
54
+ html_content (str): The HTML content to sanitize.
55
+ allowed_tags (Set[str]): Set of allowed HTML tags.
56
+ allowed_attributes (Dict[str, List[str]]): Dictionary of allowed attributes for each tag.
57
+
58
+ Returns:
59
+ str: Sanitized HTML content.
60
+ """
61
+ return bleach.clean(
62
+ html_content,
63
+ tags=allowed_tags,
64
+ attributes=allowed_attributes,
65
+ strip=True,
66
+ )
67
+
68
+
69
+ # Type alias for dataset entries
70
+ DatasetType = Dict[str, Union[pd.DataFrame, List[Dict[str, Any]]]]
71
+
72
+
73
+ @register_tool("report")
74
+ class ReportTool(BaseTool):
75
+ """
76
+ Multi-format report generation tool supporting HTML, Excel, PowerPoint, Markdown, Word, and image-based reports.
77
+
78
+ NOTE: PDF generation is temporarily disabled due to weasyprint deployment complexity.
79
+
80
+ Operations:
81
+ - generate_html: Render HTML report using Jinja2.
82
+ - generate_pdf: Currently disabled - will be re-enabled in future release.
83
+ - generate_excel: Create Excel workbook with multiple sheets and styling.
84
+ - generate_pptx: Create PowerPoint presentation with customizable slides.
85
+ - generate_markdown: Render Markdown report using Jinja2.
86
+ - generate_word: Create Word document with customizable styles.
87
+ - generate_image: Generate charts (bar, line, pie) using Matplotlib.
88
+ - batch_generate: Generate multiple reports in parallel.
89
+
90
+ Inherits from BaseTool.
91
+ """
92
+
93
+ # Configuration schema
94
+ class Config(BaseModel):
95
+ """Configuration for the report tool"""
96
+
97
+ model_config = ConfigDict(env_prefix="REPORT_TOOL_")
98
+
99
+ templates_dir: str = Field(
100
+ default=os.getcwd(), description="Directory for Jinja2 templates"
101
+ )
102
+ default_output_dir: str = Field(
103
+ default=os.path.join(tempfile.gettempdir(), "reports"),
104
+ description="Default directory for output files",
105
+ )
106
+ allowed_extensions: List[str] = Field(
107
+ default=[
108
+ ".html",
109
+ ".pdf",
110
+ ".xlsx",
111
+ ".pptx",
112
+ ".docx",
113
+ ".md",
114
+ ".png",
115
+ ],
116
+ description="Allowed file extensions for outputs",
117
+ )
118
+ pdf_page_size: str = Field(default="A4", description="Default PDF page size")
119
+ default_font: str = Field(default="Arial", description="Default font for documents")
120
+ default_font_size: int = Field(default=12, description="Default font size in points")
121
+ allowed_html_tags: Set[str] = Field(
122
+ default={
123
+ "h1",
124
+ "h2",
125
+ "h3",
126
+ "h4",
127
+ "h5",
128
+ "h6",
129
+ "p",
130
+ "br",
131
+ "a",
132
+ "ul",
133
+ "ol",
134
+ "li",
135
+ "strong",
136
+ "em",
137
+ "b",
138
+ "i",
139
+ "table",
140
+ "tr",
141
+ "td",
142
+ "th",
143
+ "thead",
144
+ "tbody",
145
+ "span",
146
+ "div",
147
+ "img",
148
+ "hr",
149
+ "code",
150
+ "pre",
151
+ },
152
+ description="Allowed HTML tags for sanitization",
153
+ )
154
+ allowed_html_attributes: Dict[str, List[str]] = Field(
155
+ default={
156
+ "a": ["href", "title", "target"],
157
+ "img": ["src", "alt", "title", "width", "height"],
158
+ "td": ["colspan", "rowspan", "align"],
159
+ "th": ["colspan", "rowspan", "align"],
160
+ "*": ["class", "id", "style"],
161
+ },
162
+ description="Allowed HTML attributes for sanitization",
163
+ )
164
+ temp_files_max_age: int = Field(
165
+ default=3600,
166
+ description="Maximum age of temporary files in seconds",
167
+ )
168
+
169
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
170
+ """
171
+ Initialize ReportTool with settings and resources.
172
+
173
+ Args:
174
+ config (Dict, optional): Configuration overrides for ReportTool.
175
+
176
+ Raises:
177
+ ValueError: If config contains invalid settings.
178
+ """
179
+ super().__init__(config)
180
+
181
+ # Parse configuration
182
+ self.config = self.Config(**(config or {}))
183
+
184
+ self.logger = logging.getLogger(__name__)
185
+ if not self.logger.handlers:
186
+ handler = logging.StreamHandler()
187
+ handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s"))
188
+ self.logger.addHandler(handler)
189
+ self.logger.setLevel(logging.INFO)
190
+ self._jinja_env = sandbox.SandboxedEnvironment(
191
+ loader=FileSystemLoader(self.config.templates_dir), autoescape=True
192
+ )
193
+ self._temp_manager = TempFileManager(
194
+ self.config.default_output_dir, self.config.temp_files_max_age
195
+ )
196
+
197
+ def generate_html(
198
+ self,
199
+ template_path: Optional[str],
200
+ template_str: Optional[str],
201
+ context: Dict[str, Any],
202
+ output_path: str,
203
+ template_variables: Optional[Dict[str, str]] = None,
204
+ ) -> str:
205
+ """
206
+ Render an HTML report using a Jinja2 template.
207
+
208
+ Args:
209
+ template_path (Optional[str]): Path to the template file.
210
+ template_str (Optional[str]): Template string content.
211
+ context (Dict[str, Any]): Template context data.
212
+ output_path (str): Path to save the HTML file.
213
+ template_variables (Optional[Dict[str, str]]): Variables for dynamic output path.
214
+
215
+ Returns:
216
+ str: Path to the generated HTML file.
217
+
218
+ Raises:
219
+ FileOperationError: If template file is not found or writing fails.
220
+ """
221
+ try:
222
+ if template_path:
223
+ os.path.join(self.config.templates_dir, template_path)
224
+ tmpl = self._jinja_env.get_template(template_path)
225
+ else:
226
+ tmpl = self._jinja_env.from_string(template_str)
227
+ html = tmpl.render(**context)
228
+ csrf_meta = "<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'self'; script-src 'self'; object-src 'none'\">\n"
229
+ csrf_meta += '<meta name="referrer" content="no-referrer">\n'
230
+ if "<head>" in html:
231
+ html = html.replace("<head>", "<head>\n" + csrf_meta)
232
+ else:
233
+ html = csrf_meta + html
234
+ html = sanitize_html(
235
+ html,
236
+ self.config.allowed_html_tags,
237
+ self.config.allowed_html_attributes,
238
+ )
239
+ with open(output_path, "w", encoding="utf-8") as f:
240
+ f.write(html)
241
+ self._temp_manager.register_file(output_path)
242
+ return output_path
243
+ except Exception as e:
244
+ raise FileOperationError(f"Failed to generate HTML: {str(e)}")
245
+
246
+ def generate_pdf(
247
+ self,
248
+ html: Optional[str],
249
+ html_schema: Optional[Dict],
250
+ output_path: str,
251
+ page_size: Optional[str] = None,
252
+ template_variables: Optional[Dict[str, str]] = None,
253
+ ) -> str:
254
+ """
255
+ Generate a PDF report from HTML content or a Jinja2 template.
256
+
257
+ NOTE: PDF generation is currently disabled due to weasyprint deployment complexity.
258
+ This feature will be re-enabled in a future release.
259
+
260
+ Args:
261
+ html (Optional[str]): HTML content.
262
+ html_schema (Optional[Dict]): Dict for HTML generation.
263
+ output_path (str): Path to save the PDF file.
264
+ page_size (Optional[str]): PDF page size.
265
+ template_variables (Optional[Dict[str, str]]): Variables for dynamic output path.
266
+
267
+ Returns:
268
+ str: Path to the generated PDF file.
269
+
270
+ Raises:
271
+ FileOperationError: PDF generation is currently disabled.
272
+ """
273
+ raise FileOperationError(
274
+ "PDF generation is currently disabled due to weasyprint deployment complexity. "
275
+ "Please use generate_html() to create HTML reports instead. "
276
+ "PDF functionality will be restored in a future release."
277
+ )
278
+
279
+ # TODO: Re-enable when weasyprint deployment issues are resolved
280
+ # try:
281
+ # if not html and html_schema:
282
+ # html_path = self.generate_html(**html_schema)
283
+ # with open(html_path, 'r', encoding='utf-8') as f:
284
+ # html = f.read()
285
+ # HTML(string=html).write_pdf(
286
+ # output_path,
287
+ # stylesheets=[{'page_size': page_size or self.settings.pdf_page_size}]
288
+ # )
289
+ # self._temp_manager.register_file(output_path)
290
+ # return output_path
291
+ # except Exception as e:
292
+ # raise FileOperationError(f"Failed to generate PDF: {str(e)}")
293
+
294
+ def generate_excel(
295
+ self,
296
+ sheets: Dict[str, Union[pd.DataFrame, List[Dict[str, Any]]]],
297
+ output_path: str,
298
+ styles: Optional[Dict[str, Dict[str, Any]]] = None,
299
+ template_variables: Optional[Dict[str, str]] = None,
300
+ ) -> str:
301
+ """
302
+ Generate an Excel workbook with multiple sheets and optional styling.
303
+
304
+ Args:
305
+ sheets (Dict[str, Union[pd.DataFrame, List[Dict[str, Any]]]]): Sheet data.
306
+ output_path (str): Path to save the Excel file.
307
+ styles (Optional[Dict[str, Dict[str, Any]]]): Cell styling.
308
+ template_variables (Optional[Dict[str, str]]): Variables for dynamic output path.
309
+
310
+ Returns:
311
+ str: Path to the generated Excel file.
312
+
313
+ Raises:
314
+ FileOperationError: If Excel generation fails.
315
+ """
316
+ try:
317
+ writer = pd.ExcelWriter(output_path, engine="xlsxwriter")
318
+ workbook = writer.book
319
+ for name, data in sheets.items():
320
+ df = data if isinstance(data, pd.DataFrame) else pd.DataFrame(data)
321
+ df.to_excel(writer, sheet_name=name[:31], index=False)
322
+ if styles and name in styles:
323
+ worksheet = writer.sheets[name[:31]]
324
+ for cell, style in styles[name].items():
325
+ format_dict = {}
326
+ if style.get("bold"):
327
+ format_dict["bold"] = True
328
+ if style.get("font_size"):
329
+ format_dict["font_size"] = style["font_size"]
330
+ if style.get("bg_color"):
331
+ format_dict["bg_color"] = style["bg_color"]
332
+ worksheet.write(
333
+ cell,
334
+ df.loc[int(cell[1:]) - 1, cell[0]],
335
+ workbook.add_format(format_dict),
336
+ )
337
+ writer.close()
338
+ self._temp_manager.register_file(output_path)
339
+ return output_path
340
+ except Exception as e:
341
+ raise FileOperationError(f"Failed to generate Excel: {str(e)}")
342
+
343
+ def generate_pptx(
344
+ self,
345
+ slides: List[Dict],
346
+ output_path: str,
347
+ default_font: Optional[str] = None,
348
+ default_font_size: Optional[int] = None,
349
+ default_font_color: Optional[Tuple[int, int, int]] = None,
350
+ template_variables: Optional[Dict[str, str]] = None,
351
+ ) -> str:
352
+ """
353
+ Generate a PowerPoint presentation with customizable slides.
354
+
355
+ Args:
356
+ slides (List[Dict]): List of slide data.
357
+ output_path (str): Path to save the PPTX file.
358
+ default_font (Optional[str]): Default font for slides.
359
+ default_font_size (Optional[int]): Default font size.
360
+ default_font_color (Optional[Tuple[int, int, int]]): Default font color.
361
+ template_variables (Optional[Dict[str, str]]): Variables for dynamic output path.
362
+
363
+ Returns:
364
+ str: Path to the generated PPTX file.
365
+
366
+ Raises:
367
+ FileOperationError: If PPTX generation fails.
368
+ """
369
+ try:
370
+ prs = Presentation()
371
+ for slide in slides:
372
+ s = prs.slides.add_slide(prs.slide_layouts[1])
373
+ title_shape = s.shapes.title
374
+ title_shape.text = slide["title"]
375
+ font = slide.get("font") or default_font or self.config.default_font
376
+ font_size = (
377
+ slide.get("font_size") or default_font_size or self.config.default_font_size
378
+ )
379
+ slide.get("font_color") or default_font_color or (0, 0, 0)
380
+ title_shape.text_frame.paragraphs[0].font.name = font
381
+ title_shape.text_frame.paragraphs[0].font.size = Pt(font_size)
382
+ # Set font color safely - skip color setting for now to avoid library issues
383
+ # Font color setting in python-pptx can be problematic,
384
+ # focusing on core functionality
385
+ body = s.shapes.placeholders[1].text_frame
386
+ for bullet in slide["bullets"]:
387
+ p = body.add_paragraph()
388
+ p.text = bullet
389
+ p.level = 0
390
+ p.font.name = font
391
+ p.font.size = Pt(font_size)
392
+ # Skip font color setting for bullet points to avoid
393
+ # library issues
394
+ prs.save(output_path)
395
+ self._temp_manager.register_file(output_path)
396
+ return output_path
397
+ except Exception as e:
398
+ raise FileOperationError(f"Failed to generate PPTX: {str(e)}")
399
+
400
+ def generate_markdown(
401
+ self,
402
+ template_path: Optional[str],
403
+ template_str: Optional[str],
404
+ context: Dict[str, Any],
405
+ output_path: str,
406
+ template_variables: Optional[Dict[str, str]] = None,
407
+ ) -> str:
408
+ """
409
+ Render a Markdown report using a Jinja2 template.
410
+
411
+ Args:
412
+ template_path (Optional[str]): Path to the template file.
413
+ template_str (Optional[str]): Template string content.
414
+ context (Dict[str, Any]): Template context data.
415
+ output_path (str): Path to save the Markdown file.
416
+ template_variables (Optional[Dict[str, str]]): Variables for dynamic output path.
417
+
418
+ Returns:
419
+ str: Path to the generated Markdown file.
420
+
421
+ Raises:
422
+ FileOperationError: If rendering or writing fails.
423
+ """
424
+ try:
425
+ if template_path:
426
+ tmpl = self._jinja_env.get_template(template_path)
427
+ else:
428
+ tmpl = self._jinja_env.from_string(template_str)
429
+ markdown_content = tmpl.render(**context)
430
+ with open(output_path, "w", encoding="utf-8") as f:
431
+ f.write(markdown_content)
432
+ self._temp_manager.register_file(output_path)
433
+ return output_path
434
+ except Exception as e:
435
+ raise FileOperationError(f"Failed to generate Markdown: {str(e)}")
436
+
437
+ def generate_word(
438
+ self,
439
+ template_path: Optional[str],
440
+ template_str: Optional[str],
441
+ context: Dict[str, Any],
442
+ output_path: str,
443
+ font: Optional[str] = None,
444
+ font_size: Optional[int] = None,
445
+ font_color: Optional[Tuple[int, int, int]] = None,
446
+ template_variables: Optional[Dict[str, str]] = None,
447
+ ) -> str:
448
+ """
449
+ Generate a Word document from a Jinja2 template with customizable styles.
450
+
451
+ Args:
452
+ template_path (Optional[str]): Path to the template file.
453
+ template_str (Optional[str]): Template string content.
454
+ context (Dict[str, Any]): Template context data.
455
+ output_path (str): Path to save the DOCX file.
456
+ font (Optional[str]): Font for the document.
457
+ font_size (Optional[int]): Font size.
458
+ font_color (Optional[Tuple[int, int, int]]): Font color.
459
+ template_variables (Optional[Dict[str, str]]): Variables for dynamic output path.
460
+
461
+ Returns:
462
+ str: Path to the generated DOCX file.
463
+
464
+ Raises:
465
+ FileOperationError: If Word generation fails.
466
+ """
467
+ try:
468
+ if template_path:
469
+ tmpl = self._jinja_env.get_template(template_path)
470
+ else:
471
+ tmpl = self._jinja_env.from_string(template_str)
472
+ content = tmpl.render(**context)
473
+ doc = Document()
474
+ font = font or self.config.default_font
475
+ font_size = font_size or self.config.default_font_size
476
+ font_color = font_color or (0, 0, 0)
477
+ for line in content.splitlines():
478
+ p = doc.add_paragraph()
479
+ run = p.add_run(line)
480
+ run.font.name = font
481
+ run.font.size = DocxPt(font_size)
482
+ # Skip font color setting for Word documents to avoid library
483
+ # issues
484
+ doc.save(output_path)
485
+ self._temp_manager.register_file(output_path)
486
+ return output_path
487
+ except Exception as e:
488
+ raise FileOperationError(f"Failed to generate Word: {str(e)}")
489
+
490
+ def generate_image(
491
+ self,
492
+ chart_type: str,
493
+ data: Union[pd.DataFrame, List[Dict[str, Any]]],
494
+ output_path: str,
495
+ x_col: Optional[str] = None,
496
+ y_col: Optional[str] = None,
497
+ labels: Optional[List[str]] = None,
498
+ title: Optional[str] = None,
499
+ width: int = 8,
500
+ height: int = 6,
501
+ template_variables: Optional[Dict[str, str]] = None,
502
+ ) -> str:
503
+ """
504
+ Generate a chart (bar, line, pie) using Matplotlib.
505
+
506
+ Args:
507
+ chart_type (str): Type of chart ('bar', 'line', 'pie').
508
+ data (Union[pd.DataFrame, List[Dict[str, Any]]]): Chart data.
509
+ output_path (str): Path to save the image file.
510
+ x_col (Optional[str]): X-axis column name.
511
+ y_col (Optional[str]): Y-axis column name.
512
+ labels (Optional[List[str]]): Labels for pie chart.
513
+ title (Optional[str]): Chart title.
514
+ width (int): Chart width.
515
+ height (int): Chart height.
516
+ template_variables (Optional[Dict[str, str]]): Variables for dynamic output path.
517
+
518
+ Returns:
519
+ str: Path to the generated image file.
520
+
521
+ Raises:
522
+ FileOperationError: If chart generation fails.
523
+ """
524
+ try:
525
+ df = data if isinstance(data, pd.DataFrame) else pd.DataFrame(data)
526
+ plt.figure(figsize=(width, height))
527
+ if chart_type == "bar":
528
+ df.plot.bar(x=x_col, y=y_col, title=title)
529
+ elif chart_type == "line":
530
+ df.plot.line(x=x_col, y=y_col, title=title)
531
+ elif chart_type == "pie":
532
+ plt.pie(
533
+ df[y_col],
534
+ labels=df[x_col] if x_col else labels,
535
+ autopct="%1.1f%%",
536
+ )
537
+ plt.title(title)
538
+ plt.savefig(output_path)
539
+ plt.close()
540
+ self._temp_manager.register_file(output_path)
541
+ return output_path
542
+ except Exception as e:
543
+ raise FileOperationError(f"Failed to generate image: {str(e)}")
544
+
545
+ def batch_generate(
546
+ self,
547
+ operation: str,
548
+ contexts: List[Dict[str, Any]],
549
+ output_paths: List[str],
550
+ datasets: Optional[List[DatasetType]] = None,
551
+ slides: Optional[List[List[Dict]]] = None,
552
+ ) -> List[str]:
553
+ """
554
+ Generate multiple reports in parallel for different contexts or datasets.
555
+
556
+ Args:
557
+ operation (str): Operation to perform.
558
+ contexts (List[Dict[str, Any]]): Contexts for HTML, Markdown, Word, PDF.
559
+ output_paths (List[str]): Paths for generated files.
560
+ datasets (Optional[List[DatasetType]]): Datasets for Excel, Image.
561
+ slides (Optional[List[List[Dict]]]): Slides for PPTX.
562
+
563
+ Returns:
564
+ List[str]: List of generated file paths.
565
+
566
+ Raises:
567
+ FileOperationError: If batch generation fails.
568
+ """
569
+ try:
570
+ tasks = []
571
+ input_data = contexts or datasets or slides
572
+ for i, output_path in enumerate(output_paths):
573
+ op_params = {"output_path": output_path}
574
+ if operation in (
575
+ "generate_html",
576
+ "generate_markdown",
577
+ "generate_word",
578
+ ):
579
+ op_params.update(input_data[i])
580
+ op_params["template_path"] = input_data[i].get("template_path")
581
+ op_params["template_str"] = input_data[i].get("template_str")
582
+ if operation == "generate_word":
583
+ op_params["font"] = input_data[i].get("font")
584
+ op_params["font_size"] = input_data[i].get("font_size")
585
+ op_params["font_color"] = input_data[i].get("font_color")
586
+ elif operation == "generate_excel":
587
+ op_params["sheets"] = input_data[i]
588
+ op_params["styles"] = input_data[i].get("styles")
589
+ elif operation == "generate_pptx":
590
+ op_params["slides"] = input_data[i]
591
+ op_params["default_font"] = (
592
+ input_data[i][0].get("font") if input_data[i] else None
593
+ )
594
+ op_params["default_font_size"] = (
595
+ input_data[i][0].get("font_size") if input_data[i] else None
596
+ )
597
+ op_params["default_font_color"] = (
598
+ input_data[i][0].get("font_color") if input_data[i] else None
599
+ )
600
+ elif operation == "generate_image":
601
+ op_params.update(input_data[i])
602
+ elif operation == "generate_pdf":
603
+ op_params["html"] = input_data[i].get("html")
604
+ op_params["html_schema"] = (
605
+ input_data[i] if input_data[i].get("context") else None
606
+ )
607
+ op_params["page_size"] = input_data[i].get("page_size")
608
+ tasks.append({"op": operation, "kwargs": op_params})
609
+ # Execute tasks synchronously for batch generation
610
+ results = []
611
+ for task in tasks:
612
+ op_name = task["op"]
613
+ kwargs = task["kwargs"]
614
+
615
+ if op_name == "generate_html":
616
+ result = self.generate_html(**kwargs)
617
+ elif op_name == "generate_excel":
618
+ result = self.generate_excel(**kwargs)
619
+ elif op_name == "generate_pptx":
620
+ result = self.generate_pptx(**kwargs)
621
+ elif op_name == "generate_markdown":
622
+ result = self.generate_markdown(**kwargs)
623
+ elif op_name == "generate_word":
624
+ result = self.generate_word(**kwargs)
625
+ elif op_name == "generate_image":
626
+ result = self.generate_image(**kwargs)
627
+ elif op_name == "generate_pdf":
628
+ result = self.generate_pdf(**kwargs)
629
+ else:
630
+ raise FileOperationError(f"Unsupported operation: {op_name}")
631
+
632
+ results.append(result)
633
+ return results
634
+ except Exception as e:
635
+ raise FileOperationError(f"Failed to generate batch reports: {str(e)}")