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.

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 +435 -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 +3949 -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 +1731 -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 +894 -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 +377 -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 +230 -37
  195. aiecs/llm/client_resolver.py +155 -0
  196. aiecs/llm/clients/__init__.py +38 -0
  197. aiecs/llm/clients/base_client.py +328 -0
  198. aiecs/llm/clients/google_function_calling_mixin.py +415 -0
  199. aiecs/llm/clients/googleai_client.py +314 -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 +1186 -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 +1464 -0
  269. aiecs/tools/docs/document_layout_tool.py +1160 -0
  270. aiecs/tools/docs/document_parser_tool.py +1016 -0
  271. aiecs/tools/docs/document_writer_tool.py +2008 -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 +220 -141
  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.17.dist-info}/METADATA +52 -15
  321. aiecs-1.7.17.dist-info/RECORD +337 -0
  322. aiecs-1.7.17.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.17.dist-info}/WHEEL +0 -0
  339. {aiecs-1.0.1.dist-info → aiecs-1.7.17.dist-info}/licenses/LICENSE +0 -0
  340. {aiecs-1.0.1.dist-info → aiecs-1.7.17.dist-info}/top_level.txt +0 -0
@@ -1,136 +1,83 @@
1
1
  import os
2
2
  import logging
3
3
  import subprocess
4
- import uuid
5
4
  import tempfile
6
5
  from typing import Dict, Any, List, Optional
7
6
  from dataclasses import dataclass
8
7
  from dataclasses import field
9
8
 
10
- from pydantic import BaseModel, ValidationError, field_validator, ConfigDict
11
- from pydantic_settings import BaseSettings
9
+ from pydantic import (
10
+ BaseModel,
11
+ ValidationError,
12
+ field_validator,
13
+ Field,
14
+ )
15
+ from pydantic_settings import BaseSettings, SettingsConfigDict
12
16
  from PIL import Image, ExifTags, ImageFilter
13
17
  from queue import Queue
14
18
 
15
19
  from aiecs.tools.base_tool import BaseTool
16
20
  from aiecs.tools import register_tool
17
21
 
18
- # Configuration for ImageTool
19
- class ImageSettings(BaseSettings):
20
- """
21
- Configuration for ImageTool.
22
+ # Module-level default configuration for validators
23
+ _DEFAULT_MAX_FILE_SIZE_MB = 50
24
+ _DEFAULT_ALLOWED_EXTENSIONS = [
25
+ ".jpg",
26
+ ".jpeg",
27
+ ".png",
28
+ ".bmp",
29
+ ".tiff",
30
+ ".gif",
31
+ ]
22
32
 
23
- Attributes:
24
- max_file_size_mb (int): Maximum file size in megabytes.
25
- allowed_extensions (List[str]): Allowed image file extensions.
26
- tesseract_pool_size (int): Number of Tesseract processes for OCR.
27
- env_prefix (str): Environment variable prefix for settings.
28
- """
29
- max_file_size_mb: int = 50
30
- allowed_extensions: List[str] = ['.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.gif']
31
- tesseract_pool_size: int = 2
32
- env_prefix: str = 'IMAGE_TOOL_'
33
+ # Exceptions
33
34
 
34
- model_config = ConfigDict(env_prefix='IMAGE_TOOL_')
35
35
 
36
- # Exceptions
37
36
  class ImageToolError(Exception):
38
37
  """Base exception for ImageTool errors."""
39
- pass
38
+
40
39
 
41
40
  class FileOperationError(ImageToolError):
42
41
  """Raised when file operations fail."""
43
- pass
42
+
44
43
 
45
44
  class SecurityError(ImageToolError):
46
45
  """Raised for security-related issues."""
47
- pass
46
+
48
47
 
49
48
  # Base schema for common fields
49
+
50
+
50
51
  class BaseFileSchema(BaseModel):
51
52
  file_path: str
52
53
  _mtime: Optional[float] = None # Internal use for cache
53
54
 
54
- @field_validator('file_path')
55
+ @field_validator("file_path")
55
56
  @classmethod
56
57
  def validate_file_path(cls, v: str) -> str:
57
58
  """Validate file path for existence, size, and extension."""
58
- settings = ImageSettings()
59
59
  abs_path = os.path.abspath(os.path.normpath(v))
60
60
  ext = os.path.splitext(abs_path)[1].lower()
