iatoolkit 2.41.0__tar.gz → 2.41.2__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.41.0/src/iatoolkit.egg-info → iatoolkit-2.41.2}/PKG-INFO +1 -1
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/__init__.py +1 -1
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/common/model_registry.py +52 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/core.py +3 -5
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/llm_proxy.py +56 -10
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/locales/en.yaml +17 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/locales/es.yaml +17 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/repositories/models.py +14 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/tenant_wiki_service.py +109 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/warmup_service.py +94 -33
- {iatoolkit-2.41.0 → iatoolkit-2.41.2/src/iatoolkit.egg-info}/PKG-INFO +1 -1
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/LICENSE +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/LICENSE_COMMUNITY.md +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/pyproject.toml +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/readme.md +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/requirements.txt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/setup.cfg +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/base_company.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/cli_commands.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/common/__init__.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/common/bridge_validation.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/common/exceptions.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/common/interfaces/__init__.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/common/interfaces/asset_storage.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/common/interfaces/database_provider.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/common/interfaces/memory_compilation_trigger.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/common/interfaces/memory_lint_trigger.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/common/interfaces/secret_provider.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/common/interfaces/signup_policy_resolver.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/common/interfaces/web_search_provider.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/common/routes.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/common/secret_resolver.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/common/session_manager.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/common/util.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/company_registry.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/__init__.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/llm_capabilities.yaml +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/channel_business_identity.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/channel_conversation_continuity.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/channel_response_style.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/chat_state_rules.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/chat_user_profile.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/core_identity.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/email_output.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/file_download_output.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/format_styles.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/html_structures.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/links_documents.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/memory_usage.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/multimodal_basics.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/output_basics.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/presentation_formatting.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/query_main.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/sql_aggregation_scope.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/sql_casting.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/sql_core.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/sql_jsonb.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/sql_mysql_casting.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/sql_mysql_json.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/sql_redshift_basics.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/sql_redshift_types.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/sql_rules.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts/tool_html_passthrough.prompt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_prompts_pack.yaml +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/config/system_tools_pack.yaml +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/__init__.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/brevo_mail_app.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/call_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/connectors/__init__.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/connectors/file_connector.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/connectors/file_connector_factory.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/connectors/google_cloud_storage_connector.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/connectors/google_drive_connector.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/connectors/local_file_connector.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/connectors/s3_connector.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/google_auth_client.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/google_chat_app.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/inference_embeddings_client.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/inference_handler.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/jina_embeddings_client.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/llm_gateway_resolver.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/llm_providers/__init__.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/llm_providers/anthropic_adapter.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/llm_providers/deepseek_adapter.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/llm_providers/gemini_adapter.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/llm_providers/openai_adapter.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/llm_providers/openai_compatible_chat_adapter.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/llm_providers/openrouter_adapter.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/llm_response.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/infra/redis_session_manager.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/repositories/__init__.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/repositories/api_key_repo.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/repositories/bridge_repo.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/repositories/database_manager.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/repositories/document_repo.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/repositories/env_secret_provider.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/repositories/filesystem_asset_repository.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/repositories/knowledge_wiki_repo.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/repositories/llm_query_repo.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/repositories/mcp_token_repo.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/repositories/memory_repo.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/repositories/profile_repo.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/repositories/prompt_resource_repo.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/repositories/sql_dataset_repo.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/repositories/sql_source_repo.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/repositories/vs_repo.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/runtime_logging.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/__init__.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/api_key_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/attachment_policy_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/auth_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/benchmark_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/branding_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/company_context_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/configuration_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/context_builder_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/dispatcher_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/embedding_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/excel_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/file_processor_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/history_manager_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/http_tool_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/i18n_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/inference_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/jwt_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/knowledge_base_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/language_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/license_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/llm_client_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/mail_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/markdown_wiki_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/mcp_token_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/memory_compiler_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/memory_lint_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/memory_lookup_policy_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/memory_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/memory_wiki_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/noop_memory_compilation_trigger.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/noop_memory_lint_trigger.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/parsers/__init__.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/parsers/contracts.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/parsers/image_normalizer.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/parsers/parsing_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/parsers/pdf_ocr_detection.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/parsers/provider_factory.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/parsers/provider_resolver.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/parsers/providers/__init__.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/parsers/providers/basic_provider.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/parsers/providers/docling_provider.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/parsers/validator.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/pdf_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/profile_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/prompt_resource_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/prompt_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/query_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/signup_policy_resolver.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/sql_dataset_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/sql_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/sql_source_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/storage_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/structured_output_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/system_prompt_catalog.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/system_tools.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/telemetry_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/tool_output_contract.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/tool_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/user_feedback_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/user_session_context_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/visual_kb_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/visual_tool_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/web_search/__init__.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/web_search/provider_factory.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/web_search/providers/__init__.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/web_search/providers/brave_provider.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/services/web_search_service.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/static/images/fernando.jpeg +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/static/images/iatoolkit_core.png +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/static/images/iatoolkit_logo.png +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/static/js/chat_feedback_button.js +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/static/js/chat_filepond.js +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/static/js/chat_help_content.js +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/static/js/chat_history_button.js +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/static/js/chat_logout_button.js +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/static/js/chat_main.js +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/static/js/chat_memory_button.js +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/static/js/chat_model_selector.js +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/static/js/chat_onboarding_button.js +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/static/js/chat_prompt_manager.js +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/static/js/chat_reload_button.js +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/static/styles/chat_iatoolkit.css +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/static/styles/chat_modal.css +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/static/styles/chat_public.css +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/static/styles/documents.css +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/static/styles/landing_page.css +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/static/styles/llm_output.css +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/static/styles/onboarding.css +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/templates/_company_header.html +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/templates/_login_widget.html +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/templates/account.html +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/templates/base.html +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/templates/change_password.html +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/templates/chat.html +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/templates/chat_memory_modal.html +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/templates/chat_modals.html +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/templates/error.html +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/templates/forgot_password.html +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/templates/home_hosted_default.html +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/templates/memory/wiki_schema.md +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/templates/onboarding_shell.html +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/templates/pdf/base.html +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/templates/pdf/letter.html +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/templates/pdf/report.html +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/templates/pdf/simple.html +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/templates/signup.html +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/__init__.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/account_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/api_key_api_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/base_login_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/categories_api_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/change_password_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/chat_context_preview_api_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/chat_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/configuration_api_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/connectors_api_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/embedding_api_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/forgot_password_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/help_content_api_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/history_api_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/home_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/init_context_api_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/llmquery_api_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/load_document_api_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/login_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/logout_api_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/memory_api_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/profile_api_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/prompt_api_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/prompt_context_preview_api_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/prompt_resource_api_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/rag_api_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/root_redirect_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/signup_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/static_page_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/tool_api_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/user_feedback_api_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/users_api_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/views/verify_user_view.py +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit.egg-info/SOURCES.txt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit.egg-info/dependency_links.txt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit.egg-info/requires.txt +0 -0
- {iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit.egg-info/top_level.txt +0 -0
|
@@ -46,6 +46,58 @@ SERVABLE_PROVIDERS: tuple[str, ...] = (
|
|
|
46
46
|
)
|
|
47
47
|
|
|
48
48
|
|
|
49
|
+
@dataclass(frozen=True)
|
|
50
|
+
class RouteParam:
|
|
51
|
+
"""One thing a provider needs to know before it can be reached.
|
|
52
|
+
|
|
53
|
+
`is_env_name` marks a value that is the *name* of a deployment environment
|
|
54
|
+
variable, not the secret itself: what gets stored is `OSS_LLM_API_KEY`, and the
|
|
55
|
+
value behind it is read at request time and never persisted here.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
name: str
|
|
59
|
+
required: bool = True
|
|
60
|
+
is_env_name: bool = False
|
|
61
|
+
#: A regular expression the value must match, or None for "any non-empty".
|
|
62
|
+
pattern: str | None = None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
#: What each provider needs in order to be reached, keyed by provider.
|
|
66
|
+
#:
|
|
67
|
+
#: The single declaration behind three things: what a catalogue entry may store,
|
|
68
|
+
#: what refuses to publish without it, and which fields a form draws. Kept beside
|
|
69
|
+
#: SERVABLE_PROVIDERS because the two answer halves of the same question — which
|
|
70
|
+
#: providers exist, and what each one needs.
|
|
71
|
+
#:
|
|
72
|
+
#: Most providers need nothing: their SDK knows its own endpoint, and the
|
|
73
|
+
#: credential comes from the company's provider_api_keys mapping. The two that
|
|
74
|
+
#: appear here are the ones whose endpoint is a decision.
|
|
75
|
+
#:
|
|
76
|
+
#: `openai_compatible` requires both, and that is the point of the whole feature:
|
|
77
|
+
#: an entry that carries its own endpoint and credential is reachable on its own,
|
|
78
|
+
#: instead of depending on a block in every company's own configuration file. Two
|
|
79
|
+
#: self-hosted models on two different endpoints stop being impossible.
|
|
80
|
+
#:
|
|
81
|
+
#: `openrouter` allows both and requires neither: there is one OpenRouter for
|
|
82
|
+
#: everyone and its base URL has a working default, so an entry only overrides
|
|
83
|
+
#: when it has a reason to.
|
|
84
|
+
PROVIDER_ROUTE_PARAMS: dict[str, tuple[RouteParam, ...]] = {
|
|
85
|
+
"openai_compatible": (
|
|
86
|
+
RouteParam("base_url", required=True, pattern=r"^https?://\S+$"),
|
|
87
|
+
RouteParam("api_key_env", required=True, is_env_name=True, pattern=r"^[A-Z][A-Z0-9_]*$"),
|
|
88
|
+
),
|
|
89
|
+
"openrouter": (
|
|
90
|
+
RouteParam("base_url", required=False, pattern=r"^https?://\S+$"),
|
|
91
|
+
RouteParam("api_key_env", required=False, is_env_name=True, pattern=r"^[A-Z][A-Z0-9_]*$"),
|
|
92
|
+
),
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def route_params_for(provider: str | None) -> tuple[RouteParam, ...]:
|
|
97
|
+
"""What this provider needs. Empty for the ones that need nothing."""
|
|
98
|
+
return PROVIDER_ROUTE_PARAMS.get(str(provider or "").strip().lower(), ())
|
|
99
|
+
|
|
100
|
+
|
|
49
101
|
@singleton
|
|
50
102
|
class ModelRegistry:
|
|
51
103
|
"""
|
|
@@ -170,11 +170,9 @@ class IAToolkit:
|
|
|
170
170
|
def _run_warmup():
|
|
171
171
|
try:
|
|
172
172
|
warmup_service = self._injector.get(WarmupService)
|
|
173
|
-
|
|
174
|
-
if
|
|
175
|
-
logging.info("🔥 Startup warm-up completed for
|
|
176
|
-
else:
|
|
177
|
-
logging.info("🔥 Startup warm-up skipped: no embedding provider enabled warmup_on_startup.")
|
|
173
|
+
woken_endpoints = warmup_service.warmup_startup_configured_companies(trigger="core_startup")
|
|
174
|
+
if woken_endpoints:
|
|
175
|
+
logging.info("🔥 Startup warm-up completed for endpoints=%s", woken_endpoints)
|
|
178
176
|
except Exception:
|
|
179
177
|
logging.exception("⚠️ Startup warm-up failed.")
|
|
180
178
|
|
|
@@ -23,6 +23,7 @@ from iatoolkit.services.telemetry_service import NoopTelemetryService
|
|
|
23
23
|
from openai import OpenAI, Timeout # For OpenAI and xAI (OpenAI-compatible)
|
|
24
24
|
|
|
25
25
|
from typing import Dict, List, Any, Tuple
|
|
26
|
+
import os
|
|
26
27
|
import threading
|
|
27
28
|
import logging
|
|
28
29
|
from injector import inject
|
|
@@ -586,11 +587,13 @@ class LLMProxy:
|
|
|
586
587
|
) -> dict:
|
|
587
588
|
provider_config = self.configuration_service.get_llm_provider_config(company_short_name, provider) or {}
|
|
588
589
|
secret_ref = self._model_credential_ref(model_config)
|
|
590
|
+
route_api_key_env = str(self._model_route(model_config).get("api_key_env") or "").strip() or None
|
|
589
591
|
provider_api_key = self._get_api_key_from_config(
|
|
590
592
|
company_short_name,
|
|
591
593
|
provider,
|
|
592
594
|
required=False,
|
|
593
595
|
secret_ref=secret_ref,
|
|
596
|
+
route_api_key_env=route_api_key_env,
|
|
594
597
|
)
|
|
595
598
|
gateway_transport = self.gateway_resolver.resolve(
|
|
596
599
|
company_short_name=company_short_name,
|
|
@@ -612,8 +615,9 @@ class LLMProxy:
|
|
|
612
615
|
provider,
|
|
613
616
|
required=True,
|
|
614
617
|
secret_ref=secret_ref,
|
|
618
|
+
route_api_key_env=route_api_key_env,
|
|
615
619
|
)
|
|
616
|
-
configured_base_url = self._get_base_url_from_config(company_short_name, provider)
|
|
620
|
+
configured_base_url = self._get_base_url_from_config(company_short_name, provider, model_config=model_config)
|
|
617
621
|
configured_headers = self._get_default_headers_from_config(company_short_name, provider)
|
|
618
622
|
effective_base_url = gateway_transport.get("base_url") or configured_base_url
|
|
619
623
|
effective_headers = dict(configured_headers)
|
|
@@ -726,6 +730,20 @@ class LLMProxy:
|
|
|
726
730
|
# -------------------------------------------------------------------------
|
|
727
731
|
# Configuration helpers
|
|
728
732
|
# -------------------------------------------------------------------------
|
|
733
|
+
@staticmethod
|
|
734
|
+
def _model_route(model_config: Dict[str, Any] | None) -> Dict[str, Any]:
|
|
735
|
+
"""The endpoint and credential variable this model carries, if any.
|
|
736
|
+
|
|
737
|
+
Comes from the catalogue entry, so two models of the same provider can sit
|
|
738
|
+
on two different endpoints — which is what `openai_compatible` could not do
|
|
739
|
+
while the endpoint lived in each company's own configuration file.
|
|
740
|
+
|
|
741
|
+
Empty means "ask the company's configuration", which is what every model
|
|
742
|
+
did before entries had routes.
|
|
743
|
+
"""
|
|
744
|
+
route = (model_config or {}).get("route_config") if isinstance(model_config, dict) else None
|
|
745
|
+
return dict(route) if isinstance(route, dict) else {}
|
|
746
|
+
|
|
729
747
|
@staticmethod
|
|
730
748
|
def _model_credential_ref(model_config: Dict[str, Any] | None) -> str | None:
|
|
731
749
|
"""The secret this model is entitled to run on, when the company owns it.
|
|
@@ -753,6 +771,7 @@ class LLMProxy:
|
|
|
753
771
|
*,
|
|
754
772
|
required: bool = True,
|
|
755
773
|
secret_ref: str | None = None,
|
|
774
|
+
route_api_key_env: str | None = None,
|
|
756
775
|
) -> str:
|
|
757
776
|
"""
|
|
758
777
|
Read the LLM API key from company configuration and environment variables.
|
|
@@ -774,20 +793,36 @@ class LLMProxy:
|
|
|
774
793
|
provider_keys = llm_config.get("provider_api_keys") or {}
|
|
775
794
|
env_var_name = None
|
|
776
795
|
|
|
777
|
-
#
|
|
778
|
-
#
|
|
779
|
-
#
|
|
780
|
-
#
|
|
781
|
-
#
|
|
782
|
-
#
|
|
796
|
+
# Tres orígenes, en este orden, y el orden es la decisión:
|
|
797
|
+
#
|
|
798
|
+
# 1) El secreto de la habilitación del modelo, cuando la empresa trae su
|
|
799
|
+
# propia key. Es la decisión explícita y por modelo de quién paga, así
|
|
800
|
+
# que gana sobre todo lo demás.
|
|
801
|
+
# 2) La credencial de la ruta del catálogo, leída del entorno del
|
|
802
|
+
# deployment y **no** del store de secretos de la empresa: el endpoint es
|
|
803
|
+
# del operador, y un cliente que guarde un secreto con ese nombre no
|
|
804
|
+
# debería apropiárselo en silencio. Quien quiera pagar con su key usa (1).
|
|
805
|
+
# 3) El mapeo del archivo de la empresa, que es el respaldo para todo lo que
|
|
806
|
+
# no tiene ruta propia — es decir, todo lo que existía antes de esto.
|
|
783
807
|
if secret_ref:
|
|
784
808
|
env_var_name = secret_ref
|
|
785
809
|
|
|
786
|
-
|
|
810
|
+
if not env_var_name and route_api_key_env:
|
|
811
|
+
route_value = str(os.getenv(route_api_key_env) or "").strip()
|
|
812
|
+
if route_value:
|
|
813
|
+
return route_value
|
|
814
|
+
if not required:
|
|
815
|
+
return ""
|
|
816
|
+
raise IAToolkitException(
|
|
817
|
+
IAToolkitException.ErrorType.API_KEY,
|
|
818
|
+
f"Environment variable '{route_api_key_env}', named by the catalogue entry "
|
|
819
|
+
f"for provider '{provider}', is not set in this deployment."
|
|
820
|
+
)
|
|
821
|
+
|
|
787
822
|
if not env_var_name and provider_keys and isinstance(provider_keys, dict):
|
|
788
823
|
env_var_name = provider_keys.get(provider)
|
|
789
824
|
|
|
790
|
-
#
|
|
825
|
+
# Compatibilidad hacia atrás: api-key global cuando no hay una por proveedor.
|
|
791
826
|
if not env_var_name and llm_config.get("api-key"):
|
|
792
827
|
env_var_name = llm_config["api-key"]
|
|
793
828
|
|
|
@@ -818,10 +853,21 @@ class LLMProxy:
|
|
|
818
853
|
|
|
819
854
|
return api_key_value
|
|
820
855
|
|
|
821
|
-
def _get_base_url_from_config(
|
|
856
|
+
def _get_base_url_from_config(
|
|
857
|
+
self,
|
|
858
|
+
company_short_name: str,
|
|
859
|
+
provider: str,
|
|
860
|
+
model_config: Dict[str, Any] | None = None,
|
|
861
|
+
) -> str:
|
|
822
862
|
if provider not in {self.PROVIDER_OPENAI_COMPATIBLE, self.PROVIDER_OPENROUTER}:
|
|
823
863
|
return ""
|
|
824
864
|
|
|
865
|
+
# The entry's own endpoint wins: it is the operator's decision about this
|
|
866
|
+
# model, while the company's configuration is a default for the provider.
|
|
867
|
+
route_base_url = str(self._model_route(model_config).get("base_url") or "").strip()
|
|
868
|
+
if route_base_url:
|
|
869
|
+
return route_base_url
|
|
870
|
+
|
|
825
871
|
provider_config = self.configuration_service.get_llm_provider_config(company_short_name, provider) or {}
|
|
826
872
|
if not isinstance(provider_config, dict):
|
|
827
873
|
provider_config = {}
|
|
@@ -703,6 +703,18 @@ ui:
|
|
|
703
703
|
mode_external_sync: "External"
|
|
704
704
|
mode_managed_help: "The wiki is edited from this workspace and pages are saved into storage."
|
|
705
705
|
mode_external_sync_help: "The wiki stays read-only in IAToolkit and updates from the configured storage root."
|
|
706
|
+
access_control: "Access control"
|
|
707
|
+
access_mode_off: "Off"
|
|
708
|
+
access_mode_off_help: "Access labels are stored but no page is filtered."
|
|
709
|
+
access_mode_dry_run: "Dry run"
|
|
710
|
+
access_mode_dry_run_help: "What would be denied is computed and logged, but every page is still served."
|
|
711
|
+
access_mode_enforce: "Enforced"
|
|
712
|
+
access_mode_enforce_help: "A reader only gets pages whose labels they hold. The admin panel still sees everything."
|
|
713
|
+
access_coverage: "{labelled} of {pages} pages carry access labels."
|
|
714
|
+
access_coverage_gap: "{unlabelled} with no label: nobody sees those while access control is enforced."
|
|
715
|
+
access_mode_ok: "Access control mode updated."
|
|
716
|
+
access_mode_error: "Could not change the access control mode."
|
|
717
|
+
access_mode_confirm: "Once enforced, each user only sees pages whose labels they hold. Continue?"
|
|
706
718
|
root: "Storage root"
|
|
707
719
|
edit_root: "Edit storage root"
|
|
708
720
|
lock_root: "Lock storage root editing"
|
|
@@ -2189,6 +2201,11 @@ ui:
|
|
|
2189
2201
|
form_secret_value: "Your provider key"
|
|
2190
2202
|
secret_hint_new: "Stored encrypted for this provider. Your provider bills you directly."
|
|
2191
2203
|
secret_hint_stored: "A key is already stored for this provider. Paste one to replace it."
|
|
2204
|
+
form_route_base_url: "Endpoint"
|
|
2205
|
+
form_route_base_url_hint: "The full URL requests go to, e.g. https://your-host/v1"
|
|
2206
|
+
form_route_api_key_env: "Credential variable"
|
|
2207
|
+
form_route_api_key_env_hint: "The NAME of an environment variable in this deployment, e.g. OSS_LLM_API_KEY. Never the key itself."
|
|
2208
|
+
route_required: "This provider needs an endpoint and the name of its credential variable."
|
|
2192
2209
|
secrets:
|
|
2193
2210
|
title: "Secrets"
|
|
2194
2211
|
subtitle: "Manage company secrets used by integrations and services."
|
|
@@ -705,6 +705,18 @@ ui:
|
|
|
705
705
|
mode_external_sync: "Externa"
|
|
706
706
|
mode_managed_help: "La wiki se edita desde esta interfaz y guarda sus páginas en storage."
|
|
707
707
|
mode_external_sync_help: "La wiki queda de solo lectura en IAToolkit y se actualiza desde la raíz storage configurada."
|
|
708
|
+
access_control: "Control de acceso"
|
|
709
|
+
access_mode_off: "Desactivado"
|
|
710
|
+
access_mode_off_help: "Las etiquetas de acceso se guardan pero no se filtra ninguna página."
|
|
711
|
+
access_mode_dry_run: "Simulación"
|
|
712
|
+
access_mode_dry_run_help: "Se calcula y registra en el log qué páginas se negarían, pero se siguen entregando todas."
|
|
713
|
+
access_mode_enforce: "Activo"
|
|
714
|
+
access_mode_enforce_help: "Cada lector recibe solo las páginas cuyas etiquetas posee. El panel de administración sigue viendo todo."
|
|
715
|
+
access_coverage: "{labelled} de {pages} páginas con etiquetas de acceso."
|
|
716
|
+
access_coverage_gap: "{unlabelled} sin etiqueta: con el control activo no las ve nadie."
|
|
717
|
+
access_mode_ok: "Modo de control de acceso actualizado."
|
|
718
|
+
access_mode_error: "No se pudo cambiar el modo de control de acceso."
|
|
719
|
+
access_mode_confirm: "Al activarlo, cada usuario verá solo las páginas cuyas etiquetas posee. ¿Continuar?"
|
|
708
720
|
root: "Raíz storage"
|
|
709
721
|
edit_root: "Editar raíz storage"
|
|
710
722
|
lock_root: "Bloquear edición de raíz storage"
|
|
@@ -2195,6 +2207,11 @@ ui:
|
|
|
2195
2207
|
form_secret_value: "Tu key del proveedor"
|
|
2196
2208
|
secret_hint_new: "Se guarda cifrada para este proveedor. Tu proveedor te factura directamente."
|
|
2197
2209
|
secret_hint_stored: "Ya hay una key guardada para este proveedor. Pegá otra para reemplazarla."
|
|
2210
|
+
form_route_base_url: "Endpoint"
|
|
2211
|
+
form_route_base_url_hint: "La URL completa a la que van las requests, ej. https://tu-host/v1"
|
|
2212
|
+
form_route_api_key_env: "Variable de la credencial"
|
|
2213
|
+
form_route_api_key_env_hint: "El NOMBRE de una variable de entorno de este deployment, ej. OSS_LLM_API_KEY. Nunca la key."
|
|
2214
|
+
route_required: "Este proveedor necesita un endpoint y el nombre de la variable con su credencial."
|
|
2198
2215
|
secrets:
|
|
2199
2216
|
title: "Secrets"
|
|
2200
2217
|
subtitle: "Administra los secrets de la empresa usados por integraciones y servicios."
|
|
@@ -1241,6 +1241,20 @@ class ModelCatalogEntry(Base):
|
|
|
1241
1241
|
model_key = Column(String(120), nullable=False, unique=True, index=True)
|
|
1242
1242
|
provider = Column(String(60), nullable=False, index=True)
|
|
1243
1243
|
|
|
1244
|
+
#: How to reach this model, when reaching it is a decision: the endpoint and
|
|
1245
|
+
#: the name of the environment variable holding its credential.
|
|
1246
|
+
#:
|
|
1247
|
+
#: A JSON object rather than columns because providers do not agree on what
|
|
1248
|
+
#: they need — an Azure deployment wants a deployment name and an api version,
|
|
1249
|
+
#: a gateway may want headers — and `base_url` is meaningless for the
|
|
1250
|
+
#: providers whose SDK knows its own. What may go in here is declared per
|
|
1251
|
+
#: provider in `PROVIDER_ROUTE_PARAMS` and validated on write: an unvalidated
|
|
1252
|
+
#: blob becomes decoration, and a typo in a key is silent.
|
|
1253
|
+
#:
|
|
1254
|
+
#: Empty means "ask the company's own configuration", which is what every
|
|
1255
|
+
#: entry did before this existed.
|
|
1256
|
+
route_config = Column(JSON, nullable=False, default=dict)
|
|
1257
|
+
|
|
1244
1258
|
display_name = Column(String(120), nullable=False)
|
|
1245
1259
|
description = Column(Text, nullable=False, default="")
|
|
1246
1260
|
|
|
@@ -445,6 +445,25 @@ class TenantWikiService:
|
|
|
445
445
|
"mcp_index_source_path": self._index_source_display_path(wiki, source_storage_key),
|
|
446
446
|
"index_path": "/",
|
|
447
447
|
"index_source_path": self._index_source_display_path(wiki, source_storage_key),
|
|
448
|
+
"access_control": self._access_summary(wiki),
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
def _access_summary(self, wiki: KnowledgeWiki) -> dict:
|
|
452
|
+
"""
|
|
453
|
+
The wiki's access control state, for the admin panel: the mode in force
|
|
454
|
+
and how many pages carry labels. A page with none is denied to everyone
|
|
455
|
+
once the mode is enforce, so that count is the number an operator has to
|
|
456
|
+
look at before raising it.
|
|
457
|
+
"""
|
|
458
|
+
access_control = self.wiki_access_control(wiki)
|
|
459
|
+
pages = self.knowledge_wiki_repo.list_pages(wiki.id, include_archived=False, limit=self.MAX_PAGES)
|
|
460
|
+
labelled = sum(1 for page in pages if (page.access_tags or []))
|
|
461
|
+
return {
|
|
462
|
+
**access_control,
|
|
463
|
+
"modes": list(self.ACCESS_MODES),
|
|
464
|
+
"pages": len(pages),
|
|
465
|
+
"labelled": labelled,
|
|
466
|
+
"unlabelled": len(pages) - labelled,
|
|
448
467
|
}
|
|
449
468
|
|
|
450
469
|
def get_page(
|
|
@@ -1004,6 +1023,96 @@ class TenantWikiService:
|
|
|
1004
1023
|
)
|
|
1005
1024
|
return None
|
|
1006
1025
|
|
|
1026
|
+
def set_access_control_mode(
|
|
1027
|
+
self,
|
|
1028
|
+
company_short_name: str,
|
|
1029
|
+
*,
|
|
1030
|
+
wiki_key: str,
|
|
1031
|
+
mode: str,
|
|
1032
|
+
allow_dark: bool = False,
|
|
1033
|
+
) -> dict:
|
|
1034
|
+
"""
|
|
1035
|
+
Raises or lowers a wiki's access control mode, and nothing else.
|
|
1036
|
+
|
|
1037
|
+
Deliberately not routed through `configure_wiki`: that one rebuilds the
|
|
1038
|
+
wiki from its arguments, so calling it with only settings would rename the
|
|
1039
|
+
wiki and repoint its storage prefix to the default. This touches
|
|
1040
|
+
`settings.access_control` and leaves every other field alone.
|
|
1041
|
+
|
|
1042
|
+
Enforcing a wiki whose pages carry no labels at all would serve nothing
|
|
1043
|
+
to anyone, which is never the intent, so that case is refused unless
|
|
1044
|
+
`allow_dark` says otherwise.
|
|
1045
|
+
"""
|
|
1046
|
+
company = self._get_company(company_short_name)
|
|
1047
|
+
if not company:
|
|
1048
|
+
return {"status": "error", "error_message": "company not found"}
|
|
1049
|
+
wiki = self.knowledge_wiki_repo.get_wiki_by_key(company.id, self.normalize_wiki_key(wiki_key))
|
|
1050
|
+
if not wiki:
|
|
1051
|
+
return {"status": "error", "error_message": "wiki not found"}
|
|
1052
|
+
|
|
1053
|
+
requested_mode = str(mode or "").strip().lower()
|
|
1054
|
+
if requested_mode not in self.ACCESS_MODES:
|
|
1055
|
+
# Not normalized silently: an unrecognized mode here would read as
|
|
1056
|
+
# 'off' and quietly turn access control off.
|
|
1057
|
+
return {
|
|
1058
|
+
"status": "error",
|
|
1059
|
+
"error_message": f"mode must be one of {', '.join(self.ACCESS_MODES)}",
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
pages = self.knowledge_wiki_repo.list_pages(wiki.id, include_archived=False, limit=self.MAX_PAGES)
|
|
1063
|
+
labelled = sum(1 for page in pages if (page.access_tags or []))
|
|
1064
|
+
previous = self.wiki_access_control(wiki)
|
|
1065
|
+
if requested_mode == self.ACCESS_MODE_ENFORCE and not labelled and not allow_dark:
|
|
1066
|
+
return {
|
|
1067
|
+
"status": "error",
|
|
1068
|
+
"error_message": (
|
|
1069
|
+
f"wiki '{wiki.wiki_key}' has no page carrying access labels, so enforcing would "
|
|
1070
|
+
"hide all of it from everyone. Sync the access manifest first."
|
|
1071
|
+
),
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
settings = dict(wiki.settings if isinstance(wiki.settings, dict) else {})
|
|
1075
|
+
# Reassigned rather than mutated in place: an in-place change to a JSON
|
|
1076
|
+
# column is not tracked and would never be written.
|
|
1077
|
+
settings["access_control"] = {
|
|
1078
|
+
"mode": requested_mode,
|
|
1079
|
+
"manifest": previous["manifest"],
|
|
1080
|
+
}
|
|
1081
|
+
wiki.settings = settings
|
|
1082
|
+
self.knowledge_wiki_repo.commit()
|
|
1083
|
+
|
|
1084
|
+
return {
|
|
1085
|
+
"status": "success",
|
|
1086
|
+
"wiki_key": wiki.wiki_key,
|
|
1087
|
+
"previous_mode": previous["mode"],
|
|
1088
|
+
"mode": requested_mode,
|
|
1089
|
+
"manifest": previous["manifest"],
|
|
1090
|
+
"manifest_status": self._probe_access_manifest(company_short_name, wiki, requested_mode),
|
|
1091
|
+
"pages": len(pages),
|
|
1092
|
+
"labelled": labelled,
|
|
1093
|
+
"unlabelled": len(pages) - labelled,
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
def _probe_access_manifest(self, company_short_name: str, wiki: KnowledgeWiki, mode: str) -> str:
|
|
1097
|
+
"""
|
|
1098
|
+
Reports whether the manifest could be read right now, without failing.
|
|
1099
|
+
|
|
1100
|
+
Answers the question that matters after raising the mode: would the next
|
|
1101
|
+
sync abort? From dry_run up the manifest is required.
|
|
1102
|
+
"""
|
|
1103
|
+
try:
|
|
1104
|
+
_pages, meta = self._load_access_manifest(
|
|
1105
|
+
company_short_name,
|
|
1106
|
+
wiki,
|
|
1107
|
+
{"mode": self.ACCESS_MODE_OFF, "manifest": self.wiki_access_control(wiki)["manifest"]},
|
|
1108
|
+
)
|
|
1109
|
+
except Exception as exc:
|
|
1110
|
+
return f"error: {exc}"
|
|
1111
|
+
status = str(meta.get("manifest_status") or "unknown")
|
|
1112
|
+
if status != "loaded" and mode != self.ACCESS_MODE_OFF:
|
|
1113
|
+
return f"{status} (the next sync will abort while the mode is '{mode}')"
|
|
1114
|
+
return status
|
|
1115
|
+
|
|
1007
1116
|
def access_visibility_report(
|
|
1008
1117
|
self,
|
|
1009
1118
|
company_short_name: str,
|
|
@@ -7,6 +7,8 @@ from __future__ import annotations
|
|
|
7
7
|
|
|
8
8
|
import logging
|
|
9
9
|
import time
|
|
10
|
+
from urllib.parse import urlparse
|
|
11
|
+
|
|
10
12
|
from injector import inject
|
|
11
13
|
|
|
12
14
|
from iatoolkit.common.interfaces.secret_provider import SecretProvider
|
|
@@ -19,7 +21,12 @@ from iatoolkit.services.embedding_service import EmbeddingService
|
|
|
19
21
|
class WarmupService:
|
|
20
22
|
"""
|
|
21
23
|
Lightweight warm-up orchestrator.
|
|
22
|
-
Keep it simple: no shared state, no locking
|
|
24
|
+
Keep it simple: no shared state, no locking.
|
|
25
|
+
|
|
26
|
+
The one exception is the startup path, which deduplicates by resolved
|
|
27
|
+
endpoint URL. That is not coordination between processes — each process
|
|
28
|
+
still knocks on its own — it is not knocking N times for N tenants that
|
|
29
|
+
share one endpoint.
|
|
23
30
|
"""
|
|
24
31
|
|
|
25
32
|
@inject
|
|
@@ -57,41 +64,80 @@ class WarmupService:
|
|
|
57
64
|
self.warmup_company(company_short_name, trigger=trigger)
|
|
58
65
|
|
|
59
66
|
def warmup_startup_configured_companies(self, trigger: str = "startup") -> list[str]:
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
"""Wakes each distinct inference endpoint once. Returns the hosts woken.
|
|
68
|
+
|
|
69
|
+
Once per endpoint, not once per tenant: waking is a property of the
|
|
70
|
+
endpoint, and tenants normally share one. Doing it per tenant only
|
|
71
|
+
multiplied requests against a container that was already starting, each
|
|
72
|
+
one willing to spend the tool's whole retry budget getting there.
|
|
73
|
+
|
|
74
|
+
Nothing is awaited beyond the call. Surviving a cold start is the
|
|
75
|
+
inference client's job, through ``retry_budget_seconds``; this only has
|
|
76
|
+
to knock on the door.
|
|
77
|
+
"""
|
|
78
|
+
targets = self._startup_endpoints()
|
|
79
|
+
if not targets:
|
|
80
|
+
logging.info("Startup warm-up skipped: no embedding provider enabled warmup_on_startup.")
|
|
81
|
+
return []
|
|
82
|
+
|
|
83
|
+
woken: list[str] = []
|
|
84
|
+
for endpoint_url, (company_short_name, profile) in targets.items():
|
|
85
|
+
model_type, _config_section, _tool_name = profile
|
|
86
|
+
label = self._format_profile_labels([profile])[0]
|
|
87
|
+
host = self._endpoint_host(endpoint_url)
|
|
88
|
+
try:
|
|
89
|
+
self.embedding_service.embed_text(
|
|
66
90
|
company_short_name,
|
|
91
|
+
"hello",
|
|
92
|
+
model_type=model_type,
|
|
93
|
+
suppress_error_logging=True,
|
|
67
94
|
)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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:
|
|
95
|
+
logging.info(
|
|
96
|
+
"🔥 Startup warm-up woke endpoint='%s' via company='%s' profile=%s trigger='%s'.",
|
|
97
|
+
host, company_short_name, label, trigger,
|
|
98
|
+
)
|
|
99
|
+
woken.append(host)
|
|
100
|
+
except Exception as e:
|
|
82
101
|
logging.warning(
|
|
83
|
-
"Startup warm-up
|
|
84
|
-
company_short_name,
|
|
85
|
-
self._format_profile_labels(profiles),
|
|
102
|
+
"Startup warm-up could not wake endpoint='%s' via company='%s' profile=%s: %s",
|
|
103
|
+
host, company_short_name, label, e,
|
|
86
104
|
)
|
|
105
|
+
|
|
106
|
+
return woken
|
|
107
|
+
|
|
108
|
+
def _startup_endpoints(self) -> dict[str, tuple[str, tuple[str, str, str]]]:
|
|
109
|
+
"""One representative (company, profile) per distinct endpoint URL.
|
|
110
|
+
|
|
111
|
+
Keyed by the resolved URL rather than by company or tool name, so a
|
|
112
|
+
deployment where every tenant shares one endpoint knocks once, and a
|
|
113
|
+
tenant later pointed somewhere else gets its own knock without anyone
|
|
114
|
+
having to remember this function exists.
|
|
115
|
+
"""
|
|
116
|
+
targets: dict[str, tuple[str, tuple[str, str, str]]] = {}
|
|
117
|
+
for company_short_name in get_registered_companies().keys():
|
|
118
|
+
inference_tools = self.config_service.get_configuration(company_short_name, "inference_tools") or {}
|
|
119
|
+
if not isinstance(inference_tools, dict):
|
|
87
120
|
continue
|
|
88
121
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
company_short_name,
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
122
|
+
for profile in self._get_remote_text_embedding_profiles(company_short_name, startup_only=True):
|
|
123
|
+
_model_type, _config_section, tool_name = profile
|
|
124
|
+
endpoint_url = self._resolve_endpoint_url(company_short_name, inference_tools, tool_name)
|
|
125
|
+
if not endpoint_url:
|
|
126
|
+
continue
|
|
127
|
+
|
|
128
|
+
existing = targets.get(endpoint_url)
|
|
129
|
+
# Prefer the default text profile as the representative: it is the
|
|
130
|
+
# one rag_search uses, so if the container loads models on demand
|
|
131
|
+
# the one most likely to be asked for first is the one primed.
|
|
132
|
+
if existing is None or (profile[0] == "text" and existing[1][0] != "text"):
|
|
133
|
+
targets[endpoint_url] = (company_short_name, profile)
|
|
134
|
+
|
|
135
|
+
return targets
|
|
136
|
+
|
|
137
|
+
@staticmethod
|
|
138
|
+
def _endpoint_host(endpoint_url: str) -> str:
|
|
139
|
+
"""Host only: the URL can come from a secret, and the host is enough to log."""
|
|
140
|
+
return urlparse(endpoint_url).hostname or "unknown"
|
|
95
141
|
|
|
96
142
|
return warmed_companies
|
|
97
143
|
|
|
@@ -205,8 +251,23 @@ class WarmupService:
|
|
|
205
251
|
if provider != "huggingface":
|
|
206
252
|
return False
|
|
207
253
|
|
|
254
|
+
return bool(self._resolve_endpoint_url(company_short_name, inference_tools, tool_name))
|
|
255
|
+
|
|
256
|
+
def _resolve_endpoint_url(
|
|
257
|
+
self,
|
|
258
|
+
company_short_name: str,
|
|
259
|
+
inference_tools: dict,
|
|
260
|
+
tool_name: str,
|
|
261
|
+
) -> str:
|
|
262
|
+
"""Where a tool's requests actually go, or "" when it resolves nowhere.
|
|
263
|
+
|
|
264
|
+
Same precedence the inference client uses — literal, secret ref, then
|
|
265
|
+
environment variable — because this is what identifies the thing being
|
|
266
|
+
woken. Two profiles that resolve here to the same URL are one endpoint,
|
|
267
|
+
whatever they are called in the company's configuration.
|
|
268
|
+
"""
|
|
208
269
|
if not tool_name:
|
|
209
|
-
return
|
|
270
|
+
return ""
|
|
210
271
|
|
|
211
272
|
defaults = inference_tools.get("_defaults") or {}
|
|
212
273
|
if not isinstance(defaults, dict):
|
|
@@ -214,7 +275,7 @@ class WarmupService:
|
|
|
214
275
|
|
|
215
276
|
tool_cfg = inference_tools.get(tool_name) or {}
|
|
216
277
|
if not isinstance(tool_cfg, dict):
|
|
217
|
-
return
|
|
278
|
+
return ""
|
|
218
279
|
|
|
219
280
|
resolved_cfg = {**defaults, **tool_cfg}
|
|
220
281
|
endpoint_url = (resolved_cfg.get("endpoint_url") or "").strip()
|
|
@@ -231,4 +292,4 @@ class WarmupService:
|
|
|
231
292
|
resolve_secret(self.secret_provider, company_short_name, endpoint_url_env, default="") or ""
|
|
232
293
|
).strip()
|
|
233
294
|
|
|
234
|
-
return
|
|
295
|
+
return endpoint_url
|
|
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.41.0 → iatoolkit-2.41.2}/src/iatoolkit/common/interfaces/memory_compilation_trigger.py
RENAMED
|
File without changes
|
{iatoolkit-2.41.0 → iatoolkit-2.41.2}/src/iatoolkit/common/interfaces/memory_lint_trigger.py
RENAMED
|
File without changes
|