iatoolkit 2.68.0__tar.gz → 2.70.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {iatoolkit-2.68.0/src/iatoolkit.egg-info → iatoolkit-2.70.0}/PKG-INFO +1 -1
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/__init__.py +1 -1
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_agents/memory_document_ingest.prompt +1 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_agents/memory_meeting_ingest.prompt +1 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/locales/en.yaml +5 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/locales/es.yaml +5 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/repositories/models.py +6 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/api_key_service.py +13 -4
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/auth_service.py +37 -7
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/memory_compiler_service.py +101 -56
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/memory_service.py +12 -6
- iatoolkit-2.70.0/src/iatoolkit/services/memory_sources.py +102 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/profile_service.py +14 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/api_key_api_view.py +5 -2
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/users_api_view.py +5 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/wiki_page_view.py +2 -2
- {iatoolkit-2.68.0 → iatoolkit-2.70.0/src/iatoolkit.egg-info}/PKG-INFO +1 -1
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit.egg-info/SOURCES.txt +1 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/LICENSE +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/LICENSE_COMMUNITY.md +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/pyproject.toml +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/readme.md +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/setup.cfg +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/base_company.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/cli_commands.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/common/__init__.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/common/bridge_validation.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/common/company_paths.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/common/exceptions.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/common/interfaces/__init__.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/common/interfaces/asset_storage.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/common/interfaces/database_provider.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/common/interfaces/memory_compilation_trigger.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/common/interfaces/secret_provider.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/common/interfaces/signup_policy_resolver.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/common/interfaces/web_search_provider.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/common/memory_tools.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/common/model_registry.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/common/routes.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/common/search_terms.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/common/secret_resolver.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/common/session_manager.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/common/text_splitter.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/common/url_safety.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/common/util.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/company_registry.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/__init__.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/llm_capabilities.yaml +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_agents/memory_compiler.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_agents/memory_correction.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_agents/memory_image_ingest.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_agents/memory_question_review.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_agents_pack.yaml +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/channel_business_identity.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/channel_conversation_continuity.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/channel_response_style.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/chat_state_rules.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/chat_user_profile.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/core_identity.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/email_output.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/file_download_output.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/format_styles.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/html_structures.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/links_documents.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/memory_usage.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/multimodal_basics.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/output_basics.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/presentation_formatting.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/query_main.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/sql_aggregation_scope.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/sql_casting.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/sql_core.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/sql_jsonb.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/sql_mysql_casting.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/sql_mysql_json.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/sql_redshift_basics.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/sql_redshift_types.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/sql_rules.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts/tool_html_passthrough.prompt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_prompts_pack.yaml +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/config/system_tools_pack.yaml +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/core.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/__init__.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/brevo_mail_app.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/call_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/connectors/__init__.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/connectors/file_connector.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/connectors/file_connector_factory.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/connectors/google_cloud_storage_connector.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/connectors/google_drive_connector.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/connectors/local_file_connector.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/connectors/s3_connector.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/google_auth_client.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/google_chat_app.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/inference_embeddings_client.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/inference_handler.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/jina_embeddings_client.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/llm_gateway_resolver.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/llm_providers/__init__.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/llm_providers/anthropic_adapter.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/llm_providers/deepseek_adapter.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/llm_providers/gemini_adapter.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/llm_providers/openai_adapter.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/llm_providers/openai_compatible_chat_adapter.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/llm_providers/openrouter_adapter.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/llm_proxy.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/llm_response.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/infra/redis_session_manager.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/repositories/__init__.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/repositories/advisory_lock.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/repositories/api_key_repo.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/repositories/bridge_repo.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/repositories/database_manager.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/repositories/document_repo.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/repositories/env_secret_provider.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/repositories/filesystem_asset_repository.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/repositories/knowledge_wiki_repo.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/repositories/llm_query_repo.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/repositories/mcp_token_repo.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/repositories/memory_question_repo.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/repositories/memory_repo.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/repositories/memory_usage_repo.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/repositories/profile_repo.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/repositories/prompt_resource_repo.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/repositories/sql_dataset_repo.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/repositories/sql_source_repo.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/repositories/vs_repo.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/runtime_logging.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/__init__.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/attachment_policy_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/branding_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/company_context_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/configuration_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/context_builder_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/dispatcher_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/embedding_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/excel_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/file_processor_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/history_manager_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/http_tool_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/i18n_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/inference_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/jwt_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/knowledge_base_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/language_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/license_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/llm_client_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/mail_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/markdown_wiki_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/mcp_token_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/memory_lookup_policy_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/memory_question_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/memory_usage_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/memory_wiki_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/noop_memory_compilation_trigger.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/operator_identity_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/parsers/__init__.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/parsers/contracts.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/parsers/image_normalizer.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/parsers/parsing_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/parsers/pdf_ocr_detection.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/parsers/provider_factory.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/parsers/provider_resolver.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/parsers/providers/__init__.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/parsers/providers/basic_provider.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/parsers/providers/docling_provider.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/parsers/validator.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/pdf_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/prompt_resource_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/prompt_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/query_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/signup_policy_resolver.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/sql_dataset_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/sql_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/sql_source_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/storage_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/structured_output_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/system_agent_catalog.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/system_agent_runner.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/system_prompt_catalog.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/system_tools.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/telemetry_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/tenant_wiki_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/tool_output_contract.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/tool_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/user_feedback_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/user_session_context_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/visual_kb_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/visual_tool_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/warmup_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/web_search/__init__.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/web_search/provider_factory.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/web_search/providers/__init__.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/web_search/providers/brave_provider.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/web_search_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/services/wiki_page_index_service.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/images/favicon.ico +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/images/favicon.png +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/images/fernando.jpeg +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/images/iatoolkit_core.png +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/images/iatoolkit_logo.png +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/js/chat_feedback_button.js +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/js/chat_filepond.js +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/js/chat_header_navigation.js +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/js/chat_help_content.js +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/js/chat_history_button.js +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/js/chat_logout_button.js +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/js/chat_main.js +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/js/chat_memory_button.js +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/js/chat_model_selector.js +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/js/chat_onboarding_button.js +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/js/chat_prompt_manager.js +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/js/chat_reload_button.js +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/styles/account.css +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/styles/chat_iatoolkit.css +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/styles/chat_modal.css +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/styles/chat_public.css +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/styles/documents.css +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/styles/landing_page.css +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/styles/llm_output.css +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/styles/onboarding.css +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/static/styles/wiki_page.css +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/templates/_chat_header.html +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/templates/_company_header.html +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/templates/_login_widget.html +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/templates/account.html +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/templates/base.html +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/templates/change_password.html +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/templates/chat.html +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/templates/chat_memory_modal.html +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/templates/chat_modals.html +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/templates/error.html +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/templates/forgot_password.html +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/templates/home_hosted_default.html +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/templates/login_global.html +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/templates/memory/wiki_schema.md +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/templates/onboarding_shell.html +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/templates/pdf/base.html +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/templates/pdf/letter.html +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/templates/pdf/report.html +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/templates/pdf/simple.html +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/templates/signup.html +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/templates/wiki_page.html +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/__init__.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/account_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/base_login_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/categories_api_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/change_password_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/chat_context_preview_api_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/chat_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/configuration_api_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/connectors_api_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/embedding_api_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/forgot_password_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/help_content_api_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/history_api_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/home_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/init_context_api_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/llmquery_api_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/load_document_api_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/login_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/logout_api_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/memory_api_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/profile_api_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/prompt_api_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/prompt_context_preview_api_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/prompt_resource_api_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/rag_api_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/root_redirect_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/signup_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/static_page_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/tool_api_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/user_feedback_api_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit/views/verify_user_view.py +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit.egg-info/dependency_links.txt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit.egg-info/requires.txt +0 -0
- {iatoolkit-2.68.0 → iatoolkit-2.70.0}/src/iatoolkit.egg-info/top_level.txt +0 -0
|
@@ -13,6 +13,7 @@ How to write the pages:
|
|
|
13
13
|
- Update one of the CANDIDATE_PAGES when the part belongs to its subject; create a new page when none covers it (check PAGE_INDEX first so you do not duplicate a page). One part may touch several pages; return each one.
|
|
14
14
|
- When you update, return the COMPLETE new page: keep everything already on it, word for word, including people's attributions. Add the new facts in the section where they belong.
|
|
15
15
|
- When this part says something different from what the page already says, keep both statements with their attributions and make the difference explicit; never delete what someone else said. Also list each contradiction in `conflicts`.
|
|
16
|
+
- A line marked **[obsoleto: …]** is no longer valid: it stays on the page as it is, but it is not a contradiction. Neither is a line attributed to an earlier version of this same document ({{ source.label }} with an earlier date): the new version replaces it, so write the current fact and do not list a conflict for it. Contradictions are only with other sources (people, other documents).
|
|
16
17
|
- Attribute every fact you add as ({{ source.label }}, {{ captured_on }}).
|
|
17
18
|
- Follow the WIKI_SCHEMA. Write in the language of the page, or of the document for a new page.
|
|
18
19
|
- If this part has nothing worth keeping, return an empty `pages` list.
|
|
@@ -16,6 +16,7 @@ How to write the pages:
|
|
|
16
16
|
- Update one of the CANDIDATE_PAGES when the part belongs to its subject; create a new page when none covers it (check PAGE_INDEX first so you do not duplicate a page). One part may touch several pages; return each one.
|
|
17
17
|
- When you update, return the COMPLETE new page: keep everything already on it, word for word, including people's attributions. Add the new facts in the section where they belong.
|
|
18
18
|
- When this part says something different from what the page already says, keep both statements with their attributions and make the difference explicit; never delete what someone else said. Also list each contradiction in `conflicts`. A meeting that changes a rule is a contradiction worth showing.
|
|
19
|
+
- A line marked **[obsoleto: …]** is no longer valid: it stays on the page as it is, but it is not a contradiction. Neither is a line attributed to an earlier version of this same document ({{ source.label }} with an earlier date): the new version replaces it, so write the current fact and do not list a conflict for it. Contradictions are only with other sources (people, other documents).
|
|
19
20
|
- Attribute every fact you add to the meeting: ({{ source.label }}, {{ captured_on }}).
|
|
20
21
|
- Follow the WIKI_SCHEMA. Write in the language of the page, or of the transcript for a new page.
|
|
21
22
|
- If this part decided or taught nothing worth keeping, return an empty `pages` list.
|
|
@@ -2087,6 +2087,11 @@ ui:
|
|
|
2087
2087
|
routing_profile_hint: "Used by enterprise tool routing to rank and select candidate tools."
|
|
2088
2088
|
|
|
2089
2089
|
api_key:
|
|
2090
|
+
can_act_for_users: "May act on behalf of users"
|
|
2091
|
+
can_act_for_users_help: "Without this, the key acts as itself (service:<name>) and cannot pass for anyone. Turn it on only for integrations that must name the user they act for (a portal with its own login, an MCP gateway)."
|
|
2092
|
+
acts: "Acts"
|
|
2093
|
+
acts_as_itself: "As itself"
|
|
2094
|
+
acts_for_users: "On behalf of users"
|
|
2090
2095
|
title: "API Keys"
|
|
2091
2096
|
subtitle: "Manage company API keys used to authenticate external requests."
|
|
2092
2097
|
new_btn: "New API Key"
|
|
@@ -2105,6 +2105,11 @@ ui:
|
|
|
2105
2105
|
routing_profile_hint: "Usado por el router enterprise para rankear y seleccionar herramientas candidatas."
|
|
2106
2106
|
|
|
2107
2107
|
api_key:
|
|
2108
|
+
can_act_for_users: "Puede actuar en nombre de usuarios"
|
|
2109
|
+
can_act_for_users_help: "Sin esta opción, la llave actúa como sí misma (service:<nombre>) y no puede hacerse pasar por nadie. Actívala solo para integraciones que deben indicar el usuario por el que actúan (un portal con login propio, un gateway MCP)."
|
|
2110
|
+
acts: "Actúa"
|
|
2111
|
+
acts_as_itself: "Como sí misma"
|
|
2112
|
+
acts_for_users: "En nombre de usuarios"
|
|
2108
2113
|
title: "API Keys"
|
|
2109
2114
|
subtitle: "Administra las API keys de la empresa para autenticar solicitudes externas."
|
|
2110
2115
|
new_btn: "Nueva API Key"
|
|
@@ -9,6 +9,7 @@ from sqlalchemy import Column, Integer, BigInteger, String, DateTime, Enum, Text
|
|
|
9
9
|
from sqlalchemy.orm import DeclarativeBase
|
|
10
10
|
from sqlalchemy.orm import relationship, class_mapper
|
|
11
11
|
from sqlalchemy.sql import func, literal_column
|
|
12
|
+
from sqlalchemy.sql import text as sql_text
|
|
12
13
|
from sqlalchemy import UniqueConstraint, Index
|
|
13
14
|
from sqlalchemy.dialects.postgresql import JSONB
|
|
14
15
|
from datetime import datetime
|
|
@@ -125,6 +126,11 @@ class ApiKey(Base):
|
|
|
125
126
|
is_active = Column(Boolean, default=True, nullable=False)
|
|
126
127
|
created_at = Column(DateTime, default=datetime.now)
|
|
127
128
|
last_used_at = Column(DateTime, nullable=True) # Opcional: para rastrear uso
|
|
129
|
+
# A key is a service account that acts as itself ("service:<key name>"). Only with
|
|
130
|
+
# this on may it name the user it acts for, who must belong to the company
|
|
131
|
+
# (BL-010, Fase 0). Keys created before it existed keep acting for users;
|
|
132
|
+
# migration: iatoolkit-enterprise/migrations/2026-10-18-api-keys-can-act-for-users.sql
|
|
133
|
+
can_act_for_users = Column(Boolean, nullable=False, default=False, server_default=sql_text("true"))
|
|
128
134
|
|
|
129
135
|
company = relationship("Company", back_populates="api_keys")
|
|
130
136
|
|
|
@@ -30,6 +30,8 @@ class ApiKeyService:
|
|
|
30
30
|
"key_name": api_key.key_name,
|
|
31
31
|
"key": api_key.key,
|
|
32
32
|
"is_active": api_key.is_active,
|
|
33
|
+
# BL-010, Fase 0: whether the key may name the user it acts for.
|
|
34
|
+
"can_act_for_users": bool(getattr(api_key, "can_act_for_users", False)),
|
|
33
35
|
"created_at": api_key.created_at.isoformat() if api_key.created_at else None,
|
|
34
36
|
"last_used_at": api_key.last_used_at.isoformat() if api_key.last_used_at else None,
|
|
35
37
|
}
|
|
@@ -65,7 +67,7 @@ class ApiKeyService:
|
|
|
65
67
|
|
|
66
68
|
return {"data": self._api_key_to_dict(api_key)}
|
|
67
69
|
|
|
68
|
-
def create_api_key_entry(self, company_short_name: str, key_name: str) -> Dict:
|
|
70
|
+
def create_api_key_entry(self, company_short_name: str, key_name: str, can_act_for_users: bool = False) -> Dict:
|
|
69
71
|
company, error = self._get_company(company_short_name)
|
|
70
72
|
if error:
|
|
71
73
|
return error
|
|
@@ -81,7 +83,8 @@ class ApiKeyService:
|
|
|
81
83
|
alphabet = string.ascii_letters + string.digits
|
|
82
84
|
key = ''.join(secrets.choice(alphabet) for _ in range(length))
|
|
83
85
|
|
|
84
|
-
|
|
86
|
+
# A new key acts as itself unless the admin allows it to act for users (BL-010).
|
|
87
|
+
api_key = ApiKey(key=key, company_id=company.id, key_name=key_name, can_act_for_users=bool(can_act_for_users))
|
|
85
88
|
created = self.api_key_repo.create_api_key(api_key)
|
|
86
89
|
return {"data": self._api_key_to_dict(created)}
|
|
87
90
|
|
|
@@ -89,7 +92,8 @@ class ApiKeyService:
|
|
|
89
92
|
company_short_name: str,
|
|
90
93
|
api_key_id: int,
|
|
91
94
|
key_name: str | None = None,
|
|
92
|
-
is_active: bool | None = None
|
|
95
|
+
is_active: bool | None = None,
|
|
96
|
+
can_act_for_users: bool | None = None) -> Dict:
|
|
93
97
|
company, error = self._get_company(company_short_name)
|
|
94
98
|
if error:
|
|
95
99
|
return error
|
|
@@ -98,7 +102,7 @@ class ApiKeyService:
|
|
|
98
102
|
if not api_key:
|
|
99
103
|
return {"error": "API key not found.", "status_code": 404}
|
|
100
104
|
|
|
101
|
-
if key_name is None and is_active is None:
|
|
105
|
+
if key_name is None and is_active is None and can_act_for_users is None:
|
|
102
106
|
return {"error": "No changes provided.", "status_code": 400}
|
|
103
107
|
|
|
104
108
|
if key_name is not None:
|
|
@@ -116,6 +120,11 @@ class ApiKeyService:
|
|
|
116
120
|
return {"error": "Invalid value for is_active.", "status_code": 400}
|
|
117
121
|
api_key.is_active = is_active
|
|
118
122
|
|
|
123
|
+
if can_act_for_users is not None:
|
|
124
|
+
if not isinstance(can_act_for_users, bool):
|
|
125
|
+
return {"error": "Invalid value for can_act_for_users.", "status_code": 400}
|
|
126
|
+
api_key.can_act_for_users = can_act_for_users
|
|
127
|
+
|
|
119
128
|
updated = self.api_key_repo.update_api_key(api_key)
|
|
120
129
|
return {"data": self._api_key_to_dict(updated)}
|
|
121
130
|
|
|
@@ -14,6 +14,8 @@ from iatoolkit.repositories.models import AccessLog
|
|
|
14
14
|
from iatoolkit.infra.google_auth_client import GoogleAuthClient, GoogleAuthError
|
|
15
15
|
from flask import request
|
|
16
16
|
import logging
|
|
17
|
+
import re
|
|
18
|
+
import unicodedata
|
|
17
19
|
import hashlib
|
|
18
20
|
|
|
19
21
|
|
|
@@ -284,7 +286,11 @@ class AuthService:
|
|
|
284
286
|
"success": True,
|
|
285
287
|
"company_short_name": session_info['company_short_name'],
|
|
286
288
|
"user_identifier": session_info['user_identifier'],
|
|
287
|
-
|
|
289
|
+
# From the database, never from the stored session profile: an SSO
|
|
290
|
+
# hook writes that profile, and must not be able to grant admin
|
|
291
|
+
# (BL-010, Fase 0).
|
|
292
|
+
"user_role": self._role_in_company(session_info['company_short_name'],
|
|
293
|
+
session_info['user_identifier']),
|
|
288
294
|
}
|
|
289
295
|
|
|
290
296
|
# --- Priority 2: Check for a valid API Key in headers ---
|
|
@@ -311,7 +317,22 @@ class AuthService:
|
|
|
311
317
|
# get the company from the api_key_entry
|
|
312
318
|
company = api_key_entry.company
|
|
313
319
|
|
|
314
|
-
#
|
|
320
|
+
# The key is a service account (BL-010, Fase 0): it acts as itself unless it
|
|
321
|
+
# was allowed to act for users. Every result says which key acted.
|
|
322
|
+
principal = self.service_principal(api_key_entry)
|
|
323
|
+
base = {
|
|
324
|
+
"success": True,
|
|
325
|
+
"company_short_name": company.short_name,
|
|
326
|
+
"auth_type": "api_key",
|
|
327
|
+
"auth_principal": principal,
|
|
328
|
+
"api_key_id": api_key_entry.id,
|
|
329
|
+
}
|
|
330
|
+
# Only an explicit False: a key loaded before the column existed keeps acting for users.
|
|
331
|
+
if getattr(api_key_entry, "can_act_for_users", None) is False:
|
|
332
|
+
# Whatever user the request names is ignored: the key cannot pass for anyone.
|
|
333
|
+
return {**base, "user_identifier": principal, "acting_for": None}
|
|
334
|
+
|
|
335
|
+
# A key allowed to act for users must say who (unless the endpoint allows none).
|
|
315
336
|
data = request.get_json(silent=True) or {}
|
|
316
337
|
user_identifier = data.get('user_identifier', '')
|
|
317
338
|
if not anonymous and not user_identifier:
|
|
@@ -320,11 +341,20 @@ class AuthService:
|
|
|
320
341
|
"error_message": self.i18n_service.t('errors.auth.no_user_identifier_api'),
|
|
321
342
|
"status_code": 403}
|
|
322
343
|
|
|
323
|
-
return {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
344
|
+
return {**base, "user_identifier": user_identifier, "acting_for": user_identifier or None}
|
|
345
|
+
|
|
346
|
+
SERVICE_PREFIX = "service:"
|
|
347
|
+
|
|
348
|
+
@classmethod
|
|
349
|
+
def service_principal(cls, api_key_entry) -> str:
|
|
350
|
+
"""The identity a key acts with by itself: "service:<key name>" (lowercase, dashes)."""
|
|
351
|
+
raw = unicodedata.normalize("NFKD", str(getattr(api_key_entry, "key_name", "") or ""))
|
|
352
|
+
ascii_name = raw.encode("ascii", "ignore").decode("ascii").strip().lower()
|
|
353
|
+
name = re.sub(r"[^a-z0-9._-]+", "-", ascii_name)
|
|
354
|
+
return f"{cls.SERVICE_PREFIX}{name.strip('-') or getattr(api_key_entry, 'id', 'key')}"
|
|
355
|
+
|
|
356
|
+
def _role_in_company(self, company_short_name: str, user_identifier: str) -> str | None:
|
|
357
|
+
return self.profile_service.get_role_in_company(company_short_name, user_identifier)
|
|
328
358
|
|
|
329
359
|
def verify_for_company(self, company_short_name: str, anonymous: bool = False) -> dict:
|
|
330
360
|
"""
|
|
@@ -9,6 +9,7 @@ import base64
|
|
|
9
9
|
import logging
|
|
10
10
|
import mimetypes
|
|
11
11
|
import re
|
|
12
|
+
import time
|
|
12
13
|
from contextlib import contextmanager
|
|
13
14
|
from datetime import datetime
|
|
14
15
|
from html import unescape
|
|
@@ -25,6 +26,7 @@ from iatoolkit.repositories.models import (
|
|
|
25
26
|
Company, MemoryCapture, MemoryCaptureStatus, MemoryConflict, MemoryItem, MemoryItemStatus, MemoryItemType, VSDoc,
|
|
26
27
|
)
|
|
27
28
|
from iatoolkit.repositories.profile_repo import ProfileRepo
|
|
29
|
+
from iatoolkit.services.memory_sources import MemorySourceRegistry, register_core_sources
|
|
28
30
|
from iatoolkit.services.memory_wiki_service import MemoryWikiService
|
|
29
31
|
from iatoolkit.services.storage_service import StorageService
|
|
30
32
|
from iatoolkit.services.system_agent_runner import SystemAgentDisabled, SystemAgentRunner
|
|
@@ -50,6 +52,10 @@ class MemoryCompilerService:
|
|
|
50
52
|
MAX_CANDIDATE_PAGES = 3
|
|
51
53
|
MAX_INDEX_ENTRIES_IN_PROMPT = 80
|
|
52
54
|
MAX_BATCHES_PER_RUN = 20
|
|
55
|
+
# A run gives its worker back after this long and the rest goes to a new task.
|
|
56
|
+
MAX_RUN_SECONDS = 15 * 60
|
|
57
|
+
# Times a run takes the lock again for captures saved as it was releasing it.
|
|
58
|
+
MAX_LOCK_ROUNDS = 3
|
|
53
59
|
MAX_SAVE_ATTEMPTS = 3
|
|
54
60
|
COMPILER_SOURCE = "memory_compiler"
|
|
55
61
|
# A capture that asks to change one page (Memoria → "¿Qué hay que corregir?").
|
|
@@ -79,6 +85,10 @@ class MemoryCompilerService:
|
|
|
79
85
|
# A document that fed the wiki was deleted: its facts are marked, never erased.
|
|
80
86
|
DOCUMENT_REMOVED_MODE = "document_removed"
|
|
81
87
|
OBSOLETE_MARK = "obsoleto: se eliminó el documento"
|
|
88
|
+
# A new version of the document replaced the one these lines came from (BL-016, Fase 3).
|
|
89
|
+
OBSOLETE_REPLACED_MARK = "obsoleto: hay una versión nueva del documento"
|
|
90
|
+
# Any obsolete mark, whatever its reason: a line is marked once.
|
|
91
|
+
OBSOLETE_PREFIX = "**[obsoleto"
|
|
82
92
|
CORRECTION_AGENT = "memory_correction"
|
|
83
93
|
|
|
84
94
|
@inject
|
|
@@ -109,31 +119,48 @@ class MemoryCompilerService:
|
|
|
109
119
|
compiled_count = 0
|
|
110
120
|
processed_count = 0
|
|
111
121
|
waiting_count = 0
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
122
|
+
stopped = None
|
|
123
|
+
started = time.monotonic()
|
|
124
|
+
for _ in range(self.MAX_LOCK_ROUNDS):
|
|
125
|
+
with self._company_lock(company.id) as held:
|
|
126
|
+
if not held:
|
|
127
|
+
# Another run of this company holds the lock and drains every
|
|
128
|
+
# pending capture, including this one; this task ends now
|
|
129
|
+
# instead of keeping a worker waiting (BL-016, Fase 3).
|
|
130
|
+
return {"status": "success", "absorbed": True, "compiled_count": 0, "processed_count": 0,
|
|
131
|
+
"waiting_count": 0, "pending_count": None}
|
|
132
|
+
waiting_count = 0
|
|
133
|
+
for _batch in range(self.MAX_BATCHES_PER_RUN):
|
|
134
|
+
pending_captures = self.memory_repo.get_pending_captures(company.id, limit=limit)
|
|
135
|
+
if not pending_captures:
|
|
136
|
+
break
|
|
137
|
+
batch_waiting = 0
|
|
138
|
+
for capture in pending_captures:
|
|
139
|
+
agent_key = self._agent_for(capture)
|
|
140
|
+
if agent_key and not SystemAgentRunner.is_enabled(agent_key):
|
|
141
|
+
# Its agent is turned off by the platform: the capture stays
|
|
142
|
+
# pending and is compiled when the agent is turned on again.
|
|
143
|
+
batch_waiting += 1
|
|
144
|
+
continue
|
|
145
|
+
processed_count += 1
|
|
146
|
+
compiled = self._compile_capture_safely(company, capture)
|
|
147
|
+
if compiled is None:
|
|
148
|
+
batch_waiting += 1
|
|
149
|
+
elif compiled:
|
|
150
|
+
compiled_count += 1
|
|
151
|
+
if time.monotonic() - started > self.MAX_RUN_SECONDS:
|
|
152
|
+
stopped = "time_limit"
|
|
153
|
+
break
|
|
154
|
+
waiting_count += batch_waiting
|
|
155
|
+
if stopped or batch_waiting == len(pending_captures):
|
|
156
|
+
# Out of time, or nothing in this batch could move.
|
|
157
|
+
break
|
|
158
|
+
if stopped:
|
|
159
|
+
break
|
|
160
|
+
# A capture saved just as the lock was released found it taken and was
|
|
161
|
+
# absorbed: it is pending now, and this run takes it.
|
|
162
|
+
if len(self.memory_repo.get_pending_captures(company.id, limit=limit)) <= waiting_count:
|
|
163
|
+
break
|
|
137
164
|
|
|
138
165
|
return {
|
|
139
166
|
"status": "success",
|
|
@@ -141,6 +168,8 @@ class MemoryCompilerService:
|
|
|
141
168
|
"processed_count": processed_count,
|
|
142
169
|
"waiting_count": waiting_count,
|
|
143
170
|
"pending_count": len(self.memory_repo.get_pending_captures(company.id, limit=limit)),
|
|
171
|
+
# The task stopped to free its worker; the caller queues the rest.
|
|
172
|
+
"stopped": stopped,
|
|
144
173
|
}
|
|
145
174
|
|
|
146
175
|
def compile_pending_for_user(self, company_short_name: str, user_identifier: str | None = None, limit: int = 12) -> dict:
|
|
@@ -150,26 +179,19 @@ class MemoryCompilerService:
|
|
|
150
179
|
@contextmanager
|
|
151
180
|
def _company_lock(self, company_id: int):
|
|
152
181
|
"""
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
182
|
+
One compilation run per company at a time, across workers and web
|
|
183
|
+
processes; yields whether this run holds it. It does not wait: a run that
|
|
184
|
+
finds it taken leaves (the holder drains what is pending), so no worker
|
|
185
|
+
is kept waiting (BL-016, Fase 3). Postgres advisory lock on a dedicated
|
|
186
|
+
connection; on SQLite (tests) it is always held.
|
|
158
187
|
"""
|
|
159
|
-
with advisory_lock(self.db_manager, self.ADVISORY_LOCK_NAMESPACE, company_id):
|
|
160
|
-
yield
|
|
161
|
-
|
|
162
|
-
def _agent_for(self, capture: MemoryCapture) -> str:
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
if mode == self.DOCUMENT_MODE:
|
|
167
|
-
if dict(capture.meta or {}).get("kind") == self.MEETING_KIND:
|
|
168
|
-
return self.MEETING_AGENT
|
|
169
|
-
return self.DOCUMENT_AGENT
|
|
170
|
-
if mode == self.DOCUMENT_REMOVED_MODE:
|
|
171
|
-
return None # marked without the model
|
|
172
|
-
return self.COMPILER_AGENT
|
|
188
|
+
with advisory_lock(self.db_manager, self.ADVISORY_LOCK_NAMESPACE, company_id, wait=False) as held:
|
|
189
|
+
yield held
|
|
190
|
+
|
|
191
|
+
def _agent_for(self, capture: MemoryCapture) -> str | None:
|
|
192
|
+
"""The system agent that reads the capture, as its source declares (memory_sources)."""
|
|
193
|
+
meta = dict(capture.meta or {})
|
|
194
|
+
return MemorySourceRegistry.for_capture(meta).agent_for(meta)
|
|
173
195
|
|
|
174
196
|
def _compile_capture_safely(self, company: Company, capture: MemoryCapture) -> bool | None:
|
|
175
197
|
"""Whether the capture became a page; None when it stays pending (its agent was turned off)."""
|
|
@@ -198,12 +220,12 @@ class MemoryCompilerService:
|
|
|
198
220
|
return False
|
|
199
221
|
|
|
200
222
|
def _compile_single_capture(self, company: Company, capture: MemoryCapture, items: list[MemoryItem]) -> bool:
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
223
|
+
"""Reads the capture with the step its source declares (memory_sources)."""
|
|
224
|
+
source = MemorySourceRegistry.for_capture(dict(capture.meta or {}))
|
|
225
|
+
return getattr(self, source.handler)(company, capture, items)
|
|
226
|
+
|
|
227
|
+
def _compile_person_capture(self, company: Company, capture: MemoryCapture, items: list[MemoryItem]) -> bool:
|
|
228
|
+
"""What a person explained, filed into the pages it belongs to."""
|
|
207
229
|
author = self._author_label(capture.user_identifier)
|
|
208
230
|
captured_on = (capture.created_at or datetime.now()).date().isoformat()
|
|
209
231
|
|
|
@@ -389,6 +411,12 @@ class MemoryCompilerService:
|
|
|
389
411
|
meta = dict(capture.meta or {})
|
|
390
412
|
document_id = self._as_int(meta.get("document_id"))
|
|
391
413
|
today = (capture.created_at or datetime.now()).date().isoformat()
|
|
414
|
+
replaced = meta.get("reason") == "replaced"
|
|
415
|
+
mark = self.OBSOLETE_REPLACED_MARK if replaced else self.OBSOLETE_MARK
|
|
416
|
+
why = "hay una versión nueva del documento" if replaced else "se eliminó el documento"
|
|
417
|
+
# A replaced version marks only lines from before this capture: if the new
|
|
418
|
+
# version was already read (a retry that ran late), its lines stay.
|
|
419
|
+
before = today if replaced else None
|
|
392
420
|
marked_pages = []
|
|
393
421
|
for link in self.memory_repo.list_document_links(company.id, document_id):
|
|
394
422
|
labels = {label for label in (link.label, meta.get("label")) if label}
|
|
@@ -397,7 +425,7 @@ class MemoryCompilerService:
|
|
|
397
425
|
if not page:
|
|
398
426
|
break
|
|
399
427
|
body = self.memory_wiki_service.page_body(page)
|
|
400
|
-
marked = self._mark_obsolete_lines(body, labels, today)
|
|
428
|
+
marked = self._mark_obsolete_lines(body, labels, today, mark=mark, before=before)
|
|
401
429
|
if marked == body:
|
|
402
430
|
break
|
|
403
431
|
response = self.memory_wiki_service.update_page(
|
|
@@ -407,7 +435,7 @@ class MemoryCompilerService:
|
|
|
407
435
|
summary=str(page.get("summary") or "").strip(),
|
|
408
436
|
body=marked,
|
|
409
437
|
edited_by=capture.user_identifier,
|
|
410
|
-
change_summary=f"Datos de «{sorted(labels)[0] if labels else document_id}» marcados como obsoletos:
|
|
438
|
+
change_summary=f"Datos de «{sorted(labels)[0] if labels else document_id}» marcados como obsoletos: {why}.",
|
|
411
439
|
revision_metadata={
|
|
412
440
|
"source": self.COMPILER_SOURCE,
|
|
413
441
|
"capture_id": capture.id,
|
|
@@ -428,12 +456,17 @@ class MemoryCompilerService:
|
|
|
428
456
|
return bool(marked_pages)
|
|
429
457
|
|
|
430
458
|
@classmethod
|
|
431
|
-
def _mark_obsolete_lines(cls, body: str, labels: set[str], day: str
|
|
432
|
-
|
|
459
|
+
def _mark_obsolete_lines(cls, body: str, labels: set[str], day: str, *, mark: str | None = None,
|
|
460
|
+
before: str | None = None) -> str:
|
|
461
|
+
"""Marks each line attributed to the document; with `before`, only those dated earlier."""
|
|
462
|
+
mark = mark or cls.OBSOLETE_MARK
|
|
463
|
+
patterns = [re.compile(r"\(\s*" + re.escape(label) + r"\s*,\s*(\d{4}-\d{2}-\d{2})\s*\)") for label in labels]
|
|
433
464
|
lines = []
|
|
434
465
|
for line in str(body or "").split("\n"):
|
|
435
|
-
if cls.
|
|
436
|
-
|
|
466
|
+
if cls.OBSOLETE_PREFIX not in line:
|
|
467
|
+
dates = [match.group(1) for pattern in patterns for match in pattern.finditer(line)]
|
|
468
|
+
if dates and (before is None or min(dates) < before):
|
|
469
|
+
line = f"{line.rstrip()} **[{mark}, {day}]**"
|
|
437
470
|
lines.append(line)
|
|
438
471
|
return "\n".join(lines)
|
|
439
472
|
|
|
@@ -1067,3 +1100,15 @@ class MemoryCompilerService:
|
|
|
1067
1100
|
return f"Archivo: {item.filename}"
|
|
1068
1101
|
item = items[0]
|
|
1069
1102
|
return f"{item.item_type.value if hasattr(item.item_type, 'value') else item.item_type}"
|
|
1103
|
+
|
|
1104
|
+
|
|
1105
|
+
register_core_sources(
|
|
1106
|
+
compiler_agent=MemoryCompilerService.COMPILER_AGENT,
|
|
1107
|
+
correction_agent=MemoryCompilerService.CORRECTION_AGENT,
|
|
1108
|
+
document_agent=MemoryCompilerService.DOCUMENT_AGENT,
|
|
1109
|
+
meeting_agent=MemoryCompilerService.MEETING_AGENT,
|
|
1110
|
+
correction_mode=MemoryCompilerService.CORRECTION_MODE,
|
|
1111
|
+
document_mode=MemoryCompilerService.DOCUMENT_MODE,
|
|
1112
|
+
document_removed_mode=MemoryCompilerService.DOCUMENT_REMOVED_MODE,
|
|
1113
|
+
meeting_kind=MemoryCompilerService.MEETING_KIND,
|
|
1114
|
+
)
|
|
@@ -203,11 +203,15 @@ class MemoryService:
|
|
|
203
203
|
*,
|
|
204
204
|
document_id: int,
|
|
205
205
|
label: str,
|
|
206
|
-
removed_by: str | None = None
|
|
206
|
+
removed_by: str | None = None,
|
|
207
|
+
reason: str = "removed") -> dict:
|
|
207
208
|
"""
|
|
208
|
-
A document that fed the wiki was deleted (BL-014)
|
|
209
|
-
|
|
210
|
-
|
|
209
|
+
A document that fed the wiki was deleted (BL-014), or a new version of it
|
|
210
|
+
replaced this one (`reason="replaced"`, BL-016 Fase 3): queued so the
|
|
211
|
+
compiler marks its facts as obsolete on the pages that cite it. Nothing is
|
|
212
|
+
erased. Queue it before the new version: captures compile in order, so the
|
|
213
|
+
new version is read with the old lines already marked. Does nothing when
|
|
214
|
+
no page cites the document.
|
|
211
215
|
"""
|
|
212
216
|
company = self.profile_repo.get_company_by_short_name(company_short_name)
|
|
213
217
|
if not company:
|
|
@@ -222,10 +226,12 @@ class MemoryService:
|
|
|
222
226
|
new_items=[{
|
|
223
227
|
"item_type": "note",
|
|
224
228
|
"title": label,
|
|
225
|
-
"content_text": f"
|
|
229
|
+
"content_text": (f"Hay una versión nueva del documento «{label}»." if reason == "replaced"
|
|
230
|
+
else f"Se eliminó el documento «{label}»."),
|
|
226
231
|
"source_meta": {"origin": "memory_document_removed", "rag_document_id": document_id},
|
|
227
232
|
}],
|
|
228
|
-
meta={"mode": MemoryCompilerService.DOCUMENT_REMOVED_MODE, "document_id": document_id, "label": label
|
|
233
|
+
meta={"mode": MemoryCompilerService.DOCUMENT_REMOVED_MODE, "document_id": document_id, "label": label,
|
|
234
|
+
"reason": "replaced" if reason == "replaced" else "removed"},
|
|
229
235
|
)
|
|
230
236
|
|
|
231
237
|
# Fase 0 (R4): a capture whose compilation failed (the model was down, a
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
+
# Product: IAToolkit
|
|
3
|
+
#
|
|
4
|
+
# IAToolkit is open source software.
|
|
5
|
+
|
|
6
|
+
"""The contract of a Memoria source (BL-016, Fase 3).
|
|
7
|
+
|
|
8
|
+
Everything enters Memoria as a capture; what kind of source a capture comes
|
|
9
|
+
from is its `meta.mode` (and, for a document, its `meta.kind`). Before, each
|
|
10
|
+
kind was a string in `meta` plus an `if` in the compiler, with its rules spread
|
|
11
|
+
over three services. A source now declares, in one place:
|
|
12
|
+
|
|
13
|
+
- `mode`: the capture's `meta.mode` it answers for (None: a person's capture);
|
|
14
|
+
- `handler`: the compiler step that reads it;
|
|
15
|
+
- `agent`: the system agent that reads it (None: no model), and `kind_agents`
|
|
16
|
+
for variants of the same source (a meeting transcript is a document read by
|
|
17
|
+
the meeting reader);
|
|
18
|
+
- `on_new_version`: what a new version of the same source does to what the
|
|
19
|
+
previous one taught ("replace": its lines are marked obsolete before the new
|
|
20
|
+
version is read; "add": both stay, as two people saying things);
|
|
21
|
+
- `on_removal`: what its removal does ("mark_obsolete": its lines are marked,
|
|
22
|
+
never erased; "none");
|
|
23
|
+
- `visibility`: who may read what it teaches. "company" for every source today;
|
|
24
|
+
the field is where Fase 4 (BL-010) will declare restricted sources.
|
|
25
|
+
|
|
26
|
+
A new source registers here (`MemorySourceRegistry.register`) instead of adding
|
|
27
|
+
another branch to the compiler.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
from dataclasses import dataclass, field
|
|
33
|
+
|
|
34
|
+
VERSION_REPLACE = "replace"
|
|
35
|
+
VERSION_ADD = "add"
|
|
36
|
+
REMOVAL_MARK_OBSOLETE = "mark_obsolete"
|
|
37
|
+
REMOVAL_NONE = "none"
|
|
38
|
+
VISIBILITY_COMPANY = "company"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass(frozen=True)
|
|
42
|
+
class MemorySource:
|
|
43
|
+
key: str
|
|
44
|
+
mode: str | None
|
|
45
|
+
handler: str
|
|
46
|
+
agent: str | None
|
|
47
|
+
kind_agents: dict = field(default_factory=dict)
|
|
48
|
+
on_new_version: str = VERSION_ADD
|
|
49
|
+
on_removal: str = REMOVAL_NONE
|
|
50
|
+
visibility: str = VISIBILITY_COMPANY
|
|
51
|
+
description: str = ""
|
|
52
|
+
|
|
53
|
+
def agent_for(self, meta: dict | None) -> str | None:
|
|
54
|
+
kind = str((meta or {}).get("kind") or "").strip()
|
|
55
|
+
return self.kind_agents.get(kind, self.agent)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class MemorySourceRegistry:
|
|
59
|
+
"""Which source each capture is, by its `meta.mode`."""
|
|
60
|
+
|
|
61
|
+
_sources: dict[str | None, MemorySource] = {}
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def register(cls, source: MemorySource) -> None:
|
|
65
|
+
cls._sources[source.mode] = source
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
def for_capture(cls, meta: dict | None) -> MemorySource:
|
|
69
|
+
mode = (meta or {}).get("mode")
|
|
70
|
+
return cls._sources.get(mode) or cls._sources[None]
|
|
71
|
+
|
|
72
|
+
@classmethod
|
|
73
|
+
def get(cls, key: str) -> MemorySource | None:
|
|
74
|
+
return next((source for source in cls._sources.values() if source.key == key), None)
|
|
75
|
+
|
|
76
|
+
@classmethod
|
|
77
|
+
def all(cls) -> list[MemorySource]:
|
|
78
|
+
return list(cls._sources.values())
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def register_core_sources(*, compiler_agent: str, correction_agent: str, document_agent: str,
|
|
82
|
+
meeting_agent: str, correction_mode: str, document_mode: str,
|
|
83
|
+
document_removed_mode: str, meeting_kind: str) -> None:
|
|
84
|
+
"""The sources core knows; MemoryCompilerService registers them with its constants."""
|
|
85
|
+
MemorySourceRegistry.register(MemorySource(
|
|
86
|
+
key="people", mode=None, handler="_compile_person_capture", agent=compiler_agent,
|
|
87
|
+
description="What a person explained: the chat, MCP, the console, an answered question, the capture API.",
|
|
88
|
+
))
|
|
89
|
+
MemorySourceRegistry.register(MemorySource(
|
|
90
|
+
key="correction", mode=correction_mode, handler="_apply_correction", agent=correction_agent,
|
|
91
|
+
description="'¿Qué hay que corregir?' on one page.",
|
|
92
|
+
))
|
|
93
|
+
MemorySourceRegistry.register(MemorySource(
|
|
94
|
+
key="document", mode=document_mode, handler="_ingest_document", agent=document_agent,
|
|
95
|
+
kind_agents={meeting_kind: meeting_agent},
|
|
96
|
+
on_new_version=VERSION_REPLACE, on_removal=REMOVAL_MARK_OBSOLETE,
|
|
97
|
+
description="A document of a Memoria folder (Drive or uploaded), or a meeting transcript.",
|
|
98
|
+
))
|
|
99
|
+
MemorySourceRegistry.register(MemorySource(
|
|
100
|
+
key="document_removed", mode=document_removed_mode, handler="_mark_document_obsolete", agent=None,
|
|
101
|
+
description="A document that left Memoria, or the previous version of one: its lines marked obsolete.",
|
|
102
|
+
))
|
|
@@ -396,6 +396,20 @@ class ProfileService:
|
|
|
396
396
|
"profile": profile
|
|
397
397
|
}
|
|
398
398
|
|
|
399
|
+
def get_role_in_company(self, company_short_name: str, user_identifier: str) -> str | None:
|
|
400
|
+
"""
|
|
401
|
+
The person's role in the company as iat_user_company records it; None if
|
|
402
|
+
not a member. Every authorization reads the role here, never from the
|
|
403
|
+
stored session profile: an SSO hook writes that profile (BL-010, Fase 0).
|
|
404
|
+
"""
|
|
405
|
+
try:
|
|
406
|
+
company = self.profile_repo.get_company_by_short_name(company_short_name)
|
|
407
|
+
user = self.profile_repo.get_user_by_email(str(user_identifier or "").strip().lower()) if company else None
|
|
408
|
+
return self.profile_repo.get_user_role_in_company(company.id, user.id) if user else None
|
|
409
|
+
except Exception as exc:
|
|
410
|
+
logging.warning("Role of %s in %s could not be read: %s", user_identifier, company_short_name, exc)
|
|
411
|
+
return None
|
|
412
|
+
|
|
399
413
|
def _account_user(self, company_short_name: str, session_info: dict):
|
|
400
414
|
profile = session_info.get("profile") or {}
|
|
401
415
|
method = (profile.get("extras") or {}).get("auth_method")
|