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