iatoolkit 2.67.0__tar.gz → 2.68.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 (276) hide show
  1. {iatoolkit-2.67.0/src/iatoolkit.egg-info → iatoolkit-2.68.0}/PKG-INFO +1 -1
  2. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/__init__.py +1 -1
  3. iatoolkit-2.68.0/src/iatoolkit/common/memory_tools.py +25 -0
  4. iatoolkit-2.68.0/src/iatoolkit/repositories/advisory_lock.py +48 -0
  5. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/repositories/knowledge_wiki_repo.py +135 -1
  6. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/repositories/memory_question_repo.py +23 -0
  7. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/repositories/memory_repo.py +13 -0
  8. iatoolkit-2.68.0/src/iatoolkit/repositories/memory_usage_repo.py +68 -0
  9. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/repositories/models.py +63 -0
  10. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/dispatcher_service.py +21 -1
  11. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/memory_compiler_service.py +13 -20
  12. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/memory_question_service.py +36 -38
  13. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/memory_service.py +43 -7
  14. iatoolkit-2.68.0/src/iatoolkit/services/memory_usage_service.py +79 -0
  15. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/tenant_wiki_service.py +83 -2
  16. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/tool_service.py +8 -0
  17. iatoolkit-2.68.0/src/iatoolkit/services/wiki_page_index_service.py +72 -0
  18. {iatoolkit-2.67.0 → iatoolkit-2.68.0/src/iatoolkit.egg-info}/PKG-INFO +1 -1
  19. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit.egg-info/SOURCES.txt +5 -0
  20. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/LICENSE +0 -0
  21. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/LICENSE_COMMUNITY.md +0 -0
  22. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/pyproject.toml +0 -0
  23. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/readme.md +0 -0
  24. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/setup.cfg +0 -0
  25. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/base_company.py +0 -0
  26. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/cli_commands.py +0 -0
  27. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/common/__init__.py +0 -0
  28. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/common/bridge_validation.py +0 -0
  29. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/common/company_paths.py +0 -0
  30. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/common/exceptions.py +0 -0
  31. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/common/interfaces/__init__.py +0 -0
  32. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/common/interfaces/asset_storage.py +0 -0
  33. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/common/interfaces/database_provider.py +0 -0
  34. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/common/interfaces/memory_compilation_trigger.py +0 -0
  35. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/common/interfaces/secret_provider.py +0 -0
  36. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/common/interfaces/signup_policy_resolver.py +0 -0
  37. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/common/interfaces/web_search_provider.py +0 -0
  38. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/common/model_registry.py +0 -0
  39. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/common/routes.py +0 -0
  40. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/common/search_terms.py +0 -0
  41. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/common/secret_resolver.py +0 -0
  42. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/common/session_manager.py +0 -0
  43. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/common/text_splitter.py +0 -0
  44. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/common/url_safety.py +0 -0
  45. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/common/util.py +0 -0
  46. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/company_registry.py +0 -0
  47. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/__init__.py +0 -0
  48. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/llm_capabilities.yaml +0 -0
  49. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_agents/memory_compiler.prompt +0 -0
  50. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_agents/memory_correction.prompt +0 -0
  51. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_agents/memory_document_ingest.prompt +0 -0
  52. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_agents/memory_image_ingest.prompt +0 -0
  53. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_agents/memory_meeting_ingest.prompt +0 -0
  54. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_agents/memory_question_review.prompt +0 -0
  55. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_agents_pack.yaml +0 -0
  56. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/channel_business_identity.prompt +0 -0
  57. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/channel_conversation_continuity.prompt +0 -0
  58. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/channel_response_style.prompt +0 -0
  59. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/chat_state_rules.prompt +0 -0
  60. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/chat_user_profile.prompt +0 -0
  61. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/core_identity.prompt +0 -0
  62. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/email_output.prompt +0 -0
  63. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/file_download_output.prompt +0 -0
  64. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/format_styles.prompt +0 -0
  65. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/html_structures.prompt +0 -0
  66. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/links_documents.prompt +0 -0
  67. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/memory_usage.prompt +0 -0
  68. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/multimodal_basics.prompt +0 -0
  69. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/output_basics.prompt +0 -0
  70. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/presentation_formatting.prompt +0 -0
  71. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/query_main.prompt +0 -0
  72. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/sql_aggregation_scope.prompt +0 -0
  73. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/sql_casting.prompt +0 -0
  74. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/sql_core.prompt +0 -0
  75. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/sql_jsonb.prompt +0 -0
  76. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/sql_mysql_casting.prompt +0 -0
  77. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/sql_mysql_json.prompt +0 -0
  78. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/sql_redshift_basics.prompt +0 -0
  79. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/sql_redshift_types.prompt +0 -0
  80. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/sql_rules.prompt +0 -0
  81. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts/tool_html_passthrough.prompt +0 -0
  82. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_prompts_pack.yaml +0 -0
  83. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/config/system_tools_pack.yaml +0 -0
  84. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/core.py +0 -0
  85. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/__init__.py +0 -0
  86. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/brevo_mail_app.py +0 -0
  87. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/call_service.py +0 -0
  88. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/connectors/__init__.py +0 -0
  89. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/connectors/file_connector.py +0 -0
  90. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/connectors/file_connector_factory.py +0 -0
  91. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/connectors/google_cloud_storage_connector.py +0 -0
  92. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/connectors/google_drive_connector.py +0 -0
  93. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/connectors/local_file_connector.py +0 -0
  94. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/connectors/s3_connector.py +0 -0
  95. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/google_auth_client.py +0 -0
  96. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/google_chat_app.py +0 -0
  97. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/inference_embeddings_client.py +0 -0
  98. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/inference_handler.py +0 -0
  99. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/jina_embeddings_client.py +0 -0
  100. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/llm_gateway_resolver.py +0 -0
  101. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/llm_providers/__init__.py +0 -0
  102. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/llm_providers/anthropic_adapter.py +0 -0
  103. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/llm_providers/deepseek_adapter.py +0 -0
  104. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/llm_providers/gemini_adapter.py +0 -0
  105. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/llm_providers/openai_adapter.py +0 -0
  106. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/llm_providers/openai_compatible_chat_adapter.py +0 -0
  107. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/llm_providers/openrouter_adapter.py +0 -0
  108. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/llm_proxy.py +0 -0
  109. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/llm_response.py +0 -0
  110. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/infra/redis_session_manager.py +0 -0
  111. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/locales/en.yaml +0 -0
  112. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/locales/es.yaml +0 -0
  113. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/repositories/__init__.py +0 -0
  114. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/repositories/api_key_repo.py +0 -0
  115. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/repositories/bridge_repo.py +0 -0
  116. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/repositories/database_manager.py +0 -0
  117. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/repositories/document_repo.py +0 -0
  118. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/repositories/env_secret_provider.py +0 -0
  119. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/repositories/filesystem_asset_repository.py +0 -0
  120. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/repositories/llm_query_repo.py +0 -0
  121. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/repositories/mcp_token_repo.py +0 -0
  122. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/repositories/profile_repo.py +0 -0
  123. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/repositories/prompt_resource_repo.py +0 -0
  124. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/repositories/sql_dataset_repo.py +0 -0
  125. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/repositories/sql_source_repo.py +0 -0
  126. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/repositories/vs_repo.py +0 -0
  127. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/runtime_logging.py +0 -0
  128. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/__init__.py +0 -0
  129. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/api_key_service.py +0 -0
  130. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/attachment_policy_service.py +0 -0
  131. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/auth_service.py +0 -0
  132. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/branding_service.py +0 -0
  133. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/company_context_service.py +0 -0
  134. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/configuration_service.py +0 -0
  135. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/context_builder_service.py +0 -0
  136. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/embedding_service.py +0 -0
  137. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/excel_service.py +0 -0
  138. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/file_processor_service.py +0 -0
  139. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/history_manager_service.py +0 -0
  140. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/http_tool_service.py +0 -0
  141. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/i18n_service.py +0 -0
  142. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/inference_service.py +0 -0
  143. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/jwt_service.py +0 -0
  144. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/knowledge_base_service.py +0 -0
  145. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/language_service.py +0 -0
  146. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/license_service.py +0 -0
  147. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/llm_client_service.py +0 -0
  148. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/mail_service.py +0 -0
  149. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/markdown_wiki_service.py +0 -0
  150. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/mcp_token_service.py +0 -0
  151. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/memory_lookup_policy_service.py +0 -0
  152. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/memory_wiki_service.py +0 -0
  153. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/noop_memory_compilation_trigger.py +0 -0
  154. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/operator_identity_service.py +0 -0
  155. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/parsers/__init__.py +0 -0
  156. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/parsers/contracts.py +0 -0
  157. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/parsers/image_normalizer.py +0 -0
  158. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/parsers/parsing_service.py +0 -0
  159. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/parsers/pdf_ocr_detection.py +0 -0
  160. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/parsers/provider_factory.py +0 -0
  161. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/parsers/provider_resolver.py +0 -0
  162. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/parsers/providers/__init__.py +0 -0
  163. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/parsers/providers/basic_provider.py +0 -0
  164. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/parsers/providers/docling_provider.py +0 -0
  165. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/parsers/validator.py +0 -0
  166. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/pdf_service.py +0 -0
  167. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/profile_service.py +0 -0
  168. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/prompt_resource_service.py +0 -0
  169. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/prompt_service.py +0 -0
  170. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/query_service.py +0 -0
  171. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/signup_policy_resolver.py +0 -0
  172. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/sql_dataset_service.py +0 -0
  173. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/sql_service.py +0 -0
  174. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/sql_source_service.py +0 -0
  175. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/storage_service.py +0 -0
  176. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/structured_output_service.py +0 -0
  177. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/system_agent_catalog.py +0 -0
  178. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/system_agent_runner.py +0 -0
  179. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/system_prompt_catalog.py +0 -0
  180. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/system_tools.py +0 -0
  181. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/telemetry_service.py +0 -0
  182. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/tool_output_contract.py +0 -0
  183. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/user_feedback_service.py +0 -0
  184. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/user_session_context_service.py +0 -0
  185. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/visual_kb_service.py +0 -0
  186. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/visual_tool_service.py +0 -0
  187. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/warmup_service.py +0 -0
  188. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/web_search/__init__.py +0 -0
  189. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/web_search/provider_factory.py +0 -0
  190. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/web_search/providers/__init__.py +0 -0
  191. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/web_search/providers/brave_provider.py +0 -0
  192. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/services/web_search_service.py +0 -0
  193. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/images/favicon.ico +0 -0
  194. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/images/favicon.png +0 -0
  195. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/images/fernando.jpeg +0 -0
  196. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/images/iatoolkit_core.png +0 -0
  197. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/images/iatoolkit_logo.png +0 -0
  198. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/js/chat_feedback_button.js +0 -0
  199. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/js/chat_filepond.js +0 -0
  200. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/js/chat_header_navigation.js +0 -0
  201. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/js/chat_help_content.js +0 -0
  202. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/js/chat_history_button.js +0 -0
  203. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/js/chat_logout_button.js +0 -0
  204. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/js/chat_main.js +0 -0
  205. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/js/chat_memory_button.js +0 -0
  206. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/js/chat_model_selector.js +0 -0
  207. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/js/chat_onboarding_button.js +0 -0
  208. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/js/chat_prompt_manager.js +0 -0
  209. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/js/chat_reload_button.js +0 -0
  210. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/styles/account.css +0 -0
  211. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/styles/chat_iatoolkit.css +0 -0
  212. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/styles/chat_modal.css +0 -0
  213. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/styles/chat_public.css +0 -0
  214. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/styles/documents.css +0 -0
  215. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/styles/landing_page.css +0 -0
  216. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/styles/llm_output.css +0 -0
  217. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/styles/onboarding.css +0 -0
  218. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/static/styles/wiki_page.css +0 -0
  219. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/templates/_chat_header.html +0 -0
  220. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/templates/_company_header.html +0 -0
  221. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/templates/_login_widget.html +0 -0
  222. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/templates/account.html +0 -0
  223. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/templates/base.html +0 -0
  224. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/templates/change_password.html +0 -0
  225. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/templates/chat.html +0 -0
  226. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/templates/chat_memory_modal.html +0 -0
  227. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/templates/chat_modals.html +0 -0
  228. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/templates/error.html +0 -0
  229. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/templates/forgot_password.html +0 -0
  230. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/templates/home_hosted_default.html +0 -0
  231. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/templates/login_global.html +0 -0
  232. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/templates/memory/wiki_schema.md +0 -0
  233. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/templates/onboarding_shell.html +0 -0
  234. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/templates/pdf/base.html +0 -0
  235. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/templates/pdf/letter.html +0 -0
  236. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/templates/pdf/report.html +0 -0
  237. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/templates/pdf/simple.html +0 -0
  238. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/templates/signup.html +0 -0
  239. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/templates/wiki_page.html +0 -0
  240. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/__init__.py +0 -0
  241. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/account_view.py +0 -0
  242. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/api_key_api_view.py +0 -0
  243. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/base_login_view.py +0 -0
  244. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/categories_api_view.py +0 -0
  245. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/change_password_view.py +0 -0
  246. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/chat_context_preview_api_view.py +0 -0
  247. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/chat_view.py +0 -0
  248. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/configuration_api_view.py +0 -0
  249. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/connectors_api_view.py +0 -0
  250. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/embedding_api_view.py +0 -0
  251. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/forgot_password_view.py +0 -0
  252. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/help_content_api_view.py +0 -0
  253. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/history_api_view.py +0 -0
  254. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/home_view.py +0 -0
  255. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/init_context_api_view.py +0 -0
  256. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/llmquery_api_view.py +0 -0
  257. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/load_document_api_view.py +0 -0
  258. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/login_view.py +0 -0
  259. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/logout_api_view.py +0 -0
  260. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/memory_api_view.py +0 -0
  261. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/profile_api_view.py +0 -0
  262. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/prompt_api_view.py +0 -0
  263. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/prompt_context_preview_api_view.py +0 -0
  264. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/prompt_resource_api_view.py +0 -0
  265. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/rag_api_view.py +0 -0
  266. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/root_redirect_view.py +0 -0
  267. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/signup_view.py +0 -0
  268. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/static_page_view.py +0 -0
  269. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/tool_api_view.py +0 -0
  270. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/user_feedback_api_view.py +0 -0
  271. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/users_api_view.py +0 -0
  272. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/verify_user_view.py +0 -0
  273. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit/views/wiki_page_view.py +0 -0
  274. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit.egg-info/dependency_links.txt +0 -0
  275. {iatoolkit-2.67.0 → iatoolkit-2.68.0}/src/iatoolkit.egg-info/requires.txt +0 -0
  276. {iatoolkit-2.67.0 → iatoolkit-2.68.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.67.0
3
+ Version: 2.68.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.67.0"
6
+ __version__ = "2.68.0"
7
7
 
8
8
  # Expose main classes and functions at the top level of the package
9
9
 
@@ -0,0 +1,25 @@
1
+ # Copyright (c) 2024 Fernando Libedinsky
2
+ # Product: IAToolkit
3
+ #
4
+ # IAToolkit is open source software.
5
+
6
+ """What "used the Memoria" means, in one place (Fase 2 of the Memoria architecture review).
7
+
8
+ Before, five modules each kept their own list of Memoria tools, so the hourly
9
+ question review, Impacto and the prompts counted different things.
10
+ """
11
+
12
+ # Looking for knowledge: a page search or a document search.
13
+ SEARCH_TOOLS = frozenset({"iat_memory_search", "iat_document_search"})
14
+ # Reading what a search found.
15
+ READ_TOOLS = frozenset({"iat_memory_get_page", "iat_document_read", "iat_document_list"})
16
+ # Every tool whose use is a use of the company's knowledge (usage events, Impacto).
17
+ KNOWLEDGE_TOOLS = SEARCH_TOOLS | READ_TOOLS
18
+
19
+ PAGE_SEARCH_TOOL = "iat_memory_search"
20
+ PAGE_READ_TOOL = "iat_memory_get_page"
21
+ DOCUMENT_SEARCH_TOOL = "iat_document_search"
22
+ DOCUMENT_READ_TOOL = "iat_document_read"
23
+
24
+ CHANNEL_CHAT = "chat"
25
+ CHANNEL_MCP = "mcp"
@@ -0,0 +1,48 @@
1
+ # Copyright (c) 2024 Fernando Libedinsky
2
+ # Product: IAToolkit
3
+ #
4
+ # IAToolkit is open source software.
5
+
6
+ """One Postgres advisory lock helper for everything that runs once per key across workers.
7
+
8
+ A Postgres advisory lock belongs to the *database connection* that took it. An
9
+ ORM session hands its connection back to the pool on every commit, so a lock
10
+ taken through the session can end up held by a pooled connection nobody owns,
11
+ and released (or not) through another one. This helper always takes and
12
+ releases the lock on its own dedicated connection, for exactly the `with` block.
13
+
14
+ On SQLite (tests) there is no advisory lock: the block runs as if it held it.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from contextlib import contextmanager
20
+
21
+ from sqlalchemy import text
22
+
23
+
24
+ @contextmanager
25
+ def advisory_lock(db_manager, namespace: int, key: int, *, wait: bool = True):
26
+ """
27
+ Holds the two-key advisory lock (namespace, key) for the block and yields
28
+ whether it is held: always True when `wait` (it blocks until it gets it);
29
+ with `wait=False`, False when someone else holds it (the block should skip).
30
+ """
31
+ if db_manager.get_dialect() not in ("postgresql", "postgres"):
32
+ yield True
33
+ return
34
+ params = {"namespace": int(namespace), "key": int(key)}
35
+ connection = db_manager.get_connection()
36
+ try:
37
+ if wait:
38
+ connection.execute(text("SELECT pg_advisory_lock(:namespace, :key)"), params)
39
+ held = True
40
+ else:
41
+ held = bool(connection.execute(text("SELECT pg_try_advisory_lock(:namespace, :key)"), params).scalar())
42
+ try:
43
+ yield held
44
+ finally:
45
+ if held:
46
+ connection.execute(text("SELECT pg_advisory_unlock(:namespace, :key)"), params)
47
+ finally:
48
+ connection.close()
@@ -5,15 +5,17 @@
5
5
 
6
6
  from __future__ import annotations
7
7
 
8
+ import hashlib
8
9
  from datetime import datetime
9
10
 
10
11
  from injector import inject
11
- from sqlalchemy import or_
12
+ from sqlalchemy import or_, text
12
13
 
13
14
  from iatoolkit.repositories.database_manager import DatabaseManager
14
15
  from iatoolkit.repositories.models import (
15
16
  KnowledgeWiki,
16
17
  KnowledgeWikiPage,
18
+ KnowledgeWikiPageIndex,
17
19
  KnowledgeWikiPageRevision,
18
20
  KnowledgeWikiPageStatus,
19
21
  KnowledgeWikiStatus,
@@ -117,6 +119,16 @@ class KnowledgeWikiRepo:
117
119
  .first()
118
120
  )
119
121
 
122
+ def get_pages_by_ids(self, wiki_ids: list[int], page_ids: list[int]) -> dict[int, KnowledgeWikiPage]:
123
+ if not wiki_ids or not page_ids:
124
+ return {}
125
+ pages = (
126
+ self.session.query(KnowledgeWikiPage)
127
+ .filter(KnowledgeWikiPage.wiki_id.in_(list(wiki_ids)), KnowledgeWikiPage.id.in_(list(page_ids)))
128
+ .all()
129
+ )
130
+ return {page.id: page for page in pages}
131
+
120
132
  def get_page_by_slug(self, wiki_id: int, slug: str) -> KnowledgeWikiPage | None:
121
133
  if not wiki_id or not slug:
122
134
  return None
@@ -203,21 +215,143 @@ class KnowledgeWikiRepo:
203
215
  )
204
216
  self.session.add(page)
205
217
  self.session.commit()
218
+ self.index_page(page)
206
219
  return page
207
220
 
208
221
  def save_page(self, page: KnowledgeWikiPage) -> KnowledgeWikiPage:
209
222
  self.session.add(page)
210
223
  self.session.commit()
224
+ self.index_page(page)
211
225
  return page
212
226
 
213
227
  def delete_page(self, wiki_id: int, path: str) -> KnowledgeWikiPage | None:
214
228
  page = self.get_page_by_path(wiki_id, path)
215
229
  if page is None:
216
230
  return None
231
+ self.session.query(KnowledgeWikiPageIndex).filter(KnowledgeWikiPageIndex.page_id == page.id).delete()
217
232
  self.session.delete(page)
218
233
  self.session.commit()
219
234
  return page
220
235
 
236
+ # =====================================================================
237
+ # Search index (iat_knowledge_wiki_page_index)
238
+ # =====================================================================
239
+
240
+ # What the word branch searches; the meaning branch embeds the same text, cut.
241
+ INDEX_TEXT_CHARS = 60000
242
+
243
+ @classmethod
244
+ def page_search_text(cls, page: KnowledgeWikiPage) -> str:
245
+ parts = [page.title or "", page.summary or "", " ".join(str(t) for t in (page.tags or [])), page.body_text or ""]
246
+ return "\n".join(part.strip() for part in parts if str(part or "").strip())[:cls.INDEX_TEXT_CHARS]
247
+
248
+ def index_page(self, page: KnowledgeWikiPage) -> KnowledgeWikiPageIndex:
249
+ """Writes the page's search text; a changed text clears the embedding so it is computed again."""
250
+ search_text = self.page_search_text(page)
251
+ content_hash = hashlib.sha256(search_text.encode("utf-8")).hexdigest()
252
+ row = self.session.query(KnowledgeWikiPageIndex).filter(KnowledgeWikiPageIndex.page_id == page.id).first()
253
+ if row is None:
254
+ row = KnowledgeWikiPageIndex(page_id=page.id, wiki_id=page.wiki_id, company_id=page.company_id)
255
+ self.session.add(row)
256
+ elif row.content_hash == content_hash and row.wiki_id == page.wiki_id:
257
+ return row
258
+ row.wiki_id = page.wiki_id
259
+ row.search_text = search_text
260
+ row.content_hash = content_hash
261
+ if row.embedded_hash != content_hash:
262
+ row.embedding = None
263
+ row.embedded_hash = None
264
+ self.session.commit()
265
+ return row
266
+
267
+ def list_pages_to_embed(self, limit: int = 100, company_id: int | None = None) -> list[KnowledgeWikiPageIndex]:
268
+ query = (
269
+ self.session.query(KnowledgeWikiPageIndex)
270
+ .join(KnowledgeWikiPage, KnowledgeWikiPage.id == KnowledgeWikiPageIndex.page_id)
271
+ .filter(KnowledgeWikiPage.status != KnowledgeWikiPageStatus.ARCHIVED)
272
+ .filter(or_(KnowledgeWikiPageIndex.embedded_hash.is_(None),
273
+ KnowledgeWikiPageIndex.embedded_hash != KnowledgeWikiPageIndex.content_hash))
274
+ )
275
+ if company_id:
276
+ query = query.filter(KnowledgeWikiPageIndex.company_id == company_id)
277
+ return query.order_by(KnowledgeWikiPageIndex.updated_at.asc()).limit(limit).all()
278
+
279
+ def list_unindexed_pages(self, limit: int = 500) -> list[KnowledgeWikiPage]:
280
+ """Pages written before the index existed (the backfill)."""
281
+ return (
282
+ self.session.query(KnowledgeWikiPage)
283
+ .outerjoin(KnowledgeWikiPageIndex, KnowledgeWikiPageIndex.page_id == KnowledgeWikiPage.id)
284
+ .filter(KnowledgeWikiPageIndex.id.is_(None))
285
+ .filter(KnowledgeWikiPage.status != KnowledgeWikiPageStatus.ARCHIVED)
286
+ .limit(limit)
287
+ .all()
288
+ )
289
+
290
+ def set_page_embedding(self, row: KnowledgeWikiPageIndex, embedding: list[float], embedded_hash: str) -> None:
291
+ row.embedding = embedding
292
+ row.embedded_hash = embedded_hash
293
+ self.session.commit()
294
+
295
+ def is_postgres(self) -> bool:
296
+ bind = self.session.get_bind()
297
+ return getattr(getattr(bind, "dialect", None), "name", "") == "postgresql"
298
+
299
+ RRF_K = 60
300
+
301
+ def hybrid_search_pages(self, wiki_ids: list[int], query_text: str, query_embedding: list[float] | None,
302
+ candidates: int = 40) -> list[tuple[int, float]]:
303
+ """
304
+ (page_id, rrf_score) of the best pages of these wikis, best first: by
305
+ words (Postgres full text, 'simple', terms OR'ed, the GIN index) and, when
306
+ there is a query embedding, by meaning (pages whose embedding has its
307
+ size), fused by Reciprocal Rank Fusion. Archived pages are left out.
308
+ Postgres only.
309
+ """
310
+ if not wiki_ids:
311
+ return []
312
+ tsvector = "to_tsvector('simple'::regconfig, i.search_text)"
313
+ tsquery = "replace(plainto_tsquery('simple'::regconfig, :text_terms)::text, ' & ', ' | ')::tsquery"
314
+ params = {"wiki_ids": list(wiki_ids), "text_terms": query_text or "", "candidates": int(candidates),
315
+ "archived": KnowledgeWikiPageStatus.ARCHIVED.name}
316
+ branches = [f"""
317
+ SELECT page_id, ROW_NUMBER() OVER (ORDER BY text_rank DESC, page_id ASC) AS rank
318
+ FROM (
319
+ SELECT i.page_id, ts_rank_cd({tsvector}, terms.q) AS text_rank
320
+ FROM iat_knowledge_wiki_page_index i
321
+ JOIN iat_knowledge_wiki_pages p ON p.id = i.page_id,
322
+ (SELECT {tsquery} AS q) terms
323
+ WHERE i.wiki_id = ANY(:wiki_ids) AND p.status::text <> :archived
324
+ AND {tsvector} @@ terms.q
325
+ ORDER BY text_rank DESC
326
+ LIMIT :candidates
327
+ ) words"""]
328
+ if query_embedding:
329
+ dims = len(query_embedding)
330
+ if 0 < dims <= 2000:
331
+ params["query_embedding"] = str(list(query_embedding))
332
+ branches.append(f"""
333
+ SELECT page_id, ROW_NUMBER() OVER (ORDER BY distance ASC, page_id ASC) AS rank
334
+ FROM (
335
+ SELECT i.page_id,
336
+ i.embedding::vector({dims}) <=> CAST(:query_embedding AS vector({dims})) AS distance
337
+ FROM iat_knowledge_wiki_page_index i
338
+ JOIN iat_knowledge_wiki_pages p ON p.id = i.page_id
339
+ WHERE i.wiki_id = ANY(:wiki_ids) AND p.status::text <> :archived
340
+ AND i.embedding IS NOT NULL AND vector_dims(i.embedding) = {dims}
341
+ ORDER BY distance ASC
342
+ LIMIT :candidates
343
+ ) meaning""")
344
+ union = " UNION ALL ".join(f"({branch})" for branch in branches)
345
+ sql = f"""
346
+ SELECT page_id, SUM(1.0 / ({self.RRF_K} + rank)) AS rrf_score
347
+ FROM ({union}) hits
348
+ GROUP BY page_id
349
+ ORDER BY rrf_score DESC, page_id ASC
350
+ LIMIT :candidates
351
+ """
352
+ rows = self.session.execute(text(sql), params).fetchall()
353
+ return [(int(row[0]), float(row[1])) for row in rows]
354
+
221
355
  def create_page_revision(
222
356
  self,
223
357
  *,
@@ -51,6 +51,29 @@ class MemoryQuestionRepo:
51
51
  .all()
52
52
  )
53
53
 
54
+ def claim_open(self, company_id: int, question_id: int, status: str, resolved_by: str, resolved_at) -> bool:
55
+ """Resolves the question only if it is still open, in one UPDATE: of two people answering at
56
+ once, exactly one wins (Fase 0, R17). Commits."""
57
+ claimed = (
58
+ self.session.query(MemoryQuestion)
59
+ .filter(MemoryQuestion.company_id == company_id, MemoryQuestion.id == question_id,
60
+ MemoryQuestion.status == MemoryQuestion.STATUS_OPEN)
61
+ .update({MemoryQuestion.status: status, MemoryQuestion.resolved_by: resolved_by,
62
+ MemoryQuestion.resolved_at: resolved_at}, synchronize_session=False)
63
+ )
64
+ self.commit()
65
+ return bool(claimed)
66
+
67
+ def reopen(self, company_id: int, question_id: int) -> None:
68
+ """Undoes a claim whose answer could not be saved."""
69
+ (
70
+ self.session.query(MemoryQuestion)
71
+ .filter(MemoryQuestion.company_id == company_id, MemoryQuestion.id == question_id)
72
+ .update({MemoryQuestion.status: MemoryQuestion.STATUS_OPEN, MemoryQuestion.resolved_by: None,
73
+ MemoryQuestion.resolved_at: None}, synchronize_session=False)
74
+ )
75
+ self.commit()
76
+
54
77
  def get_question(self, company_id: int, question_id: int) -> MemoryQuestion | None:
55
78
  return (
56
79
  self.session.query(MemoryQuestion)
@@ -131,6 +131,19 @@ class MemoryRepo:
131
131
  .all()
132
132
  )
133
133
 
134
+ def list_failed_captures(self, company_id: int, failed_before, limit: int = 50) -> list[MemoryCapture]:
135
+ """Captures whose compilation failed before `failed_before`, oldest first (to retry them)."""
136
+ if not company_id:
137
+ return []
138
+ return (
139
+ self.session.query(MemoryCapture)
140
+ .filter_by(company_id=company_id, status=MemoryCaptureStatus.FAILED)
141
+ .filter(MemoryCapture.updated_at < failed_before)
142
+ .order_by(MemoryCapture.updated_at.asc(), MemoryCapture.id.asc())
143
+ .limit(limit)
144
+ .all()
145
+ )
146
+
134
147
  def add_page_sources(self, company_id: int, wiki_page_id: int, item_ids: list[int]) -> None:
135
148
  """Links items to a wiki page, keeping the links it already has."""
136
149
  if not company_id or not wiki_page_id:
@@ -0,0 +1,68 @@
1
+ # Copyright (c) 2024 Fernando Libedinsky
2
+ # Product: IAToolkit
3
+ #
4
+ # IAToolkit is open source software.
5
+
6
+ from __future__ import annotations
7
+
8
+ from collections import Counter
9
+ from datetime import datetime
10
+
11
+ from injector import inject
12
+ from sqlalchemy import case, func
13
+
14
+ from iatoolkit.common import memory_tools
15
+ from iatoolkit.repositories.database_manager import DatabaseManager
16
+ from iatoolkit.repositories.models import MemoryUsageEvent
17
+
18
+
19
+ class MemoryUsageRepo:
20
+ """iat_memory_usage_events: one row per use of the company's knowledge (Fase 2)."""
21
+
22
+ @inject
23
+ def __init__(self, db_manager: DatabaseManager):
24
+ self.session = db_manager.get_session()
25
+
26
+ def add(self, event: MemoryUsageEvent) -> None:
27
+ self.session.add(event)
28
+ self.session.commit()
29
+
30
+ def rollback(self) -> None:
31
+ self.session.rollback()
32
+
33
+ def summarize(self, company_id: int, start: datetime, end: datetime) -> dict:
34
+ """What the company's knowledge was used for in [start, end), by tool, channel and day."""
35
+ base = (
36
+ self.session.query(MemoryUsageEvent)
37
+ .filter(MemoryUsageEvent.company_id == company_id,
38
+ MemoryUsageEvent.created_at >= start, MemoryUsageEvent.created_at < end)
39
+ )
40
+ searches = found = 0
41
+ by_channel: Counter = Counter()
42
+ by_day: Counter = Counter()
43
+ found_flag = case((MemoryUsageEvent.results > 0, 1), else_=0)
44
+ day = func.date(MemoryUsageEvent.created_at)
45
+ rows = (
46
+ base.with_entities(MemoryUsageEvent.channel, day, found_flag, func.count(MemoryUsageEvent.id))
47
+ .filter(MemoryUsageEvent.tool.in_(list(memory_tools.SEARCH_TOOLS)))
48
+ .group_by(MemoryUsageEvent.channel, day, found_flag)
49
+ .all()
50
+ )
51
+ for channel, event_day, was_found, count in rows:
52
+ searches += count
53
+ if was_found:
54
+ found += count
55
+ by_channel[channel] += count
56
+ by_day[str(event_day)] += count
57
+ page_reads = Counter({str(page_id): count for page_id, count in (
58
+ base.with_entities(MemoryUsageEvent.page_id, func.count(MemoryUsageEvent.id))
59
+ .filter(MemoryUsageEvent.tool == memory_tools.PAGE_READ_TOOL, MemoryUsageEvent.page_id.isnot(None))
60
+ .group_by(MemoryUsageEvent.page_id).all()
61
+ )})
62
+ document_reads = Counter({str(document_id): count for document_id, count in (
63
+ base.with_entities(MemoryUsageEvent.document_id, func.count(MemoryUsageEvent.id))
64
+ .filter(MemoryUsageEvent.tool == memory_tools.DOCUMENT_READ_TOOL, MemoryUsageEvent.document_id.isnot(None))
65
+ .group_by(MemoryUsageEvent.document_id).all()
66
+ )})
67
+ return {"searches": searches, "searches_found": found, "by_channel": dict(by_channel),
68
+ "by_day": dict(by_day), "page_reads": page_reads, "document_reads": document_reads}
@@ -871,6 +871,69 @@ class KnowledgeWikiPage(Base):
871
871
  return {column.key: getattr(self, column.key) for column in class_mapper(self.__class__).columns}
872
872
 
873
873
 
874
+ class MemoryUsageEvent(Base):
875
+ """
876
+ One use of the company's knowledge (Fase 2): a page or document search, or
877
+ a read of what it found, from the chat or an MCP client. Written by the tool
878
+ handlers (core) and the MCP backend (enterprise); Impacto and the
879
+ recognition read these instead of `iat_queries`.
880
+ """
881
+ __tablename__ = "iat_memory_usage_events"
882
+
883
+ id = Column(Integer, primary_key=True, autoincrement=True)
884
+ company_id = Column(Integer, ForeignKey(f"{ORM_SCHEMA}.iat_companies.id", ondelete="CASCADE"), nullable=False)
885
+ created_at = Column(DateTime, default=datetime.now, nullable=False)
886
+ user_identifier = Column(String, nullable=True)
887
+ channel = Column(String(16), nullable=False, default="chat")
888
+ tool = Column(String(64), nullable=False)
889
+ # Searches: what was asked and how much came back. Reads: what was read.
890
+ query = Column(String(300), nullable=True)
891
+ results = Column(Integer, nullable=False, default=0)
892
+ page_id = Column(Integer, nullable=True)
893
+ document_id = Column(Integer, nullable=True)
894
+
895
+ __table_args__ = (
896
+ Index("ix_iat_memory_usage_events_company_created", "company_id", "created_at"),
897
+ )
898
+
899
+
900
+ class KnowledgeWikiPageIndex(Base):
901
+ """
902
+ The search index of a wiki page (Fase 1 of the Memoria architecture review):
903
+ the text to search and its embedding, so `search_pages` is one hybrid SQL
904
+ query (words + meaning, fused by RRF, as the RAG) instead of scoring every
905
+ page in Python.
906
+
907
+ Its own table, so `iat_knowledge_wiki_pages` keeps its shape and create_all()
908
+ builds this one with its index (no migration). The text is written with the
909
+ page (KnowledgeWikiRepo); the embedding afterwards (WikiPageIndexService),
910
+ and it is cleared whenever the text changes. A page without an embedding is
911
+ still found by its words.
912
+ """
913
+ __tablename__ = "iat_knowledge_wiki_page_index"
914
+
915
+ id = Column(Integer, primary_key=True, autoincrement=True)
916
+ page_id = Column(Integer, ForeignKey(f"{ORM_SCHEMA}.iat_knowledge_wiki_pages.id", ondelete="CASCADE"),
917
+ nullable=False, unique=True)
918
+ wiki_id = Column(Integer, ForeignKey(f"{ORM_SCHEMA}.iat_knowledge_wikis.id", ondelete="CASCADE"),
919
+ nullable=False, index=True)
920
+ company_id = Column(Integer, ForeignKey(f"{ORM_SCHEMA}.iat_companies.id", ondelete="CASCADE"), nullable=False)
921
+ search_text = Column(Text, nullable=False, default="")
922
+ content_hash = Column(String(64), nullable=False, default="")
923
+ # Dynamic size, as iat_vsdocs: whatever the company's embedding model returns.
924
+ embedding = Column(Vector, nullable=True)
925
+ embedded_hash = Column(String(64), nullable=True)
926
+ updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, nullable=False)
927
+
928
+ __table_args__ = (
929
+ Index(
930
+ "ix_iat_knowledge_wiki_page_index_text_simple_tsv",
931
+ func.to_tsvector(literal_column("'simple'::regconfig"), search_text),
932
+ postgresql_using="gin",
933
+ ).ddl_if(dialect="postgresql"),
934
+ )
935
+
936
+
874
937
  class KnowledgeWikiPageRevision(Base):
