iatoolkit 2.18.6__tar.gz → 2.19.0__tar.gz

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 (245) hide show
  1. {iatoolkit-2.18.6/src/iatoolkit.egg-info → iatoolkit-2.19.0}/PKG-INFO +1 -1
  2. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/__init__.py +1 -1
  3. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/locales/en.yaml +8 -0
  4. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/locales/es.yaml +8 -0
  5. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/repositories/database_manager.py +25 -1
  6. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/company_context_service.py +84 -10
  7. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/sql_service.py +107 -12
  8. {iatoolkit-2.18.6 → iatoolkit-2.19.0/src/iatoolkit.egg-info}/PKG-INFO +1 -1
  9. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/LICENSE +0 -0
  10. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/LICENSE_COMMUNITY.md +0 -0
  11. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/pyproject.toml +0 -0
  12. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/readme.md +0 -0
  13. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/requirements.txt +0 -0
  14. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/setup.cfg +0 -0
  15. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/base_company.py +0 -0
  16. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/cli_commands.py +0 -0
  17. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/common/__init__.py +0 -0
  18. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/common/exceptions.py +0 -0
  19. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/common/interfaces/__init__.py +0 -0
  20. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/common/interfaces/asset_storage.py +0 -0
  21. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/common/interfaces/database_provider.py +0 -0
  22. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/common/interfaces/memory_compilation_trigger.py +0 -0
  23. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/common/interfaces/memory_lint_trigger.py +0 -0
  24. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/common/interfaces/secret_provider.py +0 -0
  25. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/common/interfaces/signup_policy_resolver.py +0 -0
  26. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/common/interfaces/web_search_provider.py +0 -0
  27. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/common/model_registry.py +0 -0
  28. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/common/routes.py +0 -0
  29. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/common/secret_resolver.py +0 -0
  30. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/common/session_manager.py +0 -0
  31. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/common/util.py +0 -0
  32. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/company_registry.py +0 -0
  33. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/__init__.py +0 -0
  34. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/llm_capabilities.yaml +0 -0
  35. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/channel_business_identity.prompt +0 -0
  36. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/channel_conversation_continuity.prompt +0 -0
  37. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/channel_response_style.prompt +0 -0
  38. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/chat_state_rules.prompt +0 -0
  39. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/chat_user_profile.prompt +0 -0
  40. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/core_identity.prompt +0 -0
  41. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/email_output.prompt +0 -0
  42. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/file_download_output.prompt +0 -0
  43. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/format_styles.prompt +0 -0
  44. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/html_structures.prompt +0 -0
  45. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/links_documents.prompt +0 -0
  46. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/memory_usage.prompt +0 -0
  47. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/multimodal_basics.prompt +0 -0
  48. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/output_basics.prompt +0 -0
  49. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/presentation_formatting.prompt +0 -0
  50. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/query_main.prompt +0 -0
  51. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/sql_aggregation_scope.prompt +0 -0
  52. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/sql_casting.prompt +0 -0
  53. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/sql_core.prompt +0 -0
  54. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/sql_jsonb.prompt +0 -0
  55. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/sql_mysql_casting.prompt +0 -0
  56. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/sql_mysql_json.prompt +0 -0
  57. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/sql_redshift_basics.prompt +0 -0
  58. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/sql_redshift_types.prompt +0 -0
  59. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/sql_rules.prompt +0 -0
  60. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts/tool_html_passthrough.prompt +0 -0
  61. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_prompts_pack.yaml +0 -0
  62. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/config/system_tools_pack.yaml +0 -0
  63. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/core.py +0 -0
  64. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/__init__.py +0 -0
  65. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/brevo_mail_app.py +0 -0
  66. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/call_service.py +0 -0
  67. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/connectors/__init__.py +0 -0
  68. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/connectors/file_connector.py +0 -0
  69. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/connectors/file_connector_factory.py +0 -0
  70. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/connectors/google_cloud_storage_connector.py +0 -0
  71. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/connectors/google_drive_connector.py +0 -0
  72. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/connectors/local_file_connector.py +0 -0
  73. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/connectors/s3_connector.py +0 -0
  74. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/google_auth_client.py +0 -0
  75. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/google_chat_app.py +0 -0
  76. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/inference_embeddings_client.py +0 -0
  77. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/inference_handler.py +0 -0
  78. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/jina_embeddings_client.py +0 -0
  79. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/llm_gateway_resolver.py +0 -0
  80. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/llm_providers/__init__.py +0 -0
  81. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/llm_providers/anthropic_adapter.py +0 -0
  82. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/llm_providers/deepseek_adapter.py +0 -0
  83. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/llm_providers/gemini_adapter.py +0 -0
  84. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/llm_providers/openai_adapter.py +0 -0
  85. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/llm_providers/openai_compatible_chat_adapter.py +0 -0
  86. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/llm_providers/openrouter_adapter.py +0 -0
  87. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/llm_proxy.py +0 -0
  88. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/llm_response.py +0 -0
  89. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/infra/redis_session_manager.py +0 -0
  90. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/repositories/__init__.py +0 -0
  91. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/repositories/api_key_repo.py +0 -0
  92. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/repositories/document_repo.py +0 -0
  93. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/repositories/env_secret_provider.py +0 -0
  94. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/repositories/filesystem_asset_repository.py +0 -0
  95. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/repositories/llm_query_repo.py +0 -0
  96. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/repositories/mcp_token_repo.py +0 -0
  97. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/repositories/memory_repo.py +0 -0
  98. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/repositories/models.py +0 -0
  99. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/repositories/profile_repo.py +0 -0
  100. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/repositories/prompt_resource_repo.py +0 -0
  101. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/repositories/sql_dataset_repo.py +0 -0
  102. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/repositories/sql_source_repo.py +0 -0
  103. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/repositories/vs_repo.py +0 -0
  104. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/runtime_logging.py +0 -0
  105. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/__init__.py +0 -0
  106. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/api_key_service.py +0 -0
  107. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/attachment_policy_service.py +0 -0
  108. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/auth_service.py +0 -0
  109. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/benchmark_service.py +0 -0
  110. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/branding_service.py +0 -0
  111. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/configuration_service.py +0 -0
  112. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/context_builder_service.py +0 -0
  113. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/dispatcher_service.py +0 -0
  114. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/embedding_service.py +0 -0
  115. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/excel_service.py +0 -0
  116. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/file_processor_service.py +0 -0
  117. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/history_manager_service.py +0 -0
  118. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/http_tool_service.py +0 -0
  119. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/i18n_service.py +0 -0
  120. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/inference_service.py +0 -0
  121. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/jwt_service.py +0 -0
  122. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/knowledge_base_service.py +0 -0
  123. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/language_service.py +0 -0
  124. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/license_service.py +0 -0
  125. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/llm_client_service.py +0 -0
  126. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/mail_service.py +0 -0
  127. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/mcp_token_service.py +0 -0
  128. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/memory_compiler_service.py +0 -0
  129. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/memory_lint_service.py +0 -0
  130. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/memory_lookup_policy_service.py +0 -0
  131. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/memory_service.py +0 -0
  132. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/memory_wiki_service.py +0 -0
  133. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/noop_memory_compilation_trigger.py +0 -0
  134. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/noop_memory_lint_trigger.py +0 -0
  135. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/parsers/__init__.py +0 -0
  136. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/parsers/contracts.py +0 -0
  137. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/parsers/image_normalizer.py +0 -0
  138. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/parsers/parsing_service.py +0 -0
  139. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/parsers/pdf_ocr_detection.py +0 -0
  140. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/parsers/provider_factory.py +0 -0
  141. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/parsers/provider_resolver.py +0 -0
  142. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/parsers/providers/__init__.py +0 -0
  143. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/parsers/providers/basic_provider.py +0 -0
  144. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/parsers/providers/docling_provider.py +0 -0
  145. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/parsers/validator.py +0 -0
  146. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/pdf_service.py +0 -0
  147. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/profile_service.py +0 -0
  148. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/prompt_resource_service.py +0 -0
  149. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/prompt_service.py +0 -0
  150. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/query_service.py +0 -0
  151. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/signup_policy_resolver.py +0 -0
  152. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/sql_dataset_service.py +0 -0
  153. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/sql_source_service.py +0 -0
  154. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/storage_service.py +0 -0
  155. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/structured_output_service.py +0 -0
  156. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/system_prompt_catalog.py +0 -0
  157. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/system_tools.py +0 -0
  158. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/telemetry_service.py +0 -0
  159. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/tool_service.py +0 -0
  160. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/user_feedback_service.py +0 -0
  161. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/user_session_context_service.py +0 -0
  162. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/visual_kb_service.py +0 -0
  163. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/visual_tool_service.py +0 -0
  164. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/warmup_service.py +0 -0
  165. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/web_search/__init__.py +0 -0
  166. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/web_search/provider_factory.py +0 -0
  167. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/web_search/providers/__init__.py +0 -0
  168. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/web_search/providers/brave_provider.py +0 -0
  169. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/services/web_search_service.py +0 -0
  170. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/static/images/fernando.jpeg +0 -0
  171. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/static/images/iatoolkit_core.png +0 -0
  172. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/static/images/iatoolkit_logo.png +0 -0
  173. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/static/js/chat_feedback_button.js +0 -0
  174. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/static/js/chat_filepond.js +0 -0
  175. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/static/js/chat_help_content.js +0 -0
  176. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/static/js/chat_history_button.js +0 -0
  177. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/static/js/chat_logout_button.js +0 -0
  178. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/static/js/chat_main.js +0 -0
  179. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/static/js/chat_memory_button.js +0 -0
  180. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/static/js/chat_model_selector.js +0 -0
  181. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/static/js/chat_onboarding_button.js +0 -0
  182. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/static/js/chat_prompt_manager.js +0 -0
  183. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/static/js/chat_reload_button.js +0 -0
  184. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/static/styles/chat_iatoolkit.css +0 -0
  185. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/static/styles/chat_modal.css +0 -0
  186. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/static/styles/chat_public.css +0 -0
  187. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/static/styles/documents.css +0 -0
  188. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/static/styles/landing_page.css +0 -0
  189. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/static/styles/llm_output.css +0 -0
  190. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/static/styles/onboarding.css +0 -0
  191. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/templates/_company_header.html +0 -0
  192. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/templates/_login_widget.html +0 -0
  193. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/templates/account.html +0 -0
  194. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/templates/base.html +0 -0
  195. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/templates/change_password.html +0 -0
  196. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/templates/chat.html +0 -0
  197. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/templates/chat_memory_modal.html +0 -0
  198. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/templates/chat_modals.html +0 -0
  199. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/templates/error.html +0 -0
  200. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/templates/forgot_password.html +0 -0
  201. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/templates/home_hosted_default.html +0 -0
  202. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/templates/memory/wiki_schema.md +0 -0
  203. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/templates/onboarding_shell.html +0 -0
  204. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/templates/pdf/base.html +0 -0
  205. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/templates/pdf/letter.html +0 -0
  206. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/templates/pdf/report.html +0 -0
  207. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/templates/pdf/simple.html +0 -0
  208. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/templates/signup.html +0 -0
  209. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/__init__.py +0 -0
  210. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/account_view.py +0 -0
  211. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/api_key_api_view.py +0 -0
  212. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/base_login_view.py +0 -0
  213. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/categories_api_view.py +0 -0
  214. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/change_password_view.py +0 -0
  215. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/chat_context_preview_api_view.py +0 -0
  216. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/chat_view.py +0 -0
  217. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/configuration_api_view.py +0 -0
  218. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/connectors_api_view.py +0 -0
  219. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/embedding_api_view.py +0 -0
  220. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/forgot_password_view.py +0 -0
  221. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/help_content_api_view.py +0 -0
  222. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/history_api_view.py +0 -0
  223. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/home_view.py +0 -0
  224. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/init_context_api_view.py +0 -0
  225. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/llmquery_api_view.py +0 -0
  226. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/load_document_api_view.py +0 -0
  227. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/login_view.py +0 -0
  228. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/logout_api_view.py +0 -0
  229. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/memory_api_view.py +0 -0
  230. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/profile_api_view.py +0 -0
  231. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/prompt_api_view.py +0 -0
  232. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/prompt_context_preview_api_view.py +0 -0
  233. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/prompt_resource_api_view.py +0 -0
  234. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/rag_api_view.py +0 -0
  235. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/root_redirect_view.py +0 -0
  236. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/signup_view.py +0 -0
  237. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/static_page_view.py +0 -0
  238. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/tool_api_view.py +0 -0
  239. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/user_feedback_api_view.py +0 -0
  240. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/users_api_view.py +0 -0
  241. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit/views/verify_user_view.py +0 -0
  242. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit.egg-info/SOURCES.txt +0 -0
  243. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit.egg-info/dependency_links.txt +0 -0
  244. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit.egg-info/requires.txt +0 -0
  245. {iatoolkit-2.18.6 → iatoolkit-2.19.0}/src/iatoolkit.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iatoolkit