61
- if ext not in settings.allowed_extensions:
62
- raise SecurityError(f"Extension '{ext}' not allowed, expected {settings.allowed_extensions}")
61
+ if ext not in _DEFAULT_ALLOWED_EXTENSIONS:
62
+ raise SecurityError(f"Extension '{ext}' not allowed, expected {_DEFAULT_ALLOWED_EXTENSIONS}")
63
63
  if not os.path.isfile(abs_path):
64
64
  raise FileOperationError(f"File not found: {abs_path}")
65
65
  size_mb = os.path.getsize(abs_path) / (1024 * 1024)
66
- if size_mb > settings.max_file_size_mb:
67
- raise FileOperationError(f"File too large: {size_mb:.1f}MB, max {settings.max_file_size_mb}MB")
66
+ if size_mb > _DEFAULT_MAX_FILE_SIZE_MB:
67
+ raise FileOperationError(f"File too large: {size_mb:.1f}MB, max {_DEFAULT_MAX_FILE_SIZE_MB}MB")
68
68
  return abs_path
69
69
 
70
- # Schemas for operations
71
- class LoadSchema(BaseFileSchema):
72
- """Schema for load operation."""
73
- pass
74
-
75
- class OCRSchema(BaseFileSchema):
76
- """Schema for OCR operation."""
77
- lang: Optional[str] = None
78
-
79
-
80
- class MetadataSchema(BaseFileSchema):
81
- """Schema for metadata extraction operation."""
82
- include_exif: bool = False
83
70
 
84
- class ResizeSchema(BaseFileSchema):
85
- """Schema for resize operation."""
86
- output_path: str
87
- width: int
88
- height: int
71
+ # Schemas for operations - moved to ImageTool class as inner classes
89
72
 
90
- @field_validator('output_path')
91
- @classmethod
92
- def validate_output_path(cls, v: str) -> str:
93
- """Validate output path for existence and extension."""
94
- settings = ImageSettings()
95
- abs_path = os.path.abspath(os.path.normpath(v))
96
- ext = os.path.splitext(abs_path)[1].lower()
97
- if ext not in settings.allowed_extensions:
98
- raise SecurityError(f"Output extension '{ext}' not allowed, expected {settings.allowed_extensions}")
99
- if os.path.exists(abs_path):
100
- raise FileOperationError(f"Output file already exists: {abs_path}")
101
- return abs_path
102
73
 
103
- class FilterSchema(BaseFileSchema):
104
- """Schema for filter operation."""
105
- output_path: str
106
- filter_type: str = 'blur'
74
+ # Tesseract process manager
107
75
 
108
- @field_validator('filter_type')
109
- @classmethod
110
- def validate_filter_type(cls, v: str) -> str:
111
- """Validate filter type."""
112
- valid_filters = ['blur', 'sharpen', 'edge_enhance']
113
- if v not in valid_filters:
114
- raise ValueError(f"Invalid filter_type '{v}', expected {valid_filters}")
115
- return v
116
-
117
- @field_validator('output_path')
118
- @classmethod
119
- def validate_output_path(cls, v: str) -> str:
120
- """Validate output path for existence and extension."""
121
- settings = ImageSettings()
122
- abs_path = os.path.abspath(os.path.normpath(v))
123
- ext = os.path.splitext(abs_path)[1].lower()
124
- if ext not in settings.allowed_extensions:
125
- raise SecurityError(f"Output extension '{ext}' not allowed, expected {settings.allowed_extensions}")
126
- if os.path.exists(abs_path):
127
- raise FileOperationError(f"Output file already exists: {abs_path}")
128
- return abs_path
129
76
 
130
- # Tesseract process manager
131
77
  @dataclass
132
78
  class TesseractManager:
133
79
  """Manages a pool of Tesseract processes for OCR."""
80
+
134
81
  pool_size: int
135
82
  processes: List[subprocess.Popen] = field(default_factory=list)
136
83
  queue: Queue = field(default_factory=lambda: Queue())
@@ -140,11 +87,11 @@ class TesseractManager:
140
87
  for _ in range(self.pool_size):
141
88
  try:
142
89
  proc = subprocess.Popen(
143
- ['tesseract', '--oem', '1', '-', 'stdout', '-l', 'eng'],
90
+ ["tesseract", "--oem", "1", "-", "stdout", "-l", "eng"],
144
91
  stdin=subprocess.PIPE,
145
92
  stdout=subprocess.PIPE,
146
93
  stderr=subprocess.PIPE,
147
- text=True
94
+ text=True,
148
95
  )
