iatoolkit 2.70.0__tar.gz → 2.71.1__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 (278) hide show
  1. {iatoolkit-2.70.0/src/iatoolkit.egg-info → iatoolkit-2.71.1}/PKG-INFO +1 -1
  2. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/__init__.py +1 -1
  3. iatoolkit-2.71.1/src/iatoolkit/common/access.py +206 -0
  4. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/core.py +11 -1
  5. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/locales/en.yaml +91 -0
  6. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/locales/es.yaml +91 -0
  7. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/auth_service.py +8 -0
  8. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/memory_wiki_service.py +5 -3
  9. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/profile_service.py +11 -1
  10. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/prompt_service.py +7 -1
  11. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/query_service.py +7 -0
  12. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/tenant_wiki_service.py +13 -12
  13. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/_chat_header.html +1 -1
  14. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/api_key_api_view.py +2 -2
  15. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/base_login_view.py +1 -1
  16. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/chat_context_preview_api_view.py +2 -2
  17. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/chat_view.py +1 -1
  18. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/configuration_api_view.py +4 -7
  19. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/login_view.py +1 -1
  20. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/prompt_api_view.py +11 -6
  21. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/prompt_context_preview_api_view.py +2 -2
  22. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/prompt_resource_api_view.py +3 -4
  23. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/tool_api_view.py +2 -2
  24. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/users_api_view.py +2 -2
  25. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/wiki_page_view.py +2 -4
  26. {iatoolkit-2.70.0 → iatoolkit-2.71.1/src/iatoolkit.egg-info}/PKG-INFO +1 -1
  27. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit.egg-info/SOURCES.txt +1 -0
  28. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/LICENSE +0 -0
  29. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/LICENSE_COMMUNITY.md +0 -0
  30. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/pyproject.toml +0 -0
  31. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/readme.md +0 -0
  32. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/setup.cfg +0 -0
  33. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/base_company.py +0 -0
  34. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/cli_commands.py +0 -0
  35. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/__init__.py +0 -0
  36. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/bridge_validation.py +0 -0
  37. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/company_paths.py +0 -0
  38. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/exceptions.py +0 -0
  39. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/interfaces/__init__.py +0 -0
  40. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/interfaces/asset_storage.py +0 -0
  41. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/interfaces/database_provider.py +0 -0
  42. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/interfaces/memory_compilation_trigger.py +0 -0
  43. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/interfaces/secret_provider.py +0 -0
  44. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/interfaces/signup_policy_resolver.py +0 -0
  45. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/interfaces/web_search_provider.py +0 -0
  46. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/memory_tools.py +0 -0
  47. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/model_registry.py +0 -0
  48. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/routes.py +0 -0
  49. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/search_terms.py +0 -0
  50. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/secret_resolver.py +0 -0
  51. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/session_manager.py +0 -0
  52. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/text_splitter.py +0 -0
  53. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/url_safety.py +0 -0
  54. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/util.py +0 -0
  55. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/company_registry.py +0 -0
  56. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/__init__.py +0 -0
  57. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/llm_capabilities.yaml +0 -0
  58. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_agents/memory_compiler.prompt +0 -0
  59. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_agents/memory_correction.prompt +0 -0
  60. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_agents/memory_document_ingest.prompt +0 -0
  61. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_agents/memory_image_ingest.prompt +0 -0
  62. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_agents/memory_meeting_ingest.prompt +0 -0
  63. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_agents/memory_question_review.prompt +0 -0
  64. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_agents_pack.yaml +0 -0
  65. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/channel_business_identity.prompt +0 -0
  66. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/channel_conversation_continuity.prompt +0 -0
  67. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/channel_response_style.prompt +0 -0
  68. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/chat_state_rules.prompt +0 -0
  69. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/chat_user_profile.prompt +0 -0
  70. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/core_identity.prompt +0 -0
  71. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/email_output.prompt +0 -0
  72. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/file_download_output.prompt +0 -0
  73. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/format_styles.prompt +0 -0
  74. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/html_structures.prompt +0 -0
  75. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/links_documents.prompt +0 -0
  76. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/memory_usage.prompt +0 -0
  77. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/multimodal_basics.prompt +0 -0
  78. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/output_basics.prompt +0 -0
  79. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/presentation_formatting.prompt +0 -0
  80. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/query_main.prompt +0 -0
  81. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/sql_aggregation_scope.prompt +0 -0
  82. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/sql_casting.prompt +0 -0
  83. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/sql_core.prompt +0 -0
  84. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/sql_jsonb.prompt +0 -0
  85. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/sql_mysql_casting.prompt +0 -0
  86. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/sql_mysql_json.prompt +0 -0
  87. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/sql_redshift_basics.prompt +0 -0
  88. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/sql_redshift_types.prompt +0 -0
  89. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/sql_rules.prompt +0 -0
  90. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/tool_html_passthrough.prompt +0 -0
  91. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts_pack.yaml +0 -0
  92. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_tools_pack.yaml +0 -0
  93. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/__init__.py +0 -0
  94. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/brevo_mail_app.py +0 -0
  95. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/call_service.py +0 -0
  96. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/connectors/__init__.py +0 -0
  97. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/connectors/file_connector.py +0 -0
  98. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/connectors/file_connector_factory.py +0 -0
  99. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/connectors/google_cloud_storage_connector.py +0 -0
  100. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/connectors/google_drive_connector.py +0 -0
  101. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/connectors/local_file_connector.py +0 -0
  102. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/connectors/s3_connector.py +0 -0
  103. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/google_auth_client.py +0 -0
  104. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/google_chat_app.py +0 -0
  105. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/inference_embeddings_client.py +0 -0
  106. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/inference_handler.py +0 -0
  107. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/jina_embeddings_client.py +0 -0
  108. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/llm_gateway_resolver.py +0 -0
  109. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/llm_providers/__init__.py +0 -0
  110. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/llm_providers/anthropic_adapter.py +0 -0
  111. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/llm_providers/deepseek_adapter.py +0 -0
  112. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/llm_providers/gemini_adapter.py +0 -0
  113. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/llm_providers/openai_adapter.py +0 -0
  114. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/llm_providers/openai_compatible_chat_adapter.py +0 -0
  115. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/llm_providers/openrouter_adapter.py +0 -0
  116. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/llm_proxy.py +0 -0
  117. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/llm_response.py +0 -0
  118. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/redis_session_manager.py +0 -0
  119. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/__init__.py +0 -0
  120. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/advisory_lock.py +0 -0
  121. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/api_key_repo.py +0 -0
  122. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/bridge_repo.py +0 -0
  123. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/database_manager.py +0 -0
  124. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/document_repo.py +0 -0
  125. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/env_secret_provider.py +0 -0
  126. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/filesystem_asset_repository.py +0 -0
  127. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/knowledge_wiki_repo.py +0 -0
  128. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/llm_query_repo.py +0 -0
  129. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/mcp_token_repo.py +0 -0
  130. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/memory_question_repo.py +0 -0
  131. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/memory_repo.py +0 -0
  132. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/memory_usage_repo.py +0 -0
  133. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/models.py +0 -0
  134. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/profile_repo.py +0 -0
  135. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/prompt_resource_repo.py +0 -0
  136. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/sql_dataset_repo.py +0 -0
  137. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/sql_source_repo.py +0 -0
  138. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/vs_repo.py +0 -0
  139. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/runtime_logging.py +0 -0
  140. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/__init__.py +0 -0
  141. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/api_key_service.py +0 -0
  142. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/attachment_policy_service.py +0 -0
  143. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/branding_service.py +0 -0
  144. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/company_context_service.py +0 -0
  145. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/configuration_service.py +0 -0
  146. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/context_builder_service.py +0 -0
  147. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/dispatcher_service.py +0 -0
  148. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/embedding_service.py +0 -0
  149. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/excel_service.py +0 -0
  150. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/file_processor_service.py +0 -0
  151. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/history_manager_service.py +0 -0
  152. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/http_tool_service.py +0 -0
  153. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/i18n_service.py +0 -0
  154. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/inference_service.py +0 -0
  155. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/jwt_service.py +0 -0
  156. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/knowledge_base_service.py +0 -0
  157. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/language_service.py +0 -0
  158. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/license_service.py +0 -0
  159. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/llm_client_service.py +0 -0
  160. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/mail_service.py +0 -0
  161. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/markdown_wiki_service.py +0 -0
  162. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/mcp_token_service.py +0 -0
  163. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/memory_compiler_service.py +0 -0
  164. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/memory_lookup_policy_service.py +0 -0
  165. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/memory_question_service.py +0 -0
  166. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/memory_service.py +0 -0
  167. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/memory_sources.py +0 -0
  168. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/memory_usage_service.py +0 -0
  169. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/noop_memory_compilation_trigger.py +0 -0
  170. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/operator_identity_service.py +0 -0
  171. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/parsers/__init__.py +0 -0
  172. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/parsers/contracts.py +0 -0
  173. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/parsers/image_normalizer.py +0 -0
  174. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/parsers/parsing_service.py +0 -0
  175. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/parsers/pdf_ocr_detection.py +0 -0
  176. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/parsers/provider_factory.py +0 -0
  177. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/parsers/provider_resolver.py +0 -0
  178. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/parsers/providers/__init__.py +0 -0
  179. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/parsers/providers/basic_provider.py +0 -0
  180. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/parsers/providers/docling_provider.py +0 -0
  181. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/parsers/validator.py +0 -0
  182. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/pdf_service.py +0 -0
  183. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/prompt_resource_service.py +0 -0
  184. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/signup_policy_resolver.py +0 -0
  185. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/sql_dataset_service.py +0 -0
  186. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/sql_service.py +0 -0
  187. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/sql_source_service.py +0 -0
  188. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/storage_service.py +0 -0
  189. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/structured_output_service.py +0 -0
  190. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/system_agent_catalog.py +0 -0
  191. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/system_agent_runner.py +0 -0
  192. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/system_prompt_catalog.py +0 -0
  193. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/system_tools.py +0 -0
  194. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/telemetry_service.py +0 -0
  195. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/tool_output_contract.py +0 -0
  196. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/tool_service.py +0 -0
  197. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/user_feedback_service.py +0 -0
  198. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/user_session_context_service.py +0 -0
  199. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/visual_kb_service.py +0 -0
  200. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/visual_tool_service.py +0 -0
  201. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/warmup_service.py +0 -0
  202. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/web_search/__init__.py +0 -0
  203. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/web_search/provider_factory.py +0 -0
  204. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/web_search/providers/__init__.py +0 -0
  205. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/web_search/providers/brave_provider.py +0 -0
  206. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/web_search_service.py +0 -0
  207. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/wiki_page_index_service.py +0 -0
  208. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/images/favicon.ico +0 -0
  209. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/images/favicon.png +0 -0
  210. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/images/fernando.jpeg +0 -0
  211. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/images/iatoolkit_core.png +0 -0
  212. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/images/iatoolkit_logo.png +0 -0
  213. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/js/chat_feedback_button.js +0 -0
  214. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/js/chat_filepond.js +0 -0
  215. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/js/chat_header_navigation.js +0 -0
  216. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/js/chat_help_content.js +0 -0
  217. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/js/chat_history_button.js +0 -0
  218. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/js/chat_logout_button.js +0 -0
  219. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/js/chat_main.js +0 -0
  220. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/js/chat_memory_button.js +0 -0
  221. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/js/chat_model_selector.js +0 -0
  222. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/js/chat_onboarding_button.js +0 -0
  223. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/js/chat_prompt_manager.js +0 -0
  224. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/js/chat_reload_button.js +0 -0
  225. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/styles/account.css +0 -0
  226. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/styles/chat_iatoolkit.css +0 -0
  227. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/styles/chat_modal.css +0 -0
  228. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/styles/chat_public.css +0 -0
  229. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/styles/documents.css +0 -0
  230. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/styles/landing_page.css +0 -0
  231. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/styles/llm_output.css +0 -0
  232. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/styles/onboarding.css +0 -0
  233. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/styles/wiki_page.css +0 -0
  234. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/_company_header.html +0 -0
  235. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/_login_widget.html +0 -0
  236. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/account.html +0 -0
  237. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/base.html +0 -0
  238. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/change_password.html +0 -0
  239. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/chat.html +0 -0
  240. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/chat_memory_modal.html +0 -0
  241. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/chat_modals.html +0 -0
  242. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/error.html +0 -0
  243. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/forgot_password.html +0 -0
  244. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/home_hosted_default.html +0 -0
  245. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/login_global.html +0 -0
  246. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/memory/wiki_schema.md +0 -0
  247. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/onboarding_shell.html +0 -0
  248. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/pdf/base.html +0 -0
  249. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/pdf/letter.html +0 -0
  250. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/pdf/report.html +0 -0
  251. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/pdf/simple.html +0 -0
  252. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/signup.html +0 -0
  253. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/wiki_page.html +0 -0
  254. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/__init__.py +0 -0
  255. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/account_view.py +0 -0
  256. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/categories_api_view.py +0 -0
  257. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/change_password_view.py +0 -0
  258. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/connectors_api_view.py +0 -0
  259. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/embedding_api_view.py +0 -0
  260. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/forgot_password_view.py +0 -0
  261. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/help_content_api_view.py +0 -0
  262. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/history_api_view.py +0 -0
  263. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/home_view.py +0 -0
  264. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/init_context_api_view.py +0 -0
  265. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/llmquery_api_view.py +0 -0
  266. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/load_document_api_view.py +0 -0
  267. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/logout_api_view.py +0 -0
  268. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/memory_api_view.py +0 -0
  269. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/profile_api_view.py +0 -0
  270. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/rag_api_view.py +0 -0
  271. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/root_redirect_view.py +0 -0
  272. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/signup_view.py +0 -0
  273. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/static_page_view.py +0 -0
  274. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/user_feedback_api_view.py +0 -0
  275. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/verify_user_view.py +0 -0
  276. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit.egg-info/dependency_links.txt +0 -0
  277. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit.egg-info/requires.txt +0 -0
  278. {iatoolkit-2.70.0 → iatoolkit-2.71.1}/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.70.0
