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
|
@@ -7,20 +7,21 @@ in different formats. It supports template-based rendering, data visualization,
|
|
|
7
7
|
Author: Your Organization
|
|
8
8
|
Version: 1.0.0
|
|
9
9
|
"""
|
|
10
|
+
|
|
10
11
|
import os
|
|
11
12
|
import bleach
|
|
12
13
|
from typing import Dict, Any, List, Optional, Union, Tuple, Set
|
|
13
14
|
from jinja2 import FileSystemLoader, sandbox
|
|
15
|
+
|
|
14
16
|
# from weasyprint import HTML # TODO: Re-enable when deployment issues are resolved
|
|
15
|
-
import pandas as pd
|
|
17
|
+
import pandas as pd # type: ignore[import-untyped]
|
|
16
18
|
from pptx import Presentation
|
|
17
19
|
from pptx.util import Pt
|
|
18
20
|
from docx import Document
|
|
19
|
-
from docx.shared import Pt as DocxPt
|
|
20
|
-
import markdown
|
|
21
|
+
from docx.shared import Pt as DocxPt
|
|
21
22
|
import matplotlib.pyplot as plt
|
|
22
|
-
from pydantic import
|
|
23
|
-
from pydantic_settings import BaseSettings
|
|
23
|
+
from pydantic import Field, BaseModel, ConfigDict
|
|
24
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
24
25
|
import tempfile
|
|
25
26
|
import logging
|
|
26
27
|
|
|
@@ -28,57 +29,24 @@ from aiecs.tools.base_tool import BaseTool
|
|
|
28
29
|
from aiecs.tools import register_tool
|
|
29
30
|
from aiecs.tools.temp_file_manager import TempFileManager
|
|
30
31
|
|
|
31
|
-
# Configuration for ReportTool
|
|
32
|
-
class ReportSettings(BaseSettings):
|
|
33
|
-
"""
|
|
34
|
-
Configuration for ReportTool.
|
|
35
|
-
|
|
36
|
-
Attributes:
|
|
37
|
-
templates_dir (str): Directory for Jinja2 templates.
|
|
38
|
-
default_output_dir (str): Default directory for output files.
|
|
39
|
-
allowed_extensions (List[str]): Allowed file extensions for outputs.
|
|
40
|
-
pdf_page_size (str): Default PDF page size.
|
|
41
|
-
default_font (str): Default font for documents.
|
|
42
|
-
default_font_size (int): Default font size in points.
|
|
43
|
-
allowed_html_tags (Set[str]): Allowed HTML tags for sanitization.
|
|
44
|
-
allowed_html_attributes (Dict[str, List[str]]): Allowed HTML attributes for sanitization.
|
|
45
|
-
temp_files_max_age (int): Maximum age of temporary files in seconds.
|
|
46
|
-
env_prefix (str): Environment variable prefix for settings.
|
|
47
|
-
"""
|
|
48
|
-
templates_dir: str = os.getcwd()
|
|
49
|
-
default_output_dir: str = os.path.join(tempfile.gettempdir(), 'reports')
|
|
50
|
-
allowed_extensions: List[str] = ['.html', '.pdf', '.xlsx', '.pptx', '.docx', '.md', '.png']
|
|
51
|
-
pdf_page_size: str = 'A4'
|
|
52
|
-
default_font: str = 'Arial'
|
|
53
|
-
default_font_size: int = 12
|
|
54
|
-
allowed_html_tags: Set[str] = {
|
|
55
|
-
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'br', 'a', 'ul', 'ol', 'li',
|
|
56
|
-
'strong', 'em', 'b', 'i', 'table', 'tr', 'td', 'th', 'thead', 'tbody',
|
|
57
|
-
'span', 'div', 'img', 'hr', 'code', 'pre'
|
|
58
|
-
}
|
|
59
|
-
allowed_html_attributes: Dict[str, List[str]] = {
|
|
60
|
-
'a': ['href', 'title', 'target'],
|
|
61
|
-
'img': ['src', 'alt', 'title', 'width', 'height'],
|
|
62
|
-
'td': ['colspan', 'rowspan', 'align'],
|
|
63
|
-
'th': ['colspan', 'rowspan', 'align'],
|
|
64
|
-
'*': ['class', 'id', 'style']
|
|
65
|
-
}
|
|
66
|
-
temp_files_max_age: int = 3600 # 1 hour in seconds
|
|
67
|
-
env_prefix: str = 'REPORT_TOOL_'
|
|
68
|
-
|
|
69
|
-
model_config = ConfigDict(env_prefix='REPORT_TOOL_')
|
|
70
32
|
|
|
71
33
|
# Exceptions
|
|
72
34
|
class ReportToolError(Exception):
|
|
73
35
|
"""Base exception for ReportTool errors."""
|
|
74
|
-
|
|
36
|
+
|
|
75
37
|
|
|
76
38
|
class FileOperationError(ReportToolError):
|
|
77
39
|
"""Raised when file operations fail."""
|
|
78
|
-
|
|
40
|
+
|
|
79
41
|
|
|
80
42
|
# Helper function for HTML sanitization
|
|
81
|
-
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def sanitize_html(
|
|
46
|
+
html_content: str,
|
|
47
|
+
allowed_tags: Set[str],
|
|
48
|
+
allowed_attributes: Dict[str, List[str]],
|
|
49
|
+
) -> str:
|
|
82
50
|
"""
|
|
83
51
|
Sanitize HTML content to prevent XSS attacks.
|
|
84
52
|
|
|
@@ -94,13 +62,17 @@ def sanitize_html(html_content: str, allowed_tags: Set[str], allowed_attributes:
|
|
|
94
62
|
html_content,
|
|
95
63
|
tags=allowed_tags,
|
|
96
64
|
attributes=allowed_attributes,
|
|
97
|
-
strip=True
|
|
65
|
+
strip=True,
|
|
98
66
|
)
|
|
99
67
|
|
|
68
|
+
|
|
100
69
|
# Type alias for dataset entries
|
|
101
|
-
|
|
70
|
+
# Note: Using Any instead of pd.DataFrame to avoid Pydantic schema generation errors
|
|
71
|
+
# The actual runtime type can be pd.DataFrame or List[Dict[str, Any]]
|
|
72
|
+
DatasetType = Dict[str, Union[Any, List[Dict[str, Any]]]]
|
|
73
|
+
|
|
102
74
|
|
|
103
|
-
@register_tool(
|
|
75
|
+
@register_tool("report")
|
|
104
76
|
class ReportTool(BaseTool):
|
|
105
77
|
"""
|
|
106
78
|
Multi-format report generation tool supporting HTML, Excel, PowerPoint, Markdown, Word, and image-based reports.
|
|
@@ -119,36 +91,212 @@ class ReportTool(BaseTool):
|
|
|
119
91
|
|
|
120
92
|
Inherits from BaseTool.
|
|
121
93
|
"""
|
|
122
|
-
|
|
94
|
+
|
|
95
|
+
# Configuration schema
|
|
96
|
+
class Config(BaseSettings):
|
|
97
|
+
"""Configuration for the report tool
|
|
98
|
+
|
|
99
|
+
Automatically reads from environment variables with REPORT_TOOL_ prefix.
|
|
100
|
+
Example: REPORT_TOOL_TEMPLATES_DIR -> templates_dir
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
model_config = SettingsConfigDict(env_prefix="REPORT_TOOL_")
|
|
104
|
+
|
|
105
|
+
templates_dir: str = Field(default=os.getcwd(), description="Directory for Jinja2 templates")
|
|
106
|
+
default_output_dir: str = Field(
|
|
107
|
+
default=os.path.join(tempfile.gettempdir(), "reports"),
|
|
108
|
+
description="Default directory for output files",
|
|
109
|
+
)
|
|
110
|
+
allowed_extensions: List[str] = Field(
|
|
111
|
+
default=[
|
|
112
|
+
".html",
|
|
113
|
+
".pdf",
|
|
114
|
+
".xlsx",
|
|
115
|
+
".pptx",
|
|
116
|
+
".docx",
|
|
117
|
+
".md",
|
|
118
|
+
".png",
|
|
119
|
+
],
|
|
120
|
+
description="Allowed file extensions for outputs",
|
|
121
|
+
)
|
|
122
|
+
pdf_page_size: str = Field(default="A4", description="Default PDF page size")
|
|
123
|
+
default_font: str = Field(default="Arial", description="Default font for documents")
|
|
124
|
+
default_font_size: int = Field(default=12, description="Default font size in points")
|
|
125
|
+
allowed_html_tags: Set[str] = Field(
|
|
126
|
+
default={
|
|
127
|
+
"h1",
|
|
128
|
+
"h2",
|
|
129
|
+
"h3",
|
|
130
|
+
"h4",
|
|
131
|
+
"h5",
|
|
132
|
+
"h6",
|
|
133
|
+
"p",
|
|
134
|
+
"br",
|
|
135
|
+
"a",
|
|
136
|
+
"ul",
|
|
137
|
+
"ol",
|
|
138
|
+
"li",
|
|
139
|
+
"strong",
|
|
140
|
+
"em",
|
|
141
|
+
"b",
|
|
142
|
+
"i",
|
|
143
|
+
"table",
|
|
144
|
+
"tr",
|
|
145
|
+
"td",
|
|
146
|
+
"th",
|
|
147
|
+
"thead",
|
|
148
|
+
"tbody",
|
|
149
|
+
"span",
|
|
150
|
+
"div",
|
|
151
|
+
"img",
|
|
152
|
+
"hr",
|
|
153
|
+
"code",
|
|
154
|
+
"pre",
|
|
155
|
+
},
|
|
156
|
+
description="Allowed HTML tags for sanitization",
|
|
157
|
+
)
|
|
158
|
+
allowed_html_attributes: Dict[str, List[str]] = Field(
|
|
159
|
+
default={
|
|
160
|
+
"a": ["href", "title", "target"],
|
|
161
|
+
"img": ["src", "alt", "title", "width", "height"],
|
|
162
|
+
"td": ["colspan", "rowspan", "align"],
|
|
163
|
+
"th": ["colspan", "rowspan", "align"],
|
|
164
|
+
"*": ["class", "id", "style"],
|
|
165
|
+
},
|
|
166
|
+
description="Allowed HTML attributes for sanitization",
|
|
167
|
+
)
|
|
168
|
+
temp_files_max_age: int = Field(
|
|
169
|
+
default=3600,
|
|
170
|
+
description="Maximum age of temporary files in seconds",
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# Schema definitions
|
|
174
|
+
class Generate_htmlSchema(BaseModel):
|
|
175
|
+
"""Schema for generate_html operation"""
|
|
176
|
+
|
|
177
|
+
template_path: Optional[str] = Field(default=None, description="Optional path to the Jinja2 template file. Either template_path or template_str must be provided")
|
|
178
|
+
template_str: Optional[str] = Field(default=None, description="Optional template string content. Either template_path or template_str must be provided")
|
|
179
|
+
context: Dict[str, Any] = Field(description="Dictionary of template context data to render the template with")
|
|
180
|
+
output_path: str = Field(description="Path where the generated HTML file will be saved")
|
|
181
|
+
template_variables: Optional[Dict[str, str]] = Field(default=None, description="Optional dictionary of variables for dynamic output path generation")
|
|
182
|
+
|
|
183
|
+
class Generate_pdfSchema(BaseModel):
|
|
184
|
+
"""Schema for generate_pdf operation (currently disabled)"""
|
|
185
|
+
|
|
186
|
+
html: Optional[str] = Field(default=None, description="Optional HTML content string. Either html or html_schema must be provided")
|
|
187
|
+
html_schema: Optional[Dict[str, Any]] = Field(default=None, description="Optional dictionary for HTML generation. Either html or html_schema must be provided")
|
|
188
|
+
output_path: str = Field(description="Path where the generated PDF file will be saved")
|
|
189
|
+
page_size: Optional[str] = Field(default=None, description="Optional PDF page size (e.g., 'A4', 'Letter'). Uses default if not specified")
|
|
190
|
+
template_variables: Optional[Dict[str, str]] = Field(default=None, description="Optional dictionary of variables for dynamic output path generation")
|
|
191
|
+
|
|
192
|
+
class Generate_excelSchema(BaseModel):
|
|
193
|
+
"""Schema for generate_excel operation"""
|
|
194
|
+
|
|
195
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
196
|
+
|
|
197
|
+
sheets: Dict[str, Union[Any, List[Dict[str, Any]]]] = Field(description="Dictionary mapping sheet names to sheet data. Data can be a pandas DataFrame or list of dictionaries")
|
|
198
|
+
output_path: str = Field(description="Path where the generated Excel file will be saved")
|
|
199
|
+
styles: Optional[Dict[str, Dict[str, Any]]] = Field(default=None, description="Optional dictionary mapping sheet names to cell styling dictionaries. Each style dict maps cell addresses (e.g., 'A1') to style properties")
|
|
200
|
+
template_variables: Optional[Dict[str, str]] = Field(default=None, description="Optional dictionary of variables for dynamic output path generation")
|
|
201
|
+
|
|
202
|
+
class Generate_pptxSchema(BaseModel):
|
|
203
|
+
"""Schema for generate_pptx operation"""
|
|
204
|
+
|
|
205
|
+
slides: List[Dict[str, Any]] = Field(description="List of slide dictionaries. Each slide should have 'title' (str) and 'bullets' (list of strings). Optional: 'font', 'font_size', 'font_color'")
|
|
206
|
+
output_path: str = Field(description="Path where the generated PowerPoint file will be saved")
|
|
207
|
+
default_font: Optional[str] = Field(default=None, description="Optional default font name for all slides. Uses tool default if not specified")
|
|
208
|
+
default_font_size: Optional[int] = Field(default=None, description="Optional default font size in points for all slides. Uses tool default if not specified")
|
|
209
|
+
default_font_color: Optional[Tuple[int, int, int]] = Field(default=None, description="Optional default font color as RGB tuple (r, g, b) for all slides")
|
|
210
|
+
template_variables: Optional[Dict[str, str]] = Field(default=None, description="Optional dictionary of variables for dynamic output path generation")
|
|
211
|
+
|
|
212
|
+
class Generate_markdownSchema(BaseModel):
|
|
213
|
+
"""Schema for generate_markdown operation"""
|
|
214
|
+
|
|
215
|
+
template_path: Optional[str] = Field(default=None, description="Optional path to the Jinja2 template file. Either template_path or template_str must be provided")
|
|
216
|
+
template_str: Optional[str] = Field(default=None, description="Optional template string content. Either template_path or template_str must be provided")
|
|
217
|
+
context: Dict[str, Any] = Field(description="Dictionary of template context data to render the template with")
|
|
218
|
+
output_path: str = Field(description="Path where the generated Markdown file will be saved")
|
|
219
|
+
template_variables: Optional[Dict[str, str]] = Field(default=None, description="Optional dictionary of variables for dynamic output path generation")
|
|
220
|
+
|
|
221
|
+
class Generate_wordSchema(BaseModel):
|
|
222
|
+
"""Schema for generate_word operation"""
|
|
223
|
+
|
|
224
|
+
template_path: Optional[str] = Field(default=None, description="Optional path to the Jinja2 template file. Either template_path or template_str must be provided")
|
|
225
|
+
template_str: Optional[str] = Field(default=None, description="Optional template string content. Either template_path or template_str must be provided")
|
|
226
|
+
context: Dict[str, Any] = Field(description="Dictionary of template context data to render the template with")
|
|
227
|
+
output_path: str = Field(description="Path where the generated Word document will be saved")
|
|
228
|
+
font: Optional[str] = Field(default=None, description="Optional font name for the document. Uses tool default if not specified")
|
|
229
|
+
font_size: Optional[int] = Field(default=None, description="Optional font size in points. Uses tool default if not specified")
|
|
230
|
+
font_color: Optional[Tuple[int, int, int]] = Field(default=None, description="Optional font color as RGB tuple (r, g, b)")
|
|
231
|
+
template_variables: Optional[Dict[str, str]] = Field(default=None, description="Optional dictionary of variables for dynamic output path generation")
|
|
232
|
+
|
|
233
|
+
class Generate_imageSchema(BaseModel):
|
|
234
|
+
"""Schema for generate_image operation"""
|
|
235
|
+
|
|
236
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
237
|
+
|
|
238
|
+
chart_type: str = Field(description="Type of chart to generate: 'bar', 'line', or 'pie'")
|
|
239
|
+
data: Union[Any, List[Dict[str, Any]]] = Field(description="Chart data as pandas DataFrame or list of dictionaries")
|
|
240
|
+
output_path: str = Field(description="Path where the generated image file will be saved")
|
|
241
|
+
x_col: Optional[str] = Field(default=None, description="Optional X-axis column name for bar and line charts. For pie charts, used as labels if labels not provided")
|
|
242
|
+
y_col: Optional[str] = Field(default=None, description="Optional Y-axis column name for bar, line, and pie charts")
|
|
243
|
+
labels: Optional[List[str]] = Field(default=None, description="Optional list of labels for pie chart. If not provided and x_col is specified, uses x_col values")
|
|
244
|
+
title: Optional[str] = Field(default=None, description="Optional chart title")
|
|
245
|
+
width: int = Field(default=8, description="Chart width in inches")
|
|
246
|
+
height: int = Field(default=6, description="Chart height in inches")
|
|
247
|
+
template_variables: Optional[Dict[str, str]] = Field(default=None, description="Optional dictionary of variables for dynamic output path generation")
|
|
248
|
+
|
|
249
|
+
class Batch_generateSchema(BaseModel):
|
|
250
|
+
"""Schema for batch_generate operation"""
|
|
251
|
+
|
|
252
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
253
|
+
|
|
254
|
+
operation: str = Field(description="Operation to perform: 'generate_html', 'generate_excel', 'generate_pptx', 'generate_markdown', 'generate_word', 'generate_image', or 'generate_pdf'")
|
|
255
|
+
contexts: List[Dict[str, Any]] = Field(default=[], description="List of context dictionaries for HTML, Markdown, Word, or PDF operations. Each dict should match the corresponding operation's parameters")
|
|
256
|
+
output_paths: List[str] = Field(description="List of output file paths, one for each report to generate")
|
|
257
|
+
datasets: Optional[List[DatasetType]] = Field(default=None, description="Optional list of dataset dictionaries for Excel or Image operations. Each dict should match the corresponding operation's parameters")
|
|
258
|
+
slides: Optional[List[List[Dict[str, Any]]]] = Field(default=None, description="Optional list of slide lists for PPTX operations. Each inner list contains slide dictionaries")
|
|
259
|
+
|
|
260
|
+
def __init__(self, config: Optional[Dict[str, Any]] = None, **kwargs):
|
|
123
261
|
"""
|
|
124
262
|
Initialize ReportTool with settings and resources.
|
|
125
263
|
|
|
126
264
|
Args:
|
|
127
|
-
config (Dict, optional): Configuration overrides for
|
|
265
|
+
config (Dict, optional): Configuration overrides for ReportTool.
|
|
266
|
+
**kwargs: Additional arguments passed to BaseTool (e.g., tool_name)
|
|
128
267
|
|
|
129
268
|
Raises:
|
|
130
269
|
ValueError: If config contains invalid settings.
|
|
270
|
+
|
|
271
|
+
Configuration is automatically loaded by BaseTool from:
|
|
272
|
+
1. Explicit config dict (highest priority)
|
|
273
|
+
2. YAML config files (config/tools/report.yaml)
|
|
274
|
+
3. Environment variables (via dotenv from .env files)
|
|
275
|
+
4. Tool defaults (lowest priority)
|
|
131
276
|
"""
|
|
132
|
-
super().__init__(config)
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
raise ValueError(f"Invalid configuration: {e}")
|
|
277
|
+
super().__init__(config, **kwargs)
|
|
278
|
+
|
|
279
|
+
# Configuration is automatically loaded by BaseTool into self._config_obj
|
|
280
|
+
# Access config via self._config_obj (BaseSettings instance)
|
|
281
|
+
self.config = self._config_obj if self._config_obj else self.Config()
|
|
282
|
+
|
|
139
283
|
self.logger = logging.getLogger(__name__)
|
|
140
284
|
if not self.logger.handlers:
|
|
141
285
|
handler = logging.StreamHandler()
|
|
142
|
-
handler.setFormatter(logging.Formatter(
|
|
286
|
+
handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s"))
|
|
143
287
|
self.logger.addHandler(handler)
|
|
144
288
|
self.logger.setLevel(logging.INFO)
|
|
145
|
-
self._jinja_env = sandbox.SandboxedEnvironment(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
self
|
|
150
|
-
|
|
151
|
-
|
|
289
|
+
self._jinja_env = sandbox.SandboxedEnvironment(loader=FileSystemLoader(self.config.templates_dir), autoescape=True)
|
|
290
|
+
self._temp_manager = TempFileManager(self.config.default_output_dir, self.config.temp_files_max_age)
|
|
291
|
+
|
|
292
|
+
def generate_html(
|
|
293
|
+
self,
|
|
294
|
+
template_path: Optional[str],
|
|
295
|
+
template_str: Optional[str],
|
|
296
|
+
context: Dict[str, Any],
|
|
297
|
+
output_path: str,
|
|
298
|
+
template_variables: Optional[Dict[str, str]] = None,
|
|
299
|
+
) -> str:
|
|
152
300
|
"""
|
|
153
301
|
Render an HTML report using a Jinja2 template.
|
|
154
302
|
|
|
@@ -167,26 +315,39 @@ class ReportTool(BaseTool):
|
|
|
167
315
|
"""
|
|
168
316
|
try:
|
|
169
317
|
if template_path:
|
|
170
|
-
|
|
318
|
+
os.path.join(self.config.templates_dir, template_path)
|
|
171
319
|
tmpl = self._jinja_env.get_template(template_path)
|
|
172
320
|
else:
|
|
321
|
+
if template_str is None:
|
|
322
|
+
raise FileOperationError("Either template_path or template_str must be provided")
|
|
173
323
|
tmpl = self._jinja_env.from_string(template_str)
|
|
174
324
|
html = tmpl.render(**context)
|
|
175
|
-
csrf_meta =
|
|
325
|
+
csrf_meta = "<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'self'; script-src 'self'; object-src 'none'\">\n"
|
|
176
326
|
csrf_meta += '<meta name="referrer" content="no-referrer">\n'
|
|
177
|
-
if
|
|
178
|
-
html = html.replace(
|
|
327
|
+
if "<head>" in html:
|
|
328
|
+
html = html.replace("<head>", "<head>\n" + csrf_meta)
|
|
179
329
|
else:
|
|
180
330
|
html = csrf_meta + html
|
|
181
|
-
html = sanitize_html(
|
|
182
|
-
|
|
331
|
+
html = sanitize_html(
|
|
332
|
+
html,
|
|
333
|
+
self.config.allowed_html_tags,
|
|
334
|
+
self.config.allowed_html_attributes,
|
|
335
|
+
)
|
|
336
|
+
with open(output_path, "w", encoding="utf-8") as f:
|
|
183
337
|
f.write(html)
|
|
184
338
|
self._temp_manager.register_file(output_path)
|
|
185
339
|
return output_path
|
|
186
340
|
except Exception as e:
|
|
187
341
|
raise FileOperationError(f"Failed to generate HTML: {str(e)}")
|
|
188
342
|
|
|
189
|
-
def generate_pdf(
|
|
343
|
+
def generate_pdf(
|
|
344
|
+
self,
|
|
345
|
+
html: Optional[str],
|
|
346
|
+
html_schema: Optional[Dict],
|
|
347
|
+
output_path: str,
|
|
348
|
+
page_size: Optional[str] = None,
|
|
349
|
+
template_variables: Optional[Dict[str, str]] = None,
|
|
350
|
+
) -> str:
|
|
190
351
|
"""
|
|
191
352
|
Generate a PDF report from HTML content or a Jinja2 template.
|
|
192
353
|
|
|
@@ -227,7 +388,13 @@ class ReportTool(BaseTool):
|
|
|
227
388
|
# except Exception as e:
|
|
228
389
|
# raise FileOperationError(f"Failed to generate PDF: {str(e)}")
|
|
229
390
|
|
|
230
|
-
def generate_excel(
|
|
391
|
+
def generate_excel(
|
|
392
|
+
self,
|
|
393
|
+
sheets: Dict[str, Union[pd.DataFrame, List[Dict[str, Any]]]],
|
|
394
|
+
output_path: str,
|
|
395
|
+
styles: Optional[Dict[str, Dict[str, Any]]] = None,
|
|
396
|
+
template_variables: Optional[Dict[str, str]] = None,
|
|
397
|
+
) -> str:
|
|
231
398
|
"""
|
|
232
399
|
Generate an Excel workbook with multiple sheets and optional styling.
|
|
233
400
|
|
|
@@ -244,7 +411,7 @@ class ReportTool(BaseTool):
|
|
|
244
411
|
FileOperationError: If Excel generation fails.
|
|
245
412
|
"""
|
|
246
413
|
try:
|
|
247
|
-
writer = pd.ExcelWriter(output_path, engine=
|
|
414
|
+
writer = pd.ExcelWriter(output_path, engine="xlsxwriter")
|
|
248
415
|
workbook = writer.book
|
|
249
416
|
for name, data in sheets.items():
|
|
250
417
|
df = data if isinstance(data, pd.DataFrame) else pd.DataFrame(data)
|
|
@@ -253,20 +420,32 @@ class ReportTool(BaseTool):
|
|
|
253
420
|
worksheet = writer.sheets[name[:31]]
|
|
254
421
|
for cell, style in styles[name].items():
|
|
255
422
|
format_dict = {}
|
|
256
|
-
if style.get(
|
|
257
|
-
format_dict[
|
|
258
|
-
if style.get(
|
|
259
|
-
format_dict[
|
|
260
|
-
if style.get(
|
|
261
|
-
format_dict[
|
|
262
|
-
worksheet.write(
|
|
423
|
+
if style.get("bold"):
|
|
424
|
+
format_dict["bold"] = True
|
|
425
|
+
if style.get("font_size"):
|
|
426
|
+
format_dict["font_size"] = style["font_size"]
|
|
427
|
+
if style.get("bg_color"):
|
|
428
|
+
format_dict["bg_color"] = style["bg_color"]
|
|
429
|
+
worksheet.write(
|
|
430
|
+
cell,
|
|
431
|
+
df.loc[int(cell[1:]) - 1, cell[0]],
|
|
432
|
+
workbook.add_format(format_dict),
|
|
433
|
+
)
|
|
263
434
|
writer.close()
|
|
264
435
|
self._temp_manager.register_file(output_path)
|
|
265
436
|
return output_path
|
|
266
437
|
except Exception as e:
|
|
267
438
|
raise FileOperationError(f"Failed to generate Excel: {str(e)}")
|
|
268
439
|
|
|
269
|
-
def generate_pptx(
|
|
440
|
+
def generate_pptx(
|
|
441
|
+
self,
|
|
442
|
+
slides: List[Dict],
|
|
443
|
+
output_path: str,
|
|
444
|
+
default_font: Optional[str] = None,
|
|
445
|
+
default_font_size: Optional[int] = None,
|
|
446
|
+
default_font_color: Optional[Tuple[int, int, int]] = None,
|
|
447
|
+
template_variables: Optional[Dict[str, str]] = None,
|
|
448
|
+
) -> str:
|
|
270
449
|
"""
|
|
271
450
|
Generate a PowerPoint presentation with customizable slides.
|
|
272
451
|
|
|
@@ -289,31 +468,38 @@ class ReportTool(BaseTool):
|
|
|
289
468
|
for slide in slides:
|
|
290
469
|
s = prs.slides.add_slide(prs.slide_layouts[1])
|
|
291
470
|
title_shape = s.shapes.title
|
|
292
|
-
title_shape.text = slide[
|
|
293
|
-
font = slide.get(
|
|
294
|
-
font_size = slide.get(
|
|
295
|
-
|
|
471
|
+
title_shape.text = slide["title"]
|
|
472
|
+
font = slide.get("font") or default_font or self.config.default_font
|
|
473
|
+
font_size = slide.get("font_size") or default_font_size or self.config.default_font_size
|
|
474
|
+
slide.get("font_color") or default_font_color or (0, 0, 0)
|
|
296
475
|
title_shape.text_frame.paragraphs[0].font.name = font
|
|
297
476
|
title_shape.text_frame.paragraphs[0].font.size = Pt(font_size)
|
|
298
477
|
# Set font color safely - skip color setting for now to avoid library issues
|
|
299
|
-
# Font color setting in python-pptx can be problematic,
|
|
300
|
-
|
|
478
|
+
# Font color setting in python-pptx can be problematic,
|
|
479
|
+
# focusing on core functionality
|
|
301
480
|
body = s.shapes.placeholders[1].text_frame
|
|
302
|
-
for bullet in slide[
|
|
481
|
+
for bullet in slide["bullets"]:
|
|
303
482
|
p = body.add_paragraph()
|
|
304
483
|
p.text = bullet
|
|
305
484
|
p.level = 0
|
|
306
485
|
p.font.name = font
|
|
307
486
|
p.font.size = Pt(font_size)
|
|
308
|
-
# Skip font color setting for bullet points to avoid
|
|
309
|
-
|
|
487
|
+
# Skip font color setting for bullet points to avoid
|
|
488
|
+
# library issues
|
|
310
489
|
prs.save(output_path)
|
|
311
490
|
self._temp_manager.register_file(output_path)
|
|
312
491
|
return output_path
|
|
313
492
|
except Exception as e:
|
|
314
493
|
raise FileOperationError(f"Failed to generate PPTX: {str(e)}")
|
|
315
494
|
|
|
316
|
-
def generate_markdown(
|
|
495
|
+
def generate_markdown(
|
|
496
|
+
self,
|
|
497
|
+
template_path: Optional[str],
|
|
498
|
+
template_str: Optional[str],
|
|
499
|
+
context: Dict[str, Any],
|
|
500
|
+
output_path: str,
|
|
501
|
+
template_variables: Optional[Dict[str, str]] = None,
|
|
502
|
+
) -> str:
|
|
317
503
|
"""
|
|
318
504
|
Render a Markdown report using a Jinja2 template.
|
|
319
505
|
|
|
@@ -334,16 +520,28 @@ class ReportTool(BaseTool):
|
|
|
334
520
|
if template_path:
|
|
335
521
|
tmpl = self._jinja_env.get_template(template_path)
|
|
336
522
|
else:
|
|
523
|
+
if template_str is None:
|
|
524
|
+
raise FileOperationError("Either template_path or template_str must be provided")
|
|
337
525
|
tmpl = self._jinja_env.from_string(template_str)
|
|
338
526
|
markdown_content = tmpl.render(**context)
|
|
339
|
-
with open(output_path,
|
|
527
|
+
with open(output_path, "w", encoding="utf-8") as f:
|
|
340
528
|
f.write(markdown_content)
|
|
341
529
|
self._temp_manager.register_file(output_path)
|
|
342
530
|
return output_path
|
|
343
531
|
except Exception as e:
|
|
344
532
|
raise FileOperationError(f"Failed to generate Markdown: {str(e)}")
|
|
345
533
|
|
|
346
|
-
def generate_word(
|
|
534
|
+
def generate_word(
|
|
535
|
+
self,
|
|
536
|
+
template_path: Optional[str],
|
|
537
|
+
template_str: Optional[str],
|
|
538
|
+
context: Dict[str, Any],
|
|
539
|
+
output_path: str,
|
|
540
|
+
font: Optional[str] = None,
|
|
541
|
+
font_size: Optional[int] = None,
|
|
542
|
+
font_color: Optional[Tuple[int, int, int]] = None,
|
|
543
|
+
template_variables: Optional[Dict[str, str]] = None,
|
|
544
|
+
) -> str:
|
|
347
545
|
"""
|
|
348
546
|
Generate a Word document from a Jinja2 template with customizable styles.
|
|
349
547
|
|
|
@@ -367,26 +565,40 @@ class ReportTool(BaseTool):
|
|
|
367
565
|
if template_path:
|
|
368
566
|
tmpl = self._jinja_env.get_template(template_path)
|
|
369
567
|
else:
|
|
568
|
+
if template_str is None:
|
|
569
|
+
raise FileOperationError("Either template_path or template_str must be provided")
|
|
370
570
|
tmpl = self._jinja_env.from_string(template_str)
|
|
371
571
|
content = tmpl.render(**context)
|
|
372
572
|
doc = Document()
|
|
373
|
-
font = font or self.
|
|
374
|
-
font_size = font_size or self.
|
|
573
|
+
font = font or self.config.default_font
|
|
574
|
+
font_size = font_size or self.config.default_font_size
|
|
375
575
|
font_color = font_color or (0, 0, 0)
|
|
376
576
|
for line in content.splitlines():
|
|
377
577
|
p = doc.add_paragraph()
|
|
378
578
|
run = p.add_run(line)
|
|
379
579
|
run.font.name = font
|
|
380
580
|
run.font.size = DocxPt(font_size)
|
|
381
|
-
# Skip font color setting for Word documents to avoid library
|
|
382
|
-
|
|
581
|
+
# Skip font color setting for Word documents to avoid library
|
|
582
|
+
# issues
|
|
383
583
|
doc.save(output_path)
|
|
384
584
|
self._temp_manager.register_file(output_path)
|
|
385
585
|
return output_path
|
|
386
586
|
except Exception as e:
|
|
387
587
|
raise FileOperationError(f"Failed to generate Word: {str(e)}")
|
|
388
588
|
|
|
389
|
-
def generate_image(
|
|
589
|
+
def generate_image(
|
|
590
|
+
self,
|
|
591
|
+
chart_type: str,
|
|
592
|
+
data: Union[pd.DataFrame, List[Dict[str, Any]]],
|
|
593
|
+
output_path: str,
|
|
594
|
+
x_col: Optional[str] = None,
|
|
595
|
+
y_col: Optional[str] = None,
|
|
596
|
+
labels: Optional[List[str]] = None,
|
|
597
|
+
title: Optional[str] = None,
|
|
598
|
+
width: int = 8,
|
|
599
|
+
height: int = 6,
|
|
600
|
+
template_variables: Optional[Dict[str, str]] = None,
|
|
601
|
+
) -> str:
|
|
390
602
|
"""
|
|
391
603
|
Generate a chart (bar, line, pie) using Matplotlib.
|
|
392
604
|
|
|
@@ -411,13 +623,18 @@ class ReportTool(BaseTool):
|
|
|
411
623
|
try:
|
|
412
624
|
df = data if isinstance(data, pd.DataFrame) else pd.DataFrame(data)
|
|
413
625
|
plt.figure(figsize=(width, height))
|
|
414
|
-
if chart_type ==
|
|
626
|
+
if chart_type == "bar":
|
|
415
627
|
df.plot.bar(x=x_col, y=y_col, title=title)
|
|
416
|
-
elif chart_type ==
|
|
628
|
+
elif chart_type == "line":
|
|
417
629
|
df.plot.line(x=x_col, y=y_col, title=title)
|
|
418
|
-
elif chart_type ==
|
|
419
|
-
plt.pie(
|
|
420
|
-
|
|
630
|
+
elif chart_type == "pie":
|
|
631
|
+
plt.pie(
|
|
632
|
+
df[y_col],
|
|
633
|
+
labels=df[x_col] if x_col else labels,
|
|
634
|
+
autopct="%1.1f%%",
|
|
635
|
+
)
|
|
636
|
+
if title:
|
|
637
|
+
plt.title(title)
|
|
421
638
|
plt.savefig(output_path)
|
|
422
639
|
plt.close()
|
|
423
640
|
self._temp_manager.register_file(output_path)
|
|
@@ -425,7 +642,14 @@ class ReportTool(BaseTool):
|
|
|
425
642
|
except Exception as e:
|
|
426
643
|
raise FileOperationError(f"Failed to generate image: {str(e)}")
|
|
427
644
|
|
|
428
|
-
def batch_generate(
|
|
645
|
+
def batch_generate(
|
|
646
|
+
self,
|
|
647
|
+
operation: str,
|
|
648
|
+
contexts: List[Dict[str, Any]],
|
|
649
|
+
output_paths: List[str],
|
|
650
|
+
datasets: Optional[List[DatasetType]] = None,
|
|
651
|
+
slides: Optional[List[List[Dict]]] = None,
|
|
652
|
+
) -> List[str]:
|
|
429
653
|
"""
|
|
430
654
|
Generate multiple reports in parallel for different contexts or datasets.
|
|
431
655
|
|
|
@@ -445,54 +669,78 @@ class ReportTool(BaseTool):
|
|
|
445
669
|
try:
|
|
446
670
|
tasks = []
|
|
447
671
|
input_data = contexts or datasets or slides
|
|
672
|
+
if input_data is None:
|
|
673
|
+
raise ValueError("At least one of contexts, datasets, or slides must be provided")
|
|
674
|
+
if not isinstance(input_data, list):
|
|
675
|
+
raise ValueError("input_data must be a list")
|
|
448
676
|
for i, output_path in enumerate(output_paths):
|
|
449
|
-
op_params = {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
elif operation ==
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
677
|
+
op_params: Dict[str, Any] = {"output_path": output_path}
|
|
678
|
+
|
|
679
|
+
# Type narrowing: ensure input_data[i] is a dict for operations that need it
|
|
680
|
+
if operation in (
|
|
681
|
+
"generate_html",
|
|
682
|
+
"generate_markdown",
|
|
683
|
+
"generate_word",
|
|
684
|
+
):
|
|
685
|
+
item = input_data[i]
|
|
686
|
+
if isinstance(item, dict):
|
|
687
|
+
op_params.update(item)
|
|
688
|
+
op_params["template_path"] = item.get("template_path")
|
|
689
|
+
op_params["template_str"] = item.get("template_str")
|
|
690
|
+
if operation == "generate_word":
|
|
691
|
+
op_params["font"] = item.get("font")
|
|
692
|
+
op_params["font_size"] = item.get("font_size")
|
|
693
|
+
op_params["font_color"] = item.get("font_color")
|
|
694
|
+
elif operation == "generate_excel":
|
|
695
|
+
item = input_data[i]
|
|
696
|
+
op_params["sheets"] = item
|
|
697
|
+
if isinstance(item, dict):
|
|
698
|
+
op_params["styles"] = item.get("styles")
|
|
699
|
+
elif operation == "generate_pptx":
|
|
700
|
+
item = input_data[i]
|
|
701
|
+
op_params["slides"] = item
|
|
702
|
+
if isinstance(item, list) and len(item) > 0 and isinstance(item[0], dict):
|
|
703
|
+
op_params["default_font"] = item[0].get("font")
|
|
704
|
+
op_params["default_font_size"] = item[0].get("font_size")
|
|
705
|
+
op_params["default_font_color"] = item[0].get("font_color")
|
|
706
|
+
else:
|
|
707
|
+
op_params["default_font"] = None
|
|
708
|
+
op_params["default_font_size"] = None
|
|
709
|
+
op_params["default_font_color"] = None
|
|
710
|
+
elif operation == "generate_image":
|
|
711
|
+
item = input_data[i]
|
|
712
|
+
if isinstance(item, dict):
|
|
713
|
+
op_params.update(item)
|
|
714
|
+
elif operation == "generate_pdf":
|
|
715
|
+
item = input_data[i]
|
|
716
|
+
if isinstance(item, dict):
|
|
717
|
+
op_params["html"] = item.get("html")
|
|
718
|
+
op_params["html_schema"] = item if item.get("context") else None
|
|
719
|
+
op_params["page_size"] = item.get("page_size")
|
|
720
|
+
tasks.append({"op": operation, "kwargs": op_params})
|
|
473
721
|
# Execute tasks synchronously for batch generation
|
|
474
722
|
results = []
|
|
475
723
|
for task in tasks:
|
|
476
|
-
op_name = task[
|
|
477
|
-
kwargs = task[
|
|
478
|
-
|
|
479
|
-
if op_name ==
|
|
724
|
+
op_name = task["op"]
|
|
725
|
+
kwargs: Dict[str, Any] = task["kwargs"] # type: ignore[assignment]
|
|
726
|
+
|
|
727
|
+
if op_name == "generate_html":
|
|
480
728
|
result = self.generate_html(**kwargs)
|
|
481
|
-
elif op_name ==
|
|
729
|
+
elif op_name == "generate_excel":
|
|
482
730
|
result = self.generate_excel(**kwargs)
|
|
483
|
-
elif op_name ==
|
|
731
|
+
elif op_name == "generate_pptx":
|
|
484
732
|
result = self.generate_pptx(**kwargs)
|
|
485
|
-
elif op_name ==
|
|
733
|
+
elif op_name == "generate_markdown":
|
|
486
734
|
result = self.generate_markdown(**kwargs)
|
|
487
|
-
elif op_name ==
|
|
735
|
+
elif op_name == "generate_word":
|
|
488
736
|
result = self.generate_word(**kwargs)
|
|
489
|
-
elif op_name ==
|
|
737
|
+
elif op_name == "generate_image":
|
|
490
738
|
result = self.generate_image(**kwargs)
|
|
491
|
-
elif op_name ==
|
|
739
|
+
elif op_name == "generate_pdf":
|
|
492
740
|
result = self.generate_pdf(**kwargs)
|
|
493
741
|
else:
|
|
494
742
|
raise FileOperationError(f"Unsupported operation: {op_name}")
|
|
495
|
-
|
|
743
|
+
|
|
496
744
|
results.append(result)
|
|
497
745
|
return results
|
|
498
746
|
except Exception as e:
|