149
96
  self.queue.put(proc)
150
97
  self.processes.append(proc)
@@ -171,7 +118,8 @@ class TesseractManager:
171
118
  except (subprocess.TimeoutExpired, OSError) as e:
172
119
  logging.getLogger(__name__).warning(f"Error terminating Tesseract process: {e}")
173
120
 
174
- @register_tool('image')
121
+
122
+ @register_tool("image")
175
123
  class ImageTool(BaseTool):
176
124
  """
177
125
  Image processing tool supporting:
@@ -183,38 +131,142 @@ class ImageTool(BaseTool):
183
131
 
184
132
  Inherits from BaseTool to leverage ToolExecutor for caching, concurrency, and error handling.
185
133
  """
186
- def __init__(self, config: Dict[Any, Any] = None):
134
+
135
+ # Configuration schema
136
+ class Config(BaseSettings):
137
+ """Configuration for the image tool
138
+
139
+ Automatically reads from environment variables with IMAGE_TOOL_ prefix.
140
+ Example: IMAGE_TOOL_MAX_FILE_SIZE_MB -> max_file_size_mb
141
+ """
142
+
143
+ model_config = SettingsConfigDict(env_prefix="IMAGE_TOOL_")
144
+
145
+ max_file_size_mb: int = Field(default=50, description="Maximum file size in megabytes")
146
+ allowed_extensions: List[str] = Field(
147
+ default=[".jpg", ".jpeg", ".png", ".bmp", ".tiff", ".gif"],
148
+ description="Allowed image file extensions",
149
+ )
150
+ tesseract_pool_size: int = Field(default=2, description="Number of Tesseract processes for OCR")
151
+ default_ocr_language: str = Field(
152
+ default="eng",
153
+ description="Default OCR language code (e.g., 'eng', 'chi_sim'). Supports multi-language format like 'eng+chi_sim'"
154
+ )
155
+
156
+ # Schema definitions
157
+ class LoadSchema(BaseFileSchema):
158
+ """Schema for load operation"""
159
+
160
+ file_path: str = Field(description="Path to the image file to load")
161
+
162
+ class OcrSchema(BaseFileSchema):
163
+ """Schema for ocr operation"""
164
+
165
+ file_path: str = Field(description="Path to the image file for OCR text extraction")
166
+ lang: Optional[str] = Field(
167
+ default=None,
168
+ description="Optional language code for OCR (e.g., 'eng', 'chi_sim', 'eng+chi_sim'). If not specified, uses the configured default_ocr_language"
169
+ )
170
+
171
+ class MetadataSchema(BaseFileSchema):
172
+ """Schema for metadata operation"""
173
+
174
+ file_path: str = Field(description="Path to the image file to extract metadata from")
175
+ include_exif: bool = Field(default=False, description="Whether to include EXIF data in the metadata. If False, only basic info (size, mode) is returned")
176
+
177
+ class ResizeSchema(BaseFileSchema):
178
+ """Schema for resize operation"""
179
+
180
+ file_path: str = Field(description="Path to the source image file")
181
+ output_path: str = Field(description="Path where the resized image will be saved")
182
+ width: int = Field(description="Target width in pixels for the resized image")
183
+ height: int = Field(description="Target height in pixels for the resized image")
184
+
185
+ @field_validator("output_path")
186
+ @classmethod
187
+ def validate_output_path(cls, v: str) -> str:
188
+ """Validate output path for existence and extension."""
189
+ abs_path = os.path.abspath(os.path.normpath(v))
190
+ ext = os.path.splitext(abs_path)[1].lower()
191
+ if ext not in _DEFAULT_ALLOWED_EXTENSIONS:
192
+ raise SecurityError(f"Output extension '{ext}' not allowed, expected {_DEFAULT_ALLOWED_EXTENSIONS}")
193
+ if os.path.exists(abs_path):
194
+ raise FileOperationError(f"Output file already exists: {abs_path}")
195
+ return abs_path
196
+
197
+ class FilterSchema(BaseFileSchema):
198
+ """Schema for filter operation"""
199
+
200
+ file_path: str = Field(description="Path to the source image file")
201
+ output_path: str = Field(description="Path where the filtered image will be saved")
202
+ filter_type: str = Field(default="blur", description="Type of filter to apply: 'blur', 'sharpen', or 'edge_enhance'")
203
+
204
+ @field_validator("filter_type")
205
+ @classmethod
206
+ def validate_filter_type(cls, v: str) -> str:
207
+ """Validate filter type."""
208
+ valid_filters = ["blur", "sharpen", "edge_enhance"]
209
+ if v not in valid_filters:
210
+ raise ValueError(f"Invalid filter_type '{v}', expected {valid_filters}")
211
+ return v
212
+
213
+ @field_validator("output_path")
214
+ @classmethod
215
+ def validate_output_path(cls, v: str) -> str:
216
+ """Validate output path for existence and extension."""
217
+ abs_path = os.path.abspath(os.path.normpath(v))
218
+ ext = os.path.splitext(abs_path)[1].lower()
219
+ if ext not in _DEFAULT_ALLOWED_EXTENSIONS:
220
+ raise SecurityError(f"Output extension '{ext}' not allowed, expected {_DEFAULT_ALLOWED_EXTENSIONS}")
221
+ if os.path.exists(abs_path):
222
+ raise FileOperationError(f"Output file already exists: {abs_path}")
223
+ return abs_path
224
+
225
+ def __init__(self, config: Optional[Dict[str, Any]] = None, **kwargs):
187
226
  """
188
- Initialize ImageTool with settings and resources.
227
+ Initialize ImageTool with configuration and resources.
189
228
 
190
229
  Args:
191
- config (Dict, optional): Configuration overrides for ImageSettings.
230
+ config (Dict, optional): Configuration overrides for ImageTool.
231
+ **kwargs: Additional arguments passed to BaseTool (e.g., tool_name)
192
232
 
193
233
  Raises:
194
234
  ValueError: If config contains invalid settings.
235
+
236
+ Configuration is automatically loaded by BaseTool from:
237
+ 1. Explicit config dict (highest priority)
238
+ 2. YAML config files (config/tools/image.yaml, config/tools/image_tool.yaml, or config/tools/ImageTool.yaml)
239
+ 3. Environment variables (via dotenv from .env files with IMAGE_TOOL_ prefix)
240
+ 4. Tool defaults (lowest priority)
241
+
242
+ YAML configuration files are automatically discovered by ToolConfigLoader using multiple naming conventions.
243
+ See examples/config/tools/image_tool.yaml.example for a configuration template.
195
244
  """