875
938
  """Immutable audit snapshot for a manual tenant wiki page change."""
876
939
  __tablename__ = "iat_knowledge_wiki_page_revisions"
@@ -3,6 +3,7 @@
3
3
  #
4
4
  # IAToolkit is open source software.
5
5
 
6
+ from iatoolkit.common import memory_tools
6
7
  from iatoolkit.common.exceptions import IAToolkitException
7
8
  from iatoolkit.repositories.llm_query_repo import LLMQueryRepo
8
9
  from iatoolkit.services.inference_service import InferenceService
@@ -88,6 +89,19 @@ class Dispatcher:
88
89
  def clear_tool_execution_hook(cls):
89
90
  cls._tool_execution_hook = None
90
91
 
92
+ @staticmethod
93
+ def _record_knowledge_use(company_short_name, function_name, arguments, result, user_identifier, runtime_source):
94
+ """Fase 2: one usage event per search or read of the company's knowledge (best effort)."""
95
+ try:
96
+ from iatoolkit import current_iatoolkit
97
+ from iatoolkit.services.memory_usage_service import MemoryUsageService
98
+
99
+ current_iatoolkit().get_injector().get(MemoryUsageService).record(
100
+ company_short_name, function_name, arguments, result, user_identifier=user_identifier,
101
+ channel=str(runtime_source or memory_tools.CHANNEL_CHAT))
102
+ except Exception as exc:
103
+ logging.warning("Memoria usage event skipped for %s: %s", function_name, exc)
104
+
91
105
  @classmethod
