iatoolkit 2.74.0__tar.gz → 2.74.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.74.0/src/iatoolkit.egg-info → iatoolkit-2.74.2}/PKG-INFO +1 -1
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/__init__.py +1 -1
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/common/model_registry.py +42 -5
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/llm_providers/openai_adapter.py +9 -5
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/llm_proxy.py +42 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/attachment_policy_service.py +51 -132
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/configuration_service.py +5 -1
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/history_compaction_service.py +98 -20
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/history_manager_service.py +54 -10
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/system_agent_runner.py +3 -2
- {iatoolkit-2.74.0 → iatoolkit-2.74.2/src/iatoolkit.egg-info}/PKG-INFO +1 -1
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/LICENSE +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/LICENSE_COMMUNITY.md +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/pyproject.toml +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/readme.md +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/setup.cfg +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/base_company.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/cli_commands.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/common/__init__.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/common/access.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/common/bridge_validation.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/common/company_paths.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/common/exceptions.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/common/interfaces/__init__.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/common/interfaces/asset_storage.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/common/interfaces/database_provider.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/common/interfaces/history_compaction_trigger.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/common/interfaces/memory_compilation_trigger.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/common/interfaces/secret_provider.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/common/interfaces/signup_policy_resolver.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/common/interfaces/web_search_provider.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/common/memory_tools.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/common/routes.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/common/search_terms.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/common/secret_resolver.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/common/session_manager.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/common/text_splitter.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/common/url_safety.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/common/util.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/company_registry.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/__init__.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/llm_capabilities.yaml +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_agents/history_compaction.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_agents/memory_compiler.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_agents/memory_correction.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_agents/memory_document_ingest.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_agents/memory_image_ingest.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_agents/memory_meeting_ingest.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_agents/memory_question_review.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_agents_pack.yaml +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/channel_business_identity.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/channel_conversation_continuity.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/channel_response_style.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/chat_state_rules.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/chat_user_profile.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/core_identity.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/email_output.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/file_download_output.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/format_styles.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/html_structures.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/links_documents.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/memory_usage.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/multimodal_basics.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/output_basics.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/presentation_formatting.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/query_main.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/sql_aggregation_scope.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/sql_casting.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/sql_core.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/sql_jsonb.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/sql_mysql_casting.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/sql_mysql_json.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/sql_redshift_basics.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/sql_redshift_types.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/sql_rules.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts/tool_html_passthrough.prompt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_prompts_pack.yaml +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/config/system_tools_pack.yaml +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/core.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/__init__.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/brevo_mail_app.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/call_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/connectors/__init__.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/connectors/file_connector.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/connectors/file_connector_factory.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/connectors/google_cloud_storage_connector.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/connectors/google_drive_connector.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/connectors/local_file_connector.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/connectors/s3_connector.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/google_auth_client.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/google_chat_app.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/inference_embeddings_client.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/inference_handler.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/jina_embeddings_client.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/llm_gateway_resolver.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/llm_providers/__init__.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/llm_providers/anthropic_adapter.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/llm_providers/deepseek_adapter.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/llm_providers/gemini_adapter.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/llm_providers/openai_compatible_chat_adapter.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/llm_providers/openrouter_adapter.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/llm_response.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/infra/redis_session_manager.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/locales/en.yaml +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/locales/es.yaml +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/repositories/__init__.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/repositories/advisory_lock.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/repositories/api_key_repo.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/repositories/bridge_repo.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/repositories/database_manager.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/repositories/document_repo.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/repositories/env_secret_provider.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/repositories/filesystem_asset_repository.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/repositories/knowledge_wiki_repo.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/repositories/llm_query_repo.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/repositories/mcp_token_repo.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/repositories/memory_question_repo.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/repositories/memory_repo.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/repositories/memory_usage_repo.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/repositories/models.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/repositories/profile_repo.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/repositories/prompt_resource_repo.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/repositories/sql_dataset_repo.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/repositories/sql_source_repo.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/repositories/vs_repo.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/runtime_logging.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/__init__.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/api_key_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/auth_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/branding_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/company_context_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/context_builder_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/dispatcher_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/embedding_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/excel_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/file_processor_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/http_tool_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/i18n_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/inference_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/jwt_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/knowledge_base_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/language_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/license_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/llm_client_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/mail_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/markdown_wiki_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/mcp_token_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/memory_compiler_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/memory_lookup_policy_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/memory_question_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/memory_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/memory_sources.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/memory_usage_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/memory_wiki_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/noop_history_compaction_trigger.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/noop_memory_compilation_trigger.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/operator_identity_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/parsers/__init__.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/parsers/contracts.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/parsers/image_normalizer.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/parsers/parsing_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/parsers/pdf_ocr_detection.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/parsers/provider_factory.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/parsers/provider_resolver.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/parsers/providers/__init__.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/parsers/providers/basic_provider.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/parsers/providers/docling_provider.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/parsers/validator.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/pdf_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/profile_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/prompt_resource_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/prompt_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/query_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/resource_access_scope.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/signup_policy_resolver.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/sql_dataset_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/sql_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/sql_source_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/storage_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/structured_output_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/system_agent_catalog.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/system_prompt_catalog.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/system_tools.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/telemetry_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/tenant_wiki_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/tool_output_contract.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/tool_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/user_feedback_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/user_session_context_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/visual_kb_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/visual_tool_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/warmup_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/web_search/__init__.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/web_search/provider_factory.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/web_search/providers/__init__.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/web_search/providers/brave_provider.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/web_search_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/services/wiki_page_index_service.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/static/images/favicon.ico +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/static/images/favicon.png +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/static/images/fernando.jpeg +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/static/images/iatoolkit_core.png +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/static/images/iatoolkit_logo.png +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/static/js/chat_feedback_button.js +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/static/js/chat_filepond.js +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/static/js/chat_header_navigation.js +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/static/js/chat_help_content.js +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/static/js/chat_history_button.js +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/static/js/chat_logout_button.js +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/static/js/chat_main.js +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/static/js/chat_memory_button.js +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/static/js/chat_model_selector.js +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/static/js/chat_onboarding_button.js +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/static/js/chat_prompt_manager.js +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/static/styles/account.css +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/static/styles/chat_iatoolkit.css +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/static/styles/chat_modal.css +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/static/styles/chat_public.css +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/static/styles/documents.css +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/static/styles/landing_page.css +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/static/styles/llm_output.css +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/static/styles/onboarding.css +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/static/styles/wiki_page.css +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/templates/_chat_header.html +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/templates/_company_header.html +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/templates/_login_widget.html +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/templates/account.html +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/templates/base.html +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/templates/change_password.html +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/templates/chat.html +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/templates/chat_memory_modal.html +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/templates/chat_modals.html +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/templates/error.html +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/templates/forgot_password.html +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/templates/home_hosted_default.html +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/templates/login_global.html +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/templates/memory/wiki_schema.md +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/templates/onboarding_shell.html +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/templates/pdf/base.html +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/templates/pdf/letter.html +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/templates/pdf/report.html +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/templates/pdf/simple.html +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/templates/signup.html +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/templates/wiki_page.html +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/__init__.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/account_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/api_key_api_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/base_login_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/categories_api_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/change_password_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/chat_context_preview_api_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/chat_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/configuration_api_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/connectors_api_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/embedding_api_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/forgot_password_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/help_content_api_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/history_api_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/home_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/init_context_api_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/llmquery_api_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/load_document_api_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/login_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/logout_api_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/memory_api_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/profile_api_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/prompt_api_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/prompt_context_preview_api_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/prompt_resource_api_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/rag_api_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/root_redirect_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/signup_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/static_page_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/tool_api_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/user_feedback_api_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/users_api_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/verify_user_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit/views/wiki_page_view.py +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit.egg-info/SOURCES.txt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit.egg-info/dependency_links.txt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit.egg-info/requires.txt +0 -0
- {iatoolkit-2.74.0 → iatoolkit-2.74.2}/src/iatoolkit.egg-info/top_level.txt +0 -0
|
@@ -165,21 +165,58 @@ class ModelRegistry:
|
|
|
165
165
|
return candidate # type: ignore[return-value]
|
|
166
166
|
return self.get_provider(model or "")
|
|
167
167
|
|
|
168
|
-
|
|
168
|
+
@staticmethod
|
|
169
|
+
def catalog_capabilities(model_config: dict | None) -> dict | None:
|
|
170
|
+
"""The capabilities the model catalogue holds for a model, or None when unknown.
|
|
171
|
+
|
|
172
|
+
Written by `models-sync` from OpenRouter (BL-009, Fase F) and carried in
|
|
173
|
+
the model's configuration. Empty, or a company.yaml model outside the
|
|
174
|
+
catalogue, means unknown: the provider's rules decide.
|
|
175
|
+
"""
|
|
176
|
+
capabilities = (model_config or {}).get("capabilities") if isinstance(model_config, dict) else None
|
|
177
|
+
if not isinstance(capabilities, dict):
|
|
178
|
+
return None
|
|
179
|
+
if not capabilities.get("supported_parameters") and not capabilities.get("input_modalities"):
|
|
180
|
+
return None
|
|
181
|
+
return capabilities
|
|
182
|
+
|
|
183
|
+
@staticmethod
|
|
184
|
+
def _declares(capabilities: dict | None, *parameters: str) -> bool | None:
|
|
185
|
+
"""Whether the model declares any of `parameters`; None when nothing is known."""
|
|
186
|
+
if not capabilities:
|
|
187
|
+
return None
|
|
188
|
+
declared = {str(value).strip().lower() for value in capabilities.get("supported_parameters") or []}
|
|
189
|
+
if not declared:
|
|
190
|
+
return None
|
|
191
|
+
return any(parameter in declared for parameter in parameters)
|
|
192
|
+
|
|
193
|
+
def get_capabilities(self, model: str, provider: str | None = None, capabilities: dict | None = None) -> dict:
|
|
194
|
+
"""What the request may carry for this model.
|
|
195
|
+
|
|
196
|
+
Two halves, both required: what the provider's adapter knows how to send,
|
|
197
|
+
and what the model accepts. The second comes from the model catalogue
|
|
198
|
+
(`capabilities`) when it is known; without it, the provider decides alone.
|
|
199
|
+
The catalogue only ever narrows: a parameter the adapter cannot send is
|
|
200
|
+
never offered because the model would accept it.
|
|
201
|
+
"""
|
|
169
202
|
normalized_provider = self.normalize_provider(provider=provider, model=model)
|
|
170
203
|
# `openai_compatible` is absent on purpose. Its adapter targets any server
|
|
171
204
|
# that speaks chat.completions — llama.cpp, vLLM, a private gateway — and
|
|
172
205
|
# forwards no reasoning field, because one the server does not know earns a
|
|
173
206
|
# 400. Claiming the capability here made the tenant's Models page offer a
|
|
174
207
|
# reasoning control whose value went nowhere.
|
|
175
|
-
|
|
208
|
+
adapter_sends_reasoning = normalized_provider in {
|
|
176
209
|
"openai",
|
|
177
210
|
"xai",
|
|
178
211
|
"openrouter",
|
|
179
212
|
"deepseek",
|
|
180
213
|
}
|
|
181
|
-
|
|
182
|
-
|
|
214
|
+
adapter_sends_verbosity = normalized_provider in {"openai", "xai", "openrouter"}
|
|
215
|
+
|
|
216
|
+
model_reasons = self._declares(capabilities, "reasoning", "reasoning_effort")
|
|
217
|
+
model_takes_verbosity = self._declares(capabilities, "verbosity")
|
|
218
|
+
supports_reasoning_effort = adapter_sends_reasoning and model_reasons is not False
|
|
219
|
+
supports_text_verbosity = adapter_sends_verbosity and model_takes_verbosity is not False
|
|
183
220
|
|
|
184
221
|
return {
|
|
185
222
|
"provider": normalized_provider,
|
|
@@ -187,7 +224,7 @@ class ModelRegistry:
|
|
|
187
224
|
"allowed_reasoning_efforts": list(self._reasoning_effort_options) if supports_reasoning_effort else [],
|
|
188
225
|
"supports_text_verbosity": supports_text_verbosity,
|
|
189
226
|
"allowed_text_verbosity": list(self._text_verbosity_options) if supports_text_verbosity else [],
|
|
190
|
-
"
|
|
227
|
+
"capabilities_source": "catalog" if model_reasons is not None else "provider",
|
|
191
228
|
}
|
|
192
229
|
|
|
193
230
|
def get_request_defaults(self, model: str) -> dict:
|
|
@@ -19,8 +19,9 @@ class OpenAIAdapter:
|
|
|
19
19
|
self.client = openai_client
|
|
20
20
|
|
|
21
21
|
#: Reasoning models whose reasoning can travel between calls as encrypted
|
|
22
|
-
#: items
|
|
23
|
-
#: `include` with a 400.
|
|
22
|
+
#: items, for a model the catalogue does not describe: a model without
|
|
23
|
+
#: reasoning answers the `include` with a 400. When the catalogue knows the
|
|
24
|
+
#: model, the proxy says so (`carries_reasoning`) and this list is not read.
|
|
24
25
|
_ENCRYPTED_REASONING_MODEL_PREFIXES = ("gpt-5", "o1", "o3", "o4")
|
|
25
26
|
|
|
26
27
|
def create_response(self,
|
|
@@ -35,7 +36,8 @@ class OpenAIAdapter:
|
|
|
35
36
|
attachments: Optional[List[Dict]] = None,
|
|
36
37
|
store: Optional[bool] = None,
|
|
37
38
|
metadata: Optional[Dict[str, str]] = None,
|
|
38
|
-
telemetry_execution: Any = None
|
|
39
|
+
telemetry_execution: Any = None,
|
|
40
|
+
carries_reasoning: Optional[bool] = None) -> LLMResponse:
|
|
39
41
|
"""Llamada a la API de OpenAI y mapeo a estructura común.
|
|
40
42
|
|
|
41
43
|
IAToolkit owns the history (BL-009): every call carries the whole
|
|
@@ -62,7 +64,7 @@ class OpenAIAdapter:
|
|
|
62
64
|
|
|
63
65
|
# Nothing stays at OpenAI: the next call sends the history again.
|
|
64
66
|
params['store'] = False
|
|
65
|
-
if self._carries_encrypted_reasoning(model):
|
|
67
|
+
if self._carries_encrypted_reasoning(model, carries_reasoning):
|
|
66
68
|
# Without a stored response the model would lose its reasoning
|
|
67
69
|
# between tool rounds; it comes back encrypted and is sent again.
|
|
68
70
|
params['include'] = ["reasoning.encrypted_content"]
|
|
@@ -93,7 +95,9 @@ class OpenAIAdapter:
|
|
|
93
95
|
raise IAToolkitException(IAToolkitException.ErrorType.LLM_ERROR, error_message)
|
|
94
96
|
|
|
95
97
|
@classmethod
|
|
96
|
-
def _carries_encrypted_reasoning(cls, model: str) -> bool:
|
|
98
|
+
def _carries_encrypted_reasoning(cls, model: str, carries_reasoning: Optional[bool] = None) -> bool:
|
|
99
|
+
if carries_reasoning is not None:
|
|
100
|
+
return carries_reasoning
|
|
97
101
|
name = str(model or "").strip().lower()
|
|
98
102
|
if "-chat" in name:
|
|
99
103
|
return False # gpt-5-chat-latest and the like do not reason
|
|
@@ -160,6 +160,12 @@ class LLMProxy:
|
|
|
160
160
|
provider=provider,
|
|
161
161
|
request_kwargs=request_kwargs,
|
|
162
162
|
)
|
|
163
|
+
request_kwargs = self._apply_model_capabilities(
|
|
164
|
+
model=model,
|
|
165
|
+
provider=provider,
|
|
166
|
+
model_config=model_config,
|
|
167
|
+
request_kwargs=request_kwargs,
|
|
168
|
+
)
|
|
163
169
|
if telemetry_execution is not None:
|
|
164
170
|
request_kwargs["telemetry_execution"] = telemetry_execution
|
|
165
171
|
|
|
@@ -451,6 +457,42 @@ class LLMProxy:
|
|
|
451
457
|
effective_kwargs["reasoning"] = merged_reasoning
|
|
452
458
|
return effective_kwargs
|
|
453
459
|
|
|
460
|
+
def _apply_model_capabilities(
|
|
461
|
+
self,
|
|
462
|
+
model: str,
|
|
463
|
+
provider: str,
|
|
464
|
+
model_config: Dict[str, Any] | None,
|
|
465
|
+
request_kwargs: Dict[str, Any],
|
|
466
|
+
) -> Dict[str, Any]:
|
|
467
|
+
"""Drops the parameters the model itself does not accept (BL-009, Fase F).
|
|
468
|
+
|
|
469
|
+
The steps before this one decide by provider: every OpenAI model gets a
|
|
470
|
+
reasoning effort and a verbosity, which a model that does not reason
|
|
471
|
+
(gpt-4.1, gpt-5-chat) refuses. When the catalogue knows the model, its
|
|
472
|
+
`supported_parameters` have the last word. When it does not, nothing
|
|
473
|
+
changes: the provider's rules stay as they were.
|
|
474
|
+
"""
|
|
475
|
+
catalog_capabilities = ModelRegistry.catalog_capabilities(model_config)
|
|
476
|
+
if catalog_capabilities is None:
|
|
477
|
+
return request_kwargs
|
|
478
|
+
|
|
479
|
+
capabilities = self.model_registry.get_capabilities(
|
|
480
|
+
model, provider=provider, capabilities=catalog_capabilities
|
|
481
|
+
)
|
|
482
|
+
effective_kwargs = dict(request_kwargs or {})
|
|
483
|
+
if not capabilities.get("supports_reasoning_effort"):
|
|
484
|
+
effective_kwargs.pop("reasoning", None)
|
|
485
|
+
effective_kwargs.pop("reasoning_effort", None)
|
|
486
|
+
if not capabilities.get("supports_text_verbosity"):
|
|
487
|
+
text = effective_kwargs.get("text")
|
|
488
|
+
if isinstance(text, dict) and "verbosity" in text:
|
|
489
|
+
effective_kwargs["text"] = {key: value for key, value in text.items() if key != "verbosity"}
|
|
490
|
+
if provider == self.PROVIDER_OPENAI:
|
|
491
|
+
# Whether the model's reasoning can travel between calls: the adapter
|
|
492
|
+
# asks for it only then, instead of guessing from the model's name.
|
|
493
|
+
effective_kwargs["carries_reasoning"] = bool(capabilities.get("supports_reasoning_effort"))
|
|
494
|
+
return effective_kwargs
|
|
495
|
+
|
|
454
496
|
# -------------------------------------------------------------------------
|
|
455
497
|
# Client cache
|
|
456
498
|
# -------------------------------------------------------------------------
|
|
@@ -10,13 +10,10 @@ from typing import Any, Dict, List
|
|
|
10
10
|
import base64
|
|
11
11
|
import logging
|
|
12
12
|
import mimetypes
|
|
13
|
-
import threading
|
|
14
|
-
import time
|
|
15
|
-
|
|
16
|
-
import requests
|
|
17
13
|
|
|
18
14
|
from injector import inject
|
|
19
15
|
|
|
16
|
+
from iatoolkit.common.model_registry import ModelRegistry
|
|
20
17
|
from iatoolkit.services.configuration_service import ConfigurationService
|
|
21
18
|
from iatoolkit.common.util import Utility
|
|
22
19
|
|
|
@@ -30,16 +27,12 @@ class AttachmentPolicyService:
|
|
|
30
27
|
|
|
31
28
|
DEFAULT_MODE = MODE_EXTRACTED_ONLY
|
|
32
29
|
DEFAULT_FALLBACK = FALLBACK_EXTRACT
|
|
33
|
-
OPENROUTER_MODELS_CACHE_TTL_SECONDS = 300
|
|
34
|
-
OPENROUTER_MODELS_TIMEOUT_SECONDS = 8
|
|
35
30
|
|
|
36
31
|
@inject
|
|
37
32
|
def __init__(self, configuration_service: ConfigurationService, util: Utility):
|
|
38
33
|
self.configuration_service = configuration_service
|
|
39
34
|
self.util = util
|
|
40
35
|
self._default_capabilities: dict | None = None
|
|
41
|
-
self._openrouter_models_cache: dict[str, dict[str, Any]] = {}
|
|
42
|
-
self._openrouter_models_cache_lock = threading.Lock()
|
|
43
36
|
self._default_capabilities_path = (
|
|
44
37
|
Path(__file__).resolve().parent.parent / "config" / "llm_capabilities.yaml"
|
|
45
38
|
)
|
|
@@ -72,7 +65,7 @@ class AttachmentPolicyService:
|
|
|
72
65
|
) -> dict:
|
|
73
66
|
mode = self.normalize_mode((policy or {}).get("attachment_mode"))
|
|
74
67
|
fallback = self.normalize_fallback((policy or {}).get("attachment_fallback"))
|
|
75
|
-
capabilities = self.
|
|
68
|
+
capabilities = self.get_effective_capabilities(company_short_name, provider, model)
|
|
76
69
|
|
|
77
70
|
if not files:
|
|
78
71
|
return {
|
|
@@ -102,8 +95,6 @@ class AttachmentPolicyService:
|
|
|
102
95
|
"fallback_to_extract": 0,
|
|
103
96
|
"errors": 0,
|
|
104
97
|
}
|
|
105
|
-
openrouter_native_image_error: str | None = None
|
|
106
|
-
openrouter_native_image_checked = False
|
|
107
98
|
|
|
108
99
|
for file_obj in files:
|
|
109
100
|
stats["total_files"] += 1
|
|
@@ -114,36 +105,17 @@ class AttachmentPolicyService:
|
|
|
114
105
|
wants_native = self._wants_native(mode, meta, capabilities)
|
|
115
106
|
wants_extract = self._wants_extract(mode, meta, capabilities)
|
|
116
107
|
native_ok = False
|
|
117
|
-
native_blocked_by_model_catalog = False
|
|
118
108
|
|
|
119
109
|
if wants_native:
|
|
120
|
-
if
|
|
121
|
-
if not openrouter_native_image_checked:
|
|
122
|
-
openrouter_native_image_error = self._get_openrouter_native_image_error(
|
|
123
|
-
company_short_name=company_short_name,
|
|
124
|
-
model=model,
|
|
125
|
-
)
|
|
126
|
-
openrouter_native_image_checked = True
|
|
127
|
-
if openrouter_native_image_error:
|
|
128
|
-
if fallback == self.FALLBACK_EXTRACT or wants_extract:
|
|
129
|
-
stats["fallback_to_extract"] += 1
|
|
130
|
-
wants_extract = True
|
|
131
|
-
native_blocked_by_model_catalog = True
|
|
132
|
-
else:
|
|
133
|
-
if openrouter_native_image_error not in errors:
|
|
134
|
-
errors.append(openrouter_native_image_error)
|
|
135
|
-
continue
|
|
136
|
-
if (not native_blocked_by_model_catalog) and bool(capabilities.get("supports_native_images")):
|
|
110
|
+
if bool(capabilities.get("supports_native_images")):
|
|
137
111
|
files_for_context.append(dict(file_obj))
|
|
138
112
|
native_ok = True
|
|
139
113
|
stats["native_sent_count"] += 1
|
|
140
|
-
elif
|
|
114
|
+
elif fallback == self.FALLBACK_EXTRACT or wants_extract:
|
|
141
115
|
stats["fallback_to_extract"] += 1
|
|
142
116
|
wants_extract = True
|
|
143
|
-
|
|
144
|
-
errors.append(
|
|
145
|
-
f"Image attachment '{filename}' cannot be sent as native image for provider '{provider}'."
|
|
146
|
-
)
|
|
117
|
+
else:
|
|
118
|
+
errors.append(self._native_image_error(filename, provider, model, capabilities))
|
|
147
119
|
|
|
148
120
|
if wants_extract:
|
|
149
121
|
extract_copy = dict(file_obj)
|
|
@@ -204,6 +176,51 @@ class AttachmentPolicyService:
|
|
|
204
176
|
"stats": stats,
|
|
205
177
|
}
|
|
206
178
|
|
|
179
|
+
def get_effective_capabilities(self, company_short_name: str, provider: str, model: str | None) -> dict:
|
|
180
|
+
"""What can go natively to this model: what the provider's adapter sends, narrowed by the model.
|
|
181
|
+
|
|
182
|
+
The provider half (llm_capabilities.yaml and the company's overrides)
|
|
183
|
+
says what our adapter knows how to send. The model half comes from the
|
|
184
|
+
model catalogue (BL-009, Fase F): `input_modalities` as OpenRouter
|
|
185
|
+
publishes them. Both must hold, so a provider that takes images does not
|
|
186
|
+
make a text-only model take them. A model the catalogue does not know
|
|
187
|
+
keeps the provider's answer.
|
|
188
|
+
"""
|
|
189
|
+
capabilities = dict(self.get_effective_provider_capabilities(company_short_name, provider))
|
|
190
|
+
capabilities["capabilities_source"] = "provider"
|
|
191
|
+
modalities = self._model_input_modalities(company_short_name, model)
|
|
192
|
+
if modalities is None:
|
|
193
|
+
return capabilities
|
|
194
|
+
capabilities["supports_native_images"] = bool(capabilities.get("supports_native_images")) and "image" in modalities
|
|
195
|
+
capabilities["supports_native_files"] = bool(capabilities.get("supports_native_files")) and "file" in modalities
|
|
196
|
+
capabilities["input_modalities"] = sorted(modalities)
|
|
197
|
+
capabilities["capabilities_source"] = "catalog"
|
|
198
|
+
return capabilities
|
|
199
|
+
|
|
200
|
+
def _model_input_modalities(self, company_short_name: str, model: str | None) -> set[str] | None:
|
|
201
|
+
if not model:
|
|
202
|
+
return None
|
|
203
|
+
try:
|
|
204
|
+
model_config = self.configuration_service.get_llm_model_config(company_short_name, model) or {}
|
|
205
|
+
except Exception as e:
|
|
206
|
+
logging.debug("Could not read the configuration of model '%s': %s", model, e)
|
|
207
|
+
return None
|
|
208
|
+
catalog = ModelRegistry.catalog_capabilities(model_config)
|
|
209
|
+
if not catalog or not catalog.get("input_modalities"):
|
|
210
|
+
return None
|
|
211
|
+
return {str(value).strip().lower() for value in catalog["input_modalities"] if str(value or "").strip()}
|
|
212
|
+
|
|
213
|
+
@staticmethod
|
|
214
|
+
def _native_image_error(filename: str, provider: str, model: str | None, capabilities: dict) -> str:
|
|
215
|
+
if capabilities.get("capabilities_source") == "catalog":
|
|
216
|
+
modalities = ", ".join(capabilities.get("input_modalities") or []) or "(none)"
|
|
217
|
+
return (
|
|
218
|
+
f"Image attachment '{filename}' cannot be sent as a native image: the model catalogue "
|
|
219
|
+
f"publishes '{model}' with input modalities {modalities}, without 'image'. "
|
|
220
|
+
"Use a model that takes images, or allow text extraction."
|
|
221
|
+
)
|
|
222
|
+
return f"Image attachment '{filename}' cannot be sent as native image for provider '{provider}'."
|
|
223
|
+
|
|
207
224
|
def get_effective_provider_capabilities(self, company_short_name: str, provider: str) -> dict:
|
|
208
225
|
provider_key = str(provider or "unknown").strip().lower() or "unknown"
|
|
209
226
|
defaults = self._load_default_capabilities().get(provider_key) or self._load_default_capabilities().get("unknown", {})
|
|
@@ -270,104 +287,6 @@ class AttachmentPolicyService:
|
|
|
270
287
|
merged.setdefault("max_files_per_request", 0)
|
|
271
288
|
return merged
|
|
272
289
|
|
|
273
|
-
def _get_openrouter_native_image_error(self, company_short_name: str, model: str | None) -> str | None:
|
|
274
|
-
model_id = str(model or "").strip()
|
|
275
|
-
if not model_id:
|
|
276
|
-
return (
|
|
277
|
-
"Este modelo/provider no expone visión nativa por API: no se resolvio el modelo efectivo "
|
|
278
|
-
"para OpenRouter, por lo que no puedo confirmar soporte de imagenes nativas."
|
|
279
|
-
)
|
|
280
|
-
|
|
281
|
-
model_metadata, verified = self._lookup_openrouter_model_metadata(company_short_name, model_id)
|
|
282
|
-
if not verified:
|
|
283
|
-
return None
|
|
284
|
-
if not isinstance(model_metadata, dict):
|
|
285
|
-
return (
|
|
286
|
-
"Este modelo/provider no expone visión nativa por API verificable: "
|
|
287
|
-
f"OpenRouter no publica metadata para el modelo '{model_id}', por lo que no puedo confirmar "
|
|
288
|
-
"que acepte imagenes nativas."
|
|
289
|
-
)
|
|
290
|
-
|
|
291
|
-
architecture = model_metadata.get("architecture") or {}
|
|
292
|
-
raw_input_modalities = architecture.get("input_modalities") or []
|
|
293
|
-
input_modalities = [
|
|
294
|
-
str(modality or "").strip().lower()
|
|
295
|
-
for modality in raw_input_modalities
|
|
296
|
-
if str(modality or "").strip()
|
|
297
|
-
]
|
|
298
|
-
if "image" in input_modalities:
|
|
299
|
-
return None
|
|
300
|
-
|
|
301
|
-
published_model_id = str(model_metadata.get("id") or model_id).strip() or model_id
|
|
302
|
-
published_modalities = ", ".join(input_modalities) if input_modalities else "(vacio)"
|
|
303
|
-
return (
|
|
304
|
-
"Este modelo/provider no expone visión nativa por API: "
|
|
305
|
-
f"OpenRouter publica el modelo '{published_model_id}' con input_modalities={published_modalities}, "
|
|
306
|
-
"sin 'image'. Para enviar imagenes nativas usa un modelo/provider que publique 'image' "
|
|
307
|
-
"como modalidad de entrada."
|
|
308
|
-
)
|
|
309
|
-
|
|
310
|
-
def _lookup_openrouter_model_metadata(self, company_short_name: str, model: str) -> tuple[dict | None, bool]:
|
|
311
|
-
provider_config = self.configuration_service.get_llm_provider_config(company_short_name, "openrouter") or {}
|
|
312
|
-
base_url = str(provider_config.get("base_url") or "https://openrouter.ai/api/v1").strip()
|
|
313
|
-
models_url = f"{base_url.rstrip('/')}/models"
|
|
314
|
-
catalog = self._get_openrouter_models_catalog(models_url)
|
|
315
|
-
if catalog is None:
|
|
316
|
-
return None, False
|
|
317
|
-
|
|
318
|
-
model_key = str(model or "").strip().lower()
|
|
319
|
-
for item in catalog:
|
|
320
|
-
if not isinstance(item, dict):
|
|
321
|
-
continue
|
|
322
|
-
candidates = {
|
|
323
|
-
str(item.get("id") or "").strip().lower(),
|
|
324
|
-
str(item.get("canonical_slug") or "").strip().lower(),
|
|
325
|
-
}
|
|
326
|
-
candidates.update(
|
|
327
|
-
{
|
|
328
|
-
candidate.split("/")[-1]
|
|
329
|
-
for candidate in list(candidates)
|
|
330
|
-
if candidate and "/" in candidate
|
|
331
|
-
}
|
|
332
|
-
)
|
|
333
|
-
if model_key and model_key in candidates:
|
|
334
|
-
return item, True
|
|
335
|
-
|
|
336
|
-
return None, True
|
|
337
|
-
|
|
338
|
-
def _get_openrouter_models_catalog(self, models_url: str) -> list[dict] | None:
|
|
339
|
-
current_time = time.time()
|
|
340
|
-
with self._openrouter_models_cache_lock:
|
|
341
|
-
cached_entry = self._openrouter_models_cache.get(models_url)
|
|
342
|
-
if cached_entry and (current_time - float(cached_entry.get("fetched_at") or 0)) < self.OPENROUTER_MODELS_CACHE_TTL_SECONDS:
|
|
343
|
-
cached_data = cached_entry.get("data")
|
|
344
|
-
if isinstance(cached_data, list):
|
|
345
|
-
return cached_data
|
|
346
|
-
|
|
347
|
-
try:
|
|
348
|
-
response = requests.get(
|
|
349
|
-
models_url,
|
|
350
|
-
params={"output_modalities": "all"},
|
|
351
|
-
timeout=self.OPENROUTER_MODELS_TIMEOUT_SECONDS,
|
|
352
|
-
headers={"User-Agent": "IAToolkit AttachmentPolicy/1.0"},
|
|
353
|
-
)
|
|
354
|
-
response.raise_for_status()
|
|
355
|
-
payload = response.json()
|
|
356
|
-
catalog = payload.get("data") if isinstance(payload, dict) else None
|
|
357
|
-
if not isinstance(catalog, list):
|
|
358
|
-
logging.warning("OpenRouter models API returned an unexpected payload shape for '%s'.", models_url)
|
|
359
|
-
return None
|
|
360
|
-
except Exception as exc:
|
|
361
|
-
logging.warning("Could not fetch OpenRouter model metadata from '%s': %s", models_url, exc)
|
|
362
|
-
return None
|
|
363
|
-
|
|
364
|
-
with self._openrouter_models_cache_lock:
|
|
365
|
-
self._openrouter_models_cache[models_url] = {
|
|
366
|
-
"fetched_at": current_time,
|
|
367
|
-
"data": catalog,
|
|
368
|
-
}
|
|
369
|
-
return catalog
|
|
370
|
-
|
|
371
290
|
def _normalize_file_meta(self, file_obj: dict) -> dict:
|
|
372
291
|
if not isinstance(file_obj, dict):
|
|
373
292
|
return {"name": "attachment", "payload": "", "mime_type": "application/octet-stream", "is_image": False}
|
|
@@ -1435,7 +1435,11 @@ class ConfigurationService:
|
|
|
1435
1435
|
provider=normalized.get("provider"),
|
|
1436
1436
|
model=model_id,
|
|
1437
1437
|
)
|
|
1438
|
-
capabilities = self.model_registry.get_capabilities(
|
|
1438
|
+
capabilities = self.model_registry.get_capabilities(
|
|
1439
|
+
model_id,
|
|
1440
|
+
provider=provider,
|
|
1441
|
+
capabilities=self.model_registry.catalog_capabilities(normalized),
|
|
1442
|
+
)
|
|
1439
1443
|
descriptor = dict(normalized)
|
|
1440
1444
|
descriptor["id"] = model_id
|
|
1441
1445
|
descriptor["label"] = str(descriptor.get("label") or model_id).strip()
|
|
@@ -54,7 +54,13 @@ class HistoryCompactionService:
|
|
|
54
54
|
#: Tool outputs and long answers are cut in the transcript the agent reads:
|
|
55
55
|
#: the summary keeps facts, and a 200-row result would crowd out the rest.
|
|
56
56
|
MAX_MESSAGE_CHARS = 4000
|
|
57
|
+
#: What one call to the agent reads. A longer stretch is summarized in
|
|
58
|
+
#: parts, oldest first, each part extending the summary of the previous one.
|
|
57
59
|
MAX_TRANSCRIPT_CHARS = 400_000
|
|
60
|
+
#: Parts per compaction: in the background, enough for a history that grew
|
|
61
|
+
#: without compaction (before BL-009) without summarizing forever; in line,
|
|
62
|
+
#: one, because the person is waiting.
|
|
63
|
+
MAX_CHUNKS = 6
|
|
58
64
|
|
|
59
65
|
@inject
|
|
60
66
|
def __init__(self,
|
|
@@ -73,7 +79,19 @@ class HistoryCompactionService:
|
|
|
73
79
|
# Entry point
|
|
74
80
|
# ------------------------------------------------------------------
|
|
75
81
|
|
|
76
|
-
def compact(self, company_short_name: str, user_identifier: str, model: str
|
|
82
|
+
def compact(self, company_short_name: str, user_identifier: str, model: str,
|
|
83
|
+
max_chunks: Optional[int] = None) -> dict:
|
|
84
|
+
result = self._compact(company_short_name, user_identifier, model, max_chunks)
|
|
85
|
+
if result.get("status") != "compacted":
|
|
86
|
+
# A run that ends without applying anything is as worth knowing as
|
|
87
|
+
# one that does: in production both looked the same ("executed").
|
|
88
|
+
log = logging.warning if result.get("status") == "error" else logging.info
|
|
89
|
+
log("History of %s/%s (model=%s) not compacted: %s",
|
|
90
|
+
company_short_name, user_identifier, model, result)
|
|
91
|
+
return result
|
|
92
|
+
|
|
93
|
+
def _compact(self, company_short_name: str, user_identifier: str, model: str,
|
|
94
|
+
max_chunks: Optional[int]) -> dict:
|
|
77
95
|
history = self.session_context.get_context_history(company_short_name, user_identifier, model=model) or []
|
|
78
96
|
budget = self.history_manager.context_budget_tokens(company_short_name, model)
|
|
79
97
|
plan = self.plan(history, budget)
|
|
@@ -86,24 +104,68 @@ class HistoryCompactionService:
|
|
|
86
104
|
if not company:
|
|
87
105
|
return {"status": "error", "reason": "company_not_found"}
|
|
88
106
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
user_identifier
|
|
107
|
+
chunks = self.chunk_material(plan.material)
|
|
108
|
+
limit = max(int(max_chunks or self.MAX_CHUNKS), 1)
|
|
109
|
+
dropped_chunks = max(len(chunks) - limit, 0)
|
|
110
|
+
if dropped_chunks:
|
|
111
|
+
# The oldest parts go unsummarized: a cost bound, logged so a short
|
|
112
|
+
# summary of a very long conversation can be explained.
|
|
113
|
+
logging.warning(
|
|
114
|
+
"History of %s/%s (model=%s) needs %s parts to summarize; only the %s most recent are.",
|
|
115
|
+
company_short_name, user_identifier, model, len(chunks), limit,
|
|
98
116
|
)
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
117
|
+
chunks = chunks[-limit:]
|
|
118
|
+
|
|
119
|
+
summary = plan.previous_summary
|
|
120
|
+
for chunk in chunks:
|
|
121
|
+
try:
|
|
122
|
+
response = self.system_agent_runner.run(
|
|
123
|
+
company,
|
|
124
|
+
self.AGENT_KEY,
|
|
125
|
+
{"previous_summary": summary, "transcript": self.render_transcript(chunk)},
|
|
126
|
+
user_identifier=user_identifier,
|
|
127
|
+
)
|
|
128
|
+
except SystemAgentUnavailable as e:
|
|
129
|
+
return {"status": "skipped", "reason": str(e)}
|
|
130
|
+
part = self._summary_from(response)
|
|
131
|
+
if not part:
|
|
132
|
+
return {"status": "error", "reason": "empty_summary"}
|
|
133
|
+
summary = part
|
|
134
|
+
|
|
135
|
+
result = self._apply(company_short_name, user_identifier, model, plan, summary)
|
|
136
|
+
if result.get("status") == "compacted":
|
|
137
|
+
result["chunks"] = len(chunks)
|
|
138
|
+
if dropped_chunks:
|
|
139
|
+
result["dropped_chunks"] = dropped_chunks
|
|
140
|
+
return result
|
|
105
141
|
|
|
106
|
-
|
|
142
|
+
@classmethod
|
|
143
|
+
def chunk_material(cls, material: list) -> list[list]:
|
|
144
|
+
"""The material split into parts the agent can read in one call, at turn boundaries.
|
|
145
|
+
|
|
146
|
+
A turn is never split, so a tool call stays with its result. A single
|
|
147
|
+
turn longer than a part is a part of its own (render_transcript clips it).
|
|
148
|
+
"""
|
|
149
|
+
turns: list[list] = []
|
|
150
|
+
for message in material or []:
|
|
151
|
+
if (isinstance(message, dict) and message.get("role") == "user") or not turns:
|
|
152
|
+
turns.append([message])
|
|
153
|
+
else:
|
|
154
|
+
turns[-1].append(message)
|
|
155
|
+
|
|
156
|
+
chunks: list[list] = []
|
|
157
|
+
current: list = []
|
|
158
|
+
current_chars = 0
|
|
159
|
+
for turn in turns:
|
|
160
|
+
turn_chars = len(cls.render_transcript(turn))
|
|
161
|
+
if current and current_chars + turn_chars > cls.MAX_TRANSCRIPT_CHARS:
|
|
162
|
+
chunks.append(current)
|
|
163
|
+
current, current_chars = [], 0
|
|
164
|
+
current.extend(turn)
|
|
165
|
+
current_chars += turn_chars
|
|
166
|
+
if current:
|
|
167
|
+
chunks.append(current)
|
|
168
|
+
return chunks
|
|
107
169
|
|
|
108
170
|
# ------------------------------------------------------------------
|
|
109
171
|
# Planning
|
|
@@ -253,11 +315,27 @@ class HistoryCompactionService:
|
|
|
253
315
|
|
|
254
316
|
@staticmethod
|
|
255
317
|
def _summary_from(response: Any) -> str:
|
|
318
|
+
"""The summary, however the model returned it.
|
|
319
|
+
|
|
320
|
+
The agent asks for `{"summary": ...}`, but over a long transcript a model
|
|
321
|
+
may answer the Markdown alone. That is still the summary; dropping it
|
|
322
|
+
threw away minutes of work and left the history to grow.
|
|
323
|
+
"""
|
|
256
324
|
if not isinstance(response, dict):
|
|
257
325
|
return ""
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
326
|
+
for key in ("structured_output", "parsed_output"):
|
|
327
|
+
structured = response.get(key)
|
|
328
|
+
if isinstance(structured, dict) and isinstance(structured.get("summary"), str):
|
|
329
|
+
return structured["summary"].strip()
|
|
330
|
+
answer = response.get("answer")
|
|
331
|
+
if isinstance(answer, str) and answer.strip():
|
|
332
|
+
text = answer.strip()
|
|
333
|
+
try:
|
|
334
|
+
parsed = json.loads(text)
|
|
335
|
+
except (TypeError, ValueError):
|
|
336
|
+
return text
|
|
337
|
+
if isinstance(parsed, dict) and isinstance(parsed.get("summary"), str):
|
|
338
|
+
return parsed["summary"].strip()
|
|
261
339
|
return ""
|
|
262
340
|
|
|
263
341
|
def _count(self, message: Any) -> int:
|