196
- super().__init__(config)
197
- self.settings = ImageSettings()
198
- if config:
199
- try:
200
- self.settings = self.settings.model_validate({**self.settings.model_dump(), **config})
201
- except ValidationError as e:
202
- raise ValueError(f"Invalid configuration: {e}")
245
+ # Pass tool_name="image" to BaseTool so it can find config/tools/image.yaml
246
+ if "tool_name" not in kwargs:
247
+ kwargs["tool_name"] = "image"
248
+ super().__init__(config, **kwargs)
249
+
250
+ # Configuration is automatically loaded by BaseTool into self._config_obj
251
+ # Access config via self._config_obj (BaseSettings instance)
252
+ self.config = self._config_obj if self._config_obj else self.Config()
253
+
203
254
  self.logger = logging.getLogger(__name__)
204
255
  if not self.logger.handlers:
205
256
  handler = logging.StreamHandler()
206
- handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
257
+ handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s"))
207
258
  self.logger.addHandler(handler)
208
259
  self.logger.setLevel(logging.INFO)
260
+
209
261
  # Initialize Tesseract manager
210
- self._tesseract_manager = TesseractManager(self.settings.tesseract_pool_size)
262
+ self._tesseract_manager = TesseractManager(self.config.tesseract_pool_size)
211
263
  self._tesseract_manager.initialize()
212
264
 
213
265
  def __del__(self):
214
266
  """Clean up Tesseract processes on destruction."""
215
267
  self._tesseract_manager.cleanup()
216
268
 
217
- def update_settings(self, config: Dict):
269
+ def update_config(self, config: Dict) -> None:
218
270
  """
219
271
  Update configuration settings dynamically.
220
272
 
@@ -225,11 +277,11 @@ class ImageTool(BaseTool):
225
277
  ValueError: If config contains invalid settings.
226
278
  """
227
279
  try:
228
- self.settings = self.settings.model_validate({**self.settings.model_dump(), **config})
280
+ self.config = self.Config(**{**self.config.model_dump(), **config})
229
281
  # Reinitialize Tesseract if pool size changes