3
- Version: 2.18.6
3
+ Version: 2.19.0
4
4
  Summary: IAToolkit
5
5
  Author: Fernando Libedinsky
6
6
  License-Expression: MIT
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # IAToolkit is open source software.
5
5
 
6
- __version__ = "2.18.6"
6
+ __version__ = "2.19.0"
7
7
 
8
8
  # Expose main classes and functions at the top level of the package
9
9
 
@@ -1741,6 +1741,14 @@ ui:
1741
1741
  custom_resources_toggle: "Use manual visibility"
1742
1742
  custom_resources_help: "If this is disabled, MCP resources are enabled automatically based on the published catalog."
1743
1743
  context_resources_help: "MCP resources are client-readable context or discovery; SQL sources and collections are managed under Data sources."
1744
+ company_context_title: "Company context"
1745
+ company_context_help: "Control which markdown files are concatenated to build get_company_context."
1746
+ company_context_preview_title: "Company context preview"
1747
+ custom_company_context_toggle: "Use manual selection"
1748
+ company_context_auto_help: "Automatic mode includes every context markdown file available for this tenant."
1749
+ company_context_custom_help: "Select the markdown files that should be concatenated into get_company_context."
1750
+ company_context_empty: "No context markdown files are available."
1751
+ company_context_empty_preview: "No company context was generated."
1744
1752
  validation_title: "Validation"