3
+ Version: 2.71.1
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.70.0"
6
+ __version__ = "2.71.1"
7
7
 
8
8
  # Expose main classes and functions at the top level of the package
9
9
 
@@ -0,0 +1,206 @@
1
+ # Copyright (c) 2024 Fernando Libedinsky
2
+ # Product: IAToolkit
3
+ #
4
+ # IAToolkit is open source software.
5
+
6
+ """Who may do what: the one question every check asks (BL-010, Fase 1).
7
+
8
+ Code never names a role. It asks whether a principal (a person's email, or a
9
+ service account `service:<key>`) has a level over a resource:
10
+
11
+ access.can(company, user_identifier, EDIT, "company")
12
+ access.can(company, user_identifier, USE, "agent", "cobranza")
13
+
14
+ Three levels, each including the one below:
15
+ 1 USE use an agent, tool, model, SQL source / see content
16
+ 2 EDIT change the resource (an agent's prompt, a folder's documents)
17
+ 3 ADMIN decide on it: who has access, delete it, its settings
18
+
19
+ Over the resource type "company", EDIT is entering the admin console and
20
+ editing every resource, and ADMIN is administering the company (its team,
21
+ settings). Billing and deleting the tenant are for its owners only (`is_owner`).
22
+
23
+ Enterprise registers the decider backed by groups and grants. Without it,
24
+ the decision falls back to the person's role in the company, as before.
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ import logging
30
+ from typing import Callable, Protocol
31
+
32
+ USE = 1
33
+ EDIT = 2
34
+ ADMIN = 3
35
+
36
+ LEVEL_NAMES = {USE: "use", EDIT: "edit", ADMIN: "admin"}
37
+
38
+ # The level each legacy role holds over everything (core without enterprise).
39
+ ROLE_LEVELS = {"user": USE, "editor": EDIT, "admin": ADMIN, "owner": ADMIN}
40
+
41
+
42
+ class AccessDecider(Protocol):
43
+ def can(self, company_short_name: str, user_identifier: str | None, level: int,
44
+ resource_type: str, resource_key: str | None = None,
45
+ resource_path: str | None = None) -> bool: ...
46
+
47
+ def is_owner(self, company_short_name: str, user_identifier: str | None) -> bool: ...
48
+
49
+ def effective_role(self, company_short_name: str, user_identifier: str | None) -> str | None: ...
50
+
51
+
52
+ class RoleAccessDecider:
53
+ """Core's fallback: the role of iat_user_company grants its level over everything."""
54
+
55
+ def __init__(self, role_reader: Callable[[str, str], str | None]):
56
+ self.role_reader = role_reader
57
+
58
+ def _role(self, company_short_name, user_identifier) -> str | None:
59
+ if not user_identifier:
60
+ return None
61
+ return self.role_reader(company_short_name, user_identifier)
62
+
63
+ def can(self, company_short_name, user_identifier, level, resource_type, resource_key=None,
64
+ resource_path=None) -> bool:
65
+ if level <= USE and resource_type != "company":
66
+ # Without groups nothing is restricted: whoever enters the company uses
67
+ # what it offers (members, and the end users a key acts for).
68
+ return True
69
+ role = self._role(company_short_name, user_identifier)
70
+ if role is None:
71
+ return False
72
+ return ROLE_LEVELS.get(role, USE) >= level
73
+
74
+ def is_owner(self, company_short_name, user_identifier) -> bool:
75
+ return self._role(company_short_name, user_identifier) == "owner"
76
+
77
+ def effective_role(self, company_short_name, user_identifier) -> str | None:
78
+ return self._role(company_short_name, user_identifier)
79
+
80
+
81
+ class Access:
82
+ """The registered decider; enterprise replaces core's fallback at boot."""
83
+
84
+ _decider: AccessDecider | None = None
85
+
86
+ @classmethod
87
+ def register(cls, decider: AccessDecider) -> None:
88
+ cls._decider = decider
89
+
90
+ @classmethod
91
+ def clear(cls) -> None:
92
+ cls._decider = None
93
+
94
+ @classmethod
95
+ def registered(cls) -> bool:
96
+ """Whether a decider other than core's role fallback is in place (enterprise)."""
97
+ return cls._decider is not None and not isinstance(cls._decider, RoleAccessDecider)
98
+
99
+ @classmethod
100
+ def decider(cls) -> AccessDecider:
101
+ if cls._decider is None:
102
+ cls._decider = RoleAccessDecider(_role_from_profile_service)
103
+ return cls._decider
104
+
105
+
106
+ def decider_for(role_reader: Callable[[str, str], str | None]) -> AccessDecider:
107
+ """The registered decider, or the role fallback reading roles with `role_reader`
108
+ (views that already hold a ProfileRepo)."""
109
+ return Access.decider() if Access.registered() else RoleAccessDecider(role_reader)
110
+
111
+
112
+ def _role_from_profile_service(company_short_name: str, user_identifier: str) -> str | None:
113
+ from iatoolkit import current_iatoolkit
114
+ from iatoolkit.services.profile_service import ProfileService
115
+
116
+ return current_iatoolkit().get_injector().get(ProfileService).get_role_in_company(
117
+ company_short_name, user_identifier)
118
+
119
+
120
+ def can(company_short_name: str, user_identifier: str | None, level: int, resource_type: str,
121
+ resource_key: str | None = None, resource_path: str | None = None) -> bool:
122
+ try:
123
+ return bool(Access.decider().can(company_short_name, user_identifier, level, resource_type,
124
+ resource_key, resource_path))
125
+ except Exception as exc:
126
+ # Deciding failed: deny, never fall open.
127
+ logging.warning("Access check failed (%s, %s, %s %s): %s", company_short_name, user_identifier,
128
+ level, resource_type, exc)
129
+ return False
130
+
131
+
132
+ def is_owner(company_short_name: str, user_identifier: str | None) -> bool:
133
+ try:
134
+ return bool(Access.decider().is_owner(company_short_name, user_identifier))
135
+ except Exception as exc:
136
+ logging.warning("Owner check failed (%s, %s): %s", company_short_name, user_identifier, exc)
137
+ return False
138
+
139
+
140
+ def effective_roles(company_short_name: str, user_identifiers: list[str]) -> dict[str, str | None]:
141
+ """`effective_role` for many people at once (the Users screen)."""
142
+ decider = Access.decider()
143
+ try:
144
+ bulk = getattr(decider, "effective_roles", None)
145
+ if callable(bulk):
146
+ return bulk(company_short_name, user_identifiers)
147
+ return {u: decider.effective_role(company_short_name, u) for u in user_identifiers}
148
+ except Exception as exc:
149
+ logging.warning("Effective roles failed (%s): %s", company_short_name, exc)
150
+ return {}
151
+
152
+
153
+ def effective_role(company_short_name: str, user_identifier: str | None) -> str | None:
154
+ """What the person can do over the company, as the console's old role names.
155
+
156
+ Only for screens that still show or branch on a role name (labels, menus);
157
+ access is always decided with `can`.
158
+ """
159
+ try:
160
+ return Access.decider().effective_role(company_short_name, user_identifier)
161
+ except Exception as exc:
162
+ logging.warning("Effective role failed (%s, %s): %s", company_short_name, user_identifier, exc)
163
+ return None
164
+
165
+
166
+ def role_assigned(company_short_name: str, user_identifier: str, role: str) -> None:
167
+ """Somebody was given a legacy role (signup's bootstrap admin, company activation,
168
+ `init-install --owner`): the registered decider puts them in the matching group."""
169
+ decider = Access._decider
170
+ hook = getattr(decider, "role_assigned", None) if decider is not None else None
171
+ if callable(hook):
172
+ try:
173
+ hook(company_short_name, user_identifier, role)
174
+ except Exception as exc:
175
+ logging.warning("Role %s of %s in %s not moved to its group: %s", role, user_identifier,
176
+ company_short_name, exc)
177
+
178
+
179
+ def person_can(company_short_name: str, auth_result: dict | None, level: int, resource_type: str = "company",
180
+ resource_key: str | None = None) -> bool:
181
+ """A signed-in person's access, from an AuthService result.
182
+
183
+ Only sessions carry `user_role`; an API key is not a person and gets False
184
+ (each view decides on its own whether a key may pass).
185
+ """
186
+ if not isinstance(auth_result, dict) or "user_role" not in auth_result:
187
+ return False
188
+ if not Access.registered():
189
+ # Core alone: the session's role was just read from iat_user_company.
190
+ return _session_role_decider(auth_result).can(company_short_name, auth_result.get("user_identifier") or "session",
191
+ level, resource_type, resource_key)
192
+ return can(company_short_name, auth_result.get("user_identifier"), level, resource_type, resource_key)
193
+
194
+
195
+ def person_is_owner(company_short_name: str, auth_result: dict | None) -> bool:
196
+ if not isinstance(auth_result, dict) or "user_role" not in auth_result:
197
+ return False
198
+ if not Access.registered():
199
+ return _session_role_decider(auth_result).is_owner(company_short_name,
200
+ auth_result.get("user_identifier") or "session")
201
+ return is_owner(company_short_name, auth_result.get("user_identifier"))
202
+
203
+
204
+ def _session_role_decider(auth_result: dict) -> RoleAccessDecider:
205
+ role = str(auth_result.get("user_role") or "").strip().lower() or None
206
+ return RoleAccessDecider(lambda company_short_name, user_identifier: role)
@@ -195,6 +195,13 @@ class IAToolkit:
195
195
  thread.start()