92
106
  def register_tool_type_handler(cls, tool_type: str, handler):
93
107
  """
@@ -292,7 +306,13 @@ class Dispatcher:
292
306
  handler_kwargs = dict(kwargs)
293
307
  if user_identifier and function_name in self.USER_SCOPED_SYSTEM_TOOLS:
294
308
  handler_kwargs["user_identifier"] = user_identifier
295
- return handler(company_short_name, **handler_kwargs)
309
+ result = handler(company_short_name, **handler_kwargs)
310
+ # An evaluation replaying an agent is not a use of the company's knowledge.
311
+ if (function_name in memory_tools.KNOWLEDGE_TOOLS
312
+ and str(runtime_source or "").strip().lower() != EVALUATION_REQUEST_SOURCE):
313
+ self._record_knowledge_use(company_short_name, function_name, kwargs, result,
314
+ user_identifier, runtime_source)
315
+ return result
296
316
 
297
317
  elif tool_def.tool_type == 'INFERENCE':
298
318
  # Delegate to Inference Service with DB config
@@ -15,8 +15,9 @@ from html import unescape
15
15
 
16
16
  import requests
17
17
  from injector import inject
18
- from sqlalchemy import text
19
18
 
19
+ from iatoolkit.common.url_safety import fetch_with_safe_redirects
20
+ from iatoolkit.repositories.advisory_lock import advisory_lock
20
21
  from iatoolkit.repositories.database_manager import DatabaseManager
21
22
  from iatoolkit.repositories.document_repo import DocumentRepo
22
23
  from iatoolkit.repositories.memory_repo import MemoryRepo
@@ -64,6 +65,8 @@ class MemoryCompilerService:
64
65
  # A document of the RAG that feeds the wiki (BL-014): read whole, part by part.
65
66
  DOCUMENT_MODE = "document"
66
67
  DOCUMENT_AGENT = "memory_document_ingest"
68
+ # A saved link is read up to this much (its title and the start of its text).
69
+ LINK_FETCH_MAX_BYTES = 512 * 1024
67
70
  # A meeting transcript (BL-015): decisions, procedures and owners, not the conversation.
68
71
  MEETING_AGENT = "memory_meeting_ingest"
69
72
  MEETING_KIND = "meeting"
@@ -153,24 +156,8 @@ class MemoryCompilerService:
153
156
  so the many commits made while compiling cannot release it early. Other
154
157
  databases (SQLite in tests and local runs) have no concurrent compilers.
155
158
  """