1745
1753
  validation_help: "Review publication gaps before exposing this tenant through MCP."
1746
1754
  access_oauth_title: "OAuth first, tokens as an advanced option"
@@ -1745,6 +1745,14 @@ ui:
1745
1745
  custom_resources_toggle: "Usar visibilidad manual"
1746
1746
  custom_resources_help: "Si no activas esta opción, las resources MCP se habilitan automáticamente según el catálogo publicado."
1747
1747
  context_resources_help: "Los resources MCP son contexto o discovery legible por el cliente; las fuentes SQL y colecciones se administran en Fuentes de datos."
1748
+ company_context_title: "Contexto empresa"
1749
+ company_context_help: "Controla qué archivos markdown se concatenan para construir get_company_context."
1750
+ company_context_preview_title: "Vista previa del contexto empresa"
1751
+ custom_company_context_toggle: "Usar selección manual"
1752
+ company_context_auto_help: "El modo automático incluye todos los archivos markdown de contexto disponibles para este tenant."
1753
+ company_context_custom_help: "Selecciona los archivos markdown que se concatenarán en get_company_context."
1754
+ company_context_empty: "No hay archivos markdown de contexto disponibles."
1755
+ company_context_empty_preview: "No se generó contexto empresa."
1748
1756
  validation_title: "Validación"
