iatoolkit 2.61.1__tar.gz → 2.62.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.61.1/src/iatoolkit.egg-info → iatoolkit-2.62.0}/PKG-INFO +1 -1
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/__init__.py +1 -1
- iatoolkit-2.62.0/src/iatoolkit/common/search_terms.py +70 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_tools_pack.yaml +8 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/repositories/models.py +15 -1
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/repositories/vs_repo.py +112 -6
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/configuration_service.py +4 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/knowledge_base_service.py +30 -1
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/tool_service.py +3 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/rag_api_view.py +11 -1
- {iatoolkit-2.61.1 → iatoolkit-2.62.0/src/iatoolkit.egg-info}/PKG-INFO +1 -1
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit.egg-info/SOURCES.txt +1 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/LICENSE +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/LICENSE_COMMUNITY.md +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/pyproject.toml +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/readme.md +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/setup.cfg +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/base_company.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/cli_commands.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/common/__init__.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/common/bridge_validation.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/common/company_paths.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/common/exceptions.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/common/interfaces/__init__.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/common/interfaces/asset_storage.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/common/interfaces/database_provider.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/common/interfaces/memory_compilation_trigger.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/common/interfaces/secret_provider.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/common/interfaces/signup_policy_resolver.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/common/interfaces/web_search_provider.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/common/model_registry.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/common/routes.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/common/secret_resolver.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/common/session_manager.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/common/text_splitter.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/common/url_safety.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/common/util.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/company_registry.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/__init__.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/llm_capabilities.yaml +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/channel_business_identity.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/channel_conversation_continuity.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/channel_response_style.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/chat_state_rules.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/chat_user_profile.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/core_identity.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/email_output.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/file_download_output.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/format_styles.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/html_structures.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/links_documents.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/memory_usage.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/multimodal_basics.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/output_basics.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/presentation_formatting.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/query_main.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/sql_aggregation_scope.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/sql_casting.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/sql_core.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/sql_jsonb.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/sql_mysql_casting.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/sql_mysql_json.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/sql_redshift_basics.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/sql_redshift_types.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/sql_rules.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/tool_html_passthrough.prompt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts_pack.yaml +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/core.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/__init__.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/brevo_mail_app.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/call_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/connectors/__init__.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/connectors/file_connector.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/connectors/file_connector_factory.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/connectors/google_cloud_storage_connector.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/connectors/google_drive_connector.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/connectors/local_file_connector.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/connectors/s3_connector.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/google_auth_client.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/google_chat_app.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/inference_embeddings_client.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/inference_handler.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/jina_embeddings_client.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/llm_gateway_resolver.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/llm_providers/__init__.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/llm_providers/anthropic_adapter.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/llm_providers/deepseek_adapter.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/llm_providers/gemini_adapter.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/llm_providers/openai_adapter.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/llm_providers/openai_compatible_chat_adapter.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/llm_providers/openrouter_adapter.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/llm_proxy.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/llm_response.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/redis_session_manager.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/locales/en.yaml +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/locales/es.yaml +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/repositories/__init__.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/repositories/api_key_repo.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/repositories/bridge_repo.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/repositories/database_manager.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/repositories/document_repo.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/repositories/env_secret_provider.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/repositories/filesystem_asset_repository.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/repositories/knowledge_wiki_repo.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/repositories/llm_query_repo.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/repositories/mcp_token_repo.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/repositories/memory_question_repo.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/repositories/memory_repo.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/repositories/profile_repo.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/repositories/prompt_resource_repo.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/repositories/sql_dataset_repo.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/repositories/sql_source_repo.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/runtime_logging.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/__init__.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/api_key_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/attachment_policy_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/auth_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/branding_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/company_context_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/context_builder_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/dispatcher_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/embedding_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/excel_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/file_processor_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/history_manager_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/http_tool_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/i18n_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/inference_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/jwt_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/language_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/license_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/llm_client_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/mail_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/markdown_wiki_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/mcp_token_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/memory_compiler_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/memory_lookup_policy_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/memory_question_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/memory_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/memory_wiki_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/noop_memory_compilation_trigger.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/operator_identity_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/parsers/__init__.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/parsers/contracts.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/parsers/image_normalizer.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/parsers/parsing_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/parsers/pdf_ocr_detection.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/parsers/provider_factory.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/parsers/provider_resolver.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/parsers/providers/__init__.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/parsers/providers/basic_provider.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/parsers/providers/docling_provider.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/parsers/validator.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/pdf_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/profile_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/prompt_resource_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/prompt_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/query_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/signup_policy_resolver.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/sql_dataset_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/sql_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/sql_source_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/storage_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/structured_output_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/system_prompt_catalog.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/system_tools.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/telemetry_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/tenant_wiki_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/tool_output_contract.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/user_feedback_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/user_session_context_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/visual_kb_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/visual_tool_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/warmup_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/web_search/__init__.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/web_search/provider_factory.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/web_search/providers/__init__.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/web_search/providers/brave_provider.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/services/web_search_service.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/static/images/favicon.ico +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/static/images/favicon.png +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/static/images/fernando.jpeg +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/static/images/iatoolkit_core.png +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/static/images/iatoolkit_logo.png +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/static/js/chat_feedback_button.js +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/static/js/chat_filepond.js +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/static/js/chat_header_navigation.js +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/static/js/chat_help_content.js +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/static/js/chat_history_button.js +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/static/js/chat_logout_button.js +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/static/js/chat_main.js +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/static/js/chat_memory_button.js +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/static/js/chat_model_selector.js +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/static/js/chat_onboarding_button.js +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/static/js/chat_prompt_manager.js +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/static/js/chat_reload_button.js +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/static/styles/account.css +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/static/styles/chat_iatoolkit.css +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/static/styles/chat_modal.css +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/static/styles/chat_public.css +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/static/styles/documents.css +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/static/styles/landing_page.css +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/static/styles/llm_output.css +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/static/styles/onboarding.css +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/templates/_chat_header.html +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/templates/_company_header.html +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/templates/_login_widget.html +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/templates/account.html +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/templates/base.html +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/templates/change_password.html +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/templates/chat.html +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/templates/chat_memory_modal.html +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/templates/chat_modals.html +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/templates/error.html +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/templates/forgot_password.html +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/templates/home_hosted_default.html +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/templates/login_global.html +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/templates/memory/wiki_schema.md +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/templates/onboarding_shell.html +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/templates/pdf/base.html +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/templates/pdf/letter.html +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/templates/pdf/report.html +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/templates/pdf/simple.html +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/templates/signup.html +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/__init__.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/account_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/api_key_api_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/base_login_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/categories_api_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/change_password_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/chat_context_preview_api_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/chat_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/configuration_api_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/connectors_api_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/embedding_api_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/forgot_password_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/help_content_api_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/history_api_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/home_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/init_context_api_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/llmquery_api_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/load_document_api_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/login_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/logout_api_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/memory_api_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/profile_api_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/prompt_api_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/prompt_context_preview_api_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/prompt_resource_api_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/root_redirect_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/signup_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/static_page_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/tool_api_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/user_feedback_api_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/users_api_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/views/verify_user_view.py +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit.egg-info/dependency_links.txt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit.egg-info/requires.txt +0 -0
- {iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
+
# Product: IAToolkit
|
|
3
|
+
#
|
|
4
|
+
# IAToolkit is open source software.
|
|
5
|
+
|
|
6
|
+
"""The words the hybrid search looks for (BL-005, Fase 4).
|
|
7
|
+
|
|
8
|
+
The word branch searches the chunks' full text with Postgres' 'simple'
|
|
9
|
+
configuration, which has no stopwords: a question would match every chunk with
|
|
10
|
+
"de" or "the". So the question is reduced here to the words that mean something,
|
|
11
|
+
and Postgres ORs them (see VSRepo). Exact terms the model passes as `keywords`
|
|
12
|
+
take the place of the question.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import re
|
|
18
|
+
|
|
19
|
+
# Spanish and English function words. Only what never identifies a document:
|
|
20
|
+
# no domain words, no numbers.
|
|
21
|
+
STOPWORDS = frozenset("""
|
|
22
|
+
a al algo algun alguna algunas alguno algunos ante antes aquel aquella aquello aqui asi aun cada como con
|
|
23
|
+
contra cual cuales cualquier cuando cuanto de del desde donde dos el ella ellas ellos en entre era eran es
|
|
24
|
+
esa esas ese eso esos esta estaba estado estan estar estas este esto estos fue fueron ha hay hace hacer hacia
|
|
25
|
+
han hasta la las le les lo los mas me mi mis mucho muy nada ni no nos nosotros o otra otras otro otros para
|
|
26
|
+
pero poco por porque que quien quienes se sea ser si sido sin sobre son su sus tambien tan tanto te tiene
|
|
27
|
+
tienen todo todos tu tus un una unas uno unos usted ustedes ya yo
|
|
28
|
+
cómo cuál cuáles cuándo cuánto dónde qué quién quiénes también aquí así aún más está están sí tú
|
|
29
|
+
a an and are as at be been but by can could did do does for from had has have how i if in into is it its
|
|
30
|
+
me my no not of on or our should so than that the their them then there these they this those to was we
|
|
31
|
+
were what when where which who why will with would you your
|
|
32
|
+
""".split())
|
|
33
|
+
|
|
34
|
+
# Leading and trailing punctuation that is never part of a term. Inner
|
|
35
|
+
# punctuation is kept: AX-2241, 76.123.456-7, v3.2.
|
|
36
|
+
_EDGE_PUNCTUATION = "¿?¡!.,;:\"'()[]{}«»“”‘’…"
|
|
37
|
+
_MAX_TERMS = 24
|
|
38
|
+
_MAX_TERM_CHARS = 64
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def search_terms(query_text: str | None, keywords: str | list | None = None) -> str:
|
|
42
|
+
"""
|
|
43
|
+
The text the word branch looks for: the keywords when given, else the query
|
|
44
|
+
without stopwords. Empty when nothing is left, and the caller then searches
|
|
45
|
+
by meaning only.
|
|
46
|
+
"""
|
|
47
|
+
if keywords:
|
|
48
|
+
raw = keywords if isinstance(keywords, list) else [keywords]
|
|
49
|
+
source = " ".join(str(item) for item in raw if str(item or "").strip())
|
|
50
|
+
return _clean(source, drop_stopwords=False)
|
|
51
|
+
return _clean(str(query_text or ""), drop_stopwords=True)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _clean(text: str, *, drop_stopwords: bool) -> str:
|
|
55
|
+
terms: list[str] = []
|
|
56
|
+
seen: set[str] = set()
|
|
57
|
+
for token in re.split(r"\s+", text):
|
|
58
|
+
term = token.strip(_EDGE_PUNCTUATION)
|
|
59
|
+
if not term or not any(char.isalnum() for char in term):
|
|
60
|
+
continue
|
|
61
|
+
if drop_stopwords and term.lower() in STOPWORDS:
|
|
62
|
+
continue
|
|
63
|
+
term = term[:_MAX_TERM_CHARS]
|
|
64
|
+
if term.lower() in seen:
|
|
65
|
+
continue
|
|
66
|
+
seen.add(term.lower())
|
|
67
|
+
terms.append(term)
|
|
68
|
+
if len(terms) >= _MAX_TERMS:
|
|
69
|
+
break
|
|
70
|
+
return " ".join(terms)
|
|
@@ -280,6 +280,14 @@ tools:
|
|
|
280
280
|
- value
|
|
281
281
|
additionalProperties: false
|
|
282
282
|
description: "Opcional en la práctica. Usa null cuando no necesites filtrar por metadata. No inventes filtros ni envíes [] por defecto. Ejemplo: [{'key':'doc.type','value':'invoice'},{'key':'chunk.source_type','value':'table'}]."
|
|
283
|
+
keywords:
|
|
284
|
+
anyOf:
|
|
285
|
+
- type: "null"
|
|
286
|
+
- type: string
|
|
287
|
+
- type: array
|
|
288
|
+
items:
|
|
289
|
+
type: string
|
|
290
|
+
description: "Opcional. Términos exactos que deben aparecer en el texto: códigos, números de contrato, RUT, nombres propios o siglas (ej. [\"AX-2241\", \"Pérez\"]). La búsqueda combina el significado de `query` con estos términos. Usa null si la pregunta no tiene términos exactos."
|
|
283
291
|
required:
|
|
284
292
|
- query
|
|
285
293
|
- function_name: iat_document_list
|
|
@@ -8,7 +8,7 @@ import enum
|
|
|
8
8
|
from sqlalchemy import Column, Integer, BigInteger, String, DateTime, Enum, Text, JSON, Boolean, ForeignKey, Table, MetaData
|
|
9
9
|
from sqlalchemy.orm import DeclarativeBase
|
|
10
10
|
from sqlalchemy.orm import relationship, class_mapper
|
|
11
|
-
from sqlalchemy.sql import func
|
|
11
|
+
from sqlalchemy.sql import func, literal_column
|
|
12
12
|
from sqlalchemy import UniqueConstraint, Index
|
|
13
13
|
from sqlalchemy.dialects.postgresql import JSONB
|
|
14
14
|
from datetime import datetime
|
|
@@ -690,6 +690,20 @@ class VSDoc(Base):
|
|
|
690
690
|
embedding = Column(Vector, nullable=False)
|
|
691
691
|
company = relationship("Company", back_populates="vsdocs")
|
|
692
692
|
|
|
693
|
+
# Full-text index for the hybrid search's word branch (BL-005, Fase 4): an
|
|
694
|
+
# expression index, no extra column, so the table is not rewritten. The
|
|
695
|
+
# 'simple' configuration does not stem, so codes such as AX-2241 or names
|
|
696
|
+
# such as Pérez are indexed as written. Postgres only; existing databases get
|
|
697
|
+
# it from iatoolkit-enterprise/migrations/2026-10-15-vsdocs-text-index.sql,
|
|
698
|
+
# since create_all() never adds an index to a table that already exists.
|
|
699
|
+
__table_args__ = (
|
|
700
|
+
Index(
|
|
701
|
+
"ix_iat_vsdocs_text_simple_tsv",
|
|
702
|
+
func.to_tsvector(literal_column("'simple'::regconfig"), text),
|
|
703
|
+
postgresql_using="gin",
|
|
704
|
+
).ddl_if(dialect="postgresql"),
|
|
705
|
+
)
|
|
706
|
+
|
|
693
707
|
def to_dict(self):
|
|
694
708
|
return {column.key: getattr(self, column.key) for column in class_mapper(self.__class__).columns}
|
|
695
709
|
|
|
@@ -14,6 +14,7 @@ from sqlalchemy import text
|
|
|
14
14
|
from injector import inject
|
|
15
15
|
|
|
16
16
|
from iatoolkit.common.exceptions import IAToolkitException
|
|
17
|
+
from iatoolkit.common.search_terms import search_terms
|
|
17
18
|
from iatoolkit.repositories.database_manager import DatabaseManager
|
|
18
19
|
from iatoolkit.repositories.models import Company, VSDoc, VSImage
|
|
19
20
|
from iatoolkit.services.embedding_service import EmbeddingService
|
|
@@ -96,6 +97,14 @@ class VSRepo:
|
|
|
96
97
|
query = query.limit(max(int(limit), 0))
|
|
97
98
|
return list(query.all())
|
|
98
99
|
|
|
100
|
+
SEARCH_MODE_VECTOR = "vector"
|
|
101
|
+
SEARCH_MODE_HYBRID = "hybrid"
|
|
102
|
+
SEARCH_MODES = (SEARCH_MODE_VECTOR, SEARCH_MODE_HYBRID)
|
|
103
|
+
# Each branch of the hybrid search brings this many candidates to the fusion.
|
|
104
|
+
HYBRID_CANDIDATES = 40
|
|
105
|
+
# Reciprocal Rank Fusion constant: a chunk adds 1 / (RRF_K + rank) per list.
|
|
106
|
+
RRF_K = 60
|
|
107
|
+
|
|
99
108
|
def query(self,
|
|
100
109
|
company_short_name: str,
|
|
101
110
|
query_text: str,
|
|
@@ -103,6 +112,8 @@ class VSRepo:
|
|
|
103
112
|
metadata_filter=None,
|
|
104
113
|
collection_ids: List[int] | None = None,
|
|
105
114
|
include_urls: bool = True,
|
|
115
|
+
search_mode: str = SEARCH_MODE_VECTOR,
|
|
116
|
+
keywords: str | list | None = None,
|
|
106
117
|
) -> list[Dict]:
|
|
107
118
|
"""
|
|
108
119
|
search documents similar to the query for a company
|
|
@@ -113,6 +124,10 @@ class VSRepo:
|
|
|
113
124
|
n_results: max number of results to return
|
|
114
125
|
metadata_filter: (e.g., {"document_type": "certificate"})
|
|
115
126
|
include_urls: Generate storage URLs for returned chunks when true.
|
|
127
|
+
search_mode: "vector" (by meaning) or "hybrid" (by meaning and by
|
|
128
|
+
words, fused by rank; BL-005, Fase 4).
|
|
129
|
+
keywords: exact terms for the hybrid search's word branch, instead
|
|
130
|
+
of the query's own words.
|
|
116
131
|
|
|
117
132
|
Returns:
|
|
118
133
|
list of documents matching the query and filters
|
|
@@ -214,11 +229,18 @@ class VSRepo:
|
|
|
214
229
|
sql_query_parts.extend(metadata_sql)
|
|
215
230
|
params.update(metadata_params)
|
|
216
231
|
|
|
217
|
-
|
|
218
|
-
|
|
232
|
+
text_terms = search_terms(query_text, keywords) if search_mode == self.SEARCH_MODE_HYBRID else ""
|
|
233
|
+
hybrid = bool(text_terms)
|
|
234
|
+
if hybrid:
|
|
235
|
+
params["text_terms"] = text_terms
|
|
236
|
+
params["candidates"] = max(int(n_results or 5), self.HYBRID_CANDIDATES)
|
|
237
|
+
sql_query = self._hybrid_sql(sql_query_parts, distance_expression)
|
|
238
|
+
else:
|
|
239
|
+
# join all the query parts
|
|
240
|
+
sql_query = "".join(sql_query_parts)
|
|
219
241
|
|
|
220
|
-
|
|
221
|
-
|
|
242
|
+
# add sorting and limit of results
|
|
243
|
+
sql_query += " ORDER BY distance ASC LIMIT :n_results"
|
|
222
244
|
|
|
223
245
|
logging.debug(f"Executing SQL query: {sql_query}")
|
|
224
246
|
logging.debug(f"With parameters: {params}")
|
|
@@ -231,6 +253,7 @@ class VSRepo:
|
|
|
231
253
|
rows = result.fetchall()
|
|
232
254
|
vector_sql_duration_ms = round((perf_counter() - vector_sql_started_at) * 1000)
|
|
233
255
|
vs_documents = []
|
|
256
|
+
matches = {"both": 0, "vector": 0, "text": 0}
|
|
234
257
|
|
|
235
258
|
for row in rows:
|
|
236
259
|
# create the document object with the data
|
|
@@ -238,6 +261,12 @@ class VSRepo:
|
|
|
238
261
|
chunk_meta = row[6] if len(row) > 6 and row[6] is not None else {}
|
|
239
262
|
|
|
240
263
|
distance = float(row[7]) if len(row) > 7 and row[7] is not None else None
|
|
264
|
+
hybrid_fields = {}
|
|
265
|
+
if hybrid:
|
|
266
|
+
in_vector, in_text = bool(row[9]), bool(row[10])
|
|
267
|
+
match = "both" if in_vector and in_text else "vector" if in_vector else "text"
|
|
268
|
+
matches[match] += 1
|
|
269
|
+
hybrid_fields = {"match": match, "rrf_score": float(row[8]) if row[8] is not None else None}
|
|
241
270
|
|
|
242
271
|
# get the url of the document
|
|
243
272
|
storage_key = row[3] if len(row) > 3 and row[3] is not None else None
|
|
@@ -257,17 +286,21 @@ class VSRepo:
|
|
|
257
286
|
'distance': distance,
|
|
258
287
|
'distance_metric': distance_metric,
|
|
259
288
|
'score': self._score_from_l2_distance(distance),
|
|
289
|
+
'search_mode': self.SEARCH_MODE_HYBRID if hybrid else self.SEARCH_MODE_VECTOR,
|
|
290
|
+
**hybrid_fields,
|
|
260
291
|
}
|
|
261
292
|
)
|
|
262
293
|
|
|
263
294
|
logging.info(
|
|
264
|
-
"RAG
|
|
265
|
-
"embedding_ms=%s company_lookup_ms=%s vector_sql_ms=%s total_ms=%s",
|
|
295
|
+
"RAG %s query completed: company=%s collection_ids=%s dimensions=%s n_results=%s rows=%s "
|
|
296
|
+
"matches=%s embedding_ms=%s company_lookup_ms=%s vector_sql_ms=%s total_ms=%s",
|
|
297
|
+
self.SEARCH_MODE_HYBRID if hybrid else self.SEARCH_MODE_VECTOR,
|
|
266
298
|
company_short_name,
|
|
267
299
|
normalized_collection_ids,
|
|
268
300
|
embedding_dimensions,
|
|
269
301
|
n_results,
|
|
270
302
|
len(rows),
|
|
303
|
+
matches if hybrid else None,
|
|
271
304
|
embedding_duration_ms,
|
|
272
305
|
company_lookup_duration_ms,
|
|
273
306
|
vector_sql_duration_ms,
|
|
@@ -288,6 +321,79 @@ class VSRepo:
|
|
|
288
321
|
finally:
|
|
289
322
|
self.session.close()
|
|
290
323
|
|
|
324
|
+
def _hybrid_sql(self, sql_query_parts: list[str], distance_expression: str) -> str:
|
|
325
|
+
"""
|
|
326
|
+
Hybrid search in one statement: the nearest chunks by meaning and the
|
|
327
|
+
best chunks by words (Postgres full text, 'simple' configuration, the
|
|
328
|
+
terms OR'ed), each ranked, fused by Reciprocal Rank Fusion.
|
|
329
|
+
|
|
330
|
+
`sql_query_parts` is the vector query with every filter (company,
|
|
331
|
+
dimension, collections, metadata); both branches reuse its WHERE, so they
|
|
332
|
+
see exactly the same chunks. The word branch has its own expression, the
|
|
333
|
+
one the GIN index ix_iat_vsdocs_text_simple_tsv is built on.
|
|
334
|
+
"""
|
|
335
|
+
base = "".join(sql_query_parts)
|
|
336
|
+
_, where = base.split("WHERE", 1)
|
|
337
|
+
# The dimension condition is for the vector index; the word branch does
|
|
338
|
+
# not need it (a chunk is a chunk whatever its embedding size).
|
|
339
|
+
text_where = re.sub(r"\s+AND vector_dims\(iat_vsdocs\.embedding\) = \d+", "", where)
|
|
340
|
+
tsvector = "to_tsvector('simple'::regconfig, iat_vsdocs.text)"
|
|
341
|
+
# plainto_tsquery parses the terms exactly as the index parsed the text
|
|
342
|
+
# (and cannot be broken by any character); its ANDs become ORs.
|
|
343
|
+
tsquery = "replace(plainto_tsquery('simple'::regconfig, :text_terms)::text, ' & ', ' | ')::tsquery"
|
|
344
|
+
return f"""
|
|
345
|
+
WITH vector_hits AS (
|
|
346
|
+
SELECT id, distance, ROW_NUMBER() OVER (ORDER BY distance ASC) AS rank
|
|
347
|
+
FROM (
|
|
348
|
+
SELECT iat_vsdocs.id, ({distance_expression}) AS distance
|
|
349
|
+
FROM iat_vsdocs, iat_documents
|
|
350
|
+
WHERE {where}
|
|
351
|
+
ORDER BY distance ASC
|
|
352
|
+
LIMIT :candidates
|
|
353
|
+
) nearest
|
|
354
|
+
),
|
|
355
|
+
text_hits AS (
|
|
356
|
+
SELECT id, ROW_NUMBER() OVER (ORDER BY text_rank DESC, id ASC) AS rank
|
|
357
|
+
FROM (
|
|
358
|
+
SELECT iat_vsdocs.id, ts_rank_cd({tsvector}, terms.q) AS text_rank
|
|
359
|
+
FROM iat_vsdocs, iat_documents, (SELECT {tsquery} AS q) terms
|
|
360
|
+
WHERE {text_where}
|
|
361
|
+
AND {tsvector} @@ terms.q
|
|
362
|
+
ORDER BY text_rank DESC
|
|
363
|
+
LIMIT :candidates
|
|
364
|
+
) best
|
|
365
|
+
),
|
|
366
|
+
fused AS (
|
|
367
|
+
SELECT id,
|
|
368
|
+
SUM(1.0 / ({self.RRF_K} + rank)) AS rrf_score,
|
|
369
|
+
MIN(distance) AS distance,
|
|
370
|
+
BOOL_OR(source = 'vector') AS in_vector,
|
|
371
|
+
BOOL_OR(source = 'text') AS in_text
|
|
372
|
+
FROM (
|
|
373
|
+
SELECT id, rank, distance, 'vector' AS source FROM vector_hits
|
|
374
|
+
UNION ALL
|
|
375
|
+
SELECT id, rank, NULL::float AS distance, 'text' AS source FROM text_hits
|
|
376
|
+
) both_lists
|
|
377
|
+
GROUP BY id
|
|
378
|
+
)
|
|
379
|
+
SELECT iat_vsdocs.id,
|
|
380
|
+
iat_documents.filename,
|
|
381
|
+
iat_vsdocs.text,
|
|
382
|
+
iat_documents.storage_key,
|
|
383
|
+
iat_documents.meta,
|
|
384
|
+
iat_documents.id,
|
|
385
|
+
iat_vsdocs.meta,
|
|
386
|
+
fused.distance,
|
|
387
|
+
fused.rrf_score,
|
|
388
|
+
fused.in_vector,
|
|
389
|
+
fused.in_text
|
|
390
|
+
FROM fused
|
|
391
|
+
JOIN iat_vsdocs ON iat_vsdocs.id = fused.id
|
|
392
|
+
JOIN iat_documents ON iat_documents.id = iat_vsdocs.document_id
|
|
393
|
+
ORDER BY fused.rrf_score DESC, fused.distance ASC NULLS LAST
|
|
394
|
+
LIMIT :n_results
|
|
395
|
+
"""
|
|
396
|
+
|
|
291
397
|
def query_images(self,
|
|
292
398
|
company_short_name: str,
|
|
293
399
|
query_text: str,
|
|
@@ -1059,6 +1059,10 @@ class ConfigurationService:
|
|
|
1059
1059
|
if kb_config and not isinstance(kb_config, dict):
|
|
1060
1060
|
add_error("knowledge_base", "Section must be a dictionary.")
|
|
1061
1061
|
elif kb_config:
|
|
1062
|
+
search_mode = kb_config.get("search_mode")
|
|
1063
|
+
if search_mode is not None and str(search_mode).strip().lower() not in {"vector", "hybrid"}:
|
|
1064
|
+
add_error("knowledge_base.search_mode", "Must be 'vector' or 'hybrid'.")
|
|
1065
|
+
|
|
1062
1066
|
parsing_provider = kb_config.get("parsing_provider")
|
|
1063
1067
|
allowed_parsing_providers = {"auto", "docling", "basic"}
|
|
1064
1068
|
if parsing_provider is not None:
|
|
@@ -491,9 +491,15 @@ class KnowledgeBaseService:
|
|
|
491
491
|
collection: str | list[str] | None = None,
|
|
492
492
|
metadata_filter: dict = None,
|
|
493
493
|
include_urls: bool = True,
|
|
494
|
+
keywords: str | list | None = None,
|
|
495
|
+
search_mode: str | None = None,
|
|
494
496
|
):
|
|
495
497
|
"""
|
|
496
|
-
|
|
498
|
+
Searches the company's chunks and returns them (as dicts).
|
|
499
|
+
|
|
500
|
+
By meaning and by words, fused, unless the company sets
|
|
501
|
+
`knowledge_base.search_mode: vector` (BL-005, Fase 4). `keywords` are
|
|
502
|
+
exact terms for the word part; `search_mode` overrides the company's.
|
|
497
503
|
Useful for UI displays where structured data is needed instead of a raw string context.
|
|
498
504
|
|
|
499
505
|
Args:
|
|
@@ -536,10 +542,33 @@ class KnowledgeBaseService:
|
|
|
536
542
|
metadata_filter=metadata_filter,
|
|
537
543
|
collection_ids=collection_ids,
|
|
538
544
|
include_urls=include_urls,
|
|
545
|
+
search_mode=self.resolve_search_mode(company_short_name, search_mode),
|
|
546
|
+
keywords=keywords,
|
|
539
547
|
)
|
|
540
548
|
|
|
541
549
|
return chunk_list
|
|
542
550
|
|
|
551
|
+
DEFAULT_SEARCH_MODE = "hybrid"
|
|
552
|
+
SEARCH_MODES = ("vector", "hybrid")
|
|
553
|
+
|
|
554
|
+
def resolve_search_mode(self, company_short_name: str, requested: str | None = None) -> str:
|
|
555
|
+
"""The requested mode, else company.yaml's knowledge_base.search_mode, else hybrid."""
|
|
556
|
+
candidate = str(requested or "").strip().lower()
|
|
557
|
+
if candidate in self.SEARCH_MODES:
|
|
558
|
+
return candidate
|
|
559
|
+
try:
|
|
560
|
+
from iatoolkit import current_iatoolkit
|
|
561
|
+
from iatoolkit.services.configuration_service import ConfigurationService
|
|
562
|
+
|
|
563
|
+
# Resolved here, not injected: ConfigurationService builds this service
|
|
564
|
+
# to sync the collections when a company's config loads.
|
|
565
|
+
kb_config = current_iatoolkit().get_injector().get(ConfigurationService).get_configuration(
|
|
566
|
+
company_short_name, "knowledge_base") or {}
|
|
567
|
+
except Exception:
|
|
568
|
+
kb_config = {}
|
|
569
|
+
configured = str((kb_config or {}).get("search_mode") or "").strip().lower() if isinstance(kb_config, dict) else ""
|
|
570
|
+
return configured if configured in self.SEARCH_MODES else self.DEFAULT_SEARCH_MODE
|
|
571
|
+
|
|
543
572
|
@staticmethod
|
|
544
573
|
def _normalize_search_collection(collection: str | list[str] | None) -> str | list[str] | None:
|
|
545
574
|
if collection is None:
|
|
@@ -185,13 +185,16 @@ class ToolService:
|
|
|
185
185
|
collection: str | list[str] | None = None,
|
|
186
186
|
metadata_filter=None,
|
|
187
187
|
n_results: int = 5,
|
|
188
|
+
keywords=None,
|
|
188
189
|
**kwargs):
|
|
190
|
+
search_kwargs = {"keywords": keywords} if keywords else {}
|
|
189
191
|
chunks = self.knowledge_base_service.search(
|
|
190
192
|
company_short_name=company_short_name,
|
|
191
193
|
query=query,
|
|
192
194
|
n_results=n_results,
|
|
193
195
|
collection=collection,
|
|
194
196
|
metadata_filter=metadata_filter,
|
|
197
|
+
**search_kwargs,
|
|
195
198
|
)
|
|
196
199
|
typed_chunks = self._normalize_chunks_for_tool(chunks)
|
|
197
200
|
|
|
@@ -310,23 +310,33 @@ class RagApiView(MethodView):
|
|
|
310
310
|
n_results = int(data.get('k', 5))
|
|
311
311
|
collection = data.get('collection')
|
|
312
312
|
metadata_filter = data.get('metadata_filter')
|
|
313
|
+
# The Search Lab can compare modes; empty means the company's own.
|
|
314
|
+
search_mode = str(data.get('search_mode') or '').strip().lower() or None
|
|
315
|
+
keywords = data.get('keywords') or None
|
|
313
316
|
|
|
314
317
|
if not query:
|
|
315
318
|
msg = self.i18n_service.t('rag.search.query_required')
|
|
316
319
|
return jsonify({'result': 'error', 'message': msg}), 400
|
|
317
320
|
|
|
318
321
|
# 3. Call Service
|
|
322
|
+
extra = {}
|
|
323
|
+
if search_mode:
|
|
324
|
+
extra['search_mode'] = search_mode
|
|
325
|
+
if keywords:
|
|
326
|
+
extra['keywords'] = keywords
|
|
319
327
|
chunks = self.knowledge_base_service.search(
|
|
320
328
|
company_short_name=company_short_name,
|
|
321
329
|
query=query,
|
|
322
330
|
n_results=n_results,
|
|
323
331
|
collection=collection,
|
|
324
332
|
metadata_filter=metadata_filter,
|
|
333
|
+
**extra,
|
|
325
334
|
)
|
|
326
335
|
|
|
327
336
|
return jsonify({
|
|
328
337
|
"result": "success",
|
|
329
|
-
"chunks": chunks
|
|
338
|
+
"chunks": chunks,
|
|
339
|
+
"search_mode": chunks[0].get("search_mode") if chunks else None,
|
|
330
340
|
}), 200
|
|
331
341
|
|
|
332
342
|
except IAToolkitException as e:
|
|
@@ -19,6 +19,7 @@ src/iatoolkit/common/company_paths.py
|
|
|
19
19
|
src/iatoolkit/common/exceptions.py
|
|
20
20
|
src/iatoolkit/common/model_registry.py
|
|
21
21
|
src/iatoolkit/common/routes.py
|
|
22
|
+
src/iatoolkit/common/search_terms.py
|
|
22
23
|
src/iatoolkit/common/secret_resolver.py
|
|
23
24
|
src/iatoolkit/common/session_manager.py
|
|
24
25
|
src/iatoolkit/common/text_splitter.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/common/interfaces/memory_compilation_trigger.py
RENAMED
|
File without changes
|
|
File without changes
|
{iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/common/interfaces/signup_policy_resolver.py
RENAMED
|
File without changes
|
{iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/common/interfaces/web_search_provider.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/chat_state_rules.prompt
RENAMED
|
File without changes
|
{iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/chat_user_profile.prompt
RENAMED
|
File without changes
|
{iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/core_identity.prompt
RENAMED
|
File without changes
|
{iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/email_output.prompt
RENAMED
|
File without changes
|
|
File without changes
|
{iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/format_styles.prompt
RENAMED
|
File without changes
|
{iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/html_structures.prompt
RENAMED
|
File without changes
|
{iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/links_documents.prompt
RENAMED
|
File without changes
|
{iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/memory_usage.prompt
RENAMED
|
File without changes
|
{iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/multimodal_basics.prompt
RENAMED
|
File without changes
|
{iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/output_basics.prompt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/sql_casting.prompt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/sql_mysql_casting.prompt
RENAMED
|
File without changes
|
{iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/sql_mysql_json.prompt
RENAMED
|
File without changes
|
{iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/sql_redshift_basics.prompt
RENAMED
|
File without changes
|
{iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/config/system_prompts/sql_redshift_types.prompt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/connectors/file_connector_factory.py
RENAMED
|
File without changes
|
|
File without changes
|
{iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/connectors/google_drive_connector.py
RENAMED
|
File without changes
|
{iatoolkit-2.61.1 → iatoolkit-2.62.0}/src/iatoolkit/infra/connectors/local_file_connector.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|