iatoolkit 2.29.1__tar.gz → 2.31.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.29.1/src/iatoolkit.egg-info → iatoolkit-2.31.0}/PKG-INFO +1 -1
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/__init__.py +1 -1
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/core.py +14 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/repositories/vs_repo.py +71 -7
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/configuration_service.py +17 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/inference_service.py +58 -3
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/knowledge_base_service.py +4 -1
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/tool_service.py +3 -0
- iatoolkit-2.31.0/src/iatoolkit/services/warmup_service.py +234 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/rag_api_view.py +6 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0/src/iatoolkit.egg-info}/PKG-INFO +1 -1
- iatoolkit-2.29.1/src/iatoolkit/services/warmup_service.py +0 -108
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/LICENSE +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/LICENSE_COMMUNITY.md +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/pyproject.toml +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/readme.md +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/requirements.txt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/setup.cfg +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/base_company.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/cli_commands.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/common/__init__.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/common/exceptions.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/common/interfaces/__init__.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/common/interfaces/asset_storage.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/common/interfaces/database_provider.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/common/interfaces/memory_compilation_trigger.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/common/interfaces/memory_lint_trigger.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/common/interfaces/secret_provider.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/common/interfaces/signup_policy_resolver.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/common/interfaces/web_search_provider.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/common/model_registry.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/common/routes.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/common/secret_resolver.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/common/session_manager.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/common/util.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/company_registry.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/__init__.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/llm_capabilities.yaml +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/channel_business_identity.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/channel_conversation_continuity.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/channel_response_style.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/chat_state_rules.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/chat_user_profile.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/core_identity.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/email_output.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/file_download_output.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/format_styles.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/html_structures.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/links_documents.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/memory_usage.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/multimodal_basics.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/output_basics.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/presentation_formatting.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/query_main.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/sql_aggregation_scope.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/sql_casting.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/sql_core.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/sql_jsonb.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/sql_mysql_casting.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/sql_mysql_json.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/sql_redshift_basics.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/sql_redshift_types.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/sql_rules.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts/tool_html_passthrough.prompt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_prompts_pack.yaml +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/config/system_tools_pack.yaml +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/__init__.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/brevo_mail_app.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/call_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/connectors/__init__.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/connectors/file_connector.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/connectors/file_connector_factory.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/connectors/google_cloud_storage_connector.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/connectors/google_drive_connector.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/connectors/local_file_connector.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/connectors/s3_connector.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/google_auth_client.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/google_chat_app.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/inference_embeddings_client.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/inference_handler.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/jina_embeddings_client.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/llm_gateway_resolver.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/llm_providers/__init__.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/llm_providers/anthropic_adapter.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/llm_providers/deepseek_adapter.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/llm_providers/gemini_adapter.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/llm_providers/openai_adapter.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/llm_providers/openai_compatible_chat_adapter.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/llm_providers/openrouter_adapter.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/llm_proxy.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/llm_response.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/infra/redis_session_manager.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/locales/en.yaml +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/locales/es.yaml +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/repositories/__init__.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/repositories/api_key_repo.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/repositories/database_manager.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/repositories/document_repo.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/repositories/env_secret_provider.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/repositories/filesystem_asset_repository.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/repositories/knowledge_wiki_repo.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/repositories/llm_query_repo.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/repositories/mcp_token_repo.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/repositories/memory_repo.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/repositories/models.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/repositories/profile_repo.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/repositories/prompt_resource_repo.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/repositories/sql_dataset_repo.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/repositories/sql_source_repo.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/runtime_logging.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/__init__.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/api_key_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/attachment_policy_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/auth_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/benchmark_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/branding_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/company_context_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/context_builder_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/dispatcher_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/embedding_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/excel_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/file_processor_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/history_manager_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/http_tool_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/i18n_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/jwt_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/language_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/license_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/llm_client_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/mail_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/markdown_wiki_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/mcp_token_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/memory_compiler_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/memory_lint_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/memory_lookup_policy_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/memory_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/memory_wiki_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/noop_memory_compilation_trigger.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/noop_memory_lint_trigger.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/parsers/__init__.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/parsers/contracts.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/parsers/image_normalizer.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/parsers/parsing_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/parsers/pdf_ocr_detection.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/parsers/provider_factory.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/parsers/provider_resolver.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/parsers/providers/__init__.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/parsers/providers/basic_provider.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/parsers/providers/docling_provider.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/parsers/validator.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/pdf_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/profile_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/prompt_resource_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/prompt_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/query_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/signup_policy_resolver.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/sql_dataset_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/sql_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/sql_source_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/storage_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/structured_output_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/system_prompt_catalog.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/system_tools.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/telemetry_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/tenant_wiki_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/tool_output_contract.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/user_feedback_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/user_session_context_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/visual_kb_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/visual_tool_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/web_search/__init__.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/web_search/provider_factory.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/web_search/providers/__init__.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/web_search/providers/brave_provider.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/services/web_search_service.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/static/images/fernando.jpeg +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/static/images/iatoolkit_core.png +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/static/images/iatoolkit_logo.png +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/static/js/chat_feedback_button.js +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/static/js/chat_filepond.js +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/static/js/chat_help_content.js +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/static/js/chat_history_button.js +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/static/js/chat_logout_button.js +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/static/js/chat_main.js +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/static/js/chat_memory_button.js +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/static/js/chat_model_selector.js +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/static/js/chat_onboarding_button.js +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/static/js/chat_prompt_manager.js +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/static/js/chat_reload_button.js +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/static/styles/chat_iatoolkit.css +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/static/styles/chat_modal.css +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/static/styles/chat_public.css +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/static/styles/documents.css +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/static/styles/landing_page.css +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/static/styles/llm_output.css +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/static/styles/onboarding.css +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/templates/_company_header.html +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/templates/_login_widget.html +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/templates/account.html +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/templates/base.html +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/templates/change_password.html +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/templates/chat.html +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/templates/chat_memory_modal.html +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/templates/chat_modals.html +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/templates/error.html +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/templates/forgot_password.html +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/templates/home_hosted_default.html +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/templates/memory/wiki_schema.md +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/templates/onboarding_shell.html +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/templates/pdf/base.html +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/templates/pdf/letter.html +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/templates/pdf/report.html +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/templates/pdf/simple.html +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/templates/signup.html +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/__init__.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/account_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/api_key_api_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/base_login_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/categories_api_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/change_password_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/chat_context_preview_api_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/chat_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/configuration_api_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/connectors_api_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/embedding_api_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/forgot_password_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/help_content_api_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/history_api_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/home_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/init_context_api_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/llmquery_api_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/load_document_api_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/login_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/logout_api_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/memory_api_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/profile_api_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/prompt_api_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/prompt_context_preview_api_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/prompt_resource_api_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/root_redirect_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/signup_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/static_page_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/tool_api_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/user_feedback_api_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/users_api_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit/views/verify_user_view.py +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit.egg-info/SOURCES.txt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit.egg-info/dependency_links.txt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit.egg-info/requires.txt +0 -0
- {iatoolkit-2.29.1 → iatoolkit-2.31.0}/src/iatoolkit.egg-info/top_level.txt +0 -0
|
@@ -115,6 +115,7 @@ class IAToolkit:
|
|
|
115
115
|
self._setup_request_globals()
|
|
116
116
|
self._setup_context_processors()
|
|
117
117
|
self._sync_system_tools_on_boot()
|
|
118
|
+
self._run_configured_startup_warmup()
|
|
118
119
|
|
|
119
120
|
# register data sources
|
|
120
121
|
if start:
|
|
@@ -149,6 +150,19 @@ class IAToolkit:
|
|
|
149
150
|
except Exception as exc:
|
|
150
151
|
logging.warning("⚠️ Unable to sync system tools on boot: %s", exc)
|
|
151
152
|
|
|
153
|
+
def _run_configured_startup_warmup(self):
|
|
154
|
+
from iatoolkit.services.warmup_service import WarmupService
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
warmup_service = self._injector.get(WarmupService)
|
|
158
|
+
warmed_companies = warmup_service.warmup_startup_configured_companies(trigger="core_startup")
|
|
159
|
+
if warmed_companies:
|
|
160
|
+
logging.info("🔥 Startup warm-up completed for companies=%s", warmed_companies)
|
|
161
|
+
else:
|
|
162
|
+
logging.info("🔥 Startup warm-up skipped: no embedding provider enabled warmup_on_startup.")
|
|
163
|
+
except Exception:
|
|
164
|
+
logging.exception("⚠️ Startup warm-up failed.")
|
|
165
|
+
|
|
152
166
|
def _get_config_value(self, key: str, default=None):
|
|
153
167
|
# get a value from the config dict or the environment variable
|
|
154
168
|
return self.config.get(key, os.getenv(key, default))
|
|
@@ -7,6 +7,7 @@ import ast
|
|
|
7
7
|
import json
|
|
8
8
|
import logging
|
|
9
9
|
import re
|
|
10
|
+
from time import perf_counter
|
|
10
11
|
from typing import Dict, List
|
|
11
12
|
|
|
12
13
|
from sqlalchemy import text
|
|
@@ -86,6 +87,7 @@ class VSRepo:
|
|
|
86
87
|
n_results=5,
|
|
87
88
|
metadata_filter=None,
|
|
88
89
|
collection_ids: List[int] | None = None,
|
|
90
|
+
include_urls: bool = True,
|
|
89
91
|
) -> list[Dict]:
|
|
90
92
|
"""
|
|
91
93
|
search documents similar to the query for a company
|
|
@@ -95,40 +97,75 @@ class VSRepo:
|
|
|
95
97
|
query_text: query text
|
|
96
98
|
n_results: max number of results to return
|
|
97
99
|
metadata_filter: (e.g., {"document_type": "certificate"})
|
|
100
|
+
include_urls: Generate storage URLs for returned chunks when true.
|
|
98
101
|
|
|
99
102
|
Returns:
|
|
100
103
|
list of documents matching the query and filters
|
|
101
104
|
"""
|
|
105
|
+
query_started_at = perf_counter()
|
|
106
|
+
embedding_started_at = perf_counter()
|
|
102
107
|
# Generate the embedding with the query text for the specific company
|
|
103
108
|
try:
|
|
104
109
|
query_embedding = self.embedding_service.embed_text(company_short_name, query_text)
|
|
105
110
|
except Exception as e:
|
|
106
|
-
logging.error(
|
|
111
|
+
logging.error(
|
|
112
|
+
"RAG vector query embedding failed: company=%s duration_ms=%s error=%s",
|
|
113
|
+
company_short_name,
|
|
114
|
+
round((perf_counter() - embedding_started_at) * 1000),
|
|
115
|
+
str(e),
|
|
116
|
+
)
|
|
107
117
|
raise IAToolkitException(IAToolkitException.ErrorType.EMBEDDING_ERROR,
|
|
108
118
|
f"embedding error: {str(e)}")
|
|
119
|
+
embedding_duration_ms = round((perf_counter() - embedding_started_at) * 1000)
|
|
120
|
+
try:
|
|
121
|
+
embedding_dimensions = len(query_embedding)
|
|
122
|
+
except TypeError as exc:
|
|
123
|
+
raise IAToolkitException(
|
|
124
|
+
IAToolkitException.ErrorType.EMBEDDING_ERROR,
|
|
125
|
+
"embedding error: query embedding has no dimensions",
|
|
126
|
+
) from exc
|
|
127
|
+
if embedding_dimensions < 1 or embedding_dimensions > 2000:
|
|
128
|
+
raise IAToolkitException(
|
|
129
|
+
IAToolkitException.ErrorType.EMBEDDING_ERROR,
|
|
130
|
+
f"embedding error: unsupported query embedding dimensions ({embedding_dimensions})",
|
|
131
|
+
)
|
|
109
132
|
|
|
110
133
|
sql_query, params = None, None
|
|
111
134
|
try:
|
|
112
135
|
# Get company ID from its short name for the SQL query
|
|
136
|
+
company_lookup_started_at = perf_counter()
|
|
113
137
|
company = self.session.query(Company).filter(Company.short_name == company_short_name).one_or_none()
|
|
138
|
+
company_lookup_duration_ms = round((perf_counter() - company_lookup_started_at) * 1000)
|
|
114
139
|
if not company:
|
|
115
140
|
raise IAToolkitException(IAToolkitException.ErrorType.VECTOR_STORE_ERROR,
|
|
116
141
|
f"Company with short name '{company_short_name}' not found.")
|
|
117
142
|
|
|
143
|
+
distance_metric = "l2"
|
|
144
|
+
distance_expression = (
|
|
145
|
+
f"iat_vsdocs.embedding::vector({embedding_dimensions}) "
|
|
146
|
+
f"<-> CAST(:query_embedding AS vector({embedding_dimensions}))"
|
|
147
|
+
)
|
|
148
|
+
|
|
118
149
|
# build the SQL query
|
|
119
|
-
sql_query_parts = ["""
|
|
150
|
+
sql_query_parts = [f"""
|
|
120
151
|
SELECT iat_vsdocs.id, \
|
|
121
152
|
iat_documents.filename, \
|
|
122
153
|
iat_vsdocs.text, \
|
|
123
154
|
iat_documents.storage_key, \
|
|
124
155
|
iat_documents.meta,
|
|
125
156
|
iat_documents.id, \
|
|
126
|
-
iat_vsdocs.meta
|
|
157
|
+
iat_vsdocs.meta,
|
|
158
|
+
({distance_expression}) AS distance
|
|
127
159
|
FROM iat_vsdocs, \
|
|
128
160
|
iat_documents
|
|
129
161
|
WHERE iat_vsdocs.company_id = :company_id
|
|
130
162
|
AND iat_vsdocs.document_id = iat_documents.id \
|
|
131
163
|
"""]
|
|
164
|
+
# The literal dimension lets PostgreSQL prove that a matching partial
|
|
165
|
+
# pgvector expression index can satisfy the nearest-neighbor order.
|
|
166
|
+
sql_query_parts.append(
|
|
167
|
+
f" AND vector_dims(iat_vsdocs.embedding) = {embedding_dimensions}"
|
|
168
|
+
)
|
|
132
169
|
|
|
133
170
|
# query parameters
|
|
134
171
|
params = {
|
|
@@ -166,15 +203,18 @@ class VSRepo:
|
|
|
166
203
|
sql_query = "".join(sql_query_parts)
|
|
167
204
|
|
|
168
205
|
# add sorting and limit of results
|
|
169
|
-
sql_query += " ORDER BY
|
|
206
|
+
sql_query += " ORDER BY distance ASC LIMIT :n_results"
|
|
170
207
|
|
|
171
208
|
logging.debug(f"Executing SQL query: {sql_query}")
|
|
172
209
|
logging.debug(f"With parameters: {params}")
|
|
173
210
|
|
|
174
211
|
# execute the query
|
|
212
|
+
vector_sql_started_at = perf_counter()
|
|
213
|
+
self.session.execute(text("SET LOCAL hnsw.iterative_scan = strict_order"))
|
|
214
|
+
self.session.execute(text("SET LOCAL hnsw.ef_search = 100"))
|
|
175
215
|
result = self.session.execute(text(sql_query), params)
|
|
176
|
-
|
|
177
216
|
rows = result.fetchall()
|
|
217
|
+
vector_sql_duration_ms = round((perf_counter() - vector_sql_started_at) * 1000)
|
|
178
218
|
vs_documents = []
|
|
179
219
|
|
|
180
220
|
for row in rows:
|
|
@@ -182,10 +222,12 @@ class VSRepo:
|
|
|
182
222
|
doc_meta = row[4] if len(row) > 4 and row[4] is not None else {}
|
|
183
223
|
chunk_meta = row[6] if len(row) > 6 and row[6] is not None else {}
|
|
184
224
|
|
|
225
|
+
distance = float(row[7]) if len(row) > 7 and row[7] is not None else None
|
|
226
|
+
|
|
185
227
|
# get the url of the document
|
|
186
228
|
storage_key = row[3] if len(row) > 3 and row[3] is not None else None
|
|
187
229
|
url = None
|
|
188
|
-
if storage_key:
|
|
230
|
+
if include_urls and storage_key:
|
|
189
231
|
url = self.storage_service.generate_presigned_url(company_short_name, storage_key)
|
|
190
232
|
|
|
191
233
|
vs_documents.append(
|
|
@@ -196,10 +238,26 @@ class VSRepo:
|
|
|
196
238
|
'text': row[2],
|
|
197
239
|
'meta': doc_meta,
|
|
198
240
|
'chunk_meta': chunk_meta,
|
|
199
|
-
'url': url
|
|
241
|
+
'url': url,
|
|
242
|
+
'distance': distance,
|
|
243
|
+
'distance_metric': distance_metric,
|
|
244
|
+
'score': self._score_from_l2_distance(distance),
|
|
200
245
|
}
|
|
201
246
|
)
|
|
202
247
|
|
|
248
|
+
logging.info(
|
|
249
|
+
"RAG vector query completed: company=%s collection_ids=%s dimensions=%s n_results=%s rows=%s "
|
|
250
|
+
"embedding_ms=%s company_lookup_ms=%s vector_sql_ms=%s total_ms=%s",
|
|
251
|
+
company_short_name,
|
|
252
|
+
normalized_collection_ids,
|
|
253
|
+
embedding_dimensions,
|
|
254
|
+
n_results,
|
|
255
|
+
len(rows),
|
|
256
|
+
embedding_duration_ms,
|
|
257
|
+
company_lookup_duration_ms,
|
|
258
|
+
vector_sql_duration_ms,
|
|
259
|
+
round((perf_counter() - query_started_at) * 1000),
|
|
260
|
+
)
|
|
203
261
|
return vs_documents
|
|
204
262
|
|
|
205
263
|
except Exception as e:
|
|
@@ -386,6 +444,12 @@ class VSRepo:
|
|
|
386
444
|
|
|
387
445
|
return sql_parts, params
|
|
388
446
|
|
|
447
|
+
@staticmethod
|
|
448
|
+
def _score_from_l2_distance(distance: float | None) -> float | None:
|
|
449
|
+
if distance is None:
|
|
450
|
+
return None
|
|
451
|
+
return 1 / (1 + distance)
|
|
452
|
+
|
|
389
453
|
@staticmethod
|
|
390
454
|
def _normalize_metadata_filter_input(metadata_filter):
|
|
391
455
|
if metadata_filter is None:
|
|
@@ -717,6 +717,23 @@ class ConfigurationService:
|
|
|
717
717
|
add_error("embedding_provider", "Missing required key: 'provider'")
|
|
718
718
|
if not config.get("embedding_provider", {}).get("model"):
|
|
719
719
|
add_error("embedding_provider", "Missing required key: 'model'")
|
|
720
|
+
if (
|
|
721
|
+
"warmup_on_startup" in config.get("embedding_provider", {})
|
|
722
|
+
and not isinstance(config.get("embedding_provider", {}).get("warmup_on_startup"), bool)
|
|
723
|
+
):
|
|
724
|
+
add_error("embedding_provider.warmup_on_startup", "Must be a boolean.")
|
|
725
|
+
|
|
726
|
+
embedding_providers_cfg = config.get("embedding_providers")
|
|
727
|
+
if embedding_providers_cfg is not None:
|
|
728
|
+
if not isinstance(embedding_providers_cfg, dict):
|
|
729
|
+
add_error("embedding_providers", "Section must be a dictionary.")
|
|
730
|
+
else:
|
|
731
|
+
for provider_key, provider_cfg in embedding_providers_cfg.items():
|
|
732
|
+
if not isinstance(provider_cfg, dict):
|
|
733
|
+
add_error(f"embedding_providers.{provider_key}", "Provider configuration must be a dictionary.")
|
|
734
|
+
continue
|
|
735
|
+
if "warmup_on_startup" in provider_cfg and not isinstance(provider_cfg.get("warmup_on_startup"), bool):
|
|
736
|
+
add_error(f"embedding_providers.{provider_key}.warmup_on_startup", "Must be a boolean.")
|
|
720
737
|
|
|
721
738
|
# 3b. Visual Embedding Provider (Optional)
|
|
722
739
|
if config.get("visual_embedding_provider"):
|
|
@@ -310,16 +310,47 @@ class InferenceService:
|
|
|
310
310
|
attempt: int,
|
|
311
311
|
delay_seconds: float,
|
|
312
312
|
reason: str,
|
|
313
|
+
attempt_duration_seconds: float | None = None,
|
|
314
|
+
elapsed_seconds: float | None = None,
|
|
313
315
|
suppress_error_logging: bool = False
|
|
314
316
|
) -> None:
|
|
315
317
|
log_fn = logging.debug if suppress_error_logging else logging.warning
|
|
318
|
+
if attempt_duration_seconds is None or elapsed_seconds is None:
|
|
319
|
+
log_fn(
|
|
320
|
+
"Inference request retry %s scheduled in %.1fs: %s",
|
|
321
|
+
attempt,
|
|
322
|
+
delay_seconds,
|
|
323
|
+
reason,
|
|
324
|
+
)
|
|
325
|
+
return
|
|
326
|
+
|
|
316
327
|
log_fn(
|
|
317
|
-
"Inference request retry %s scheduled in %.1fs: %s",
|
|
328
|
+
"Inference request retry %s scheduled in %.1fs after %.1fs attempt (elapsed %.1fs): %s",
|
|
318
329
|
attempt,
|
|
319
330
|
delay_seconds,
|
|
331
|
+
attempt_duration_seconds,
|
|
332
|
+
elapsed_seconds,
|
|
320
333
|
reason,
|
|
321
334
|
)
|
|
322
335
|
|
|
336
|
+
def _timeout_for_attempt(
|
|
337
|
+
self,
|
|
338
|
+
*,
|
|
339
|
+
timeout: tuple[float, float],
|
|
340
|
+
deadline: float | None,
|
|
341
|
+
attempt: int,
|
|
342
|
+
) -> tuple[float, float] | None:
|
|
343
|
+
if deadline is None or attempt == 1:
|
|
344
|
+
return timeout
|
|
345
|
+
|
|
346
|
+
remaining_seconds = deadline - time.monotonic()
|
|
347
|
+
if remaining_seconds <= 0:
|
|
348
|
+
return None
|
|
349
|
+
|
|
350
|
+
connect_timeout, read_timeout = timeout
|
|
351
|
+
bounded_timeout = max(0.1, remaining_seconds)
|
|
352
|
+
return min(connect_timeout, bounded_timeout), min(read_timeout, bounded_timeout)
|
|
353
|
+
|
|
323
354
|
def _call_endpoint(
|
|
324
355
|
self,
|
|
325
356
|
url: str,
|
|
@@ -334,28 +365,44 @@ class InferenceService:
|
|
|
334
365
|
"Authorization": f"Bearer {api_key}",
|
|
335
366
|
"Content-Type": "application/json",
|
|
336
367
|
}
|
|
337
|
-
|
|
368
|
+
started_at = time.monotonic()
|
|
369
|
+
deadline = started_at + retry_budget_seconds if retry_budget_seconds > 0 else None
|
|
338
370
|
attempt = 1
|
|
339
371
|
delay_seconds = self.DEFAULT_RETRY_INITIAL_DELAY_SECONDS
|
|
340
372
|
|
|
341
373
|
while True:
|
|
374
|
+
attempt_timeout = self._timeout_for_attempt(timeout=timeout, deadline=deadline, attempt=attempt)
|
|
375
|
+
if attempt_timeout is None:
|
|
376
|
+
error_msg = "Inference retry budget exhausted before next attempt."
|
|
377
|
+
if not suppress_error_logging:
|
|
378
|
+
logging.error(error_msg)
|
|
379
|
+
raise ValueError(error_msg)
|
|
380
|
+
|
|
381
|
+
attempt_started_at = time.monotonic()
|
|
342
382
|
try:
|
|
343
383
|
resp, status = self.call_service.post(
|
|
344
384
|
url,
|
|
345
385
|
json_dict=payload,
|
|
346
386
|
headers=headers,
|
|
347
|
-
timeout=
|
|
387
|
+
timeout=attempt_timeout
|
|
348
388
|
)
|
|
349
389
|
except Exception as exc:
|
|
390
|
+
attempt_duration = time.monotonic() - attempt_started_at
|
|
350
391
|
next_delay = self._next_retry_delay(delay_seconds=delay_seconds, deadline=deadline)
|
|
351
392
|
if self._is_retryable_request_exception(exc) and next_delay is not None:
|
|
352
393
|
self._log_retry_attempt(
|
|
353
394
|
attempt=attempt,
|
|
354
395
|
delay_seconds=next_delay,
|
|
355
396
|
reason=str(exc),
|
|
397
|
+
attempt_duration_seconds=attempt_duration,
|
|
398
|
+
elapsed_seconds=time.monotonic() - started_at,
|
|
356
399
|
suppress_error_logging=suppress_error_logging,
|
|
357
400
|
)
|
|
358
401
|
time.sleep(next_delay)
|
|
402
|
+
if deadline is not None and time.monotonic() >= deadline:
|
|
403
|
+
if not suppress_error_logging:
|
|
404
|
+
logging.error(f"Failed to call inference endpoint: {exc}")
|
|
405
|
+
raise
|
|
359
406
|
attempt += 1
|
|
360
407
|
delay_seconds = min(delay_seconds * 2, self.DEFAULT_RETRY_MAX_DELAY_SECONDS)
|
|
361
408
|
continue
|
|
@@ -367,6 +414,7 @@ class InferenceService:
|
|
|
367
414
|
if status == 200:
|
|
368
415
|
return resp
|
|
369
416
|
|
|
417
|
+
attempt_duration = time.monotonic() - attempt_started_at
|
|
370
418
|
error_msg = f"Inference Endpoint Error {status}"
|
|
371
419
|
if isinstance(resp, dict) and 'error' in resp:
|
|
372
420
|
error_msg += f": {resp['error']}"
|
|
@@ -377,9 +425,16 @@ class InferenceService:
|
|
|
377
425
|
attempt=attempt,
|
|
378
426
|
delay_seconds=next_delay,
|
|
379
427
|
reason=error_msg,
|
|
428
|
+
attempt_duration_seconds=attempt_duration,
|
|
429
|
+
elapsed_seconds=time.monotonic() - started_at,
|
|
380
430
|
suppress_error_logging=suppress_error_logging,
|
|
381
431
|
)
|
|
382
432
|
time.sleep(next_delay)
|
|
433
|
+
if deadline is not None and time.monotonic() >= deadline:
|
|
434
|
+
if not suppress_error_logging:
|
|
435
|
+
logging.error(f"{error_msg} | Payload keys: {list(payload.keys())}")
|
|
436
|
+
logging.error(f"Failed to call inference endpoint: {error_msg}")
|
|
437
|
+
raise ValueError(error_msg)
|
|
383
438
|
attempt += 1
|
|
384
439
|
delay_seconds = min(delay_seconds * 2, self.DEFAULT_RETRY_MAX_DELAY_SECONDS)
|
|
385
440
|
continue
|
|
@@ -488,7 +488,8 @@ class KnowledgeBaseService:
|
|
|
488
488
|
query: str,
|
|
489
489
|
n_results: int = 5,
|
|
490
490
|
collection: str | list[str] | None = None,
|
|
491
|
-
metadata_filter: dict = None
|
|
491
|
+
metadata_filter: dict = None,
|
|
492
|
+
include_urls: bool = True,
|
|
492
493
|
):
|
|
493
494
|
"""
|
|
494
495
|
Performs a semantic search and returns the list of Document objects (chunks).
|
|
@@ -500,6 +501,7 @@ class KnowledgeBaseService:
|
|
|
500
501
|
n_results: Max number of chunks to retrieve.
|
|
501
502
|
metadata_filter: Optional filter for document metadata.
|
|
502
503
|
collection: Optional collection name or list of collection names.
|
|
504
|
+
include_urls: Generate storage URLs for returned chunks when true.
|
|
503
505
|
|
|
504
506
|
Returns:
|
|
505
507
|
List of Document objects found.
|
|
@@ -532,6 +534,7 @@ class KnowledgeBaseService:
|
|
|
532
534
|
n_results=n_results,
|
|
533
535
|
metadata_filter=metadata_filter,
|
|
534
536
|
collection_ids=collection_ids,
|
|
537
|
+
include_urls=include_urls,
|
|
535
538
|
)
|
|
536
539
|
|
|
537
540
|
return chunk_list
|
|
@@ -374,6 +374,9 @@ class ToolService:
|
|
|
374
374
|
"page": page,
|
|
375
375
|
"caption_text": caption,
|
|
376
376
|
"document_id": item.get("document_id"),
|
|
377
|
+
"distance": item.get("distance"),
|
|
378
|
+
"distance_metric": item.get("distance_metric"),
|
|
379
|
+
"score": item.get("score"),
|
|
377
380
|
}
|
|
378
381
|
lines.append(json.dumps(header, ensure_ascii=False))
|
|
379
382
|
text_content = (item.get("text") or "").strip()
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
+
# Product: IAToolkit
|
|
3
|
+
#
|
|
4
|
+
# IAToolkit is open source software.
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import time
|
|
10
|
+
from injector import inject
|
|
11
|
+
|
|
12
|
+
from iatoolkit.common.interfaces.secret_provider import SecretProvider
|
|
13
|
+
from iatoolkit.common.secret_resolver import resolve_secret
|
|
14
|
+
from iatoolkit.company_registry import get_registered_companies
|
|
15
|
+
from iatoolkit.services.configuration_service import ConfigurationService
|
|
16
|
+
from iatoolkit.services.embedding_service import EmbeddingService
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class WarmupService:
|
|
20
|
+
"""
|
|
21
|
+
Lightweight warm-up orchestrator.
|
|
22
|
+
Keep it simple: no shared state, no locking, no dedup.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
@inject
|
|
26
|
+
def __init__(self,
|
|
27
|
+
config_service: ConfigurationService,
|
|
28
|
+
embedding_service: EmbeddingService,
|
|
29
|
+
secret_provider: SecretProvider):
|
|
30
|
+
self.config_service = config_service
|
|
31
|
+
self.embedding_service = embedding_service
|
|
32
|
+
self.secret_provider = secret_provider
|
|
33
|
+
|
|
34
|
+
def warmup_company(self, company_short_name: str, trigger: str = "manual"):
|
|
35
|
+
start = time.perf_counter()
|
|
36
|
+
try:
|
|
37
|
+
self._warmup_remote_text_embeddings(company_short_name)
|
|
38
|
+
elapsed_ms = (time.perf_counter() - start) * 1000
|
|
39
|
+
logging.info(
|
|
40
|
+
"🔥 Warm-up done for company='%s' trigger='%s' in %.2f ms",
|
|
41
|
+
company_short_name,
|
|
42
|
+
trigger,
|
|
43
|
+
elapsed_ms
|
|
44
|
+
)
|
|
45
|
+
except Exception as e:
|
|
46
|
+
elapsed_ms = (time.perf_counter() - start) * 1000
|
|
47
|
+
logging.debug(
|
|
48
|
+
"⚠️ Warm-up failed for company='%s' trigger='%s' in %.2f ms: %s",
|
|
49
|
+
company_short_name,
|
|
50
|
+
trigger,
|
|
51
|
+
elapsed_ms,
|
|
52
|
+
e
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
def warmup_registered_companies(self, trigger: str = "manual"):
|
|
56
|
+
for company_short_name in get_registered_companies().keys():
|
|
57
|
+
self.warmup_company(company_short_name, trigger=trigger)
|
|
58
|
+
|
|
59
|
+
def warmup_startup_configured_companies(self, trigger: str = "startup") -> list[str]:
|
|
60
|
+
warmed_companies: list[str] = []
|
|
61
|
+
for company_short_name in get_registered_companies().keys():
|
|
62
|
+
profiles = self._get_remote_text_embedding_profiles(company_short_name, startup_only=True)
|
|
63
|
+
if not profiles:
|
|
64
|
+
logging.info(
|
|
65
|
+
"Startup warm-up skipped for company='%s': no embedding provider enabled warmup_on_startup.",
|
|
66
|
+
company_short_name,
|
|
67
|
+
)
|
|
68
|
+
continue
|
|
69
|
+
|
|
70
|
+
logging.info(
|
|
71
|
+
"Startup warm-up starting for company='%s' profiles=%s trigger='%s'.",
|
|
72
|
+
company_short_name,
|
|
73
|
+
self._format_profile_labels(profiles),
|
|
74
|
+
trigger,
|
|
75
|
+
)
|
|
76
|
+
warmed_profiles = self._warmup_remote_text_embeddings(
|
|
77
|
+
company_short_name,
|
|
78
|
+
startup_only=True,
|
|
79
|
+
profiles=profiles,
|
|
80
|
+
)
|
|
81
|
+
if not warmed_profiles:
|
|
82
|
+
logging.warning(
|
|
83
|
+
"Startup warm-up attempted but no embedding profiles warmed for company='%s' profiles=%s.",
|
|
84
|
+
company_short_name,
|
|
85
|
+
self._format_profile_labels(profiles),
|
|
86
|
+
)
|
|
87
|
+
continue
|
|
88
|
+
|
|
89
|
+
logging.info(
|
|
90
|
+
"Startup warm-up warmed company='%s' profiles=%s.",
|
|
91
|
+
company_short_name,
|
|
92
|
+
self._format_profile_labels(warmed_profiles),
|
|
93
|
+
)
|
|
94
|
+
warmed_companies.append(company_short_name)
|
|
95
|
+
|
|
96
|
+
return warmed_companies
|
|
97
|
+
|
|
98
|
+
def is_startup_warmup_enabled(self, company_short_name: str) -> bool:
|
|
99
|
+
return bool(self._get_remote_text_embedding_profiles(company_short_name, startup_only=True))
|
|
100
|
+
|
|
101
|
+
def _warmup_remote_text_embeddings(
|
|
102
|
+
self,
|
|
103
|
+
company_short_name: str,
|
|
104
|
+
startup_only: bool = False,
|
|
105
|
+
profiles: list[tuple[str, str, str]] | None = None,
|
|
106
|
+
) -> list[tuple[str, str, str]]:
|
|
107
|
+
if profiles is None:
|
|
108
|
+
profiles = self._get_remote_text_embedding_profiles(company_short_name, startup_only=startup_only)
|
|
109
|
+
if not profiles:
|
|
110
|
+
logging.debug(
|
|
111
|
+
"Warm-up skipped for company='%s': no remote embedding inference configured.",
|
|
112
|
+
company_short_name
|
|
113
|
+
)
|
|
114
|
+
return []
|
|
115
|
+
|
|
116
|
+
warmed_profiles = []
|
|
117
|
+
for model_type, config_section, tool_name in profiles:
|
|
118
|
+
try:
|
|
119
|
+
# Prime the remote model/container and download/cache model weights.
|
|
120
|
+
self.embedding_service.embed_text(
|
|
121
|
+
company_short_name,
|
|
122
|
+
"hello",
|
|
123
|
+
model_type=model_type,
|
|
124
|
+
suppress_error_logging=True,
|
|
125
|
+
)
|
|
126
|
+
logging.debug(
|
|
127
|
+
"Warm-up primed remote embedding profile company='%s' section='%s' model_type='%s' tool='%s'.",
|
|
128
|
+
company_short_name,
|
|
129
|
+
config_section,
|
|
130
|
+
model_type,
|
|
131
|
+
tool_name,
|
|
132
|
+
)
|
|
133
|
+
warmed_profiles.append((model_type, config_section, tool_name))
|
|
134
|
+
except Exception as e:
|
|
135
|
+
log_fn = logging.warning if startup_only else logging.debug
|
|
136
|
+
log_fn(
|
|
137
|
+
"Warm-up failed for remote embedding profile company='%s' section='%s' model_type='%s' tool='%s': %s",
|
|
138
|
+
company_short_name,
|
|
139
|
+
config_section,
|
|
140
|
+
model_type,
|
|
141
|
+
tool_name,
|
|
142
|
+
e,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
return warmed_profiles
|
|
146
|
+
|
|
147
|
+
@staticmethod
|
|
148
|
+
def _format_profile_labels(profiles: list[tuple[str, str, str]]) -> list[str]:
|
|
149
|
+
return [
|
|
150
|
+
f"{config_section}:{model_type}:{tool_name}"
|
|
151
|
+
for model_type, config_section, tool_name in profiles
|
|
152
|
+
]
|
|
153
|
+
|
|
154
|
+
def _uses_remote_text_inference(self, company_short_name: str) -> bool:
|
|
155
|
+
return bool(self._get_remote_text_embedding_profiles(company_short_name))
|
|
156
|
+
|
|
157
|
+
def _get_remote_text_embedding_profiles(
|
|
158
|
+
self,
|
|
159
|
+
company_short_name: str,
|
|
160
|
+
startup_only: bool = False,
|
|
161
|
+
) -> list[tuple[str, str, str]]:
|
|
162
|
+
profiles: list[tuple[str, str, str, dict]] = []
|
|
163
|
+
|
|
164
|
+
embedding_cfg = self.config_service.get_configuration(company_short_name, "embedding_provider") or {}
|
|
165
|
+
if isinstance(embedding_cfg, dict):
|
|
166
|
+
profiles.append(("text", "embedding_provider", embedding_cfg.get("tool_name") or "text_embeddings", embedding_cfg))
|
|
167
|
+
|
|
168
|
+
embedding_providers = self.config_service.get_configuration(company_short_name, "embedding_providers") or {}
|
|
169
|
+
if isinstance(embedding_providers, dict):
|
|
170
|
+
for model_type, embedding_provider_cfg in embedding_providers.items():
|
|
171
|
+
if not isinstance(embedding_provider_cfg, dict):
|
|
172
|
+
continue
|
|
173
|
+
normalized_model_type = str(model_type or "").strip()
|
|
174
|
+
if not normalized_model_type:
|
|
175
|
+
continue
|
|
176
|
+
profiles.append((
|
|
177
|
+
normalized_model_type,
|
|
178
|
+
f"embedding_providers.{normalized_model_type}",
|
|
179
|
+
embedding_provider_cfg.get("tool_name") or normalized_model_type,
|
|
180
|
+
embedding_provider_cfg,
|
|
181
|
+
))
|
|
182
|
+
|
|
183
|
+
inference_tools = self.config_service.get_configuration(company_short_name, "inference_tools") or {}
|
|
184
|
+
if not isinstance(inference_tools, dict):
|
|
185
|
+
return []
|
|
186
|
+
|
|
187
|
+
remote_profiles = [
|
|
188
|
+
(model_type, config_section, str(tool_name or "").strip())
|
|
189
|
+
for model_type, config_section, tool_name, embedding_cfg in profiles
|
|
190
|
+
if self._is_remote_embedding_profile(company_short_name, embedding_cfg, inference_tools, str(tool_name or "").strip())
|
|
191
|
+
and (not startup_only or bool(embedding_cfg.get("warmup_on_startup")))
|
|
192
|
+
]
|
|
193
|
+
|
|
194
|
+
# Leave the default text profile warm last. It is the profile used by rag_search.
|
|
195
|
+
return sorted(remote_profiles, key=lambda item: 1 if item[0] == "text" else 0)
|
|
196
|
+
|
|
197
|
+
def _is_remote_embedding_profile(
|
|
198
|
+
self,
|
|
199
|
+
company_short_name: str,
|
|
200
|
+
embedding_cfg: dict,
|
|
201
|
+
inference_tools: dict,
|
|
202
|
+
tool_name: str,
|
|
203
|
+
) -> bool:
|
|
204
|
+
provider = (embedding_cfg.get("provider") or "").strip().lower()
|
|
205
|
+
if provider != "huggingface":
|
|
206
|
+
return False
|
|
207
|
+
|
|
208
|
+
if not tool_name:
|
|
209
|
+
return False
|
|
210
|
+
|
|
211
|
+
defaults = inference_tools.get("_defaults") or {}
|
|
212
|
+
if not isinstance(defaults, dict):
|
|
213
|
+
defaults = {}
|
|
214
|
+
|
|
215
|
+
tool_cfg = inference_tools.get(tool_name) or {}
|
|
216
|
+
if not isinstance(tool_cfg, dict):
|
|
217
|
+
return False
|
|
218
|
+
|
|
219
|
+
resolved_cfg = {**defaults, **tool_cfg}
|
|
220
|
+
endpoint_url = (resolved_cfg.get("endpoint_url") or "").strip()
|
|
221
|
+
if not endpoint_url:
|
|
222
|
+
endpoint_url_secret_ref = (resolved_cfg.get("endpoint_url_secret_ref") or "").strip()
|
|
223
|
+
if endpoint_url_secret_ref:
|
|
224
|
+
endpoint_url = (
|
|
225
|
+
resolve_secret(self.secret_provider, company_short_name, endpoint_url_secret_ref, default="") or ""
|
|
226
|
+
).strip()
|
|
227
|
+
if not endpoint_url:
|
|
228
|
+
endpoint_url_env = (resolved_cfg.get("endpoint_url_env") or "").strip()
|
|
229
|
+
if endpoint_url_env:
|
|
230
|
+
endpoint_url = (
|
|
231
|
+
resolve_secret(self.secret_provider, company_short_name, endpoint_url_env, default="") or ""
|
|
232
|
+
).strip()
|
|
233
|
+
|
|
234
|
+
return bool(endpoint_url)
|
|
@@ -499,6 +499,9 @@ class RagApiView(MethodView):
|
|
|
499
499
|
"text": item.get("text"),
|
|
500
500
|
"chunk_meta": item.get("chunk_meta") or {},
|
|
501
501
|
"document_meta": item.get("meta") or {},
|
|
502
|
+
"distance": item.get("distance"),
|
|
503
|
+
"distance_metric": item.get("distance_metric"),
|
|
504
|
+
"score": item.get("score"),
|
|
502
505
|
})
|
|
503
506
|
return normalized
|
|
504
507
|
|
|
@@ -537,6 +540,9 @@ class RagApiView(MethodView):
|
|
|
537
540
|
"chunk_id": item.get("chunk_id"),
|
|
538
541
|
"source_type": (item.get("chunk_meta") or {}).get("source_type"),
|
|
539
542
|
"page": (item.get("chunk_meta") or {}).get("page") or (item.get("chunk_meta") or {}).get("page_start"),
|
|
543
|
+
"distance": item.get("distance"),
|
|
544
|
+
"distance_metric": item.get("distance_metric"),
|
|
545
|
+
"score": item.get("score"),
|
|
540
546
|
}
|
|
541
547
|
lines.append(json.dumps(header, ensure_ascii=False))
|
|
542
548
|
if item.get("text"):
|