1749
1757
  validation_help: "Revisa brechas de publicación antes de exponer este tenant por MCP."
1750
1758
  access_oauth_title: "OAuth primero, tokens como opción avanzada"
@@ -19,6 +19,7 @@ class DatabaseManager(DatabaseProvider):
19
19
  _POSTGRES_BOOTSTRAP_PATCHES = (
20
20
  )
21
21
  _DEFAULT_CONNECT_TIMEOUT = 60
22
+ MAX_RESULT_ROWS = 1000
22
23
 
23
24
  @inject
24
25
  def __init__(self,
@@ -42,6 +43,7 @@ class DatabaseManager(DatabaseProvider):
42
43
 
43
44
  self.url = make_url(database_url)
44
45
  self.backend = self.url.get_backend_name()
46
+ self.statement_timeout_seconds = self._resolve_statement_timeout(timeout)
45
47
  if self._is_redshift():
46
48
  self._ensure_redshift_dialect_registered()
47
49
  self.engine_url = self._build_engine_url()
@@ -114,6 +116,13 @@ class DatabaseManager(DatabaseProvider):
114
116
 
115
117
  return self.timeout
116
118
 
119
+ def _resolve_statement_timeout(self, timeout: int | str | None) -> int | None:
120
+ explicit_timeout = timeout is not None
121
+ url_timeout_present = any(self.url.query.get(key) is not None for key in ("timeout", "connect_timeout"))
122
+ if not explicit_timeout and not url_timeout_present:
123
+ return None
124
+ return self._resolve_timeout()
125
+
117
126
  def _build_engine_url(self):
118
127
  normalized_url = self.url
119
128
  if self._is_redshift():
@@ -216,6 +225,15 @@ class DatabaseManager(DatabaseProvider):
216
225
  def get_dialect(self) -> str:
217
226
  return str(self.backend or "").strip().lower()
218
227
 
228
+ def _apply_statement_timeout(self, session) -> None:
229
+ if not self._is_postgres():
230
+ return
231
+ if self.statement_timeout_seconds is None:
232
+ return
233
+
234
+ timeout_ms = max(int(self.statement_timeout_seconds * 1000), 1)
235
+ session.execute(text(f"SET LOCAL statement_timeout = {timeout_ms}"))
236
+
219
237
  def create_all(self):
220
238
  # if there is a schema defined, make sure it exists before creating tables
221
239
  if self.schema and self._is_postgres():
@@ -264,6 +282,7 @@ class DatabaseManager(DatabaseProvider):
264
282
  normalized_schema = str(self.schema or "").strip()
265
283
  if normalized_schema and normalized_schema.lower() != "public":
266
284
  session.execute(text(f"SET search_path TO {normalized_schema}, public"))
285
+ self._apply_statement_timeout(session)
267
286
 
268
287
  result = session.execute(text(query), params or {})
269
288
  if commit:
@@ -272,7 +291,12 @@ class DatabaseManager(DatabaseProvider):
272
291
  if result.returns_rows:
273
292
  # Convert SQLAlchemy rows to list of dicts immediately
274
293
  cols = result.keys()
275
- return [dict(zip(cols, row)) for row in result.fetchall()]
294
+ rows = result.fetchmany(self.MAX_RESULT_ROWS + 1)
295
+ if len(rows) > self.MAX_RESULT_ROWS:
296
+ raise ValueError(
297
+ f"Query returned more than {self.MAX_RESULT_ROWS} rows. Refine the query with filters or LIMIT."
298
+ )
299
+ return [dict(zip(cols, row)) for row in rows]
276
300
 
277
301
  return {'rowcount': result.rowcount}
278
302
 
@@ -30,8 +30,11 @@ class CompanyContextService:
30
30
  self.sql_source_service = sql_source_service
31
31
  self.asset_repo = asset_repo
32
32
 
33
- def get_company_context(self, company_short_name: str) -> str:
34
- blocks = self.get_company_context_blocks(company_short_name)
33
+ def get_company_context(self, company_short_name: str, *, selected_context_files: List[str] | None = None) -> str:
34
+ blocks = self.get_company_context_blocks(
35
+ company_short_name,
36
+ selected_context_files=selected_context_files,
37
+ )
35
38
  return "\n\n---\n\n".join(
36
39
  section
37
40
  for section in (
@@ -42,7 +45,13 @@ class CompanyContextService:
42
45
  if section
43
46
  )
44
47
 
45
- def get_company_context_blocks(self, company_short_name: str, *, include_sql_context: bool = True) -> dict[str, str]:
48
+ def get_company_context_blocks(
49
+ self,
50
+ company_short_name: str,
51
+ *,
52
+ include_sql_context: bool = True,
53
+ selected_context_files: List[str] | None = None,
54
+ ) -> dict[str, str]:
46
55
  """
47
56
  Builds the full context by aggregating three sources:
48
57
  1. Static context files (Markdown).
@@ -57,7 +66,10 @@ class CompanyContextService:
57
66
 
58
67
  # 1. Context from Markdown (context/*.md) files
59
68
  try:
60
- md_context = self._get_static_file_context(company_short_name).strip()
69
+ md_context = self._get_static_file_context(
70
+ company_short_name,
71
+ selected_filenames=selected_context_files,
72
+ ).strip()
61
73
  except Exception as e:
62
74
  logging.warning(f"Could not load Markdown context for '{company_short_name}': {e}")
63
75
 
@@ -89,6 +101,38 @@ class CompanyContextService:
89
101
  "yaml_context": yaml_context,
90
102
  }
91
103
 
104
+ def list_context_files(self, company_short_name: str) -> list[dict]:
105
+ files = []
106
+ for filename in self._list_context_markdown_filenames(company_short_name):
107
+ title = ""
108
+ preview = ""
109
+ try:
110
+ content = self.asset_repo.read_text(company_short_name, AssetType.CONTEXT, filename)
111
+ except Exception as e:
112
+ logging.warning(f"Error reading context file {filename}: {e}")
113
+ content = ""
114
+
115
+ for raw_line in str(content or "").splitlines():
116
+ line = raw_line.strip()
117
+ if not line:
118
+ continue
119
+ if not title and line.startswith("#"):
120
+ title = line.lstrip("#").strip()
121
+ continue
122
+ if not preview:
123
+ preview = line
124
+ if title and preview:
125
+ break
126
+
127
+ files.append(
128
+ {
129
+ "filename": filename,
130
+ "title": title or filename,
131
+ "preview": preview,
132
+ }
133
+ )
134
+ return files
135
+
92
136
  def get_sql_context(self, company_short_name: str, allowed_databases: List[str] | None = None) -> str:
93
137
  try:
94
138
  sql_context, _ = self._get_sql_enriched_context(
@@ -370,16 +414,15 @@ class CompanyContextService:
370
414
  return "\n".join(output)
371
415
 
372
416
 
373
- def _get_static_file_context(self, company_short_name: str) -> str:
417
+ def _get_static_file_context(self, company_short_name: str, *, selected_filenames: List[str] | None = None) -> str:
374
418
  # Get context from .md files using the repository
375
419
  static_context = ''
376
420
 
377
421
  try:
378
- # 1. List markdown files in the context "folder"
379
- # Note: The repo handles where this folder actually is (FS or DB)
380
- md_files = self.asset_repo.list_files(company_short_name, AssetType.CONTEXT, extension='.md')
381
-
382
- for filename in md_files:
422
+ for filename in self._resolve_context_filenames(
423
+ company_short_name,
424
+ selected_filenames=selected_filenames,
425
+ ):
383
426
  try:
384
427
  # 2. Read content
385
428
  content = self.asset_repo.read_text(company_short_name, AssetType.CONTEXT, filename)
@@ -393,6 +436,37 @@ class CompanyContextService:
393
436
 
394
437
  return static_context
395
438
 
439
+ def _list_context_markdown_filenames(self, company_short_name: str) -> list[str]:
440
+ try:
441
+ md_files = self.asset_repo.list_files(company_short_name, AssetType.CONTEXT, extension='.md')
442
+ except Exception as e:
443
+ logging.warning(f"Error listing context files for {company_short_name}: {e}")
444
+ return []
445
+ normalized_files = []
446
+ for filename in md_files or []:
447
+ value = str(filename or "").strip()
448
+ if value and value not in normalized_files:
449
+ normalized_files.append(value)
450
+ return sorted(normalized_files)
451
+
452
+ def _resolve_context_filenames(
453
+ self,
454
+ company_short_name: str,
455
+ *,
456
+ selected_filenames: List[str] | None = None,
457
+ ) -> list[str]:
458
+ available_files = self._list_context_markdown_filenames(company_short_name)
459
+ if selected_filenames is None:
460
+ return available_files
461
+
462
+ allowed_files = set(available_files)
463
+ ordered_selection = []
464
+ for filename in selected_filenames or []:
465
+ value = str(filename or "").strip()
466
+ if value and value in allowed_files and value not in ordered_selection:
467
+ ordered_selection.append(value)
468
+ return ordered_selection
469
+
396
470
  @staticmethod
397
471
  def _normalize_identifier(value: str) -> str:
398
472
  if value is None:
@@ -12,6 +12,7 @@ from injector import inject, singleton
12
12
  from typing import Callable
13
13
  import json
14
14
  import logging
15
+ import re
15
16
 
16
17
 
17
18
  @singleton
@@ -20,6 +21,20 @@ class SqlService:
20
21
  Manages database connections and executes SQL statements.
21
22
  It maintains a cache of named DatabaseManager instances to avoid reconnecting.
22
23
  """
24
+ MAX_QUERY_ROWS = 1000
25
+ _ALLOWED_QUERY_PREFIXES = ("SELECT", "WITH")
26
+ _BLOCKED_SQL_PATTERN = re.compile(
27
+ r"\b(?:INSERT|UPDATE|DELETE|DROP|ALTER|CREATE|TRUNCATE|MERGE|CALL|COPY|GRANT|REVOKE|"
28
+ r"BEGIN|COMMIT|ROLLBACK|SAVEPOINT|RELEASE|VACUUM|ANALYZE|CLUSTER|REFRESH|"
29
+ r"EXEC(?:UTE)?|DO|SET|RESET|USE|PRAGMA|ATTACH|DETACH|REINDEX|LOCK|UNLOCK|INTO)\b",
30
+ re.IGNORECASE,
31
+ )
32
+ _SINGLE_QUOTED_LITERAL_RE = re.compile(r"'(?:''|[^'])*'")
33
+ _DOUBLE_QUOTED_LITERAL_RE = re.compile(r'"(?:""|[^"])*"')
34
+ _BACKTICK_LITERAL_RE = re.compile(r"`[^`]*`")
35
+ _BRACKET_LITERAL_RE = re.compile(r"\[[^\]]*\]")
36
+ _BLOCK_COMMENT_RE = re.compile(r"/\*.*?\*/", re.DOTALL)
37
+ _LINE_COMMENT_RE = re.compile(r"--[^\r\n]*")
23
38
 
24
39
  @inject
25
40
  def __init__(self,
@@ -155,6 +170,86 @@ class SqlService:
155
170
  f"Database '{db_name}' is not registered for this company."
156
171
  )
157
172
 
173
+ @classmethod
174
+ def _sanitize_sql_for_validation(cls, query: str) -> str:
175
+ sanitized = str(query or "")
176
+ for pattern in (
177
+ cls._SINGLE_QUOTED_LITERAL_RE,
178
+ cls._DOUBLE_QUOTED_LITERAL_RE,
179
+ cls._BACKTICK_LITERAL_RE,
180
+ cls._BRACKET_LITERAL_RE,
181
+ ):
182
+ sanitized = pattern.sub(" ", sanitized)
183
+ sanitized = cls._BLOCK_COMMENT_RE.sub("", sanitized)
184
+ sanitized = cls._LINE_COMMENT_RE.sub("", sanitized)
185
+ return re.sub(r"\s+", " ", sanitized).strip()
186
+
187
+ def _assert_read_only_query(self, query: str) -> str:
188
+ normalized_query = str(query or "").strip()
189
+ if not normalized_query:
190
+ raise IAToolkitException(
191
+ IAToolkitException.ErrorType.DATABASE_ERROR,
192
+ "SQL query is required.",
193
+ )
194
+
195
+ sanitized = self._sanitize_sql_for_validation(normalized_query)
196
+ if not sanitized:
197
+ raise IAToolkitException(
198
+ IAToolkitException.ErrorType.DATABASE_ERROR,
199
+ "SQL query is required.",
200
+ )
201
+ if ";" in sanitized:
202
+ raise IAToolkitException(
203
+ IAToolkitException.ErrorType.DATABASE_ERROR,
204
+ "Only a single read-only SQL statement is allowed.",
205
+ )
206
+
207
+ upper_sanitized = sanitized.upper()
208
+ blocked_match = self._BLOCKED_SQL_PATTERN.search(upper_sanitized)
209
+ if blocked_match:
210
+ blocked_keyword = blocked_match.group(0).upper()
211
+ raise IAToolkitException(
212
+ IAToolkitException.ErrorType.DATABASE_ERROR,
213
+ f"Blocked SQL keyword detected: {blocked_keyword}.",
214
+ )
215
+
216
+ if not upper_sanitized.startswith(self._ALLOWED_QUERY_PREFIXES):
217
+ raise IAToolkitException(
218
+ IAToolkitException.ErrorType.DATABASE_ERROR,
219
+ "Only read-only SELECT statements are allowed.",
220
+ )
221
+ if upper_sanitized.startswith("WITH") and not re.search(r"\bSELECT\b", upper_sanitized):
222
+ raise IAToolkitException(
223
+ IAToolkitException.ErrorType.DATABASE_ERROR,
224
+ "WITH queries must resolve to a read-only SELECT statement.",
225
+ )
226
+
227
+ return normalized_query
228
+
229
+ def _enforce_result_row_limit(self, result_data):
230
+ if isinstance(result_data, list) and len(result_data) > self.MAX_QUERY_ROWS:
231
+ raise IAToolkitException(
232
+ IAToolkitException.ErrorType.DATABASE_ERROR,
233
+ f"Query returned more than {self.MAX_QUERY_ROWS} rows. Refine the query with filters or LIMIT.",
234
+ )
235
+
236
+ @staticmethod
237
+ def _cleanup_provider_execution(provider: DatabaseProvider):
238
+ remove_session = getattr(provider, "remove_session", None)
239
+ if callable(remove_session):
240
+ try:
241
+ remove_session()
242
+ return
243
+ except Exception as exc:
244
+ logging.debug("Failed to remove SQL provider session: %s", exc)
245
+
246
+ rollback = getattr(provider, "rollback", None)
247
+ if callable(rollback):
248
+ try:
249
+ rollback()
250
+ except Exception as exc:
251
+ logging.debug("Failed to rollback SQL provider session: %s", exc)
252
+
158
253
  def exec_sql(self, company_short_name: str, **kwargs):
159
254
  """
160
255
  Executes a raw SQL statement against a registered database provider.
@@ -163,27 +258,32 @@ class SqlService:
163
258
  database_name = kwargs.get('database_key')
164
259
  query = kwargs.get('query')
165
260
  format = kwargs.get('format', 'json')
166
- commit = kwargs.get('commit')
167
261
  params = kwargs.get('params')
262
+ provider = None
263
+ cleanup_required = False
168
264
 
169
265
  if not database_name:
170
266
  raise IAToolkitException(IAToolkitException.ErrorType.DATABASE_ERROR,
171
267
  'missing database_name in call to exec_sql')
268
+ if kwargs.get('commit'):
269
+ logging.warning("Ignoring commit=True for read-only SQL execution against '%s'.", database_name)
172
270
 
173
271
  try:
174
272
  # 1. Get the abstract provider (could be Direct or Bridge)
175
273
  provider = self.get_database_provider(company_short_name, database_name)
176
- db_schema = self._db_schemas[(company_short_name, database_name)]
274
+ safe_query = self._assert_read_only_query(query)
177
275
 
178
276
  # 2. Delegate execution
179
277
  # The provider returns a clean List[Dict] or Dict result
180
278
  execute_kwargs = {
181
- "query": query,
182
- "commit": commit,
279
+ "query": safe_query,
280
+ "commit": False,
183
281
  }
184
282
  if params is not None:
185
283
  execute_kwargs["params"] = params
284
+ cleanup_required = True
186
285
  result_data = provider.execute_query(**execute_kwargs)
286
+ self._enforce_result_row_limit(result_data)
187
287
 
188
288
  # 3. Handle Formatting (Service layer responsibility)
189
289
  if format == 'dict':
@@ -195,14 +295,6 @@ class SqlService:
195
295
  except IAToolkitException:
196
296
  raise
197
297
  except Exception as e:
198
- # Attempt rollback if supported/needed
199
- try:
200
- provider = self.get_database_provider(company_short_name, database_name)
201
- if provider:
202
- provider.rollback()
203
- except Exception:
204
- pass
205
-
206
298
  error_message = str(e)
207
299
  if 'timed out' in str(e):
208
300
  error_message = self.i18n_service.t('errors.timeout')
@@ -210,6 +302,9 @@ class SqlService:
210
302
  logging.error(f"Error executing SQL statement: {error_message}")
211
303
  raise IAToolkitException(IAToolkitException.ErrorType.DATABASE_ERROR,
212
304
  error_message) from e
305
+ finally:
306
+ if provider is not None and cleanup_required:
307
+ self._cleanup_provider_execution(provider)
213
308
 
214
309
  def commit(self, company_short_name: str, database_name: str):
215
310
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iatoolkit
3
- Version: 2.18.6
3
+ Version: 2.19.0
4
4
  Summary: IAToolkit
5
5
  Author: Fernando Libedinsky
6
6
  License-Expression: MIT
File without changes
File without changes
File without changes
File without changes
File without changes