196
196
  return thread
197
197
 
198
+ @staticmethod
199
+ def _effective_role(company_short_name: str | None, user_identifier: str | None) -> str | None:
200
+ if not company_short_name or not user_identifier:
201
+ return None
202
+ from iatoolkit.common import access
203
+ return access.effective_role(company_short_name, user_identifier)
204
+
198
205
  def _get_config_value(self, key: str, default=None):
199
206
  # get a value from the config dict or the environment variable
200
207
  return self.config.get(key, os.getenv(key, default))
@@ -653,7 +660,10 @@ class IAToolkit:
653
660
  'app_name': 'IAToolkit',
654
661
  'user_identifier': session_info.get('user_identifier'),
655
662
  'company_short_name': route_company_short_name or session_info.get('company_short_name'),
656
- 'user_role': user_profile.get('user_role'),
663
+ # What the person can do over the company (BL-010, Fase 1), never the
664
+ # role an SSO hook may have written into the session profile.
665
+ 'user_role': self._effective_role(route_company_short_name or session_info.get('company_short_name'),
666
+ session_info.get('user_identifier')),
657
667
  'user_is_local': user_profile.get('user_is_local'),
658
668
  'user_email': user_profile.get('user_email'),
659
669
  'iatoolkit_base_url': request.url_root,