156
- if self.db_manager.get_dialect() not in ("postgresql", "postgres"):
159
+ with advisory_lock(self.db_manager, self.ADVISORY_LOCK_NAMESPACE, company_id):
157
160
  yield
158
- return
159
- connection = self.db_manager.get_connection()
160
- try:
161
- connection.execute(
162
- text("SELECT pg_advisory_lock(:namespace, :company_id)"),
163
- {"namespace": self.ADVISORY_LOCK_NAMESPACE, "company_id": int(company_id)},
164
- )
165
- try:
166
- yield
167
- finally:
168
- connection.execute(
169
- text("SELECT pg_advisory_unlock(:namespace, :company_id)"),
170
- {"namespace": self.ADVISORY_LOCK_NAMESPACE, "company_id": int(company_id)},
171
- )
172
- finally:
173
- connection.close()
174
161
 
175
162
  def _agent_for(self, capture: MemoryCapture) -> str:
176
163
  mode = dict(capture.meta or {}).get("mode")
@@ -1019,9 +1006,15 @@ class MemoryCompilerService:
1019
1006
  return
1020
1007
 
1021
1008
  try:
1022
- response = requests.get(item.source_url, timeout=8, headers={"User-Agent": "IAToolkit Memory/1.0"})
1009
+ # A link a person saved: never a private, loopback or metadata address,
1010
+ # and every redirect checked the same way (SSRF).
1011
+ response = fetch_with_safe_redirects(
1012
+ item.source_url, allowed_schemes=("http", "https"), label="Memory link",
1013
+ timeout=8, stream=True, headers={"User-Agent": "IAToolkit Memory/1.0"})
1023
1014
  response.raise_for_status()
1024
- html = response.text
1015
+ raw = response.raw.read(self.LINK_FETCH_MAX_BYTES, decode_content=True)
1016
+ response.close()
1017
+ html = raw.decode(response.encoding or "utf-8", errors="replace")
1025
1018
  except Exception as exc:
1026
1019
  logging.warning("Could not fetch memory link %s: %s", item.source_url, exc)
1027
1020
  return