aiecs 1.0.1__py3-none-any.whl → 1.7.6__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.

Files changed (340) hide show
  1. aiecs/__init__.py +13 -16
  2. aiecs/__main__.py +7 -7
  3. aiecs/aiecs_client.py +269 -75
  4. aiecs/application/executors/operation_executor.py +79 -54
  5. aiecs/application/knowledge_graph/__init__.py +7 -0
  6. aiecs/application/knowledge_graph/builder/__init__.py +37 -0
  7. aiecs/application/knowledge_graph/builder/data_quality.py +302 -0
  8. aiecs/application/knowledge_graph/builder/data_reshaping.py +293 -0
  9. aiecs/application/knowledge_graph/builder/document_builder.py +369 -0
  10. aiecs/application/knowledge_graph/builder/graph_builder.py +490 -0
  11. aiecs/application/knowledge_graph/builder/import_optimizer.py +396 -0
  12. aiecs/application/knowledge_graph/builder/schema_inference.py +462 -0
  13. aiecs/application/knowledge_graph/builder/schema_mapping.py +563 -0
  14. aiecs/application/knowledge_graph/builder/structured_pipeline.py +1384 -0
  15. aiecs/application/knowledge_graph/builder/text_chunker.py +317 -0
  16. aiecs/application/knowledge_graph/extractors/__init__.py +27 -0
  17. aiecs/application/knowledge_graph/extractors/base.py +98 -0
  18. aiecs/application/knowledge_graph/extractors/llm_entity_extractor.py +422 -0
  19. aiecs/application/knowledge_graph/extractors/llm_relation_extractor.py +347 -0
  20. aiecs/application/knowledge_graph/extractors/ner_entity_extractor.py +241 -0
  21. aiecs/application/knowledge_graph/fusion/__init__.py +78 -0
  22. aiecs/application/knowledge_graph/fusion/ab_testing.py +395 -0
  23. aiecs/application/knowledge_graph/fusion/abbreviation_expander.py +327 -0
  24. aiecs/application/knowledge_graph/fusion/alias_index.py +597 -0
  25. aiecs/application/knowledge_graph/fusion/alias_matcher.py +384 -0
  26. aiecs/application/knowledge_graph/fusion/cache_coordinator.py +343 -0
  27. aiecs/application/knowledge_graph/fusion/entity_deduplicator.py +433 -0
  28. aiecs/application/knowledge_graph/fusion/entity_linker.py +511 -0
  29. aiecs/application/knowledge_graph/fusion/evaluation_dataset.py +240 -0
  30. aiecs/application/knowledge_graph/fusion/knowledge_fusion.py +632 -0
  31. aiecs/application/knowledge_graph/fusion/matching_config.py +489 -0
  32. aiecs/application/knowledge_graph/fusion/name_normalizer.py +352 -0
  33. aiecs/application/knowledge_graph/fusion/relation_deduplicator.py +183 -0
  34. aiecs/application/knowledge_graph/fusion/semantic_name_matcher.py +464 -0
  35. aiecs/application/knowledge_graph/fusion/similarity_pipeline.py +534 -0
  36. aiecs/application/knowledge_graph/pattern_matching/__init__.py +21 -0
  37. aiecs/application/knowledge_graph/pattern_matching/pattern_matcher.py +342 -0
  38. aiecs/application/knowledge_graph/pattern_matching/query_executor.py +366 -0
  39. aiecs/application/knowledge_graph/profiling/__init__.py +12 -0
  40. aiecs/application/knowledge_graph/profiling/query_plan_visualizer.py +195 -0
  41. aiecs/application/knowledge_graph/profiling/query_profiler.py +223 -0
  42. aiecs/application/knowledge_graph/reasoning/__init__.py +27 -0
  43. aiecs/application/knowledge_graph/reasoning/evidence_synthesis.py +341 -0
  44. aiecs/application/knowledge_graph/reasoning/inference_engine.py +500 -0
  45. aiecs/application/knowledge_graph/reasoning/logic_form_parser.py +163 -0
  46. aiecs/application/knowledge_graph/reasoning/logic_parser/__init__.py +79 -0
  47. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_builder.py +513 -0
  48. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_nodes.py +913 -0
  49. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_validator.py +866 -0
  50. aiecs/application/knowledge_graph/reasoning/logic_parser/error_handler.py +475 -0
  51. aiecs/application/knowledge_graph/reasoning/logic_parser/parser.py +396 -0
  52. aiecs/application/knowledge_graph/reasoning/logic_parser/query_context.py +208 -0
  53. aiecs/application/knowledge_graph/reasoning/logic_query_integration.py +170 -0
  54. aiecs/application/knowledge_graph/reasoning/query_planner.py +855 -0
  55. aiecs/application/knowledge_graph/reasoning/reasoning_engine.py +518 -0
  56. aiecs/application/knowledge_graph/retrieval/__init__.py +27 -0
  57. aiecs/application/knowledge_graph/retrieval/query_intent_classifier.py +211 -0
  58. aiecs/application/knowledge_graph/retrieval/retrieval_strategies.py +592 -0
  59. aiecs/application/knowledge_graph/retrieval/strategy_types.py +23 -0
  60. aiecs/application/knowledge_graph/search/__init__.py +59 -0
  61. aiecs/application/knowledge_graph/search/hybrid_search.py +457 -0
  62. aiecs/application/knowledge_graph/search/reranker.py +293 -0
  63. aiecs/application/knowledge_graph/search/reranker_strategies.py +535 -0
  64. aiecs/application/knowledge_graph/search/text_similarity.py +392 -0
  65. aiecs/application/knowledge_graph/traversal/__init__.py +15 -0
  66. aiecs/application/knowledge_graph/traversal/enhanced_traversal.py +305 -0
  67. aiecs/application/knowledge_graph/traversal/path_scorer.py +271 -0
  68. aiecs/application/knowledge_graph/validators/__init__.py +13 -0
  69. aiecs/application/knowledge_graph/validators/relation_validator.py +239 -0
  70. aiecs/application/knowledge_graph/visualization/__init__.py +11 -0
  71. aiecs/application/knowledge_graph/visualization/graph_visualizer.py +313 -0
  72. aiecs/common/__init__.py +9 -0
  73. aiecs/common/knowledge_graph/__init__.py +17 -0
  74. aiecs/common/knowledge_graph/runnable.py +471 -0
  75. aiecs/config/__init__.py +20 -5
  76. aiecs/config/config.py +762 -31
  77. aiecs/config/graph_config.py +131 -0
  78. aiecs/config/tool_config.py +399 -0
  79. aiecs/core/__init__.py +29 -13
  80. aiecs/core/interface/__init__.py +2 -2
  81. aiecs/core/interface/execution_interface.py +22 -22
  82. aiecs/core/interface/storage_interface.py +37 -88
  83. aiecs/core/registry/__init__.py +31 -0
  84. aiecs/core/registry/service_registry.py +92 -0
  85. aiecs/domain/__init__.py +270 -1
  86. aiecs/domain/agent/__init__.py +191 -0
  87. aiecs/domain/agent/base_agent.py +3870 -0
  88. aiecs/domain/agent/exceptions.py +99 -0
  89. aiecs/domain/agent/graph_aware_mixin.py +569 -0
  90. aiecs/domain/agent/hybrid_agent.py +1435 -0
  91. aiecs/domain/agent/integration/__init__.py +29 -0
  92. aiecs/domain/agent/integration/context_compressor.py +216 -0
  93. aiecs/domain/agent/integration/context_engine_adapter.py +587 -0
  94. aiecs/domain/agent/integration/protocols.py +281 -0
  95. aiecs/domain/agent/integration/retry_policy.py +218 -0
  96. aiecs/domain/agent/integration/role_config.py +213 -0
  97. aiecs/domain/agent/knowledge_aware_agent.py +1892 -0
  98. aiecs/domain/agent/lifecycle.py +291 -0
  99. aiecs/domain/agent/llm_agent.py +692 -0
  100. aiecs/domain/agent/memory/__init__.py +12 -0
  101. aiecs/domain/agent/memory/conversation.py +1124 -0
  102. aiecs/domain/agent/migration/__init__.py +14 -0
  103. aiecs/domain/agent/migration/conversion.py +163 -0
  104. aiecs/domain/agent/migration/legacy_wrapper.py +86 -0
  105. aiecs/domain/agent/models.py +884 -0
  106. aiecs/domain/agent/observability.py +479 -0
  107. aiecs/domain/agent/persistence.py +449 -0
  108. aiecs/domain/agent/prompts/__init__.py +29 -0
  109. aiecs/domain/agent/prompts/builder.py +159 -0
  110. aiecs/domain/agent/prompts/formatters.py +187 -0
  111. aiecs/domain/agent/prompts/template.py +255 -0
  112. aiecs/domain/agent/registry.py +253 -0
  113. aiecs/domain/agent/tool_agent.py +444 -0
  114. aiecs/domain/agent/tools/__init__.py +15 -0
  115. aiecs/domain/agent/tools/schema_generator.py +364 -0
  116. aiecs/domain/community/__init__.py +155 -0
  117. aiecs/domain/community/agent_adapter.py +469 -0
  118. aiecs/domain/community/analytics.py +432 -0
  119. aiecs/domain/community/collaborative_workflow.py +648 -0
  120. aiecs/domain/community/communication_hub.py +634 -0
  121. aiecs/domain/community/community_builder.py +320 -0
  122. aiecs/domain/community/community_integration.py +796 -0
  123. aiecs/domain/community/community_manager.py +803 -0
  124. aiecs/domain/community/decision_engine.py +849 -0
  125. aiecs/domain/community/exceptions.py +231 -0
  126. aiecs/domain/community/models/__init__.py +33 -0
  127. aiecs/domain/community/models/community_models.py +234 -0
  128. aiecs/domain/community/resource_manager.py +461 -0
  129. aiecs/domain/community/shared_context_manager.py +589 -0
  130. aiecs/domain/context/__init__.py +40 -10
  131. aiecs/domain/context/context_engine.py +1910 -0
  132. aiecs/domain/context/conversation_models.py +87 -53
  133. aiecs/domain/context/graph_memory.py +582 -0
  134. aiecs/domain/execution/model.py +12 -4
  135. aiecs/domain/knowledge_graph/__init__.py +19 -0
  136. aiecs/domain/knowledge_graph/models/__init__.py +52 -0
  137. aiecs/domain/knowledge_graph/models/entity.py +148 -0
  138. aiecs/domain/knowledge_graph/models/evidence.py +178 -0
  139. aiecs/domain/knowledge_graph/models/inference_rule.py +184 -0
  140. aiecs/domain/knowledge_graph/models/path.py +171 -0
  141. aiecs/domain/knowledge_graph/models/path_pattern.py +171 -0
  142. aiecs/domain/knowledge_graph/models/query.py +261 -0
  143. aiecs/domain/knowledge_graph/models/query_plan.py +181 -0
  144. aiecs/domain/knowledge_graph/models/relation.py +202 -0
  145. aiecs/domain/knowledge_graph/schema/__init__.py +23 -0
  146. aiecs/domain/knowledge_graph/schema/entity_type.py +131 -0
  147. aiecs/domain/knowledge_graph/schema/graph_schema.py +253 -0
  148. aiecs/domain/knowledge_graph/schema/property_schema.py +143 -0
  149. aiecs/domain/knowledge_graph/schema/relation_type.py +163 -0
  150. aiecs/domain/knowledge_graph/schema/schema_manager.py +691 -0
  151. aiecs/domain/knowledge_graph/schema/type_enums.py +209 -0
  152. aiecs/domain/task/dsl_processor.py +172 -56
  153. aiecs/domain/task/model.py +20 -8
  154. aiecs/domain/task/task_context.py +27 -24
  155. aiecs/infrastructure/__init__.py +0 -2
  156. aiecs/infrastructure/graph_storage/__init__.py +11 -0
  157. aiecs/infrastructure/graph_storage/base.py +837 -0
  158. aiecs/infrastructure/graph_storage/batch_operations.py +458 -0
  159. aiecs/infrastructure/graph_storage/cache.py +424 -0
  160. aiecs/infrastructure/graph_storage/distributed.py +223 -0
  161. aiecs/infrastructure/graph_storage/error_handling.py +380 -0
  162. aiecs/infrastructure/graph_storage/graceful_degradation.py +294 -0
  163. aiecs/infrastructure/graph_storage/health_checks.py +378 -0
  164. aiecs/infrastructure/graph_storage/in_memory.py +1197 -0
  165. aiecs/infrastructure/graph_storage/index_optimization.py +446 -0
  166. aiecs/infrastructure/graph_storage/lazy_loading.py +431 -0
  167. aiecs/infrastructure/graph_storage/metrics.py +344 -0
  168. aiecs/infrastructure/graph_storage/migration.py +400 -0
  169. aiecs/infrastructure/graph_storage/pagination.py +483 -0
  170. aiecs/infrastructure/graph_storage/performance_monitoring.py +456 -0
  171. aiecs/infrastructure/graph_storage/postgres.py +1563 -0
  172. aiecs/infrastructure/graph_storage/property_storage.py +353 -0
  173. aiecs/infrastructure/graph_storage/protocols.py +76 -0
  174. aiecs/infrastructure/graph_storage/query_optimizer.py +642 -0
  175. aiecs/infrastructure/graph_storage/schema_cache.py +290 -0
  176. aiecs/infrastructure/graph_storage/sqlite.py +1373 -0
  177. aiecs/infrastructure/graph_storage/streaming.py +487 -0
  178. aiecs/infrastructure/graph_storage/tenant.py +412 -0
  179. aiecs/infrastructure/messaging/celery_task_manager.py +92 -54
  180. aiecs/infrastructure/messaging/websocket_manager.py +51 -35
  181. aiecs/infrastructure/monitoring/__init__.py +22 -0
  182. aiecs/infrastructure/monitoring/executor_metrics.py +45 -11
  183. aiecs/infrastructure/monitoring/global_metrics_manager.py +212 -0
  184. aiecs/infrastructure/monitoring/structured_logger.py +3 -7
  185. aiecs/infrastructure/monitoring/tracing_manager.py +63 -35
  186. aiecs/infrastructure/persistence/__init__.py +14 -1
  187. aiecs/infrastructure/persistence/context_engine_client.py +184 -0
  188. aiecs/infrastructure/persistence/database_manager.py +67 -43
  189. aiecs/infrastructure/persistence/file_storage.py +180 -103
  190. aiecs/infrastructure/persistence/redis_client.py +74 -21
  191. aiecs/llm/__init__.py +73 -25
  192. aiecs/llm/callbacks/__init__.py +11 -0
  193. aiecs/llm/{custom_callbacks.py → callbacks/custom_callbacks.py} +26 -19
  194. aiecs/llm/client_factory.py +224 -36
  195. aiecs/llm/client_resolver.py +155 -0
  196. aiecs/llm/clients/__init__.py +38 -0
  197. aiecs/llm/clients/base_client.py +324 -0
  198. aiecs/llm/clients/google_function_calling_mixin.py +457 -0
  199. aiecs/llm/clients/googleai_client.py +241 -0
  200. aiecs/llm/clients/openai_client.py +158 -0
  201. aiecs/llm/clients/openai_compatible_mixin.py +367 -0
  202. aiecs/llm/clients/vertex_client.py +897 -0
  203. aiecs/llm/clients/xai_client.py +201 -0
  204. aiecs/llm/config/__init__.py +51 -0
  205. aiecs/llm/config/config_loader.py +272 -0
  206. aiecs/llm/config/config_validator.py +206 -0
  207. aiecs/llm/config/model_config.py +143 -0
  208. aiecs/llm/protocols.py +149 -0
  209. aiecs/llm/utils/__init__.py +10 -0
  210. aiecs/llm/utils/validate_config.py +89 -0
  211. aiecs/main.py +140 -121
  212. aiecs/scripts/aid/VERSION_MANAGEMENT.md +138 -0
  213. aiecs/scripts/aid/__init__.py +19 -0
  214. aiecs/scripts/aid/module_checker.py +499 -0
  215. aiecs/scripts/aid/version_manager.py +235 -0
  216. aiecs/scripts/{DEPENDENCY_SYSTEM_SUMMARY.md → dependance_check/DEPENDENCY_SYSTEM_SUMMARY.md} +1 -0
  217. aiecs/scripts/{README_DEPENDENCY_CHECKER.md → dependance_check/README_DEPENDENCY_CHECKER.md} +1 -0
  218. aiecs/scripts/dependance_check/__init__.py +15 -0
  219. aiecs/scripts/dependance_check/dependency_checker.py +1835 -0
  220. aiecs/scripts/{dependency_fixer.py → dependance_check/dependency_fixer.py} +192 -90
  221. aiecs/scripts/{download_nlp_data.py → dependance_check/download_nlp_data.py} +203 -71
  222. aiecs/scripts/dependance_patch/__init__.py +7 -0
  223. aiecs/scripts/dependance_patch/fix_weasel/__init__.py +11 -0
  224. aiecs/scripts/{fix_weasel_validator.py → dependance_patch/fix_weasel/fix_weasel_validator.py} +21 -14
  225. aiecs/scripts/{patch_weasel_library.sh → dependance_patch/fix_weasel/patch_weasel_library.sh} +1 -1
  226. aiecs/scripts/knowledge_graph/__init__.py +3 -0
  227. aiecs/scripts/knowledge_graph/run_threshold_experiments.py +212 -0
  228. aiecs/scripts/migrations/multi_tenancy/README.md +142 -0
  229. aiecs/scripts/tools_develop/README.md +671 -0
  230. aiecs/scripts/tools_develop/README_CONFIG_CHECKER.md +273 -0
  231. aiecs/scripts/tools_develop/TOOLS_CONFIG_GUIDE.md +1287 -0
  232. aiecs/scripts/tools_develop/TOOL_AUTO_DISCOVERY.md +234 -0
  233. aiecs/scripts/tools_develop/__init__.py +21 -0
  234. aiecs/scripts/tools_develop/check_all_tools_config.py +548 -0
  235. aiecs/scripts/tools_develop/check_type_annotations.py +257 -0
  236. aiecs/scripts/tools_develop/pre-commit-schema-coverage.sh +66 -0
  237. aiecs/scripts/tools_develop/schema_coverage.py +511 -0
  238. aiecs/scripts/tools_develop/validate_tool_schemas.py +475 -0
  239. aiecs/scripts/tools_develop/verify_executor_config_fix.py +98 -0
  240. aiecs/scripts/tools_develop/verify_tools.py +352 -0
  241. aiecs/tasks/__init__.py +0 -1
  242. aiecs/tasks/worker.py +115 -47
  243. aiecs/tools/__init__.py +194 -72
  244. aiecs/tools/apisource/__init__.py +99 -0
  245. aiecs/tools/apisource/intelligence/__init__.py +19 -0
  246. aiecs/tools/apisource/intelligence/data_fusion.py +632 -0
  247. aiecs/tools/apisource/intelligence/query_analyzer.py +417 -0
  248. aiecs/tools/apisource/intelligence/search_enhancer.py +385 -0
  249. aiecs/tools/apisource/monitoring/__init__.py +9 -0
  250. aiecs/tools/apisource/monitoring/metrics.py +330 -0
  251. aiecs/tools/apisource/providers/__init__.py +112 -0
  252. aiecs/tools/apisource/providers/base.py +671 -0
  253. aiecs/tools/apisource/providers/census.py +397 -0
  254. aiecs/tools/apisource/providers/fred.py +535 -0
  255. aiecs/tools/apisource/providers/newsapi.py +409 -0
  256. aiecs/tools/apisource/providers/worldbank.py +352 -0
  257. aiecs/tools/apisource/reliability/__init__.py +12 -0
  258. aiecs/tools/apisource/reliability/error_handler.py +363 -0
  259. aiecs/tools/apisource/reliability/fallback_strategy.py +376 -0
  260. aiecs/tools/apisource/tool.py +832 -0
  261. aiecs/tools/apisource/utils/__init__.py +9 -0
  262. aiecs/tools/apisource/utils/validators.py +334 -0
  263. aiecs/tools/base_tool.py +415 -21
  264. aiecs/tools/docs/__init__.py +121 -0
  265. aiecs/tools/docs/ai_document_orchestrator.py +607 -0
  266. aiecs/tools/docs/ai_document_writer_orchestrator.py +2350 -0
  267. aiecs/tools/docs/content_insertion_tool.py +1320 -0
  268. aiecs/tools/docs/document_creator_tool.py +1323 -0
  269. aiecs/tools/docs/document_layout_tool.py +1160 -0
  270. aiecs/tools/docs/document_parser_tool.py +1011 -0
  271. aiecs/tools/docs/document_writer_tool.py +1829 -0
  272. aiecs/tools/knowledge_graph/__init__.py +17 -0
  273. aiecs/tools/knowledge_graph/graph_reasoning_tool.py +807 -0
  274. aiecs/tools/knowledge_graph/graph_search_tool.py +944 -0
  275. aiecs/tools/knowledge_graph/kg_builder_tool.py +524 -0
  276. aiecs/tools/langchain_adapter.py +300 -138
  277. aiecs/tools/schema_generator.py +455 -0
  278. aiecs/tools/search_tool/__init__.py +100 -0
  279. aiecs/tools/search_tool/analyzers.py +581 -0
  280. aiecs/tools/search_tool/cache.py +264 -0
  281. aiecs/tools/search_tool/constants.py +128 -0
  282. aiecs/tools/search_tool/context.py +224 -0
  283. aiecs/tools/search_tool/core.py +778 -0
  284. aiecs/tools/search_tool/deduplicator.py +119 -0
  285. aiecs/tools/search_tool/error_handler.py +242 -0
  286. aiecs/tools/search_tool/metrics.py +343 -0
  287. aiecs/tools/search_tool/rate_limiter.py +172 -0
  288. aiecs/tools/search_tool/schemas.py +275 -0
  289. aiecs/tools/statistics/__init__.py +80 -0
  290. aiecs/tools/statistics/ai_data_analysis_orchestrator.py +646 -0
  291. aiecs/tools/statistics/ai_insight_generator_tool.py +508 -0
  292. aiecs/tools/statistics/ai_report_orchestrator_tool.py +684 -0
  293. aiecs/tools/statistics/data_loader_tool.py +555 -0
  294. aiecs/tools/statistics/data_profiler_tool.py +638 -0
  295. aiecs/tools/statistics/data_transformer_tool.py +580 -0
  296. aiecs/tools/statistics/data_visualizer_tool.py +498 -0
  297. aiecs/tools/statistics/model_trainer_tool.py +507 -0
  298. aiecs/tools/statistics/statistical_analyzer_tool.py +472 -0
  299. aiecs/tools/task_tools/__init__.py +49 -36
  300. aiecs/tools/task_tools/chart_tool.py +200 -184
  301. aiecs/tools/task_tools/classfire_tool.py +268 -267
  302. aiecs/tools/task_tools/image_tool.py +175 -131
  303. aiecs/tools/task_tools/office_tool.py +226 -146
  304. aiecs/tools/task_tools/pandas_tool.py +477 -121
  305. aiecs/tools/task_tools/report_tool.py +390 -142
  306. aiecs/tools/task_tools/research_tool.py +149 -79
  307. aiecs/tools/task_tools/scraper_tool.py +339 -145
  308. aiecs/tools/task_tools/stats_tool.py +448 -209
  309. aiecs/tools/temp_file_manager.py +26 -24
  310. aiecs/tools/tool_executor/__init__.py +18 -16
  311. aiecs/tools/tool_executor/tool_executor.py +364 -52
  312. aiecs/utils/LLM_output_structor.py +74 -48
  313. aiecs/utils/__init__.py +14 -3
  314. aiecs/utils/base_callback.py +0 -3
  315. aiecs/utils/cache_provider.py +696 -0
  316. aiecs/utils/execution_utils.py +50 -31
  317. aiecs/utils/prompt_loader.py +1 -0
  318. aiecs/utils/token_usage_repository.py +37 -11
  319. aiecs/ws/socket_server.py +14 -4
  320. {aiecs-1.0.1.dist-info → aiecs-1.7.6.dist-info}/METADATA +52 -15
  321. aiecs-1.7.6.dist-info/RECORD +337 -0
  322. aiecs-1.7.6.dist-info/entry_points.txt +13 -0
  323. aiecs/config/registry.py +0 -19
  324. aiecs/domain/context/content_engine.py +0 -982
  325. aiecs/llm/base_client.py +0 -99
  326. aiecs/llm/openai_client.py +0 -125
  327. aiecs/llm/vertex_client.py +0 -186
  328. aiecs/llm/xai_client.py +0 -184
  329. aiecs/scripts/dependency_checker.py +0 -857
  330. aiecs/scripts/quick_dependency_check.py +0 -269
  331. aiecs/tools/task_tools/search_api.py +0 -7
  332. aiecs-1.0.1.dist-info/RECORD +0 -90
  333. aiecs-1.0.1.dist-info/entry_points.txt +0 -7
  334. /aiecs/scripts/{setup_nlp_data.sh → dependance_check/setup_nlp_data.sh} +0 -0
  335. /aiecs/scripts/{README_WEASEL_PATCH.md → dependance_patch/fix_weasel/README_WEASEL_PATCH.md} +0 -0
  336. /aiecs/scripts/{fix_weasel_validator.sh → dependance_patch/fix_weasel/fix_weasel_validator.sh} +0 -0
  337. /aiecs/scripts/{run_weasel_patch.sh → dependance_patch/fix_weasel/run_weasel_patch.sh} +0 -0
  338. {aiecs-1.0.1.dist-info → aiecs-1.7.6.dist-info}/WHEEL +0 -0
  339. {aiecs-1.0.1.dist-info → aiecs-1.7.6.dist-info}/licenses/LICENSE +0 -0
  340. {aiecs-1.0.1.dist-info → aiecs-1.7.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1320 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Content Insertion Tool
4
+
5
+ This tool is responsible for inserting complex content elements
6
+ into documents, including charts, tables, images, and media.
7
+
8
+ Key Features:
9
+ 1. Chart insertion (leveraging chart_tool)
10
+ 2. Table insertion (leveraging pandas_tool)
11
+ 3. Image insertion and optimization (leveraging image_tool)
12
+ 4. Media content insertion (videos, audio, etc.)
13
+ 5. Interactive elements (forms, buttons, etc.)
14
+ 6. Cross-reference and citation management
15
+ """
16
+
17
+ import os
18
+ import uuid
19
+ import tempfile
20
+ import logging
21
+ from datetime import datetime
22
+ from typing import Dict, Any, List, Optional, Union, Tuple
23
+ from enum import Enum
24
+
25
+ from pydantic import BaseModel, Field
26
+ from pydantic_settings import BaseSettings, SettingsConfigDict
27
+
28
+ from aiecs.tools.base_tool import BaseTool
29
+ from aiecs.tools import register_tool
30
+
31
+
32
+ class ContentType(str, Enum):
33
+ """Types of content that can be inserted"""
34
+
35
+ CHART = "chart"
36
+ TABLE = "table"
37
+ IMAGE = "image"
38
+ VIDEO = "video"
39
+ AUDIO = "audio"
40
+ DIAGRAM = "diagram"
41
+ FORM = "form"
42
+ BUTTON = "button"
43
+ LINK = "link"
44
+ CITATION = "citation"
45
+ FOOTNOTE = "footnote"
46
+ CALLOUT = "callout"
47
+ CODE_BLOCK = "code_block"
48
+ EQUATION = "equation"
49
+ GALLERY = "gallery"
50
+
51
+
52
+ class ChartType(str, Enum):
53
+ """Chart types supported"""
54
+
55
+ BAR = "bar"
56
+ LINE = "line"
57
+ PIE = "pie"
58
+ SCATTER = "scatter"
59
+ HISTOGRAM = "histogram"
60
+ BOX = "box"
61
+ HEATMAP = "heatmap"
62
+ AREA = "area"
63
+ BUBBLE = "bubble"
64
+ GANTT = "gantt"
65
+
66
+
67
+ class TableStyle(str, Enum):
68
+ """Table styling options"""
69
+
70
+ DEFAULT = "default"
71
+ SIMPLE = "simple"
72
+ GRID = "grid"
73
+ STRIPED = "striped"
74
+ BORDERED = "bordered"
75
+ CORPORATE = "corporate"
76
+ ACADEMIC = "academic"
77
+ MINIMAL = "minimal"
78
+ COLORFUL = "colorful"
79
+
80
+
81
+ class ImageAlignment(str, Enum):
82
+ """Image alignment options"""
83
+
84
+ LEFT = "left"
85
+ CENTER = "center"
86
+ RIGHT = "right"
87
+ INLINE = "inline"
88
+ FLOAT_LEFT = "float_left"
89
+ FLOAT_RIGHT = "float_right"
90
+
91
+
92
+ class InsertionPosition(str, Enum):
93
+ """Content insertion positions"""
94
+
95
+ BEFORE = "before"
96
+ AFTER = "after"
97
+ REPLACE = "replace"
98
+ APPEND = "append"
99
+ PREPEND = "prepend"
100
+ INLINE = "inline"
101
+
102
+
103
+ class ContentInsertionError(Exception):
104
+ """Base exception for Content Insertion errors"""
105
+
106
+
107
+ class ChartInsertionError(ContentInsertionError):
108
+ """Raised when chart insertion fails"""
109
+
110
+
111
+ class TableInsertionError(ContentInsertionError):
112
+ """Raised when table insertion fails"""
113
+
114
+
115
+ class ImageInsertionError(ContentInsertionError):
116
+ """Raised when image insertion fails"""
117
+
118
+
119
+ @register_tool("content_insertion")
120
+ class ContentInsertionTool(BaseTool):
121
+ """
122
+ Content Insertion Tool for adding complex content to documents
123
+
124
+ This tool provides:
125
+ 1. Chart generation and insertion
126
+ 2. Table formatting and insertion
127
+ 3. Image processing and insertion
128
+ 4. Media content embedding
129
+ 5. Interactive element creation
130
+ 6. Cross-reference management
131
+
132
+ Integrates with:
133
+ - ChartTool for chart generation
134
+ - PandasTool for table processing
135
+ - ImageTool for image processing
136
+ - DocumentWriterTool for content placement
137
+ """
138
+
139
+ # Configuration schema
140
+ class Config(BaseSettings):
141
+ """Configuration for the content insertion tool
142
+
143
+ Automatically reads from environment variables with CONTENT_INSERT_ prefix.
144
+ Example: CONTENT_INSERT_TEMP_DIR -> temp_dir
145
+ """
146
+
147
+ model_config = SettingsConfigDict(env_prefix="CONTENT_INSERT_")
148
+
149
+ temp_dir: str = Field(
150
+ default=os.path.join(tempfile.gettempdir(), "content_insertion"),
151
+ description="Temporary directory for content processing",
152
+ )
153
+ assets_dir: str = Field(
154
+ default=os.path.join(tempfile.gettempdir(), "document_assets"),
155
+ description="Directory for document assets",
156
+ )
157
+ max_image_size: int = Field(default=10 * 1024 * 1024, description="Maximum image size in bytes")
158
+ max_chart_size: Tuple[int, int] = Field(
159
+ default=(1200, 800),
160
+ description="Maximum chart size in pixels (width, height)",
161
+ )
162
+ default_image_format: str = Field(
163
+ default="png",
164
+ description="Default image format for generated content",
165
+ )
166
+ optimize_images: bool = Field(
167
+ default=True,
168
+ description="Whether to optimize images automatically",
169
+ )
170
+ auto_resize: bool = Field(
171
+ default=True,
172
+ description="Whether to automatically resize content to fit",
173
+ )
174
+
175
+ def __init__(self, config: Optional[Dict] = None, **kwargs):
176
+ """Initialize Content Insertion Tool with settings
177
+
178
+ Configuration is automatically loaded by BaseTool from:
179
+ 1. Explicit config dict (highest priority)
180
+ 2. YAML config files (config/tools/content_insertion.yaml)
181
+ 3. Environment variables (via dotenv from .env files)
182
+ 4. Tool defaults (lowest priority)
183
+
184
+ Args:
185
+ config: Optional configuration overrides
186
+ **kwargs: Additional arguments passed to BaseTool (e.g., tool_name)
187
+ """
188
+ super().__init__(config, **kwargs)
189
+
190
+ # Configuration is automatically loaded by BaseTool into self._config_obj
191
+ # Access config via self._config_obj (BaseSettings instance)
192
+ self.config = self._config_obj if self._config_obj else self.Config()
193
+
194
+ self.logger = logging.getLogger(__name__)
195
+
196
+ # Initialize directories
197
+ self._init_directories()
198
+
199
+ # Initialize external tools
200
+ self._init_external_tools()
201
+
202
+ # Track insertions
203
+ self._insertions: List[Any] = []
204
+
205
+ # Content registry for cross-references
206
+ self._content_registry: Dict[str, Any] = {}
207
+
208
+ def _init_directories(self):
209
+ """Initialize required directories"""
210
+ os.makedirs(self.config.temp_dir, exist_ok=True)
211
+ os.makedirs(self.config.assets_dir, exist_ok=True)
212
+
213
+ def _init_external_tools(self):
214
+ """Initialize external tools for content generation"""
215
+ self.external_tools = {}
216
+
217
+ # Try to initialize chart tool
218
+ try:
219
+ from aiecs.tools.task_tools.chart_tool import ChartTool
220
+
221
+ self.external_tools["chart"] = ChartTool()
222
+ self.logger.info("ChartTool initialized successfully")
223
+ except ImportError:
224
+ self.logger.warning("ChartTool not available")
225
+
226
+ # Try to initialize pandas tool
227
+ try:
228
+ from aiecs.tools.task_tools.pandas_tool import PandasTool
229
+
230
+ self.external_tools["pandas"] = PandasTool()
231
+ self.logger.info("PandasTool initialized successfully")
232
+ except ImportError:
233
+ self.logger.warning("PandasTool not available")
234
+
235
+ # Try to initialize image tool
236
+ try:
237
+ from aiecs.tools.task_tools.image_tool import ImageTool
238
+
239
+ self.external_tools["image"] = ImageTool()
240
+ self.logger.info("ImageTool initialized successfully")
241
+ except ImportError:
242
+ self.logger.warning("ImageTool not available")
243
+
244
+ # Schema definitions
245
+ class Insert_chartSchema(BaseModel):
246
+ """Schema for insert_chart operation"""
247
+
248
+ document_path: str = Field(description="Path to target document")
249
+ chart_data: Dict[str, Any] = Field(description="Data for chart generation")
250
+ chart_type: ChartType = Field(description="Type of chart to create")
251
+ position: Dict[str, Any] = Field(description="Position to insert chart")
252
+ chart_config: Optional[Dict[str, Any]] = Field(default=None, description="Chart configuration")
253
+ caption: Optional[str] = Field(default=None, description="Chart caption")
254
+ reference_id: Optional[str] = Field(default=None, description="Reference ID for cross-referencing")
255
+
256
+ class Insert_tableSchema(BaseModel):
257
+ """Schema for insert_table operation"""
258
+
259
+ document_path: str = Field(description="Path to target document")
260
+ table_data: Union[List[List[Any]], Dict[str, Any]] = Field(description="Table data")
261
+ position: Dict[str, Any] = Field(description="Position to insert table")
262
+ table_style: TableStyle = Field(default=TableStyle.DEFAULT, description="Table styling")
263
+ headers: Optional[List[str]] = Field(default=None, description="Table headers")
264
+ caption: Optional[str] = Field(default=None, description="Table caption")
265
+ reference_id: Optional[str] = Field(default=None, description="Reference ID for cross-referencing")
266
+
267
+ class Insert_imageSchema(BaseModel):
268
+ """Schema for insert_image operation"""
269
+
270
+ document_path: str = Field(description="Path to target document")
271
+ image_source: str = Field(description="Image source (path, URL, or base64)")
272
+ position: Dict[str, Any] = Field(description="Position to insert image")
273
+ image_config: Optional[Dict[str, Any]] = Field(default=None, description="Image configuration")
274
+ alignment: ImageAlignment = Field(default=ImageAlignment.CENTER, description="Image alignment")
275
+ caption: Optional[str] = Field(default=None, description="Image caption")
276
+ alt_text: Optional[str] = Field(default=None, description="Alternative text")
277
+ reference_id: Optional[str] = Field(default=None, description="Reference ID for cross-referencing")
278
+
279
+ class Insert_mediaSchema(BaseModel):
280
+ """Schema for insert_media operation"""
281
+
282
+ document_path: str = Field(description="Path to target document")
283
+ media_source: str = Field(description="Media source (path or URL)")
284
+ media_type: ContentType = Field(description="Type of media content")
285
+ position: Dict[str, Any] = Field(description="Position to insert media")
286
+ media_config: Optional[Dict[str, Any]] = Field(default=None, description="Media configuration")
287
+ caption: Optional[str] = Field(default=None, description="Media caption")
288
+
289
+ class Insert_interactive_elementSchema(BaseModel):
290
+ """Schema for insert_interactive_element operation"""
291
+
292
+ document_path: str = Field(description="Path to target document")
293
+ element_type: ContentType = Field(description="Type of interactive element")
294
+ element_config: Dict[str, Any] = Field(description="Element configuration")
295
+ position: Dict[str, Any] = Field(description="Position to insert element")
296
+
297
+ class Insert_citationSchema(BaseModel):
298
+ """Schema for insert_citation operation"""
299
+
300
+ document_path: str = Field(description="Path to target document")
301
+ citation_data: Dict[str, Any] = Field(description="Citation information")
302
+ position: Dict[str, Any] = Field(description="Position to insert citation")
303
+ citation_style: str = Field(default="apa", description="Citation style (apa, mla, chicago, etc.)")
304
+
305
+ class Batch_insert_contentSchema(BaseModel):
306
+ """Schema for batch_insert_content operation"""
307
+
308
+ document_path: str = Field(description="Path to target document")
309
+ content_items: List[Dict[str, Any]] = Field(description="List of content items to insert")
310
+
311
+ def insert_chart(
312
+ self,
313
+ document_path: str,
314
+ chart_data: Dict[str, Any],
315
+ chart_type: ChartType,
316
+ position: Dict[str, Any],
317
+ chart_config: Optional[Dict[str, Any]] = None,
318
+ caption: Optional[str] = None,
319
+ reference_id: Optional[str] = None,
320
+ ) -> Dict[str, Any]:
321
+ """
322
+ Insert chart into document
323
+
324
+ Args:
325
+ document_path: Path to target document
326
+ chart_data: Data for chart generation
327
+ chart_type: Type of chart to create
328
+ position: Position to insert chart
329
+ chart_config: Chart configuration options
330
+ caption: Chart caption
331
+ reference_id: Reference ID for cross-referencing
332
+
333
+ Returns:
334
+ Dict containing chart insertion results
335
+ """
336
+ try:
337
+ start_time = datetime.now()
338
+ insertion_id = str(uuid.uuid4())
339
+
340
+ self.logger.info(f"Inserting {chart_type} chart {insertion_id} into: {document_path}")
341
+
342
+ # Check if chart tool is available
343
+ if "chart" not in self.external_tools:
344
+ raise ChartInsertionError("ChartTool not available")
345
+
346
+ # Generate chart
347
+ chart_result = self._generate_chart(chart_data, chart_type, chart_config)
348
+
349
+ # Process chart for document insertion
350
+ processed_chart = self._process_chart_for_document(chart_result, document_path, chart_config)
351
+
352
+ # Generate chart markup
353
+ chart_markup = self._generate_chart_markup(processed_chart, caption, reference_id, chart_config)
354
+
355
+ # Insert chart into document
356
+ self._insert_content_at_position(document_path, chart_markup, position)
357
+
358
+ # Register for cross-references
359
+ if reference_id:
360
+ self._register_content_reference(
361
+ reference_id,
362
+ "chart",
363
+ {
364
+ "type": chart_type,
365
+ "caption": caption,
366
+ "file_path": processed_chart.get("file_path"),
367
+ },
368
+ )
369
+
370
+ # Track insertion
371
+ insertion_info = {
372
+ "insertion_id": insertion_id,
373
+ "content_type": "chart",
374
+ "chart_type": chart_type,
375
+ "document_path": document_path,
376
+ "position": position,
377
+ "chart_data": chart_data,
378
+ "chart_config": chart_config,
379
+ "caption": caption,
380
+ "reference_id": reference_id,
381
+ "chart_result": chart_result,
382
+ "processed_chart": processed_chart,
383
+ "insertion_metadata": {
384
+ "inserted_at": start_time.isoformat(),
385
+ "duration": (datetime.now() - start_time).total_seconds(),
386
+ },
387
+ }
388
+
389
+ self._insertions.append(insertion_info)
390
+
391
+ self.logger.info(f"Chart {insertion_id} inserted successfully")
392
+ return insertion_info
393
+
394
+ except Exception as e:
395
+ raise ChartInsertionError(f"Failed to insert chart: {str(e)}")
396
+
397
+ def insert_table(
398
+ self,
399
+ document_path: str,
400
+ table_data: Union[List[List[Any]], Dict[str, Any]],
401
+ position: Dict[str, Any],
402
+ table_style: TableStyle = TableStyle.DEFAULT,
403
+ headers: Optional[List[str]] = None,
404
+ caption: Optional[str] = None,
405
+ reference_id: Optional[str] = None,
406
+ ) -> Dict[str, Any]:
407
+ """
408
+ Insert table into document
409
+
410
+ Args:
411
+ document_path: Path to target document
412
+ table_data: Table data (list of lists or dict)
413
+ position: Position to insert table
414
+ table_style: Table styling options
415
+ headers: Table headers
416
+ caption: Table caption
417
+ reference_id: Reference ID for cross-referencing
418
+
419
+ Returns:
420
+ Dict containing table insertion results
421
+ """
422
+ try:
423
+ start_time = datetime.now()
424
+ insertion_id = str(uuid.uuid4())
425
+
426
+ self.logger.info(f"Inserting table {insertion_id} into: {document_path}")
427
+
428
+ # Process table data
429
+ processed_table = self._process_table_data(table_data, headers)
430
+
431
+ # Generate table markup
432
+ table_markup = self._generate_table_markup(processed_table, table_style, caption, reference_id)
433
+
434
+ # Insert table into document
435
+ self._insert_content_at_position(document_path, table_markup, position)
436
+
437
+ # Register for cross-references
438
+ if reference_id:
439
+ self._register_content_reference(
440
+ reference_id,
441
+ "table",
442
+ {
443
+ "rows": len(processed_table.get("data", [])),
444
+ "columns": len(processed_table.get("headers", [])),
445
+ "caption": caption,
446
+ "style": table_style,
447
+ },
448
+ )
449
+
450
+ # Track insertion
451
+ insertion_info = {
452
+ "insertion_id": insertion_id,
453
+ "content_type": "table",
454
+ "document_path": document_path,
455
+ "position": position,
456
+ "table_data": table_data,
457
+ "table_style": table_style,
458
+ "headers": headers,
459
+ "caption": caption,
460
+ "reference_id": reference_id,
461
+ "processed_table": processed_table,
462
+ "insertion_metadata": {
463
+ "inserted_at": start_time.isoformat(),
464
+ "duration": (datetime.now() - start_time).total_seconds(),
465
+ },
466
+ }
467
+
468
+ self._insertions.append(insertion_info)
469
+
470
+ self.logger.info(f"Table {insertion_id} inserted successfully")
471
+ return insertion_info
472
+
473
+ except Exception as e:
474
+ raise TableInsertionError(f"Failed to insert table: {str(e)}")
475
+
476
+ def insert_image(
477
+ self,
478
+ document_path: str,
479
+ image_source: str,
480
+ position: Dict[str, Any],
481
+ image_config: Optional[Dict[str, Any]] = None,
482
+ alignment: ImageAlignment = ImageAlignment.CENTER,
483
+ caption: Optional[str] = None,
484
+ alt_text: Optional[str] = None,
485
+ reference_id: Optional[str] = None,
486
+ ) -> Dict[str, Any]:
487
+ """
488
+ Insert image into document
489
+
490
+ Args:
491
+ document_path: Path to target document
492
+ image_source: Image source (path, URL, or base64)
493
+ position: Position to insert image
494
+ image_config: Image configuration (size, format, etc.)
495
+ alignment: Image alignment
496
+ caption: Image caption
497
+ alt_text: Alternative text for accessibility
498
+ reference_id: Reference ID for cross-referencing
499
+
500
+ Returns:
501
+ Dict containing image insertion results
502
+ """
503
+ try:
504
+ start_time = datetime.now()
505
+ insertion_id = str(uuid.uuid4())
506
+
507
+ self.logger.info(f"Inserting image {insertion_id} into: {document_path}")
508
+
509
+ # Process image
510
+ processed_image = self._process_image_for_document(image_source, image_config, document_path)
511
+
512
+ # Generate image markup
513
+ image_markup = self._generate_image_markup(processed_image, alignment, caption, alt_text, reference_id)
514
+
515
+ # Insert image into document
516
+ self._insert_content_at_position(document_path, image_markup, position)
517
+
518
+ # Register for cross-references
519
+ if reference_id:
520
+ self._register_content_reference(
521
+ reference_id,
522
+ "image",
523
+ {
524
+ "caption": caption,
525
+ "alt_text": alt_text,
526
+ "file_path": processed_image.get("file_path"),
527
+ "dimensions": processed_image.get("dimensions"),
528
+ },
529
+ )
530
+
531
+ # Track insertion
532
+ insertion_info = {
533
+ "insertion_id": insertion_id,
534
+ "content_type": "image",
535
+ "document_path": document_path,
536
+ "position": position,
537
+ "image_source": image_source,
538
+ "image_config": image_config,
539
+ "alignment": alignment,
540
+ "caption": caption,
541
+ "alt_text": alt_text,
542
+ "reference_id": reference_id,
543
+ "processed_image": processed_image,
544
+ "insertion_metadata": {
545
+ "inserted_at": start_time.isoformat(),
546
+ "duration": (datetime.now() - start_time).total_seconds(),
547
+ },
548
+ }
549
+
550
+ self._insertions.append(insertion_info)
551
+
552
+ self.logger.info(f"Image {insertion_id} inserted successfully")
553
+ return insertion_info
554
+
555
+ except Exception as e:
556
+ raise ImageInsertionError(f"Failed to insert image: {str(e)}")
557
+
558
+ def insert_media(
559
+ self,
560
+ document_path: str,
561
+ media_source: str,
562
+ media_type: ContentType,
563
+ position: Dict[str, Any],
564
+ media_config: Optional[Dict[str, Any]] = None,
565
+ caption: Optional[str] = None,
566
+ ) -> Dict[str, Any]:
567
+ """
568
+ Insert media content (video, audio, etc.) into document
569
+
570
+ Args:
571
+ document_path: Path to target document
572
+ media_source: Media source (path or URL)
573
+ media_type: Type of media content
574
+ position: Position to insert media
575
+ media_config: Media configuration
576
+ caption: Media caption
577
+
578
+ Returns:
579
+ Dict containing media insertion results
580
+ """
581
+ try:
582
+ start_time = datetime.now()
583
+ insertion_id = str(uuid.uuid4())
584
+
585
+ self.logger.info(f"Inserting {media_type} media {insertion_id} into: {document_path}")
586
+
587
+ # Process media
588
+ processed_media = self._process_media_for_document(media_source, media_type, media_config)
589
+
590
+ # Generate media markup
591
+ media_markup = self._generate_media_markup(processed_media, media_type, caption, media_config)
592
+
593
+ # Insert media into document
594
+ self._insert_content_at_position(document_path, media_markup, position)
595
+
596
+ # Track insertion
597
+ insertion_info = {
598
+ "insertion_id": insertion_id,
599
+ "content_type": "media",
600
+ "media_type": media_type,
601
+ "document_path": document_path,
602
+ "position": position,
603
+ "media_source": media_source,
604
+ "media_config": media_config,
605
+ "caption": caption,
606
+ "processed_media": processed_media,
607
+ "insertion_metadata": {
608
+ "inserted_at": start_time.isoformat(),
609
+ "duration": (datetime.now() - start_time).total_seconds(),
610
+ },
611
+ }
612
+
613
+ self._insertions.append(insertion_info)
614
+
615
+ self.logger.info(f"Media {insertion_id} inserted successfully")
616
+ return insertion_info
617
+
618
+ except Exception as e:
619
+ raise ContentInsertionError(f"Failed to insert media: {str(e)}")
620
+
621
+ def insert_interactive_element(
622
+ self,
623
+ document_path: str,
624
+ element_type: ContentType,
625
+ element_config: Dict[str, Any],
626
+ position: Dict[str, Any],
627
+ ) -> Dict[str, Any]:
628
+ """
629
+ Insert interactive element (form, button, etc.) into document
630
+
631
+ Args:
632
+ document_path: Path to target document
633
+ element_type: Type of interactive element
634
+ element_config: Element configuration
635
+ position: Position to insert element
636
+
637
+ Returns:
638
+ Dict containing interactive element insertion results
639
+ """
640
+ try:
641
+ start_time = datetime.now()
642
+ insertion_id = str(uuid.uuid4())
643
+
644
+ self.logger.info(f"Inserting {element_type} element {insertion_id} into: {document_path}")
645
+
646
+ # Generate interactive element markup
647
+ element_markup = self._generate_interactive_element_markup(element_type, element_config)
648
+
649
+ # Insert element into document
650
+ self._insert_content_at_position(document_path, element_markup, position)
651
+
652
+ # Track insertion
653
+ insertion_info = {
654
+ "insertion_id": insertion_id,
655
+ "content_type": "interactive",
656
+ "element_type": element_type,
657
+ "document_path": document_path,
658
+ "position": position,
659
+ "element_config": element_config,
660
+ "insertion_metadata": {
661
+ "inserted_at": start_time.isoformat(),
662
+ "duration": (datetime.now() - start_time).total_seconds(),
663
+ },
664
+ }
665
+
666
+ self._insertions.append(insertion_info)
667
+
668
+ self.logger.info(f"Interactive element {insertion_id} inserted successfully")
669
+ return insertion_info
670
+
671
+ except Exception as e:
672
+ raise ContentInsertionError(f"Failed to insert interactive element: {str(e)}")
673
+
674
+ def insert_citation(
675
+ self,
676
+ document_path: str,
677
+ citation_data: Dict[str, Any],
678
+ position: Dict[str, Any],
679
+ citation_style: str = "apa",
680
+ ) -> Dict[str, Any]:
681
+ """
682
+ Insert citation into document
683
+
684
+ Args:
685
+ document_path: Path to target document
686
+ citation_data: Citation information
687
+ position: Position to insert citation
688
+ citation_style: Citation style (apa, mla, chicago, etc.)
689
+
690
+ Returns:
691
+ Dict containing citation insertion results
692
+ """
693
+ try:
694
+ start_time = datetime.now()
695
+ insertion_id = str(uuid.uuid4())
696
+
697
+ self.logger.info(f"Inserting citation {insertion_id} into: {document_path}")
698
+
699
+ # Generate citation markup
700
+ citation_markup = self._generate_citation_markup(citation_data, citation_style)
701
+
702
+ # Insert citation into document
703
+ self._insert_content_at_position(document_path, citation_markup, position)
704
+
705
+ # Track insertion
706
+ insertion_info = {
707
+ "insertion_id": insertion_id,
708
+ "content_type": "citation",
709
+ "document_path": document_path,
710
+ "position": position,
711
+ "citation_data": citation_data,
712
+ "citation_style": citation_style,
713
+ "insertion_metadata": {
714
+ "inserted_at": start_time.isoformat(),
715
+ "duration": (datetime.now() - start_time).total_seconds(),
716
+ },
717
+ }
718
+
719
+ self._insertions.append(insertion_info)
720
+
721
+ self.logger.info(f"Citation {insertion_id} inserted successfully")
722
+ return insertion_info
723
+
724
+ except Exception as e:
725
+ raise ContentInsertionError(f"Failed to insert citation: {str(e)}")
726
+
727
+ def batch_insert_content(self, document_path: str, content_items: List[Dict[str, Any]]) -> Dict[str, Any]:
728
+ """
729
+ Insert multiple content items in batch
730
+
731
+ Args:
732
+ document_path: Path to target document
733
+ content_items: List of content items to insert
734
+
735
+ Returns:
736
+ Dict containing batch insertion results
737
+ """
738
+ try:
739
+ start_time = datetime.now()
740
+ batch_id = str(uuid.uuid4())
741
+
742
+ self.logger.info(f"Starting batch insertion {batch_id} for: {document_path}")
743
+
744
+ results: Dict[str, Any] = {
745
+ "batch_id": batch_id,
746
+ "document_path": document_path,
747
+ "total_items": len(content_items),
748
+ "successful_insertions": 0,
749
+ "failed_insertions": 0,
750
+ "insertion_results": [],
751
+ "errors": [],
752
+ }
753
+
754
+ for i, item in enumerate(content_items):
755
+ try:
756
+ content_type = item.get("content_type")
757
+
758
+ if content_type == "chart":
759
+ result = self.insert_chart(**item)
760
+ elif content_type == "table":
761
+ result = self.insert_table(**item)
762
+ elif content_type == "image":
763
+ result = self.insert_image(**item)
764
+ elif content_type == "media":
765
+ result = self.insert_media(**item)
766
+ elif content_type == "citation":
767
+ result = self.insert_citation(**item)
768
+ else:
769
+ raise ContentInsertionError(f"Unsupported content type: {content_type}")
770
+
771
+ results["insertion_results"].append(result)
772
+ successful = results.get("successful_insertions", 0)
773
+ if isinstance(successful, (int, float)):
774
+ results["successful_insertions"] = successful + 1
775
+
776
+ except Exception as e:
777
+ error_info = {
778
+ "item_index": i,
779
+ "item": item,
780
+ "error": str(e),
781
+ }
782
+ results["errors"].append(error_info)
783
+ failed = results.get("failed_insertions", 0)
784
+ if isinstance(failed, (int, float)):
785
+ results["failed_insertions"] = failed + 1
786
+ self.logger.warning(f"Failed to insert item {i}: {e}")
787
+
788
+ results["batch_metadata"] = {
789
+ "started_at": start_time.isoformat(),
790
+ "completed_at": datetime.now().isoformat(),
791
+ "duration": (datetime.now() - start_time).total_seconds(),
792
+ }
793
+
794
+ self.logger.info(f"Batch insertion {batch_id} completed: {results['successful_insertions']}/{results['total_items']} successful")
795
+ return results
796
+
797
+ except Exception as e:
798
+ raise ContentInsertionError(f"Batch insertion failed: {str(e)}")
799
+
800
+ def get_content_references(self) -> Dict[str, Any]:
801
+ """
802
+ Get all registered content references
803
+
804
+ Returns:
805
+ Dict containing content references
806
+ """
807
+ return self._content_registry.copy()
808
+
809
+ def get_insertion_history(self) -> List[Dict[str, Any]]:
810
+ """
811
+ Get insertion history
812
+
813
+ Returns:
814
+ List of insertion operations
815
+ """
816
+ return self._insertions.copy()
817
+
818
+ # Helper methods for content generation
819
+ def _generate_chart(
820
+ self,
821
+ chart_data: Dict[str, Any],
822
+ chart_type: ChartType,
823
+ config: Optional[Dict[str, Any]],
824
+ ) -> Dict[str, Any]:
825
+ """Generate chart using ChartTool"""
826
+ try:
827
+ chart_tool = self.external_tools["chart"]
828
+
829
+ # Create temporary data file for ChartTool
830
+ import tempfile
831
+ import json
832
+
833
+ temp_file = tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False)
834
+ json.dump(chart_data, temp_file)
835
+ temp_file.close()
836
+
837
+ # Map chart types to visualization types
838
+ type_mapping = {
839
+ ChartType.BAR: "bar",
840
+ ChartType.LINE: "line",
841
+ ChartType.PIE: "pie",
842
+ ChartType.SCATTER: "scatter",
843
+ ChartType.HISTOGRAM: "histogram",
844
+ ChartType.BOX: "box",
845
+ ChartType.HEATMAP: "heatmap",
846
+ ChartType.AREA: "area",
847
+ }
848
+
849
+ # Generate chart using visualize method
850
+ result = chart_tool.visualize(
851
+ file_path=temp_file.name,
852
+ plot_type=type_mapping.get(chart_type, "bar"),
853
+ title=config.get("title", "Chart") if config else "Chart",
854
+ figsize=config.get("figsize", (10, 6)) if config else (10, 6),
855
+ )
856
+
857
+ # Clean up temp file
858
+ os.unlink(temp_file.name)
859
+ return result
860
+
861
+ except Exception as e:
862
+ raise ChartInsertionError(f"Failed to generate chart: {str(e)}")
863
+
864
+ def _process_chart_for_document(
865
+ self,
866
+ chart_result: Dict[str, Any],
867
+ document_path: str,
868
+ config: Optional[Dict[str, Any]],
869
+ ) -> Dict[str, Any]:
870
+ """Process chart for document insertion"""
871
+ try:
872
+ # Get chart file path - ChartTool returns 'output_path'
873
+ chart_file = chart_result.get("output_path") or chart_result.get("file_path")
874
+ if not chart_file or not os.path.exists(chart_file):
875
+ raise ChartInsertionError("Chart file not found")
876
+
877
+ # Copy chart to assets directory
878
+ chart_filename = f"chart_{uuid.uuid4().hex[:8]}.{self.config.default_image_format}"
879
+ asset_path = os.path.join(self.config.assets_dir, chart_filename)
880
+
881
+ import shutil
882
+
883
+ shutil.copy2(chart_file, asset_path)
884
+
885
+ # Optimize if needed
886
+ if self.config.optimize_images and "image" in self.external_tools:
887
+ self._optimize_image(asset_path)
888
+
889
+ return {
890
+ "file_path": asset_path,
891
+ "filename": chart_filename,
892
+ "relative_path": os.path.relpath(asset_path, os.path.dirname(document_path)),
893
+ "chart_data": chart_result,
894
+ "dimensions": self._get_image_dimensions(asset_path),
895
+ }
896
+
897
+ except Exception as e:
898
+ raise ChartInsertionError(f"Failed to process chart: {str(e)}")
899
+
900
+ def _process_table_data(
901
+ self,
902
+ table_data: Union[List[List[Any]], Dict[str, Any]],
903
+ headers: Optional[List[str]],
904
+ ) -> Dict[str, Any]:
905
+ """Process table data for insertion"""
906
+ try:
907
+ if isinstance(table_data, dict):
908
+ # Convert dict to list format
909
+ if headers is None:
910
+ headers = list(table_data.keys())
911
+ data_rows = []
912
+ max_len = max(len(v) if isinstance(v, list) else 1 for v in table_data.values())
913
+ for i in range(max_len):
914
+ row = []
915
+ for key in headers:
916
+ value = table_data[key]
917
+ if isinstance(value, list):
918
+ row.append(value[i] if i < len(value) else "")
919
+ else:
920
+ row.append(value if i == 0 else "")
921
+ data_rows.append(row)
922
+ data = data_rows
923
+ else:
924
+ data = table_data
925
+ if headers is None and data:
926
+ headers = [f"Column {i+1}" for i in range(len(data[0]))]
927
+
928
+ return {
929
+ "headers": headers or [],
930
+ "data": data,
931
+ "rows": len(data),
932
+ "columns": len(headers or []),
933
+ }
934
+
935
+ except Exception as e:
936
+ raise TableInsertionError(f"Failed to process table data: {str(e)}")
937
+
938
+ def _process_image_for_document(
939
+ self,
940
+ image_source: str,
941
+ config: Optional[Dict[str, Any]],
942
+ document_path: str,
943
+ ) -> Dict[str, Any]:
944
+ """Process image for document insertion"""
945
+ try:
946
+ # Determine image source type
947
+ if image_source.startswith(("http://", "https://")):
948
+ # Download from URL
949
+ image_file = self._download_image(image_source)
950
+ elif image_source.startswith("data:"):
951
+ # Decode base64 image
952
+ image_file = self._decode_base64_image(image_source)
953
+ else:
954
+ # Local file
955
+ if not os.path.exists(image_source):
956
+ raise ImageInsertionError(f"Image file not found: {image_source}")
957
+ image_file = image_source
958
+
959
+ # Copy to assets directory
960
+ image_filename = f"image_{uuid.uuid4().hex[:8]}.{self.config.default_image_format}"
961
+ asset_path = os.path.join(self.config.assets_dir, image_filename)
962
+
963
+ import shutil
964
+
965
+ shutil.copy2(image_file, asset_path)
966
+
967
+ # Process image (resize, optimize, etc.)
968
+ if config:
969
+ self._apply_image_processing(asset_path, config)
970
+
971
+ return {
972
+ "file_path": asset_path,
973
+ "filename": image_filename,
974
+ "relative_path": os.path.relpath(asset_path, os.path.dirname(document_path)),
975
+ "original_source": image_source,
976
+ "dimensions": self._get_image_dimensions(asset_path),
977
+ "file_size": os.path.getsize(asset_path),
978
+ }
979
+
980
+ except Exception as e:
981
+ raise ImageInsertionError(f"Failed to process image: {str(e)}")
982
+
983
+ def _process_media_for_document(
984
+ self,
985
+ media_source: str,
986
+ media_type: ContentType,
987
+ config: Optional[Dict[str, Any]],
988
+ ) -> Dict[str, Any]:
989
+ """Process media for document insertion"""
990
+ return {
991
+ "source": media_source,
992
+ "type": media_type,
993
+ "config": config or {},
994
+ "is_external": media_source.startswith(("http://", "https://")),
995
+ }
996
+
997
+ # Markup generation methods
998
+ def _generate_chart_markup(
999
+ self,
1000
+ chart_info: Dict[str, Any],
1001
+ caption: Optional[str],
1002
+ reference_id: Optional[str],
1003
+ config: Optional[Dict[str, Any]],
1004
+ ) -> str:
1005
+ """Generate markup for chart insertion"""
1006
+ file_format = self._detect_document_format_from_config(config)
1007
+
1008
+ if file_format == "markdown":
1009
+ markup = f"![{caption or 'Chart'}]({chart_info['relative_path']})"
1010
+ if caption:
1011
+ markup += f"\n\n*{caption}*"
1012
+ if reference_id:
1013
+ markup = f"<div id='{reference_id}'>\n{markup}\n</div>"
1014
+ elif file_format == "html":
1015
+ markup = f"<img src='{chart_info['relative_path']}' alt='{caption or 'Chart'}'>"
1016
+ if caption:
1017
+ markup = f"<figure>\n{markup}\n<figcaption>{caption}</figcaption>\n</figure>"
1018
+ if reference_id:
1019
+ markup = f"<div id='{reference_id}'>\n{markup}\n</div>"
1020
+ else:
1021
+ markup = f"[Chart: {chart_info['filename']}]"
1022
+ if caption:
1023
+ markup += f"\nCaption: {caption}"
1024
+
1025
+ return markup
1026
+
1027
+ def _generate_table_markup(
1028
+ self,
1029
+ table_info: Dict[str, Any],
1030
+ style: TableStyle,
1031
+ caption: Optional[str],
1032
+ reference_id: Optional[str],
1033
+ ) -> str:
1034
+ """Generate markup for table insertion"""
1035
+ headers = table_info.get("headers", [])
1036
+ data = table_info.get("data", [])
1037
+
1038
+ # Generate Markdown table (most compatible)
1039
+ markup_lines = []
1040
+
1041
+ # Add caption
1042
+ if caption:
1043
+ markup_lines.append(f"**{caption}**\n")
1044
+
1045
+ # Add headers
1046
+ if headers:
1047
+ markup_lines.append("| " + " | ".join(str(h) for h in headers) + " |")
1048
+ markup_lines.append("| " + " | ".join("---" for _ in headers) + " |")
1049
+
1050
+ # Add data rows
1051
+ for row in data:
1052
+ markup_lines.append("| " + " | ".join(str(cell) for cell in row) + " |")
1053
+
1054
+ markup = "\n".join(markup_lines)
1055
+
1056
+ if reference_id:
1057
+ markup = f"<div id='{reference_id}'>\n\n{markup}\n\n</div>"
1058
+
1059
+ return markup
1060
+
1061
+ def _generate_image_markup(
1062
+ self,
1063
+ image_info: Dict[str, Any],
1064
+ alignment: ImageAlignment,
1065
+ caption: Optional[str],
1066
+ alt_text: Optional[str],
1067
+ reference_id: Optional[str],
1068
+ ) -> str:
1069
+ """Generate markup for image insertion"""
1070
+ alt = alt_text or caption or "Image"
1071
+
1072
+ # Basic markdown image
1073
+ markup = f"![{alt}]({image_info['relative_path']})"
1074
+
1075
+ # Add alignment if needed
1076
+ if alignment != ImageAlignment.INLINE:
1077
+ if alignment == ImageAlignment.CENTER:
1078
+ markup = f"<div align='center'>\n{markup}\n</div>"
1079
+ elif alignment == ImageAlignment.RIGHT:
1080
+ markup = f"<div align='right'>\n{markup}\n</div>"
1081
+ elif alignment == ImageAlignment.FLOAT_RIGHT:
1082
+ markup = f"<div style='float: right;'>\n{markup}\n</div>"
1083
+ elif alignment == ImageAlignment.FLOAT_LEFT:
1084
+ markup = f"<div style='float: left;'>\n{markup}\n</div>"
1085
+
1086
+ # Add caption
1087
+ if caption:
1088
+ markup += f"\n\n*{caption}*"
1089
+
1090
+ # Add reference ID
1091
+ if reference_id:
1092
+ markup = f"<div id='{reference_id}'>\n{markup}\n</div>"
1093
+
1094
+ return markup
1095
+
1096
+ def _generate_media_markup(
1097
+ self,
1098
+ media_info: Dict[str, Any],
1099
+ media_type: ContentType,
1100
+ caption: Optional[str],
1101
+ config: Optional[Dict[str, Any]],
1102
+ ) -> str:
1103
+ """Generate markup for media insertion"""
1104
+ source = media_info["source"]
1105
+
1106
+ if media_type == ContentType.VIDEO:
1107
+ markup = f'<video controls>\n<source src="{source}">\nYour browser does not support the video tag.\n</video>'
1108
+ elif media_type == ContentType.AUDIO:
1109
+ markup = f'<audio controls>\n<source src="{source}">\nYour browser does not support the audio tag.\n</audio>'
1110
+ else:
1111
+ markup = f'<object data="{source}">Media content</object>'
1112
+
1113
+ if caption:
1114
+ markup = f"<figure>\n{markup}\n<figcaption>{caption}</figcaption>\n</figure>"
1115
+
1116
+ return markup
1117
+
1118
+ def _generate_interactive_element_markup(self, element_type: ContentType, config: Dict[str, Any]) -> str:
1119
+ """Generate markup for interactive elements"""
1120
+ if element_type == ContentType.BUTTON:
1121
+ text = config.get("text", "Button")
1122
+ action = config.get("action", "#")
1123
+ return f'<button onclick="{action}">{text}</button>'
1124
+ elif element_type == ContentType.FORM:
1125
+ return self._generate_form_markup(config)
1126
+ elif element_type == ContentType.LINK:
1127
+ text = config.get("text", "Link")
1128
+ url = config.get("url", "#")
1129
+ return f'<a href="{url}">{text}</a>'
1130
+ else:
1131
+ return f"<!-- Interactive element: {element_type} -->"
1132
+
1133
+ def _generate_form_markup(self, config: Dict[str, Any]) -> str:
1134
+ """Generate form markup"""
1135
+ fields = config.get("fields", [])
1136
+ action = config.get("action", "#")
1137
+ method = config.get("method", "POST")
1138
+
1139
+ form_lines = [f'<form action="{action}" method="{method}">']
1140
+
1141
+ for field in fields:
1142
+ field_type = field.get("type", "text")
1143
+ name = field.get("name", "")
1144
+ label = field.get("label", "")
1145
+
1146
+ if label:
1147
+ form_lines.append(f' <label for="{name}">{label}:</label>')
1148
+ form_lines.append(f' <input type="{field_type}" name="{name}" id="{name}">')
1149
+
1150
+ form_lines.append(' <input type="submit" value="Submit">')
1151
+ form_lines.append("</form>")
1152
+
1153
+ return "\n".join(form_lines)
1154
+
1155
+ def _generate_citation_markup(self, citation_data: Dict[str, Any], style: str) -> str:
1156
+ """Generate citation markup"""
1157
+ if style.lower() == "apa":
1158
+ return self._generate_apa_citation(citation_data)
1159
+ elif style.lower() == "mla":
1160
+ return self._generate_mla_citation(citation_data)
1161
+ else:
1162
+ return self._generate_basic_citation(citation_data)
1163
+
1164
+ def _generate_apa_citation(self, data: Dict[str, Any]) -> str:
1165
+ """Generate APA style citation"""
1166
+ author = data.get("author", "Unknown Author")
1167
+ year = data.get("year", "n.d.")
1168
+ data.get("title", "Untitled")
1169
+ return f"({author}, {year})"
1170
+
1171
+ def _generate_mla_citation(self, data: Dict[str, Any]) -> str:
1172
+ """Generate MLA style citation"""
1173
+ author = data.get("author", "Unknown Author")
1174
+ page = data.get("page", "")
1175
+ if page:
1176
+ return f"({author} {page})"
1177
+ return f"({author})"
1178
+
1179
+ def _generate_basic_citation(self, data: Dict[str, Any]) -> str:
1180
+ """Generate basic citation"""
1181
+ author = data.get("author", "Unknown Author")
1182
+ year = data.get("year", "")
1183
+ if year:
1184
+ return f"[{author}, {year}]"
1185
+ return f"[{author}]"
1186
+
1187
+ # Content insertion methods
1188
+ def _insert_content_at_position(self, document_path: str, content: str, position: Dict[str, Any]):
1189
+ """Insert content at specified position in document"""
1190
+ try:
1191
+ with open(document_path, "r", encoding="utf-8") as f:
1192
+ doc_content = f.read()
1193
+
1194
+ if "line" in position:
1195
+ lines = doc_content.split("\n")
1196
+ line_num = position["line"]
1197
+ insertion_type = position.get("type", InsertionPosition.AFTER)
1198
+
1199
+ if insertion_type == InsertionPosition.BEFORE:
1200
+ lines.insert(line_num, content)
1201
+ elif insertion_type == InsertionPosition.AFTER:
1202
+ lines.insert(line_num + 1, content)
1203
+ elif insertion_type == InsertionPosition.REPLACE:
1204
+ lines[line_num] = content
1205
+
1206
+ doc_content = "\n".join(lines)
1207
+
1208
+ elif "offset" in position:
1209
+ offset = position["offset"]
1210
+ doc_content = doc_content[:offset] + content + doc_content[offset:]
1211
+
1212
+ elif "marker" in position:
1213
+ marker = position["marker"]
1214
+ if marker in doc_content:
1215
+ doc_content = doc_content.replace(marker, content, 1)
1216
+ else:
1217
+ doc_content += "\n\n" + content
1218
+
1219
+ else:
1220
+ # Append at end
1221
+ doc_content += "\n\n" + content
1222
+
1223
+ with open(document_path, "w", encoding="utf-8") as f:
1224
+ f.write(doc_content)
1225
+
1226
+ except Exception as e:
1227
+ raise ContentInsertionError(f"Failed to insert content: {str(e)}")
1228
+
1229
+ def _register_content_reference(self, reference_id: str, content_type: str, metadata: Dict[str, Any]):
1230
+ """Register content for cross-referencing"""
1231
+ self._content_registry[reference_id] = {
1232
+ "type": content_type,
1233
+ "metadata": metadata,
1234
+ "registered_at": datetime.now().isoformat(),
1235
+ }
1236
+
1237
+ # Utility methods
1238
+ def _detect_document_format_from_config(self, config: Optional[Dict[str, Any]]) -> str:
1239
+ """Detect document format from configuration"""
1240
+ if config and "document_format" in config:
1241
+ return config["document_format"]
1242
+ return "markdown" # Default
1243
+
1244
+ def _download_image(self, url: str) -> str:
1245
+ """Download image from URL"""
1246
+ import urllib.request
1247
+
1248
+ filename = f"downloaded_{uuid.uuid4().hex[:8]}.{self.config.default_image_format}"
1249
+ filepath = os.path.join(self.config.temp_dir, filename)
1250
+
1251
+ urllib.request.urlretrieve(url, filepath)
1252
+ return filepath
1253
+
1254
+ def _decode_base64_image(self, data_url: str) -> str:
1255
+ """Decode base64 image data"""
1256
+ import base64
1257
+
1258
+ # Extract format and data
1259
+ header, data = data_url.split(",", 1)
1260
+ format_info = header.split(";")[0].split("/")[-1]
1261
+
1262
+ # Decode data
1263
+ image_data = base64.b64decode(data)
1264
+
1265
+ filename = f"base64_{uuid.uuid4().hex[:8]}.{format_info}"
1266
+ filepath = os.path.join(self.config.temp_dir, filename)
1267
+
1268
+ with open(filepath, "wb") as f:
1269
+ f.write(image_data)
1270
+
1271
+ return filepath
1272
+
1273
+ def _get_image_dimensions(self, image_path: str) -> Optional[Tuple[int, int]]:
1274
+ """Get image dimensions"""
1275
+ try:
1276
+ from PIL import Image
1277
+
1278
+ with Image.open(image_path) as img:
1279
+ return img.size
1280
+ except ImportError:
1281
+ return None
1282
+ except Exception:
1283
+ return None
1284
+
1285
+ def _optimize_image(self, image_path: str):
1286
+ """Optimize image for document inclusion"""
1287
+ if "image" in self.external_tools:
1288
+ try:
1289
+ image_tool = self.external_tools["image"]
1290
+ # Load image to get current info
1291
+ image_info = image_tool.load(image_path)
1292
+ # For now, just log the optimization - actual optimization
1293
+ # would require more complex logic
1294
+ self.logger.info(f"Image optimization requested for: {image_path}, size: {image_info.get('size')}")
1295
+ except Exception as e:
1296
+ self.logger.warning(f"Failed to optimize image: {e}")
1297
+
1298
+ def _apply_image_processing(self, image_path: str, config: Dict[str, Any]):
1299
+ """Apply image processing based on configuration"""
1300
+ if "image" in self.external_tools:
1301
+ try:
1302
+ self.external_tools["image"]
1303
+
1304
+ # Apply resize if specified
1305
+ if "resize" in config:
1306
+ resize_params = config["resize"]
1307
+ if isinstance(resize_params, dict) and "width" in resize_params and "height" in resize_params:
1308
+ # Note: ImageTool.resize method would need to be called here
1309
+ # For now, just log the resize request
1310
+ self.logger.info(f"Resize requested: {resize_params}")
1311
+
1312
+ # Apply filter if specified
1313
+ if "filter" in config:
1314
+ filter_type = config["filter"]
1315
+ # Note: ImageTool.filter method would need to be called
1316
+ # here
1317
+ self.logger.info(f"Filter requested: {filter_type}")
1318
+
1319
+ except Exception as e:
1320
+ self.logger.warning(f"Failed to process image: {e}")