@@ -645,6 +645,95 @@ ui:
645
645
  admin:
646
646
  workspace: "Workspace"
647
647
  team_invitations: "Invitations"
648
+ team_groups: "Groups"
649
+ team_groups_edit: "Groups"
650
+ team_groups_modal_title: "Person's groups"
651
+ team_groups_save: "Save"
652
+ team_groups_close: "Close"
653
+ team_groups_saved: "Groups updated."
654
+ team_groups_everyone_hint: "Every member is in Everyone: no need to tick it."
655
+ team_group_new: "New group"
656
+ team_group_name: "Name"
657
+ team_group_description: "Description"
658
+ team_group_members: "Members"
659
+ team_group_add_member: "Add"
660
+ team_group_remove: "Remove"
661
+ team_group_delete: "Delete group"
662
+ team_group_builtin: "Built-in"
663
+ team_group_save: "Save"
664
+ team_group_no_grants_short: "Only what resources give it"
665
+ team_group_resources: "resources with their own access"
666
+ team_group_resource: "resource with its own access"
667
+ team_groups_empty_hint: "Create one to restrict an agent to certain people."
668
+ team_groups_empty_title: "No groups of your own yet"
669
+ team_groups_builtin: "Built-in groups"
670
+ team_groups_company: "Company groups"
671
+ team_groups_intro: "Group people to give them access to agents and other resources."
672
+ team_group_no_match: "Nobody in the company matches."
673
+ team_group_create: "Create group"
674
+ team_group_add_member_label: "Add a person"
675
+ team_group_description_placeholder: "What this group is for (optional)"
676
+ team_group_name_placeholder: "E.g. Risk"
677
+ team_group_details: "Group details"
678
+ team_group_edit_hint: "People in this group."
679
+ team_group_new_hint: "A group brings people together to give them access to agents and other resources."
680
+ team_group_edit: "Edit"
681
+ team_group_everyone_note: "Includes everyone in the company."
682
+ team_group_owners_note: "Only an owner can change the owners. Owners is never empty."
683
+ team_group_grants: "Can"
684
+ team_group_no_grants: "No permissions over the company."
685
+ team_group_owners_all: "everything, billing included"
686
+ team_group_delete_confirm: "Delete this group? Its permissions are lost."
687
+ team_group_none_members: "No members."
688
+ team_group_member_placeholder: "Search by name or email"
689
+ team_group_created: "Group created."
690
+ team_group_deleted: "Group deleted."
691
+ team_group_updated: "Group updated."
692
+ access_level_1: "use / see"
693
+ access_level_2: "edit"
694
+ access_level_3: "administer"
695
+ access_all_resources: "every resource"
696
+ access_company: "the company"
697
+ access_title: "Access"
698
+ access_group_owners: "Owners"
699
+ access_group_admins: "Administrators"
700
+ access_group_editors: "Editors"
701
+ access_group_everyone: "Everyone"
702
+ access_group_owners_desc: "Can do everything, including billing and deleting the company."
703
+ access_group_admins_desc: "Administer the company: team, agents, knowledge and settings."
704
+ access_group_editors_desc: "Edit every resource from the console."
705
+ access_group_everyone_desc: "Everyone in the company."
706
+ access_readonly: "Only those who administer the company can change access."
707
+ access_level_help_3: "Also decide who has access."
708
+ access_level_help_2: "Also change its configuration or content."
709
+ access_level_help_1_see: "Read its content, also through the AI."
710
+ access_level_help_1_use: "Run it in the chat, the API and MCP."
711
+ access_level_1_see: "see"
712
+ access_level_1_use: "use"
713
+ access_nobody: "No groups."
714
+ access_members: "members"
715
+ access_member: "member"
716
+ access_everyone_members: "Whole company"
717
+ access_remove: "Remove access"
718
+ access_add: "Add"
719
+ access_add_title_own: "Give access to another group"
720
+ access_add_title_default: "Restrict to a group"
721
+ access_always_hint: "Always have access; no need to add them."
722
+ access_always: "Owners and administrators"
723
+ access_levels: "Levels"
724
+ access_who: "Who has access"
725
+ access_mode_own: "Restricted"
726
+ access_mode_default: "Whole company"
727
+ access_heading_agent: "Who can use this agent?"
728
+ access_heading: "Who has access?"
729
+ access_default: "Company access (default)"
730
+ access_default_hint: "Everyone uses it under the company's general permissions. Add a group to restrict it."
731
+ access_own_hint: "Only the listed groups have access. Removing the last group returns it to the whole company."
732
+ access_add_group: "Add group"
733
+ access_reset: "Back to company access"
734
+ access_saved: "Access updated."
735
+ access_group: "Group"
736
+ access_level: "Level"
648
737
  team_invite_user: "Invite user"
