aiecs 1.5.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (302) hide show
  1. aiecs/__init__.py +72 -0
  2. aiecs/__main__.py +41 -0
  3. aiecs/aiecs_client.py +469 -0
  4. aiecs/application/__init__.py +10 -0
  5. aiecs/application/executors/__init__.py +10 -0
  6. aiecs/application/executors/operation_executor.py +363 -0
  7. aiecs/application/knowledge_graph/__init__.py +7 -0
  8. aiecs/application/knowledge_graph/builder/__init__.py +37 -0
  9. aiecs/application/knowledge_graph/builder/document_builder.py +375 -0
  10. aiecs/application/knowledge_graph/builder/graph_builder.py +356 -0
  11. aiecs/application/knowledge_graph/builder/schema_mapping.py +531 -0
  12. aiecs/application/knowledge_graph/builder/structured_pipeline.py +443 -0
  13. aiecs/application/knowledge_graph/builder/text_chunker.py +319 -0
  14. aiecs/application/knowledge_graph/extractors/__init__.py +27 -0
  15. aiecs/application/knowledge_graph/extractors/base.py +100 -0
  16. aiecs/application/knowledge_graph/extractors/llm_entity_extractor.py +327 -0
  17. aiecs/application/knowledge_graph/extractors/llm_relation_extractor.py +349 -0
  18. aiecs/application/knowledge_graph/extractors/ner_entity_extractor.py +244 -0
  19. aiecs/application/knowledge_graph/fusion/__init__.py +23 -0
  20. aiecs/application/knowledge_graph/fusion/entity_deduplicator.py +387 -0
  21. aiecs/application/knowledge_graph/fusion/entity_linker.py +343 -0
  22. aiecs/application/knowledge_graph/fusion/knowledge_fusion.py +580 -0
  23. aiecs/application/knowledge_graph/fusion/relation_deduplicator.py +189 -0
  24. aiecs/application/knowledge_graph/pattern_matching/__init__.py +21 -0
  25. aiecs/application/knowledge_graph/pattern_matching/pattern_matcher.py +344 -0
  26. aiecs/application/knowledge_graph/pattern_matching/query_executor.py +378 -0
  27. aiecs/application/knowledge_graph/profiling/__init__.py +12 -0
  28. aiecs/application/knowledge_graph/profiling/query_plan_visualizer.py +199 -0
  29. aiecs/application/knowledge_graph/profiling/query_profiler.py +223 -0
  30. aiecs/application/knowledge_graph/reasoning/__init__.py +27 -0
  31. aiecs/application/knowledge_graph/reasoning/evidence_synthesis.py +347 -0
  32. aiecs/application/knowledge_graph/reasoning/inference_engine.py +504 -0
  33. aiecs/application/knowledge_graph/reasoning/logic_form_parser.py +167 -0
  34. aiecs/application/knowledge_graph/reasoning/logic_parser/__init__.py +79 -0
  35. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_builder.py +513 -0
  36. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_nodes.py +630 -0
  37. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_validator.py +654 -0
  38. aiecs/application/knowledge_graph/reasoning/logic_parser/error_handler.py +477 -0
  39. aiecs/application/knowledge_graph/reasoning/logic_parser/parser.py +390 -0
  40. aiecs/application/knowledge_graph/reasoning/logic_parser/query_context.py +217 -0
  41. aiecs/application/knowledge_graph/reasoning/logic_query_integration.py +169 -0
  42. aiecs/application/knowledge_graph/reasoning/query_planner.py +872 -0
  43. aiecs/application/knowledge_graph/reasoning/reasoning_engine.py +554 -0
  44. aiecs/application/knowledge_graph/retrieval/__init__.py +19 -0
  45. aiecs/application/knowledge_graph/retrieval/retrieval_strategies.py +596 -0
  46. aiecs/application/knowledge_graph/search/__init__.py +59 -0
  47. aiecs/application/knowledge_graph/search/hybrid_search.py +423 -0
  48. aiecs/application/knowledge_graph/search/reranker.py +295 -0
  49. aiecs/application/knowledge_graph/search/reranker_strategies.py +553 -0
  50. aiecs/application/knowledge_graph/search/text_similarity.py +398 -0
  51. aiecs/application/knowledge_graph/traversal/__init__.py +15 -0
  52. aiecs/application/knowledge_graph/traversal/enhanced_traversal.py +329 -0
  53. aiecs/application/knowledge_graph/traversal/path_scorer.py +269 -0
  54. aiecs/application/knowledge_graph/validators/__init__.py +13 -0
  55. aiecs/application/knowledge_graph/validators/relation_validator.py +189 -0
  56. aiecs/application/knowledge_graph/visualization/__init__.py +11 -0
  57. aiecs/application/knowledge_graph/visualization/graph_visualizer.py +321 -0
  58. aiecs/common/__init__.py +9 -0
  59. aiecs/common/knowledge_graph/__init__.py +17 -0
  60. aiecs/common/knowledge_graph/runnable.py +484 -0
  61. aiecs/config/__init__.py +16 -0
  62. aiecs/config/config.py +498 -0
  63. aiecs/config/graph_config.py +137 -0
  64. aiecs/config/registry.py +23 -0
  65. aiecs/core/__init__.py +46 -0
  66. aiecs/core/interface/__init__.py +34 -0
  67. aiecs/core/interface/execution_interface.py +152 -0
  68. aiecs/core/interface/storage_interface.py +171 -0
  69. aiecs/domain/__init__.py +289 -0
  70. aiecs/domain/agent/__init__.py +189 -0
  71. aiecs/domain/agent/base_agent.py +697 -0
  72. aiecs/domain/agent/exceptions.py +103 -0
  73. aiecs/domain/agent/graph_aware_mixin.py +559 -0
  74. aiecs/domain/agent/hybrid_agent.py +490 -0
  75. aiecs/domain/agent/integration/__init__.py +26 -0
  76. aiecs/domain/agent/integration/context_compressor.py +222 -0
  77. aiecs/domain/agent/integration/context_engine_adapter.py +252 -0
  78. aiecs/domain/agent/integration/retry_policy.py +219 -0
  79. aiecs/domain/agent/integration/role_config.py +213 -0
  80. aiecs/domain/agent/knowledge_aware_agent.py +646 -0
  81. aiecs/domain/agent/lifecycle.py +296 -0
  82. aiecs/domain/agent/llm_agent.py +300 -0
  83. aiecs/domain/agent/memory/__init__.py +12 -0
  84. aiecs/domain/agent/memory/conversation.py +197 -0
  85. aiecs/domain/agent/migration/__init__.py +14 -0
  86. aiecs/domain/agent/migration/conversion.py +160 -0
  87. aiecs/domain/agent/migration/legacy_wrapper.py +90 -0
  88. aiecs/domain/agent/models.py +317 -0
  89. aiecs/domain/agent/observability.py +407 -0
  90. aiecs/domain/agent/persistence.py +289 -0
  91. aiecs/domain/agent/prompts/__init__.py +29 -0
  92. aiecs/domain/agent/prompts/builder.py +161 -0
  93. aiecs/domain/agent/prompts/formatters.py +189 -0
  94. aiecs/domain/agent/prompts/template.py +255 -0
  95. aiecs/domain/agent/registry.py +260 -0
  96. aiecs/domain/agent/tool_agent.py +257 -0
  97. aiecs/domain/agent/tools/__init__.py +12 -0
  98. aiecs/domain/agent/tools/schema_generator.py +221 -0
  99. aiecs/domain/community/__init__.py +155 -0
  100. aiecs/domain/community/agent_adapter.py +477 -0
  101. aiecs/domain/community/analytics.py +481 -0
  102. aiecs/domain/community/collaborative_workflow.py +642 -0
  103. aiecs/domain/community/communication_hub.py +645 -0
  104. aiecs/domain/community/community_builder.py +320 -0
  105. aiecs/domain/community/community_integration.py +800 -0
  106. aiecs/domain/community/community_manager.py +813 -0
  107. aiecs/domain/community/decision_engine.py +879 -0
  108. aiecs/domain/community/exceptions.py +225 -0
  109. aiecs/domain/community/models/__init__.py +33 -0
  110. aiecs/domain/community/models/community_models.py +268 -0
  111. aiecs/domain/community/resource_manager.py +457 -0
  112. aiecs/domain/community/shared_context_manager.py +603 -0
  113. aiecs/domain/context/__init__.py +58 -0
  114. aiecs/domain/context/context_engine.py +989 -0
  115. aiecs/domain/context/conversation_models.py +354 -0
  116. aiecs/domain/context/graph_memory.py +467 -0
  117. aiecs/domain/execution/__init__.py +12 -0
  118. aiecs/domain/execution/model.py +57 -0
  119. aiecs/domain/knowledge_graph/__init__.py +19 -0
  120. aiecs/domain/knowledge_graph/models/__init__.py +52 -0
  121. aiecs/domain/knowledge_graph/models/entity.py +130 -0
  122. aiecs/domain/knowledge_graph/models/evidence.py +194 -0
  123. aiecs/domain/knowledge_graph/models/inference_rule.py +186 -0
  124. aiecs/domain/knowledge_graph/models/path.py +179 -0
  125. aiecs/domain/knowledge_graph/models/path_pattern.py +173 -0
  126. aiecs/domain/knowledge_graph/models/query.py +272 -0
  127. aiecs/domain/knowledge_graph/models/query_plan.py +187 -0
  128. aiecs/domain/knowledge_graph/models/relation.py +136 -0
  129. aiecs/domain/knowledge_graph/schema/__init__.py +23 -0
  130. aiecs/domain/knowledge_graph/schema/entity_type.py +135 -0
  131. aiecs/domain/knowledge_graph/schema/graph_schema.py +271 -0
  132. aiecs/domain/knowledge_graph/schema/property_schema.py +155 -0
  133. aiecs/domain/knowledge_graph/schema/relation_type.py +171 -0
  134. aiecs/domain/knowledge_graph/schema/schema_manager.py +496 -0
  135. aiecs/domain/knowledge_graph/schema/type_enums.py +205 -0
  136. aiecs/domain/task/__init__.py +13 -0
  137. aiecs/domain/task/dsl_processor.py +613 -0
  138. aiecs/domain/task/model.py +62 -0
  139. aiecs/domain/task/task_context.py +268 -0
  140. aiecs/infrastructure/__init__.py +24 -0
  141. aiecs/infrastructure/graph_storage/__init__.py +11 -0
  142. aiecs/infrastructure/graph_storage/base.py +601 -0
  143. aiecs/infrastructure/graph_storage/batch_operations.py +449 -0
  144. aiecs/infrastructure/graph_storage/cache.py +429 -0
  145. aiecs/infrastructure/graph_storage/distributed.py +226 -0
  146. aiecs/infrastructure/graph_storage/error_handling.py +390 -0
  147. aiecs/infrastructure/graph_storage/graceful_degradation.py +306 -0
  148. aiecs/infrastructure/graph_storage/health_checks.py +378 -0
  149. aiecs/infrastructure/graph_storage/in_memory.py +514 -0
  150. aiecs/infrastructure/graph_storage/index_optimization.py +483 -0
  151. aiecs/infrastructure/graph_storage/lazy_loading.py +410 -0
  152. aiecs/infrastructure/graph_storage/metrics.py +357 -0
  153. aiecs/infrastructure/graph_storage/migration.py +413 -0
  154. aiecs/infrastructure/graph_storage/pagination.py +471 -0
  155. aiecs/infrastructure/graph_storage/performance_monitoring.py +466 -0
  156. aiecs/infrastructure/graph_storage/postgres.py +871 -0
  157. aiecs/infrastructure/graph_storage/query_optimizer.py +635 -0
  158. aiecs/infrastructure/graph_storage/schema_cache.py +290 -0
  159. aiecs/infrastructure/graph_storage/sqlite.py +623 -0
  160. aiecs/infrastructure/graph_storage/streaming.py +495 -0
  161. aiecs/infrastructure/messaging/__init__.py +13 -0
  162. aiecs/infrastructure/messaging/celery_task_manager.py +383 -0
  163. aiecs/infrastructure/messaging/websocket_manager.py +298 -0
  164. aiecs/infrastructure/monitoring/__init__.py +34 -0
  165. aiecs/infrastructure/monitoring/executor_metrics.py +174 -0
  166. aiecs/infrastructure/monitoring/global_metrics_manager.py +213 -0
  167. aiecs/infrastructure/monitoring/structured_logger.py +48 -0
  168. aiecs/infrastructure/monitoring/tracing_manager.py +410 -0
  169. aiecs/infrastructure/persistence/__init__.py +24 -0
  170. aiecs/infrastructure/persistence/context_engine_client.py +187 -0
  171. aiecs/infrastructure/persistence/database_manager.py +333 -0
  172. aiecs/infrastructure/persistence/file_storage.py +754 -0
  173. aiecs/infrastructure/persistence/redis_client.py +220 -0
  174. aiecs/llm/__init__.py +86 -0
  175. aiecs/llm/callbacks/__init__.py +11 -0
  176. aiecs/llm/callbacks/custom_callbacks.py +264 -0
  177. aiecs/llm/client_factory.py +420 -0
  178. aiecs/llm/clients/__init__.py +33 -0
  179. aiecs/llm/clients/base_client.py +193 -0
  180. aiecs/llm/clients/googleai_client.py +181 -0
  181. aiecs/llm/clients/openai_client.py +131 -0
  182. aiecs/llm/clients/vertex_client.py +437 -0
  183. aiecs/llm/clients/xai_client.py +184 -0
  184. aiecs/llm/config/__init__.py +51 -0
  185. aiecs/llm/config/config_loader.py +275 -0
  186. aiecs/llm/config/config_validator.py +236 -0
  187. aiecs/llm/config/model_config.py +151 -0
  188. aiecs/llm/utils/__init__.py +10 -0
  189. aiecs/llm/utils/validate_config.py +91 -0
  190. aiecs/main.py +363 -0
  191. aiecs/scripts/__init__.py +3 -0
  192. aiecs/scripts/aid/VERSION_MANAGEMENT.md +97 -0
  193. aiecs/scripts/aid/__init__.py +19 -0
  194. aiecs/scripts/aid/version_manager.py +215 -0
  195. aiecs/scripts/dependance_check/DEPENDENCY_SYSTEM_SUMMARY.md +242 -0
  196. aiecs/scripts/dependance_check/README_DEPENDENCY_CHECKER.md +310 -0
  197. aiecs/scripts/dependance_check/__init__.py +17 -0
  198. aiecs/scripts/dependance_check/dependency_checker.py +938 -0
  199. aiecs/scripts/dependance_check/dependency_fixer.py +391 -0
  200. aiecs/scripts/dependance_check/download_nlp_data.py +396 -0
  201. aiecs/scripts/dependance_check/quick_dependency_check.py +270 -0
  202. aiecs/scripts/dependance_check/setup_nlp_data.sh +217 -0
  203. aiecs/scripts/dependance_patch/__init__.py +7 -0
  204. aiecs/scripts/dependance_patch/fix_weasel/README_WEASEL_PATCH.md +126 -0
  205. aiecs/scripts/dependance_patch/fix_weasel/__init__.py +11 -0
  206. aiecs/scripts/dependance_patch/fix_weasel/fix_weasel_validator.py +128 -0
  207. aiecs/scripts/dependance_patch/fix_weasel/fix_weasel_validator.sh +82 -0
  208. aiecs/scripts/dependance_patch/fix_weasel/patch_weasel_library.sh +188 -0
  209. aiecs/scripts/dependance_patch/fix_weasel/run_weasel_patch.sh +41 -0
  210. aiecs/scripts/tools_develop/README.md +449 -0
  211. aiecs/scripts/tools_develop/TOOL_AUTO_DISCOVERY.md +234 -0
  212. aiecs/scripts/tools_develop/__init__.py +21 -0
  213. aiecs/scripts/tools_develop/check_type_annotations.py +259 -0
  214. aiecs/scripts/tools_develop/validate_tool_schemas.py +422 -0
  215. aiecs/scripts/tools_develop/verify_tools.py +356 -0
  216. aiecs/tasks/__init__.py +1 -0
  217. aiecs/tasks/worker.py +172 -0
  218. aiecs/tools/__init__.py +299 -0
  219. aiecs/tools/apisource/__init__.py +99 -0
  220. aiecs/tools/apisource/intelligence/__init__.py +19 -0
  221. aiecs/tools/apisource/intelligence/data_fusion.py +381 -0
  222. aiecs/tools/apisource/intelligence/query_analyzer.py +413 -0
  223. aiecs/tools/apisource/intelligence/search_enhancer.py +388 -0
  224. aiecs/tools/apisource/monitoring/__init__.py +9 -0
  225. aiecs/tools/apisource/monitoring/metrics.py +303 -0
  226. aiecs/tools/apisource/providers/__init__.py +115 -0
  227. aiecs/tools/apisource/providers/base.py +664 -0
  228. aiecs/tools/apisource/providers/census.py +401 -0
  229. aiecs/tools/apisource/providers/fred.py +564 -0
  230. aiecs/tools/apisource/providers/newsapi.py +412 -0
  231. aiecs/tools/apisource/providers/worldbank.py +357 -0
  232. aiecs/tools/apisource/reliability/__init__.py +12 -0
  233. aiecs/tools/apisource/reliability/error_handler.py +375 -0
  234. aiecs/tools/apisource/reliability/fallback_strategy.py +391 -0
  235. aiecs/tools/apisource/tool.py +850 -0
  236. aiecs/tools/apisource/utils/__init__.py +9 -0
  237. aiecs/tools/apisource/utils/validators.py +338 -0
  238. aiecs/tools/base_tool.py +201 -0
  239. aiecs/tools/docs/__init__.py +121 -0
  240. aiecs/tools/docs/ai_document_orchestrator.py +599 -0
  241. aiecs/tools/docs/ai_document_writer_orchestrator.py +2403 -0
  242. aiecs/tools/docs/content_insertion_tool.py +1333 -0
  243. aiecs/tools/docs/document_creator_tool.py +1317 -0
  244. aiecs/tools/docs/document_layout_tool.py +1166 -0
  245. aiecs/tools/docs/document_parser_tool.py +994 -0
  246. aiecs/tools/docs/document_writer_tool.py +1818 -0
  247. aiecs/tools/knowledge_graph/__init__.py +17 -0
  248. aiecs/tools/knowledge_graph/graph_reasoning_tool.py +734 -0
  249. aiecs/tools/knowledge_graph/graph_search_tool.py +923 -0
  250. aiecs/tools/knowledge_graph/kg_builder_tool.py +476 -0
  251. aiecs/tools/langchain_adapter.py +542 -0
  252. aiecs/tools/schema_generator.py +275 -0
  253. aiecs/tools/search_tool/__init__.py +100 -0
  254. aiecs/tools/search_tool/analyzers.py +589 -0
  255. aiecs/tools/search_tool/cache.py +260 -0
  256. aiecs/tools/search_tool/constants.py +128 -0
  257. aiecs/tools/search_tool/context.py +216 -0
  258. aiecs/tools/search_tool/core.py +749 -0
  259. aiecs/tools/search_tool/deduplicator.py +123 -0
  260. aiecs/tools/search_tool/error_handler.py +271 -0
  261. aiecs/tools/search_tool/metrics.py +371 -0
  262. aiecs/tools/search_tool/rate_limiter.py +178 -0
  263. aiecs/tools/search_tool/schemas.py +277 -0
  264. aiecs/tools/statistics/__init__.py +80 -0
  265. aiecs/tools/statistics/ai_data_analysis_orchestrator.py +643 -0
  266. aiecs/tools/statistics/ai_insight_generator_tool.py +505 -0
  267. aiecs/tools/statistics/ai_report_orchestrator_tool.py +694 -0
  268. aiecs/tools/statistics/data_loader_tool.py +564 -0
  269. aiecs/tools/statistics/data_profiler_tool.py +658 -0
  270. aiecs/tools/statistics/data_transformer_tool.py +573 -0
  271. aiecs/tools/statistics/data_visualizer_tool.py +495 -0
  272. aiecs/tools/statistics/model_trainer_tool.py +487 -0
  273. aiecs/tools/statistics/statistical_analyzer_tool.py +459 -0
  274. aiecs/tools/task_tools/__init__.py +86 -0
  275. aiecs/tools/task_tools/chart_tool.py +732 -0
  276. aiecs/tools/task_tools/classfire_tool.py +922 -0
  277. aiecs/tools/task_tools/image_tool.py +447 -0
  278. aiecs/tools/task_tools/office_tool.py +684 -0
  279. aiecs/tools/task_tools/pandas_tool.py +635 -0
  280. aiecs/tools/task_tools/report_tool.py +635 -0
  281. aiecs/tools/task_tools/research_tool.py +392 -0
  282. aiecs/tools/task_tools/scraper_tool.py +715 -0
  283. aiecs/tools/task_tools/stats_tool.py +688 -0
  284. aiecs/tools/temp_file_manager.py +130 -0
  285. aiecs/tools/tool_executor/__init__.py +37 -0
  286. aiecs/tools/tool_executor/tool_executor.py +881 -0
  287. aiecs/utils/LLM_output_structor.py +445 -0
  288. aiecs/utils/__init__.py +34 -0
  289. aiecs/utils/base_callback.py +47 -0
  290. aiecs/utils/cache_provider.py +695 -0
  291. aiecs/utils/execution_utils.py +184 -0
  292. aiecs/utils/logging.py +1 -0
  293. aiecs/utils/prompt_loader.py +14 -0
  294. aiecs/utils/token_usage_repository.py +323 -0
  295. aiecs/ws/__init__.py +0 -0
  296. aiecs/ws/socket_server.py +52 -0
  297. aiecs-1.5.1.dist-info/METADATA +608 -0
  298. aiecs-1.5.1.dist-info/RECORD +302 -0
  299. aiecs-1.5.1.dist-info/WHEEL +5 -0
  300. aiecs-1.5.1.dist-info/entry_points.txt +10 -0
  301. aiecs-1.5.1.dist-info/licenses/LICENSE +225 -0
  302. aiecs-1.5.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1166 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Document Layout Tool