230
- if 'tesseract_pool_size' in config:
282
+ if "tesseract_pool_size" in config:
231
283
  self._tesseract_manager.cleanup()
232
- self._tesseract_manager = TesseractManager(self.settings.tesseract_pool_size)
284
+ self._tesseract_manager = TesseractManager(self.config.tesseract_pool_size)
233
285
  self._tesseract_manager.initialize()
234
286
  except ValidationError as e:
235
287
  raise ValueError(f"Invalid configuration: {e}")
@@ -248,22 +300,23 @@ class ImageTool(BaseTool):
248
300
  FileOperationError: If file is invalid or inaccessible.
249
301
  """
250
302
  # Validate input using schema
251
- validated_input = LoadSchema(file_path=file_path)
252
-
303
+ validated_input = self.LoadSchema(file_path=file_path)
304
+
253
305
  try:
254
306
  with Image.open(validated_input.file_path) as img:
255
307
  img.load()
256
- return {'size': img.size, 'mode': img.mode}
308
+ return {"size": img.size, "mode": img.mode}
257
309
  except Exception as e:
258
310
  raise FileOperationError(f"load: Failed to load image '{file_path}': {e}")
259
311
 
260
312
  def ocr(self, file_path: str, lang: Optional[str] = None) -> str:
261
313
  """
262
- Extract text from an image using a pooled Tesseract process.
314
+ Extract text from an image using Tesseract OCR.
263
315
 
264
316
  Args:
265
317
  file_path (str): Path to the image file.
266
- lang (Optional[str]): Language code for OCR (e.g., 'eng').
318
+ lang (Optional[str]): Language code for OCR (e.g., 'eng', 'chi_sim', 'eng+chi_sim').
319
+ If not specified, uses the configured default_ocr_language.
267
320
 
268
321
  Returns:
269
322
  str: Extracted text.
@@ -272,31 +325,51 @@ class ImageTool(BaseTool):
272
325
  FileOperationError: If OCR fails or Tesseract is unavailable.
273
326
  """
274
327
  # Validate input using schema
275
- validated_input = OCRSchema(file_path=file_path, lang=lang)
328
+ validated_input = self.OcrSchema(file_path=file_path, lang=lang)
276
329
 
277
- proc = self._tesseract_manager.get_process()
278
- if not proc:
279
- raise FileOperationError(f"ocr: No Tesseract processes available (lang: {lang or 'eng'})")
280
- with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as temp_file:
330
+ # Use configured default language if lang is not specified
331
+ ocr_lang = lang if lang is not None else self.config.default_ocr_language
332
+
333
+ # Prepare temporary file for image processing
334
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as temp_file:
281
335
  temp_path = temp_file.name
282
336
  try:
283
- img = Image.open(validated_input.file_path).convert('L').filter(ImageFilter.SHARPEN)
337
+ # Preprocess image for better OCR results
338
+ img = Image.open(validated_input.file_path).convert("L").filter(ImageFilter.SHARPEN)
284
339
  img.save(temp_path)
285
- stdout, stderr = proc.communicate(input=temp_path, timeout=30)
286
- if proc.returncode != 0:
287
- raise FileOperationError(f"ocr: Tesseract failed for '{file_path}' (lang: {lang or 'eng'}): {stderr}")
288
- return stdout.strip()
340
+
341
+ # Call Tesseract with dynamic language parameter
342
+ # Use subprocess.run instead of process pool to support dynamic languages
343
+ try:
344
+ result = subprocess.run(
345
+ ["tesseract", "--oem", "1", temp_path, "stdout", "-l", ocr_lang],
346
+ capture_output=True,
347
+ text=True,
348
+ timeout=30,
349
+ check=False, # Don't raise on non-zero return code, handle manually
350
+ )
351
+ except subprocess.TimeoutExpired:
352
+ raise FileOperationError(f"ocr: Tesseract timeout for '{file_path}' (lang: {ocr_lang})")
353
+ except FileNotFoundError:
354
+ raise FileOperationError("ocr: Tesseract not found. Please install Tesseract OCR.")
355
+
356
+ if result.returncode != 0:
357
+ raise FileOperationError(
358
+ f"ocr: Tesseract failed for '{file_path}' (lang: {ocr_lang}): {result.stderr}"
359
+ )
360
+
361
+ return result.stdout.strip()
362
+ except FileOperationError:
363
+ raise # Re-raise FileOperationError as-is
289
364
  except Exception as e:
290
- raise FileOperationError(f"ocr: Failed to process '{file_path}' (lang: {lang or 'eng'}): {e}")
365
+ raise FileOperationError(f"ocr: Failed to process '{file_path}' (lang: {ocr_lang}): {e}")
291
366
  finally:
292
- self._tesseract_manager.return_process(proc)
293
367
  if os.path.exists(temp_path):
294
368
  try:
295
369
  os.unlink(temp_path)
296
370
  except Exception as e:
297
371
  self.logger.warning(f"Failed to remove temporary file {temp_path}: {e}")
298
372
 
299
-
300
373
  def metadata(self, file_path: str, include_exif: bool = False) -> Dict[str, Any]:
301
374
  """