649
738
  team_invite_none: "No invitations yet."
650
739
  team_invite_loading: "Loading invitations..."
@@ -2602,6 +2691,8 @@ ui:
2602
2691
 
2603
2692
  errors:
2604
2693
  company_not_found: "The company {company_short_name} does not exist."
2694
+ access:
2695
+ agent_denied: "You do not have access to the agent {prompt_name}."
2605
2696
  timeout: "timeout expired."
2606
2697
  auth:
2607
2698
  invalid_password: "The provided password is incorrect."
@@ -659,6 +659,95 @@ ui:
659
659
  admin:
660
660
  workspace: "Recursos"
661
661
  team_invitations: "Invitaciones"
662
+ team_groups: "Grupos"
663
+ team_groups_edit: "Grupos"
664
+ team_groups_modal_title: "Grupos de la persona"
665
+ team_groups_save: "Guardar"
666
+ team_groups_close: "Cerrar"
667
+ team_groups_saved: "Grupos actualizados."
668
+ team_groups_everyone_hint: "Todos los miembros están en Todos: no hace falta marcarlo."
669
+ team_group_new: "Nuevo grupo"
670
+ team_group_name: "Nombre"
671
+ team_group_description: "Descripción"
672
+ team_group_members: "Miembros"
673
+ team_group_add_member: "Agregar"
674
+ team_group_remove: "Quitar"
675
+ team_group_delete: "Eliminar grupo"
676
+ team_group_builtin: "De fábrica"
677
+ team_group_save: "Guardar"
678
+ team_group_no_grants_short: "Solo lo que le den los recursos"
679
+ team_group_resources: "recursos con acceso propio"
680
+ team_group_resource: "recurso con acceso propio"
681
+ team_groups_empty_hint: "Crea uno para restringir un agente a ciertas personas."
682
+ team_groups_empty_title: "Todavía no hay grupos propios"
683
+ team_groups_builtin: "Grupos de fábrica"
684
+ team_groups_company: "Grupos de la empresa"
685
+ team_groups_intro: "Agrupa personas para darles acceso a agentes y otros recursos."
686
+ team_group_no_match: "Nadie de la empresa coincide."
687
+ team_group_create: "Crear grupo"
688
+ team_group_add_member_label: "Agregar persona"
689
+ team_group_description_placeholder: "Para qué es este grupo (opcional)"
690
+ team_group_name_placeholder: "Ej.: Riesgo"
691
+ team_group_details: "Datos del grupo"
692
+ team_group_edit_hint: "Personas de este grupo."
693
+ team_group_new_hint: "Un grupo reúne personas para darles acceso a agentes y otros recursos."
694
+ team_group_edit: "Editar"
695
+ team_group_everyone_note: "Incluye a todas las personas de la empresa."
696
+ team_group_owners_note: "Solo un dueño puede cambiar los dueños. Dueños nunca queda vacío."
697
+ team_group_grants: "Puede"
698
+ team_group_no_grants: "Sin permisos sobre la empresa."
699
+ team_group_owners_all: "todo, incluida la facturación"
700
+ team_group_delete_confirm: "¿Eliminar este grupo? Sus permisos se pierden."
701
+ team_group_none_members: "Sin miembros."
702
+ team_group_member_placeholder: "Busca por nombre o email"
703
+ team_group_created: "Grupo creado."
704
+ team_group_deleted: "Grupo eliminado."
705
+ team_group_updated: "Grupo actualizado."
706
+ access_level_1: "usar / ver"
707
+ access_level_2: "editar"
708
+ access_level_3: "administrar"
709
+ access_all_resources: "todos los recursos"
710
+ access_company: "la empresa"
711
+ access_title: "Acceso"
712
+ access_group_owners: "Dueños"
713
+ access_group_admins: "Administradores"
714
+ access_group_editors: "Editores"
715
+ access_group_everyone: "Todos"
716
+ access_group_owners_desc: "Pueden todo, incluida la facturación y eliminar la empresa."
717
+ access_group_admins_desc: "Administran la empresa: equipo, agentes, conocimiento y ajustes."
718
+ access_group_editors_desc: "Editan todos los recursos desde la consola."
719
+ access_group_everyone_desc: "Todas las personas de la empresa."
720
+ access_readonly: "Solo quienes administran la empresa pueden cambiar el acceso."
721
+ access_level_help_3: "Además, decidir quién tiene acceso."
722
+ access_level_help_2: "Además, cambiar su configuración o contenido."
723
+ access_level_help_1_see: "Leer su contenido, también a través de la IA."
724
+ access_level_help_1_use: "Ejecutarlo en el chat, la API y MCP."
725
+ access_level_1_see: "ver"
726
+ access_level_1_use: "usar"
727
+ access_nobody: "Ningún grupo."
728
+ access_members: "miembros"
729
+ access_member: "miembro"
730
+ access_everyone_members: "Toda la empresa"
731
+ access_remove: "Quitar acceso"
732
+ access_add: "Agregar"
733
+ access_add_title_own: "Dar acceso a otro grupo"
734
+ access_add_title_default: "Restringir a un grupo"
735
+ access_always_hint: "Siempre tienen acceso, no hace falta agregarlos."
736
+ access_always: "Dueños y administradores"
737
+ access_levels: "Niveles"
738
+ access_who: "Quién tiene acceso"
739
+ access_mode_own: "Restringido"
740
+ access_mode_default: "Toda la empresa"
741
+ access_heading_agent: "¿Quién puede usar este agente?"
742
+ access_heading: "¿Quién tiene acceso?"
743
+ access_default: "Acceso de la empresa (por defecto)"
744
+ access_default_hint: "Lo usan todos según los permisos generales de la empresa. Agrega un grupo para restringirlo."
745
+ access_own_hint: "Solo los grupos de la lista tienen acceso. Quitar el último grupo lo devuelve a toda la empresa."
746
+ access_add_group: "Agregar grupo"
747
+ access_reset: "Volver al acceso de la empresa"
748
+ access_saved: "Acceso actualizado."
749
+ access_group: "Grupo"
750
+ access_level: "Nivel"
662
751
  team_invite_user: "Invitar usuario"
