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.
- {iatoolkit-2.70.0/src/iatoolkit.egg-info → iatoolkit-2.71.1}/PKG-INFO +1 -1
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/__init__.py +1 -1
- iatoolkit-2.71.1/src/iatoolkit/common/access.py +206 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/core.py +11 -1
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/locales/en.yaml +91 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/locales/es.yaml +91 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/auth_service.py +8 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/memory_wiki_service.py +5 -3
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/profile_service.py +11 -1
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/prompt_service.py +7 -1
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/query_service.py +7 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/tenant_wiki_service.py +13 -12
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/_chat_header.html +1 -1
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/api_key_api_view.py +2 -2
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/base_login_view.py +1 -1
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/chat_context_preview_api_view.py +2 -2
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/chat_view.py +1 -1
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/configuration_api_view.py +4 -7
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/login_view.py +1 -1
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/prompt_api_view.py +11 -6
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/prompt_context_preview_api_view.py +2 -2
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/prompt_resource_api_view.py +3 -4
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/tool_api_view.py +2 -2
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/users_api_view.py +2 -2
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/wiki_page_view.py +2 -4
- {iatoolkit-2.70.0 → iatoolkit-2.71.1/src/iatoolkit.egg-info}/PKG-INFO +1 -1
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit.egg-info/SOURCES.txt +1 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/LICENSE +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/LICENSE_COMMUNITY.md +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/pyproject.toml +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/readme.md +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/setup.cfg +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/base_company.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/cli_commands.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/__init__.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/bridge_validation.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/company_paths.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/exceptions.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/interfaces/__init__.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/interfaces/asset_storage.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/interfaces/database_provider.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/interfaces/memory_compilation_trigger.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/interfaces/secret_provider.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/interfaces/signup_policy_resolver.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/interfaces/web_search_provider.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/memory_tools.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/model_registry.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/routes.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/search_terms.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/secret_resolver.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/session_manager.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/text_splitter.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/url_safety.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/common/util.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/company_registry.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/__init__.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/llm_capabilities.yaml +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_agents/memory_compiler.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_agents/memory_correction.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_agents/memory_document_ingest.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_agents/memory_image_ingest.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_agents/memory_meeting_ingest.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_agents/memory_question_review.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_agents_pack.yaml +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/channel_business_identity.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/channel_conversation_continuity.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/channel_response_style.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/chat_state_rules.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/chat_user_profile.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/core_identity.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/email_output.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/file_download_output.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/format_styles.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/html_structures.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/links_documents.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/memory_usage.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/multimodal_basics.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/output_basics.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/presentation_formatting.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/query_main.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/sql_aggregation_scope.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/sql_casting.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/sql_core.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/sql_jsonb.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/sql_mysql_casting.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/sql_mysql_json.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/sql_redshift_basics.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/sql_redshift_types.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/sql_rules.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts/tool_html_passthrough.prompt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_prompts_pack.yaml +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/config/system_tools_pack.yaml +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/__init__.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/brevo_mail_app.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/call_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/connectors/__init__.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/connectors/file_connector.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/connectors/file_connector_factory.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/connectors/google_cloud_storage_connector.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/connectors/google_drive_connector.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/connectors/local_file_connector.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/connectors/s3_connector.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/google_auth_client.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/google_chat_app.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/inference_embeddings_client.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/inference_handler.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/jina_embeddings_client.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/llm_gateway_resolver.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/llm_providers/__init__.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/llm_providers/anthropic_adapter.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/llm_providers/deepseek_adapter.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/llm_providers/gemini_adapter.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/llm_providers/openai_adapter.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/llm_providers/openai_compatible_chat_adapter.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/llm_providers/openrouter_adapter.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/llm_proxy.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/llm_response.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/infra/redis_session_manager.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/__init__.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/advisory_lock.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/api_key_repo.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/bridge_repo.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/database_manager.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/document_repo.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/env_secret_provider.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/filesystem_asset_repository.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/knowledge_wiki_repo.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/llm_query_repo.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/mcp_token_repo.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/memory_question_repo.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/memory_repo.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/memory_usage_repo.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/models.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/profile_repo.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/prompt_resource_repo.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/sql_dataset_repo.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/sql_source_repo.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/repositories/vs_repo.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/runtime_logging.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/__init__.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/api_key_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/attachment_policy_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/branding_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/company_context_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/configuration_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/context_builder_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/dispatcher_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/embedding_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/excel_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/file_processor_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/history_manager_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/http_tool_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/i18n_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/inference_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/jwt_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/knowledge_base_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/language_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/license_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/llm_client_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/mail_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/markdown_wiki_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/mcp_token_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/memory_compiler_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/memory_lookup_policy_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/memory_question_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/memory_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/memory_sources.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/memory_usage_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/noop_memory_compilation_trigger.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/operator_identity_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/parsers/__init__.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/parsers/contracts.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/parsers/image_normalizer.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/parsers/parsing_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/parsers/pdf_ocr_detection.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/parsers/provider_factory.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/parsers/provider_resolver.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/parsers/providers/__init__.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/parsers/providers/basic_provider.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/parsers/providers/docling_provider.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/parsers/validator.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/pdf_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/prompt_resource_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/signup_policy_resolver.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/sql_dataset_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/sql_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/sql_source_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/storage_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/structured_output_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/system_agent_catalog.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/system_agent_runner.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/system_prompt_catalog.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/system_tools.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/telemetry_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/tool_output_contract.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/tool_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/user_feedback_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/user_session_context_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/visual_kb_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/visual_tool_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/warmup_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/web_search/__init__.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/web_search/provider_factory.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/web_search/providers/__init__.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/web_search/providers/brave_provider.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/web_search_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/services/wiki_page_index_service.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/images/favicon.ico +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/images/favicon.png +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/images/fernando.jpeg +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/images/iatoolkit_core.png +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/images/iatoolkit_logo.png +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/js/chat_feedback_button.js +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/js/chat_filepond.js +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/js/chat_header_navigation.js +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/js/chat_help_content.js +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/js/chat_history_button.js +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/js/chat_logout_button.js +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/js/chat_main.js +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/js/chat_memory_button.js +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/js/chat_model_selector.js +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/js/chat_onboarding_button.js +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/js/chat_prompt_manager.js +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/js/chat_reload_button.js +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/styles/account.css +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/styles/chat_iatoolkit.css +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/styles/chat_modal.css +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/styles/chat_public.css +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/styles/documents.css +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/styles/landing_page.css +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/styles/llm_output.css +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/styles/onboarding.css +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/static/styles/wiki_page.css +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/_company_header.html +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/_login_widget.html +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/account.html +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/base.html +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/change_password.html +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/chat.html +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/chat_memory_modal.html +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/chat_modals.html +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/error.html +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/forgot_password.html +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/home_hosted_default.html +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/login_global.html +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/memory/wiki_schema.md +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/onboarding_shell.html +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/pdf/base.html +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/pdf/letter.html +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/pdf/report.html +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/pdf/simple.html +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/signup.html +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/templates/wiki_page.html +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/__init__.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/account_view.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/categories_api_view.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/change_password_view.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/connectors_api_view.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/embedding_api_view.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/forgot_password_view.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/help_content_api_view.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/history_api_view.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/home_view.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/init_context_api_view.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/llmquery_api_view.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/load_document_api_view.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/logout_api_view.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/memory_api_view.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/profile_api_view.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/rag_api_view.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/root_redirect_view.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/signup_view.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/static_page_view.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/user_feedback_api_view.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit/views/verify_user_view.py +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit.egg-info/dependency_links.txt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit.egg-info/requires.txt +0 -0
- {iatoolkit-2.70.0 → iatoolkit-2.71.1}/src/iatoolkit.egg-info/top_level.txt +0 -0
|
@@ -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
|
-
|
|
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
|
|
227
|
-
# BL-014); for
|
|
228
|
-
|
|
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":
|
|
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
|
|
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
|
-
#
|
|
124
|
-
# (BL-014: Memoria's wiki for
|
|
125
|
-
#
|
|
126
|
-
|
|
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,
|
|
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.
|
|
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,
|
|
136
|
-
"""Whether this
|
|
137
|
-
|
|
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
|
-
|
|
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,
|