302
375
  Retrieve metadata (size, mode, EXIF) from an image.
@@ -312,19 +385,19 @@ class ImageTool(BaseTool):
312
385
  FileOperationError: If metadata extraction fails.
313
386
  """
314
387
  # Validate input using schema
315
- validated_input = MetadataSchema(file_path=file_path, include_exif=include_exif)
316
-
388
+ validated_input = self.MetadataSchema(file_path=file_path, include_exif=include_exif)
389
+
317
390
  try:
318
391
  with Image.open(validated_input.file_path) as img:
319
392
  img.load()
320
- info = {'size': img.size, 'mode': img.mode}
393
+ info = {"size": img.size, "mode": img.mode}
321
394
  if include_exif:
322
395
  exif = {}
323
396
  raw = img._getexif() or {}
324
397
  for tag, val in raw.items():
325
398
  decoded = ExifTags.TAGS.get(tag, tag)
326
399
  exif[decoded] = val
327
- info['exif'] = exif
400
+ info["exif"] = exif
328
401
  return info
329
402
  except Exception as e:
330
403
  raise FileOperationError(f"metadata: Failed to process '{file_path}': {e}")
@@ -346,18 +419,21 @@ class ImageTool(BaseTool):
346
419
  FileOperationError: If resizing fails.
347
420
  """
348
421
  # Validate input using schema
349
- validated_input = ResizeSchema(
350
- file_path=file_path,
351
- output_path=output_path,
352
- width=width,
353
- height=height
422
+ validated_input = self.ResizeSchema(
423
+ file_path=file_path,
424
+ output_path=output_path,
425
+ width=width,
426
+ height=height,
354
427
  )
355
-
428
+
356
429
  try:
357
430
  with Image.open(validated_input.file_path) as img:
358
431
  img = img.resize((width, height), Image.Resampling.LANCZOS)
359
432
  img.save(validated_input.output_path)
360
- return {'success': True, 'output_path': validated_input.output_path}
433
+ return {
434
+ "success": True,
435
+ "output_path": validated_input.output_path,
436
+ }
361
437
  except Exception as e:
362
438
  raise FileOperationError(f"resize: Failed to process '{file_path}' (output_path: {output_path}): {e}")
363
439
 
@@ -377,21 +453,24 @@ class ImageTool(BaseTool):
377
453
  FileOperationError: If filtering fails.
378
454
  """
379
455
  # Validate input using schema
380
- validated_input = FilterSchema(
381
- file_path=file_path,
382
- output_path=output_path,
383
- filter_type=filter_type
456
+ validated_input = self.FilterSchema(
457
+ file_path=file_path,
458
+ output_path=output_path,
459
+ filter_type=filter_type,
384
460
  )
385
-
461
+
386
462
  try:
387
463
  filter_map = {
388
- 'blur': ImageFilter.BLUR,
389
- 'sharpen': ImageFilter.SHARPEN,
390
- 'edge_enhance': ImageFilter.EDGE_ENHANCE
464
+ "blur": ImageFilter.BLUR,
465
+ "sharpen": ImageFilter.SHARPEN,
466
+ "edge_enhance": ImageFilter.EDGE_ENHANCE,
391
467
  }
392
468
  with Image.open(validated_input.file_path) as img:
393
469
  img = img.filter(filter_map[filter_type])
394
470
  img.save(validated_input.output_path)
395
- return {'success': True, 'output_path': validated_input.output_path}
471
+ return {
472
+ "success": True,
473
+ "output_path": validated_input.output_path,
474
+ }
396
475
  except Exception as e:
397
476
  raise FileOperationError(f"filter: Failed to process '{file_path}' (output_path: {output_path}, filter_type: {filter_type}): {e}")