663
752
  team_invite_none: "Todavía no hay invitaciones."
664
753
  team_invite_loading: "Cargando invitaciones..."
@@ -2617,6 +2706,8 @@ ui:
2617
2706
 
2618
2707
  errors:
2619
2708
  company_not_found: "La empresa {company_short_name} no existe."
2709
+ access:
2710
+ agent_denied: "No tienes acceso al agente {prompt_name}."
2620
2711
  timeout: "El tiempo de espera ha expirado."
2621
2712
 
2622
2713
 
@@ -17,6 +17,7 @@ import logging
17
17
  import re
18
18
  import unicodedata
19
19
  import hashlib
20
+ from iatoolkit.common import access
20
21
 
21
22
 
22
23
  class AuthService:
@@ -354,6 +355,13 @@ class AuthService:
354
355
  return f"{cls.SERVICE_PREFIX}{name.strip('-') or getattr(api_key_entry, 'id', 'key')}"
355
356
 
356
357
  def _role_in_company(self, company_short_name: str, user_identifier: str) -> str | None:
358
+ """What the person can do over the company, by its old role name (BL-010, Fase 1).
359
+
360
+ Views decide with `iatoolkit.common.access`; this name stays in the result to
361
+ tell a session from an API key and for screens that show it.
362
+ """
363
+ if access.Access.registered():
364
+ return access.effective_role(company_short_name, user_identifier)
357
365
  return self.profile_service.get_role_in_company(company_short_name, user_identifier)