4
+
5
+ This tool is responsible for document layout, page formatting, and
6
+ visual presentation of documents across different formats.
7
+
8
+ Key Features:
9
+ 1. Page layout management (margins, orientation, size)
10
+ 2. Multi-column layouts and text flow
11
+ 3. Headers, footers, and page numbering
12
+ 4. Section breaks and page breaks
13
+ 5. Typography and spacing control
14
+ 6. Format-specific layout optimization
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
23
+ from enum import Enum
24
+
25
+ from pydantic import BaseModel, Field, ConfigDict
26
+
27
+ from aiecs.tools.base_tool import BaseTool
28
+ from aiecs.tools import register_tool
29
+
30
+
31
+ class PageSize(str, Enum):
32
+ """Standard page sizes"""
33
+
34
+ A4 = "a4"
35
+ A3 = "a3"
36
+ A5 = "a5"
37
+ LETTER = "letter"
38
+ LEGAL = "legal"
39
+ TABLOID = "tabloid"
40
+ CUSTOM = "custom"
41
+
42
+
43
+ class PageOrientation(str, Enum):
44
+ """Page orientations"""
45
+
46
+ PORTRAIT = "portrait"
47
+ LANDSCAPE = "landscape"
48
+
49
+
50
+ class LayoutType(str, Enum):
51
+ """Document layout types"""
52
+
53
+ SINGLE_COLUMN = "single_column"
54
+ TWO_COLUMN = "two_column"
55
+ THREE_COLUMN = "three_column"
56
+ MULTI_COLUMN = "multi_column"
57
+ MAGAZINE = "magazine"
58
+ NEWSPAPER = "newspaper"
59
+ ACADEMIC = "academic"
60
+ CUSTOM = "custom"
61
+
62
+
63
+ class AlignmentType(str, Enum):
64
+ """Text alignment types"""
65
+
66
+ LEFT = "left"
67
+ CENTER = "center"
68
+ RIGHT = "right"
69
+ JUSTIFY = "justify"
70
+
71
+
72
+ class BreakType(str, Enum):
73
+ """Break types"""
74
+
75
+ PAGE_BREAK = "page_break"
76
+ SECTION_BREAK = "section_break"
77
+ COLUMN_BREAK = "column_break"
78
+ LINE_BREAK = "line_break"
79
+
80
+
81
+ class HeaderFooterPosition(str, Enum):
82
+ """Header/footer positions"""
83
+
84
+ HEADER_LEFT = "header_left"
85
+ HEADER_CENTER = "header_center"
86
+ HEADER_RIGHT = "header_right"
87
+ FOOTER_LEFT = "footer_left"
88
+ FOOTER_CENTER = "footer_center"
89
+ FOOTER_RIGHT = "footer_right"
90
+
91
+
92
+ class DocumentLayoutError(Exception):
93
+ """Base exception for Document Layout errors"""
94
+
95
+
96
+ class LayoutConfigurationError(DocumentLayoutError):
97
+ """Raised when layout configuration fails"""
98
+
99
+
100
+ class PageSetupError(DocumentLayoutError):
101
+ """Raised when page setup fails"""
102
+
103
+
104
+ @register_tool("document_layout")
105
+ class DocumentLayoutTool(BaseTool):
106
+ """
107
+ Document Layout Tool for managing document presentation and formatting
108
+
109
+ This tool provides:
110
+ 1. Page setup and formatting
111
+ 2. Multi-column layouts
112
+ 3. Headers, footers, and page numbering
113
+ 4. Typography and spacing control
114
+ 5. Break management (page, section, column)
115
+ 6. Format-specific layout optimization
116
+
117
+ Integrates with:
118
+ - DocumentCreatorTool for initial document setup
119
+ - DocumentWriterTool for content placement
120
+ - ContentInsertionTool for complex content positioning
121
+ """
122
+
123
+ # Configuration schema
124
+ class Config(BaseModel):
125
+ """Configuration for the document layout tool"""
126
+
127
+ model_config = ConfigDict(env_prefix="DOC_LAYOUT_")
128
+
129
+ temp_dir: str = Field(
130
+ default=os.path.join(tempfile.gettempdir(), "document_layouts"),
131
+ description="Temporary directory for layout processing",
132
+ )
133
+ default_page_size: str = Field(default="a4", description="Default page size")
134
+ default_orientation: str = Field(default="portrait", description="Default page orientation")
135
+ default_margins: Dict[str, float] = Field(
136
+ default={"top": 2.5, "bottom": 2.5, "left": 2.5, "right": 2.5},
137
+ description="Default page margins in centimeters (top, bottom, left, right)",
138
+ )
139
+ auto_adjust_layout: bool = Field(
140
+ default=True,
141
+ description="Whether to automatically adjust layout for optimal presentation",
142
+ )
143
+ preserve_formatting: bool = Field(
144
+ default=True,
145
+ description="Whether to preserve existing formatting when applying layouts",
146
+ )
147
+
148
+ def __init__(self, config: Optional[Dict] = None):
149
+ """Initialize Document Layout Tool with settings"""
150
+ super().__init__(config)
151
+
152
+ # Parse configuration
153
+ self.config = self.Config(**(config or {}))
154
+
155
+ self.logger = logging.getLogger(__name__)
156
+
157
+ # Initialize directories
158
+ self._init_directories()
159
+
160
+ # Initialize layout presets
161
+ self._init_layout_presets()
162
+
163
+ # Track layout operations
164
+ self._layout_operations = []
165
+
166
+ def _init_directories(self):
167
+ """Initialize required directories"""
168
+ os.makedirs(self.config.temp_dir, exist_ok=True)
169
+
170
+ def _init_layout_presets(self):
171
+ """Initialize built-in layout presets"""
172
+ self.layout_presets = {
173
+ "default": self._get_default_layout(),
174
+ "academic_paper": self._get_academic_paper_layout(),
175
+ "business_report": self._get_business_report_layout(),
176
+ "magazine": self._get_magazine_layout(),
177
+ "newspaper": self._get_newspaper_layout(),
178
+ "presentation": self._get_presentation_layout(),
179
+ "technical_doc": self._get_technical_doc_layout(),
180
+ "letter": self._get_letter_layout(),
181
+ "invoice": self._get_invoice_layout(),
182
+ "brochure": self._get_brochure_layout(),
183
+ }
184
+
185
+ # Schema definitions
186
+ class SetPageLayoutSchema(BaseModel):
187
+ """Schema for set_page_layout operation"""
188
+
189
+ document_path: str = Field(description="Path to document")
190
+ page_size: PageSize = Field(description="Page size")
191
+ orientation: PageOrientation = Field(description="Page orientation")
192
+ margins: Dict[str, float] = Field(description="Page margins (top, bottom, left, right)")
193
+ layout_preset: Optional[str] = Field(default=None, description="Layout preset name")
194
+
195
+ class CreateMultiColumnSchema(BaseModel):
196
+ """Schema for create_multi_column_layout operation"""
197
+
198
+ document_path: str = Field(description="Path to document")
199
+ num_columns: int = Field(description="Number of columns")
200
+ column_gap: float = Field(default=1.0, description="Gap between columns (cm)")
201
+ column_widths: Optional[List[float]] = Field(
202
+ default=None, description="Custom column widths"
203
+ )
204
+ balance_columns: bool = Field(default=True, description="Balance column heights")
205
+
206
+ class SetupHeadersFootersSchema(BaseModel):
207
+ """Schema for setup_headers_footers operation"""
208
+
209
+ document_path: str = Field(description="Path to document")
210
+ header_config: Optional[Dict[str, Any]] = Field(
211
+ default=None, description="Header configuration"
212
+ )
213
+ footer_config: Optional[Dict[str, Any]] = Field(
214
+ default=None, description="Footer configuration"
215
+ )
216
+ page_numbering: bool = Field(default=True, description="Include page numbering")
217
+ numbering_style: str = Field(default="numeric", description="Page numbering style")
218
+
219
+ class InsertBreakSchema(BaseModel):
220
+ """Schema for insert_break operation"""
221
+
222
+ document_path: str = Field(description="Path to document")
223
+ break_type: BreakType = Field(description="Type of break to insert")
224
+ position: Optional[Dict[str, Any]] = Field(
225
+ default=None, description="Position to insert break"
226
+ )
227
+ break_options: Optional[Dict[str, Any]] = Field(
228
+ default=None, description="Break-specific options"
229
+ )
230
+
231
+ class ConfigureTypographySchema(BaseModel):
232
+ """Schema for configure_typography operation"""
233
+
234
+ document_path: str = Field(description="Path to document")
235
+ font_config: Dict[str, Any] = Field(description="Font configuration")
236
+ spacing_config: Optional[Dict[str, Any]] = Field(
237
+ default=None, description="Spacing configuration"
238
+ )
239
+ alignment: Optional[AlignmentType] = Field(default=None, description="Text alignment")
240
+
241
+ def set_page_layout(
242
+ self,
243
+ document_path: str,
244
+ page_size: PageSize,
245
+ orientation: PageOrientation,
246
+ margins: Dict[str, float],
247
+ layout_preset: Optional[str] = None,
248
+ ) -> Dict[str, Any]:
249
+ """
250
+ Set page layout configuration for document
251
+
252
+ Args:
253
+ document_path: Path to document
254
+ page_size: Page size (A4, Letter, etc.)
255
+ orientation: Page orientation (portrait/landscape)
256
+ margins: Page margins in cm (top, bottom, left, right)
257
+ layout_preset: Optional layout preset to apply
258
+
259
+ Returns:
260
+ Dict containing layout configuration results
261
+ """
262
+ try:
263
+ start_time = datetime.now()
264
+ operation_id = str(uuid.uuid4())
265
+
266
+ self.logger.info(f"Setting page layout {operation_id} for: {document_path}")
267
+
268
+ # Validate margins
269
+ required_margins = ["top", "bottom", "left", "right"]
270
+ for margin in required_margins:
271
+ if margin not in margins:
272
+ raise LayoutConfigurationError(f"Missing margin: {margin}")
273
+
274
+ # Apply layout preset if specified
275
+ if layout_preset:
276
+ preset_config = self._get_layout_preset(layout_preset)
277
+ if preset_config:
278
+ page_size = preset_config.get("page_size", page_size)
279
+ orientation = preset_config.get("orientation", orientation)
280
+ margins.update(preset_config.get("margins", {}))
281
+
282
+ # Create layout configuration
283
+ layout_config = {
284
+ "page_size": page_size,
285
+ "orientation": orientation,
286
+ "margins": margins,
287
+ "layout_preset": layout_preset,
288
+ "dimensions": self._calculate_page_dimensions(page_size, orientation, margins),
289
+ }
290
+
291
+ # Apply layout to document
292
+ self._apply_page_layout_to_document(document_path, layout_config)
293
+
294
+ # Track operation
295
+ operation_info = {
296
+ "operation_id": operation_id,
297
+ "operation_type": "set_page_layout",
298
+ "document_path": document_path,
299
+ "layout_config": layout_config,
300
+ "timestamp": start_time.isoformat(),
301
+ "duration": (datetime.now() - start_time).total_seconds(),
302
+ }
303
+
304
+ self._layout_operations.append(operation_info)
305
+
306
+ self.logger.info(f"Page layout {operation_id} applied successfully")
307
+ return operation_info
308
+
309
+ except Exception as e:
310
+ raise PageSetupError(f"Failed to set page layout: {str(e)}")
311
+
312
+ def create_multi_column_layout(
313
+ self,
314
+ document_path: str,
315
+ num_columns: int,
316
+ column_gap: float = 1.0,
317
+ column_widths: Optional[List[float]] = None,
318
+ balance_columns: bool = True,
319
+ ) -> Dict[str, Any]:
320
+ """
321
+ Create multi-column layout for document
322
+
323
+ Args:
324
+ document_path: Path to document
325
+ num_columns: Number of columns
326
+ column_gap: Gap between columns in cm
327
+ column_widths: Custom column widths (if None, equal widths)
328
+ balance_columns: Whether to balance column heights
329
+
330
+ Returns:
331
+ Dict containing multi-column layout results
332
+ """
333
+ try:
334
+ start_time = datetime.now()
335
+ operation_id = str(uuid.uuid4())
336
+
337
+ self.logger.info(
338
+ f"Creating {num_columns}-column layout {operation_id} for: {document_path}"
339
+ )
340
+
341
+ # Validate parameters
342
+ if num_columns < 1:
343
+ raise LayoutConfigurationError("Number of columns must be at least 1")
344
+ if column_widths and len(column_widths) != num_columns:
345
+ raise LayoutConfigurationError("Column widths count must match number of columns")
346
+
347
+ # Calculate column configuration
348
+ column_config = self._calculate_column_configuration(
349
+ num_columns, column_gap, column_widths, balance_columns
350
+ )
351
+
352
+ # Apply multi-column layout
353
+ self._apply_multi_column_layout(document_path, column_config)
354
+
355
+ operation_info = {
356
+ "operation_id": operation_id,
357
+ "operation_type": "create_multi_column_layout",
358
+ "document_path": document_path,
359
+ "column_config": column_config,
360
+ "timestamp": start_time.isoformat(),
361
+ "duration": (datetime.now() - start_time).total_seconds(),
362
+ }
363
+
364
+ self._layout_operations.append(operation_info)
365
+
366
+ self.logger.info(f"Multi-column layout {operation_id} created successfully")
367
+ return operation_info
368
+
369
+ except Exception as e:
370
+ raise LayoutConfigurationError(f"Failed to create multi-column layout: {str(e)}")
371
+
372
+ def setup_headers_footers(
373
+ self,
374
+ document_path: str,
375
+ header_config: Optional[Dict[str, Any]] = None,
376
+ footer_config: Optional[Dict[str, Any]] = None,
377
+ page_numbering: bool = True,
378
+ numbering_style: str = "numeric",
379
+ ) -> Dict[str, Any]:
380
+ """
381
+ Setup headers and footers for document
382
+
383
+ Args:
384
+ document_path: Path to document
385
+ header_config: Header configuration
386
+ footer_config: Footer configuration
387
+ page_numbering: Include page numbering
388
+ numbering_style: Page numbering style (numeric, roman, alpha)
389
+
390
+ Returns:
391
+ Dict containing header/footer setup results
392
+ """
393
+ try:
394
+ start_time = datetime.now()
395
+ operation_id = str(uuid.uuid4())
396
+
397
+ self.logger.info(f"Setting up headers/footers {operation_id} for: {document_path}")
398
+
399
+ # Process header configuration
400
+ processed_header = self._process_header_footer_config(
401
+ header_config, "header", page_numbering, numbering_style
402
+ )
403
+
404
+ # Process footer configuration
405
+ processed_footer = self._process_header_footer_config(
406
+ footer_config, "footer", page_numbering, numbering_style
407
+ )
408
+
409
+ # Apply headers and footers
410
+ self._apply_headers_footers(document_path, processed_header, processed_footer)
411
+
412
+ operation_info = {
413
+ "operation_id": operation_id,
414
+ "operation_type": "setup_headers_footers",
415
+ "document_path": document_path,
416
+ "header_config": processed_header,
417
+ "footer_config": processed_footer,
418
+ "page_numbering": page_numbering,
419
+ "numbering_style": numbering_style,
420
+ "timestamp": start_time.isoformat(),
421
+ "duration": (datetime.now() - start_time).total_seconds(),
422
+ }
423
+
424
+ self._layout_operations.append(operation_info)
425
+
426
+ self.logger.info(f"Headers/footers {operation_id} setup successfully")
427
+ return operation_info
428
+
429
+ except Exception as e:
430
+ raise LayoutConfigurationError(f"Failed to setup headers/footers: {str(e)}")
431
+
432
+ def insert_break(
433
+ self,
434
+ document_path: str,
435
+ break_type: BreakType,
436
+ position: Optional[Dict[str, Any]] = None,
437
+ break_options: Optional[Dict[str, Any]] = None,
438
+ ) -> Dict[str, Any]:
439
+ """
440
+ Insert page, section, or column break
441
+
442
+ Args:
443
+ document_path: Path to document
444
+ break_type: Type of break to insert
445
+ position: Position to insert break (line, offset, etc.)
446
+ break_options: Break-specific options
447
+
448
+ Returns:
449
+ Dict containing break insertion results
450
+ """
451
+ try:
452
+ start_time = datetime.now()
453
+ operation_id = str(uuid.uuid4())
454
+
455
+ self.logger.info(f"Inserting {break_type} break {operation_id} in: {document_path}")
456
+
457
+ # Determine break markup based on type and format
458
+ break_markup = self._generate_break_markup(break_type, break_options)
459
+
460
+ # Insert break at specified position
461
+ self._insert_break_at_position(document_path, break_markup, position)
462
+
463
+ operation_info = {
464
+ "operation_id": operation_id,
465
+ "operation_type": "insert_break",
466
+ "document_path": document_path,
467
+ "break_type": break_type,
468
+ "position": position,
469
+ "break_options": break_options,
470
+ "break_markup": break_markup,
471
+ "timestamp": start_time.isoformat(),
472
+ "duration": (datetime.now() - start_time).total_seconds(),
473
+ }
474
+
475
+ self._layout_operations.append(operation_info)
476
+
477
+ self.logger.info(f"Break {operation_id} inserted successfully")
478
+ return operation_info
479
+
480
+ except Exception as e:
481
+ raise LayoutConfigurationError(f"Failed to insert break: {str(e)}")
482
+
483
+ def configure_typography(
484
+ self,
485
+ document_path: str,
486
+ font_config: Dict[str, Any],
487
+ spacing_config: Optional[Dict[str, Any]] = None,
488
+ alignment: Optional[AlignmentType] = None,
489
+ ) -> Dict[str, Any]:
490
+ """
491
+ Configure typography and text formatting
492
+
493
+ Args:
494
+ document_path: Path to document
495
+ font_config: Font configuration (family, size, weight, etc.)
496
+ spacing_config: Spacing configuration (line height, paragraph spacing)
497
+ alignment: Text alignment
498
+
499
+ Returns:
500
+ Dict containing typography configuration results
501
+ """
502
+ try:
503
+ start_time = datetime.now()
504
+ operation_id = str(uuid.uuid4())
505
+
506
+ self.logger.info(f"Configuring typography {operation_id} for: {document_path}")
507
+
508
+ # Process typography configuration
509
+ typography_config = self._process_typography_config(
510
+ font_config, spacing_config, alignment
511
+ )
512
+
513
+ # Apply typography settings
514
+ self._apply_typography_settings(document_path, typography_config)
515
+
516
+ operation_info = {
517
+ "operation_id": operation_id,
518
+ "operation_type": "configure_typography",
519
+ "document_path": document_path,
520
+ "typography_config": typography_config,
521
+ "timestamp": start_time.isoformat(),
522
+ "duration": (datetime.now() - start_time).total_seconds(),
523
+ }
524
+
525
+ self._layout_operations.append(operation_info)
526
+
527
+ self.logger.info(f"Typography {operation_id} configured successfully")
528
+ return operation_info
529
+
530
+ except Exception as e:
531
+ raise LayoutConfigurationError(f"Failed to configure typography: {str(e)}")
532
+
533
+ def optimize_layout_for_content(
534
+ self,
535
+ document_path: str,
536
+ content_analysis: Dict[str, Any],
537
+ optimization_goals: List[str],
538
+ ) -> Dict[str, Any]:
539
+ """
540
+ Optimize document layout based on content analysis
541
+
542
+ Args:
543
+ document_path: Path to document
544
+ content_analysis: Analysis of document content
545
+ optimization_goals: List of optimization goals
546
+
547
+ Returns:
548
+ Dict containing layout optimization results
549
+ """
550
+ try:
551
+ start_time = datetime.now()
552
+ operation_id = str(uuid.uuid4())
553
+
554
+ self.logger.info(f"Optimizing layout {operation_id} for: {document_path}")
555
+
556
+ # Analyze current layout
557
+ current_layout = self._analyze_current_layout(document_path)
558
+
559
+ # Generate optimization recommendations
560
+ optimization_plan = self._generate_optimization_plan(
561
+ current_layout, content_analysis, optimization_goals
562
+ )
563
+
564
+ # Apply optimizations
565
+ optimization_results = self._apply_layout_optimizations(
566
+ document_path, optimization_plan
567
+ )
568
+
569
+ operation_info = {
570
+ "operation_id": operation_id,
571
+ "operation_type": "optimize_layout_for_content",
572
+ "document_path": document_path,
573
+ "content_analysis": content_analysis,
574
+ "optimization_goals": optimization_goals,
575
+ "optimization_plan": optimization_plan,
576
+ "optimization_results": optimization_results,
577
+ "timestamp": start_time.isoformat(),
578
+ "duration": (datetime.now() - start_time).total_seconds(),
579
+ }
580
+
581
+ self._layout_operations.append(operation_info)
582
+
583
+ self.logger.info(f"Layout optimization {operation_id} completed successfully")
584
+ return operation_info
585
+
586
+ except Exception as e:
587
+ raise LayoutConfigurationError(f"Failed to optimize layout: {str(e)}")
588
+
589
+ def get_layout_presets(self) -> Dict[str, Any]:
590
+ """
591
+ Get available layout presets
592
+
593
+ Returns:
594
+ Dict containing available layout presets
595
+ """
596
+ return {
597
+ "presets": list(self.layout_presets.keys()),
598
+ "preset_details": {
599
+ name: preset.get("description", "") for name, preset in self.layout_presets.items()
600
+ },
601
+ }
602
+
603
+ def get_layout_operations(self) -> List[Dict[str, Any]]:
604
+ """
605
+ Get list of layout operations performed
606
+
607
+ Returns:
608
+ List of layout operation information
609
+ """
610
+ return self._layout_operations.copy()
611
+
612
+ # Layout preset definitions
613
+ def _get_default_layout(self) -> Dict[str, Any]:
614
+ """Get default layout configuration"""
615
+ return {
616
+ "description": "Standard single-column layout",
617
+ "page_size": PageSize.A4,
618
+ "orientation": PageOrientation.PORTRAIT,
619
+ "margins": {"top": 2.5, "bottom": 2.5, "left": 2.5, "right": 2.5},
620
+ "columns": 1,
621
+ "font": {"family": "Arial", "size": 12},
622
+ "spacing": {"line_height": 1.5, "paragraph_spacing": 6},
623
+ }
624
+
625
+ def _get_academic_paper_layout(self) -> Dict[str, Any]:
626
+ """Get academic paper layout configuration"""
627
+ return {
628
+ "description": "Academic paper with double spacing",
629
+ "page_size": PageSize.A4,
630
+ "orientation": PageOrientation.PORTRAIT,
631
+ "margins": {"top": 2.5, "bottom": 2.5, "left": 3.0, "right": 2.5},
632
+ "columns": 1,
633
+ "font": {"family": "Times New Roman", "size": 12},
634
+ "spacing": {"line_height": 2.0, "paragraph_spacing": 0},
635
+ "headers_footers": {
636
+ "header_right": "{author_name}",
637
+ "footer_center": "{page_number}",
638
+ },
639
+ }
640
+
641
+ def _get_business_report_layout(self) -> Dict[str, Any]:
642
+ """Get business report layout configuration"""
643
+ return {
644
+ "description": "Professional business report layout",
645
+ "page_size": PageSize.A4,
646
+ "orientation": PageOrientation.PORTRAIT,
647
+ "margins": {"top": 2.0, "bottom": 2.0, "left": 2.5, "right": 2.5},
648
+ "columns": 1,
649
+ "font": {"family": "Calibri", "size": 11},
650
+ "spacing": {"line_height": 1.15, "paragraph_spacing": 6},
651
+ "headers_footers": {
652
+ "header_left": "{document_title}",
653
+ "header_right": "{date}",
654
+ "footer_center": "Page {page_number} of {total_pages}",
655
+ "footer_right": "{company_name}",
656
+ },
657
+ }
658
+
659
+ def _get_magazine_layout(self) -> Dict[str, Any]:
660
+ """Get magazine layout configuration"""
661
+ return {
662
+ "description": "Multi-column magazine layout",
663
+ "page_size": PageSize.A4,
664
+ "orientation": PageOrientation.PORTRAIT,
665
+ "margins": {"top": 1.5, "bottom": 1.5, "left": 1.5, "right": 1.5},
666
+ "columns": 2,
667
+ "column_gap": 0.8,
668
+ "font": {"family": "Georgia", "size": 10},
669
+ "spacing": {"line_height": 1.3, "paragraph_spacing": 4},
670
+ }
671
+
672
+ def _get_newspaper_layout(self) -> Dict[str, Any]:
673
+ """Get newspaper layout configuration"""
674
+ return {
675
+ "description": "Multi-column newspaper layout",
676
+ "page_size": PageSize.TABLOID,
677
+ "orientation": PageOrientation.PORTRAIT,
678
+ "margins": {"top": 1.0, "bottom": 1.0, "left": 1.0, "right": 1.0},
679
+ "columns": 4,
680
+ "column_gap": 0.5,
681
+ "font": {"family": "Arial", "size": 9},
682
+ "spacing": {"line_height": 1.2, "paragraph_spacing": 3},
683
+ }
684
+
685
+ def _get_presentation_layout(self) -> Dict[str, Any]:
686
+ """Get presentation layout configuration"""
687
+ return {
688
+ "description": "Landscape presentation layout",
689
+ "page_size": PageSize.A4,
690
+ "orientation": PageOrientation.LANDSCAPE,
691
+ "margins": {"top": 2.0, "bottom": 2.0, "left": 2.0, "right": 2.0},
692
+ "columns": 1,
693
+ "font": {"family": "Helvetica", "size": 14},
694
+ "spacing": {"line_height": 1.4, "paragraph_spacing": 12},
695
+ }
696
+
697
+ def _get_technical_doc_layout(self) -> Dict[str, Any]:
698
+ """Get technical documentation layout configuration"""
699
+ return {
700
+ "description": "Technical documentation with wide margins for notes",
701
+ "page_size": PageSize.A4,
702
+ "orientation": PageOrientation.PORTRAIT,
703
+ "margins": {"top": 2.0, "bottom": 2.0, "left": 3.5, "right": 2.0},
704
+ "columns": 1,
705
+ "font": {"family": "Consolas", "size": 10},
706
+ "spacing": {"line_height": 1.4, "paragraph_spacing": 8},
707
+ }
708
+
709
+ def _get_letter_layout(self) -> Dict[str, Any]:
710
+ """Get letter layout configuration"""
711
+ return {
712
+ "description": "Standard business letter layout",
713
+ "page_size": PageSize.LETTER,
714
+ "orientation": PageOrientation.PORTRAIT,
715
+ "margins": {"top": 2.5, "bottom": 2.5, "left": 2.5, "right": 2.5},
716
+ "columns": 1,
717
+ "font": {"family": "Times New Roman", "size": 12},
718
+ "spacing": {"line_height": 1.0, "paragraph_spacing": 12},
719
+ }
720
+
721
+ def _get_invoice_layout(self) -> Dict[str, Any]:
722
+ """Get invoice layout configuration"""
723
+ return {
724
+ "description": "Invoice and billing document layout",
725
+ "page_size": PageSize.A4,
726
+ "orientation": PageOrientation.PORTRAIT,
727
+ "margins": {"top": 1.5, "bottom": 1.5, "left": 2.0, "right": 2.0},
728
+ "columns": 1,
729
+ "font": {"family": "Arial", "size": 10},
730
+ "spacing": {"line_height": 1.2, "paragraph_spacing": 4},
731
+ }
732
+
733
+ def _get_brochure_layout(self) -> Dict[str, Any]:
734
+ """Get brochure layout configuration"""
735
+ return {
736
+ "description": "Tri-fold brochure layout",
737
+ "page_size": PageSize.A4,
738
+ "orientation": PageOrientation.LANDSCAPE,
739
+ "margins": {"top": 1.0, "bottom": 1.0, "left": 1.0, "right": 1.0},
740
+ "columns": 3,
741
+ "column_gap": 0.5,
742
+ "font": {"family": "Verdana", "size": 9},
743
+ "spacing": {"line_height": 1.3, "paragraph_spacing": 6},
744
+ }
745
+
746
+ # Helper methods
747
+ def _get_layout_preset(self, preset_name: str) -> Optional[Dict[str, Any]]:
748
+ """Get layout preset by name"""
749
+ return self.layout_presets.get(preset_name)
750
+
751
+ def _calculate_page_dimensions(
752
+ self,
753
+ page_size: PageSize,
754
+ orientation: PageOrientation,
755
+ margins: Dict[str, float],
756
+ ) -> Dict[str, float]:
757
+ """Calculate page dimensions including margins"""
758
+ # Standard page sizes in cm
759
+ page_sizes = {
760
+ PageSize.A4: (21.0, 29.7),
761
+ PageSize.A3: (29.7, 42.0),
762
+ PageSize.A5: (14.8, 21.0),
763
+ PageSize.LETTER: (21.59, 27.94),
764
+ PageSize.LEGAL: (21.59, 35.56),
765
+ PageSize.TABLOID: (27.94, 43.18),
766
+ }
767
+
768
+ width, height = page_sizes.get(page_size, (21.0, 29.7))
769
+
770
+ if orientation == PageOrientation.LANDSCAPE:
771
+ width, height = height, width
772
+
773
+ content_width = width - margins["left"] - margins["right"]
774
+ content_height = height - margins["top"] - margins["bottom"]
775
+
776
+ return {
777
+ "page_width": width,
778
+ "page_height": height,
779
+ "content_width": content_width,
780
+ "content_height": content_height,
781
+ "margins": margins,
782
+ }
783
+
784
+ def _calculate_column_configuration(
785
+ self,
786
+ num_columns: int,
787
+ column_gap: float,
788
+ column_widths: Optional[List[float]],
789
+ balance_columns: bool,
790
+ ) -> Dict[str, Any]:
791
+ """Calculate column configuration"""
792
+ config = {
793
+ "num_columns": num_columns,
794
+ "column_gap": column_gap,
795
+ "balance_columns": balance_columns,
796
+ }
797
+
798
+ if column_widths:
799
+ config["column_widths"] = column_widths
800
+ config["custom_widths"] = True
801
+ else:
802
+ # Equal column widths
803
+ config["custom_widths"] = False
804
+
805
+ return config
806
+
807
+ def _apply_page_layout_to_document(self, document_path: str, layout_config: Dict[str, Any]):
808
+ """Apply page layout configuration to document"""
809
+ # Detect document format
810
+ file_format = self._detect_document_format(document_path)
811
+
812
+ # Generate layout markup based on format
813
+ if file_format == "markdown":
814
+ layout_markup = self._generate_markdown_layout_markup(layout_config)
815
+ elif file_format == "html":
816
+ layout_markup = self._generate_html_layout_markup(layout_config)
817
+ elif file_format == "latex":
818
+ layout_markup = self._generate_latex_layout_markup(layout_config)
819
+ else:
820
+ layout_markup = self._generate_generic_layout_markup(layout_config)
821
+
822
+ # Insert layout markup into document
823
+ self._insert_layout_markup(document_path, layout_markup, "page_layout")
824
+
825
+ def _apply_multi_column_layout(self, document_path: str, column_config: Dict[str, Any]):
826
+ """Apply multi-column layout to document"""
827
+ file_format = self._detect_document_format(document_path)
828
+
829
+ if file_format == "html":
830
+ column_markup = self._generate_html_column_markup(column_config)
831
+ elif file_format == "latex":
832
+ column_markup = self._generate_latex_column_markup(column_config)
833
+ else:
834
+ column_markup = self._generate_generic_column_markup(column_config)
835
+
836
+ self._insert_layout_markup(document_path, column_markup, "multi_column")
837
+
838
+ def _apply_headers_footers(
839
+ self,
840
+ document_path: str,
841
+ header_config: Dict[str, Any],
842
+ footer_config: Dict[str, Any],
843
+ ):
844
+ """Apply headers and footers to document"""
845
+ file_format = self._detect_document_format(document_path)
846
+
847
+ header_markup = self._generate_header_footer_markup(header_config, "header", file_format)
848
+ footer_markup = self._generate_header_footer_markup(footer_config, "footer", file_format)
849
+
850
+ self._insert_layout_markup(document_path, header_markup, "headers")
851
+ self._insert_layout_markup(document_path, footer_markup, "footers")
852
+
853
+ def _process_header_footer_config(
854
+ self,
855
+ config: Optional[Dict[str, Any]],
856
+ hf_type: str,
857
+ page_numbering: bool,
858
+ numbering_style: str,
859
+ ) -> Dict[str, Any]:
860
+ """Process header or footer configuration"""
861
+ processed = config.copy() if config else {}
862
+
863
+ # Add page numbering if requested
864
+ if page_numbering:
865
+ numbering_text = self._generate_page_numbering_text(numbering_style)
866
+ if hf_type == "footer" and "center" not in processed:
867
+ processed["center"] = numbering_text
868
+ elif hf_type == "header" and "right" not in processed:
869
+ processed["right"] = numbering_text
870
+
871
+ return processed
872
+
873
+ def _generate_page_numbering_text(self, style: str) -> str:
874
+ """Generate page numbering text based on style"""
875
+ if style == "roman":
876
+ return "{page_roman}"
877
+ elif style == "alpha":
878
+ return "{page_alpha}"
879
+ elif style == "with_total":
880
+ return "Page {page} of {total_pages}"
881
+ else: # numeric
882
+ return "{page}"
883
+
884
+ def _generate_break_markup(
885
+ self, break_type: BreakType, options: Optional[Dict[str, Any]]
886
+ ) -> str:
887
+ """Generate break markup based on type"""
888
+ if break_type == BreakType.PAGE_BREAK:
889
+ return "\n<!-- PAGE BREAK -->\n\\newpage\n"
890
+ elif break_type == BreakType.SECTION_BREAK:
891
+ return "\n<!-- SECTION BREAK -->\n\\clearpage\n"
892
+ elif break_type == BreakType.COLUMN_BREAK:
893
+ return "\n<!-- COLUMN BREAK -->\n\\columnbreak\n"
894
+ elif break_type == BreakType.LINE_BREAK:
895
+ return "\n<!-- LINE BREAK -->\n\\linebreak\n"
896
+ else:
897
+ return "\n"
898
+
899
+ def _insert_break_at_position(
900
+ self,
901
+ document_path: str,
902
+ break_markup: str,
903
+ position: Optional[Dict[str, Any]],
904
+ ):
905
+ """Insert break markup at specified position"""
906
+ try:
907
+ with open(document_path, "r", encoding="utf-8") as f:
908
+ content = f.read()
909
+
910
+ if position:
911
+ if "line" in position:
912
+ lines = content.split("\n")
913
+ line_num = position["line"]
914
+ if 0 <= line_num <= len(lines):
915
+ lines.insert(line_num, break_markup.strip())
916
+ content = "\n".join(lines)
917
+ elif "offset" in position:
918
+ offset = position["offset"]
919
+ content = content[:offset] + break_markup + content[offset:]
920
+ else:
921
+ # Append at end
922
+ content += break_markup
923
+
924
+ with open(document_path, "w", encoding="utf-8") as f:
925
+ f.write(content)
926
+
927
+ except Exception as e:
928
+ raise LayoutConfigurationError(f"Failed to insert break: {str(e)}")
929
+
930
+ def _process_typography_config(
931
+ self,
932
+ font_config: Dict[str, Any],
933
+ spacing_config: Optional[Dict[str, Any]],
934
+ alignment: Optional[AlignmentType],
935
+ ) -> Dict[str, Any]:
936
+ """Process typography configuration"""
937
+ config = {
938
+ "font": font_config,
939
+ "spacing": spacing_config or {},
940
+ "alignment": alignment,
941
+ }
942
+
943
+ # Validate font configuration
944
+ required_font_keys = ["family", "size"]
945
+ for key in required_font_keys:
946
+ if key not in font_config:
947
+ raise LayoutConfigurationError(f"Missing font configuration: {key}")
948
+
949
+ return config
950
+
951
+ def _apply_typography_settings(self, document_path: str, typography_config: Dict[str, Any]):
952
+ """Apply typography settings to document"""
953
+ file_format = self._detect_document_format(document_path)
954
+
955
+ if file_format == "html":
956
+ typography_markup = self._generate_html_typography_markup(typography_config)
957
+ elif file_format == "latex":
958
+ typography_markup = self._generate_latex_typography_markup(typography_config)
959
+ else:
960
+ typography_markup = self._generate_generic_typography_markup(typography_config)
961
+
962
+ self._insert_layout_markup(document_path, typography_markup, "typography")
963
+
964
+ def _analyze_current_layout(self, document_path: str) -> Dict[str, Any]:
965
+ """Analyze current document layout"""
966
+ try:
967
+ with open(document_path, "r", encoding="utf-8") as f:
968
+ content = f.read()
969
+
970
+ return {
971
+ "content_length": len(content),
972
+ "line_count": len(content.split("\n")),
973
+ "word_count": len(content.split()),
974
+ "has_headers": "header" in content.lower(),
975
+ "has_columns": "column" in content.lower(),
976
+ "file_format": self._detect_document_format(document_path),
977
+ }
978
+ except Exception:
979
+ return {"error": "Failed to analyze layout"}
980
+
981
+ def _generate_optimization_plan(
982
+ self,
983
+ current_layout: Dict[str, Any],
984
+ content_analysis: Dict[str, Any],
985
+ optimization_goals: List[str],
986
+ ) -> Dict[str, Any]:
987
+ """Generate layout optimization plan"""
988
+ plan = {
989
+ "optimizations": [],
990
+ "goals": optimization_goals,
991
+ "current_layout": current_layout,
992
+ "content_analysis": content_analysis,
993
+ }
994
+
995
+ # Add optimizations based on goals
996
+ for goal in optimization_goals:
997
+ if goal == "readability":
998
+ plan["optimizations"].append(
999
+ {
1000
+ "type": "typography",
1001
+ "action": "improve_readability",
1002
+ "details": "Increase line height and adjust font size",
1003
+ }
1004
+ )
1005
+ elif goal == "space_efficiency":
1006
+ plan["optimizations"].append(
1007
+ {
1008
+ "type": "layout",
1009
+ "action": "optimize_spacing",
1010
+ "details": "Reduce margins and adjust paragraph spacing",
1011
+ }
1012
+ )
1013
+ elif goal == "professional":
1014
+ plan["optimizations"].append(
1015
+ {
1016
+ "type": "styling",
1017
+ "action": "apply_professional_style",
1018
+ "details": "Use professional fonts and consistent formatting",
1019
+ }
1020
+ )
1021
+
1022
+ return plan
1023
+
1024
+ def _apply_layout_optimizations(
1025
+ self, document_path: str, optimization_plan: Dict[str, Any]
1026
+ ) -> Dict[str, Any]:
1027
+ """Apply layout optimizations based on plan"""
1028
+ results = {
1029
+ "optimizations_applied": [],
1030
+ "success_count": 0,
1031
+ "error_count": 0,
1032
+ }
1033
+
1034
+ for optimization in optimization_plan.get("optimizations", []):
1035
+ try:
1036
+ # Apply optimization based on type
1037
+ if optimization["type"] == "typography":
1038
+ self._apply_typography_optimization(document_path, optimization)
1039
+ elif optimization["type"] == "layout":
1040
+ self._apply_layout_optimization(document_path, optimization)
1041
+ elif optimization["type"] == "styling":
1042
+ self._apply_styling_optimization(document_path, optimization)
1043
+
1044
+ results["optimizations_applied"].append(optimization)
1045
+ results["success_count"] += 1
1046
+
1047
+ except Exception as e:
1048
+ results["error_count"] += 1
1049
+ self.logger.warning(f"Failed to apply optimization {optimization['type']}: {e}")
1050
+
1051
+ return results
1052
+
1053
+ def _apply_typography_optimization(self, document_path: str, optimization: Dict[str, Any]):
1054
+ """Apply typography optimization"""
1055
+ # Simplified implementation
1056
+
1057
+ def _apply_layout_optimization(self, document_path: str, optimization: Dict[str, Any]):
1058
+ """Apply layout optimization"""
1059
+ # Simplified implementation
1060
+
1061
+ def _apply_styling_optimization(self, document_path: str, optimization: Dict[str, Any]):
1062
+ """Apply styling optimization"""
1063
+ # Simplified implementation
1064
+
1065
+ def _detect_document_format(self, document_path: str) -> str:
1066
+ """Detect document format from file extension"""
1067
+ ext = os.path.splitext(document_path)[1].lower()
1068
+ format_map = {
1069
+ ".md": "markdown",
1070
+ ".markdown": "markdown",
1071
+ ".html": "html",
1072
+ ".htm": "html",
1073
+ ".tex": "latex",
1074
+ ".latex": "latex",
1075
+ ".txt": "text",
1076
+ }
1077
+ return format_map.get(ext, "text")
1078
+
1079
+ def _generate_markdown_layout_markup(self, layout_config: Dict[str, Any]) -> str:
1080
+ """Generate Markdown layout markup"""
1081
+ return f"<!-- Layout: {layout_config['page_size']} {layout_config['orientation']} -->\n"
1082
+
1083
+ def _generate_html_layout_markup(self, layout_config: Dict[str, Any]) -> str:
1084
+ """Generate HTML layout markup"""
1085
+ margins = layout_config["margins"]
1086
+ return f"""<style>
1087
+ @page {{
1088
+ size: {layout_config['page_size']};
1089
+ margin: {margins['top']}cm {margins['right']}cm {margins['bottom']}cm {margins['left']}cm;
1090
+ }}
1091
+ </style>"""
1092
+
1093
+ def _generate_latex_layout_markup(self, layout_config: Dict[str, Any]) -> str:
1094
+ """Generate LaTeX layout markup"""
1095
+ margins = layout_config["margins"]
1096
+ return f"""\\usepackage[top={margins['top']}cm,bottom={margins['bottom']}cm,left={margins['left']}cm,right={margins['right']}cm]{{geometry}}
1097
+ \\usepackage[{layout_config['orientation']}]{{geometry}}"""
1098
+
1099
+ def _generate_generic_layout_markup(self, layout_config: Dict[str, Any]) -> str:
1100
+ """Generate generic layout markup"""
1101
+ return f"# Layout Configuration\nPage: {layout_config['page_size']} {layout_config['orientation']}\n"
1102
+
1103
+ def _generate_html_column_markup(self, column_config: Dict[str, Any]) -> str:
1104
+ """Generate HTML column markup"""
1105
+ num_cols = column_config["num_columns"]
1106
+ gap = column_config["column_gap"]
1107
+ return f"""<style>
1108
+ .multi-column {{
1109
+ column-count: {num_cols};
1110
+ column-gap: {gap}cm;
1111
+ }}
1112
+ </style>
1113
+ <div class="multi-column">"""
1114
+
1115
+ def _generate_latex_column_markup(self, column_config: Dict[str, Any]) -> str:
1116
+ """Generate LaTeX column markup"""
1117
+ return f"\\begin{{multicols}}{{{column_config['num_columns']}}}"
1118
+
1119
+ def _generate_generic_column_markup(self, column_config: Dict[str, Any]) -> str:
1120
+ """Generate generic column markup"""
1121
+ return f"<!-- {column_config['num_columns']} columns -->\n"
1122
+
1123
+ def _generate_header_footer_markup(
1124
+ self, config: Dict[str, Any], hf_type: str, file_format: str
1125
+ ) -> str:
1126
+ """Generate header/footer markup"""
1127
+ if file_format == "html":
1128
+ return f"<!-- {hf_type.upper()}: {config} -->\n"
1129
+ elif file_format == "latex":
1130
+ return f"% {hf_type.upper()}: {config}\n"
1131
+ else:
1132
+ return f"# {hf_type.upper()}: {config}\n"
1133
+
1134
+ def _generate_html_typography_markup(self, typography_config: Dict[str, Any]) -> str:
1135
+ """Generate HTML typography markup"""
1136
+ font = typography_config["font"]
1137
+ return f"""<style>
1138
+ body {{
1139
+ font-family: '{font['family']}';
1140
+ font-size: {font['size']}pt;
1141
+ }}
1142
+ </style>"""
1143
+
1144
+ def _generate_latex_typography_markup(self, typography_config: Dict[str, Any]) -> str:
1145
+ """Generate LaTeX typography markup"""
1146
+ font = typography_config["font"]
1147
+ return f"\\usepackage{{fontspec}}\n\\setmainfont{{{font['family']}}}\n"
1148
+
1149
+ def _generate_generic_typography_markup(self, typography_config: Dict[str, Any]) -> str:
1150
+ """Generate generic typography markup"""
1151
+ return f"# Typography: {typography_config['font']}\n"
1152
+
1153
+ def _insert_layout_markup(self, document_path: str, markup: str, markup_type: str):
1154
+ """Insert layout markup into document"""
1155
+ try:
1156
+ with open(document_path, "r", encoding="utf-8") as f:
1157
+ content = f.read()
1158
+
1159
+ # Insert at the beginning of document
1160
+ content = markup + "\n" + content
1161
+
1162
+ with open(document_path, "w", encoding="utf-8") as f:
1163
+ f.write(content)
1164
+
1165
+ except Exception as e:
1166
+ raise LayoutConfigurationError(f"Failed to insert {markup_type} markup: {str(e)}")