358
366
 
359
367
  def verify_for_company(self, company_short_name: str, anonymous: bool = False) -> dict:
@@ -12,6 +12,7 @@ from injector import inject
12
12
 
13
13
  from iatoolkit.services.markdown_wiki_service import MarkdownWikiService
14
14
  from iatoolkit.services.tenant_wiki_service import TenantWikiService
15
+ from iatoolkit.common.access import ADMIN
15
16
 
16
17
 
17
18
  class MemoryWikiService:
@@ -223,7 +224,8 @@ def _read_schema_template(path: str) -> str:
223
224
  return ""
224
225
 
225
226
 
226
- # Memoria's wiki opens in the wiki viewer for admin and owner only ("Ver como wiki",
227
- # BL-014); for every other role, and for the wiki tools and MCP, it stays hidden.
228
- TenantWikiService.register_system_wiki_readers(MemoryWikiService.SYSTEM_OWNER, {"admin", "owner"})
227
+ # Memoria's wiki opens in the wiki viewer only for those who administer the company
228
+ # ("Ver como wiki", BL-014); for everyone else, and for the wiki tools and MCP, it
229
+ # stays hidden.
230
+ TenantWikiService.register_system_wiki_readers(MemoryWikiService.SYSTEM_OWNER, ADMIN)
229
231
 
@@ -28,6 +28,7 @@ from typing import List, Dict
28
28
  from sqlalchemy import update
29
29
  from iatoolkit.common.interfaces.signup_policy_resolver import SignupPolicyResolver
30
30
  from iatoolkit.services.signup_policy_resolver import AllowAllSignupPolicyResolver
31
+ from iatoolkit.common import access
31
32
 
32
33
 
33
34
  class ProfileService:
@@ -107,6 +108,10 @@ class ProfileService:
107
108
  return first_name, last_name
108
109
 
109
110
  def _build_session_user_profile(self, user: User, company: Company, user_role: str | None) -> dict:
111
+ if access.Access.registered():
112
+ # What the person can do over the company (BL-010, Fase 1), for screens and
113
+ # prompt context; access is always decided with `access.can`.
114
+ user_role = access.effective_role(company.short_name, user.email)
110
115
  auth_method = str(getattr(user, "auth_method", "local") or "local").lower()
111
116
  is_local = auth_method == "local"
112
117
  user_identifier = user.email
@@ -150,6 +155,7 @@ class ProfileService:
150
155
  )
151
156
  .values(role="admin")
152
157
  )
158
+ access.role_assigned(company.short_name, user_email, "admin")
153
159
 
154
160
  updated_params = dict(params)
155
161
  updated_params.pop("bootstrap_admin_email", None)
@@ -430,7 +436,8 @@ class ProfileService:
430
436
  "last_name": user.last_name if user else "",
431
437
  "full_name": f"{user.first_name} {user.last_name}" if user else profile.get("user_fullname", ""),
432
438
  "email": profile.get("user_email") or session_info["user_identifier"],
433
- "role": profile.get("user_role") or "user",
439
+ "role": (access.effective_role(company_short_name, session_info["user_identifier"])
440
+ if access.Access.registered() else profile.get("user_role")) or "user",
434
441
  "auth_method": method,
435
442
  "can_edit_name": bool(user and method == "local"),
436
443
  "language": self.lang_service.get_current_language(),
@@ -952,8 +959,11 @@ class ProfileService:
952
959
  # get the company users from the repo
953
960
  company_users = self.profile_repo.get_company_users_with_details(company_short_name)
954
961
 
962
+ effective = access.effective_roles(company_short_name, [u.email for u, _, _ in company_users]) \
963
+ if access.Access.registered() else {}
955
964
  users_data = []
956
965
  for user, role, last_access in company_users:
966
+ role = effective.get(user.email, role) if access.Access.registered() else role
957
967
  users_data.append({
958
968
  "first_name": user.first_name,
959
969
  "last_name": user.last_name,
@@ -25,6 +25,7 @@ from iatoolkit.services.structured_output_service import StructuredOutputService
25
25
  import logging
26
26
  import re
27
27
  import unicodedata
28
+ from iatoolkit.common import access
28
29
 
29
30
  class PromptService:
30
31
  PROMPT_NAME_MAX_LENGTH = 255
@@ -654,7 +655,8 @@ class PromptService:
654
655
 
655
656
  return normalized_options
656
657
 
657
- def get_prompts(self, company_short_name: str, include_all: bool = False) -> dict:
658
+ def get_prompts(self, company_short_name: str, include_all: bool = False,
659
+ user_identifier: str | None = None) -> dict:
658
660
  try:
659
661
  # validate company
660
662
  company = self.profile_repo.get_company_by_short_name(company_short_name)
@@ -677,6 +679,10 @@ class PromptService:
677
679
  self.get_runtime_policy_role(getattr(prompt, 'runtime_policy', None))
678
680
  )
679
681
  ]
682
+ if user_identifier:
683
+ # A person sees the agents they may use (BL-010, Fase 1).
684
+ all_prompts = [prompt for prompt in all_prompts
685
+ if access.can(company_short_name, user_identifier, access.USE, "agent", prompt.name)]
680
686
 
681
687
  # group by category
682
688
  prompts_by_category = defaultdict(list)
@@ -25,6 +25,7 @@ import time
25
25
  import json
26
26
  import re
27
27
  from dataclasses import dataclass
28
+ from iatoolkit.common import access
28
29
 
29
30
 
30
31
  @dataclass
@@ -1512,6 +1513,12 @@ class QueryService:
1512
1513
  return {"error": True,
1513
1514
  "error_message": self.i18n_service.t('services.start_query')}
1514
1515
 
1516
+ # Using an agent is a level-1 permission over it (BL-010, Fase 1): the chat,
1517
+ # the API and MCP all run through here.
1518
+ if prompt_name and not access.can(company_short_name, user_identifier, access.USE, "agent", prompt_name):
1519
+ return {"error": True, "status_code": 403,
1520
+ "error_message": self.i18n_service.t('errors.access.agent_denied', prompt_name=prompt_name)}
1521
+
1515
1522
  # output contract
1516
1523
  prompt_output_contract = self._resolve_prompt_output_contract(company, prompt_name)
1517
1524
  log_stage("prompt_output_contract")
@@ -32,6 +32,7 @@ from iatoolkit.repositories.models import (
32
32
  from iatoolkit.repositories.profile_repo import ProfileRepo
33
33
  from iatoolkit.services.markdown_wiki_service import MarkdownWikiService
34
34
  from iatoolkit.services.storage_service import StorageService
35
+ from iatoolkit.common import access
35
36
 
36
37
 
37
38
  class TenantWikiService:
@@ -120,29 +121,29 @@ class TenantWikiService:
120
121
  def is_system_wiki(cls, wiki: KnowledgeWiki | None) -> bool:
121
122
  return cls.wiki_system_owner(wiki) is not None
122
123
 
123
- # Roles that may read a system wiki in the browser viewer, by owning feature
124
- # (BL-014: Memoria's wiki for admin and owner). Only the viewer asks: the
125
- # wiki tools, MCP resources and reader links keep leaving system wikis out.
126
- _system_wiki_reader_roles: dict[str, frozenset[str]] = {}
124
+ # The level over the company a person needs to read a system wiki in the
125
+ # browser viewer, by owning feature (BL-014: Memoria's wiki for those who
126
+ # administer the company). Only the viewer asks: the wiki tools, MCP
127
+ # resources and reader links keep leaving system wikis out.
128
+ _system_wiki_reader_levels: dict[str, int] = {}
127
129
 
128
130
  @classmethod
129
- def register_system_wiki_readers(cls, system_owner: str, roles) -> None:
131
+ def register_system_wiki_readers(cls, system_owner: str, level: int) -> None:
130
132
  owner = str(system_owner or "").strip()
131
133
  if owner:
132
- cls._system_wiki_reader_roles[owner] = frozenset(
133
- str(role or "").strip().lower() for role in (roles or ()) if str(role or "").strip())
134
+ cls._system_wiki_reader_levels[owner] = int(level)
134
135
 
135
- def viewer_may_read_system_wiki(self, company_short_name: str, wiki_key: str, user_role: str | None) -> bool:
136
- """Whether this role may open this system wiki in the viewer; False for a wiki that is not a system wiki."""
137
- role = str(user_role or "").strip().lower()
138
- if not role:
136
+ def viewer_may_read_system_wiki(self, company_short_name: str, wiki_key: str, user_identifier: str | None) -> bool:
137
+ """Whether this person may open this system wiki in the viewer; False for a wiki that is not a system wiki."""
138
+ if not user_identifier:
139
139
  return False
140
140
  company = self._get_company(company_short_name)
141
141
  if not company:
142
142
  return False
143
143
  wiki = self.knowledge_wiki_repo.get_wiki_by_key(company.id, self.normalize_wiki_key(wiki_key))
144
144
  owner = self.wiki_system_owner(wiki)
145
- return bool(owner) and role in self._system_wiki_reader_roles.get(owner, frozenset())
145
+ level = self._system_wiki_reader_levels.get(owner) if owner else None
146
+ return level is not None and access.can(company_short_name, user_identifier, level, "company")
146
147
 
147
148
  def ensure_system_wiki(
148
149
  self,