iatoolkit 2.77.1__tar.gz → 2.77.3__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.77.1/src/iatoolkit.egg-info → iatoolkit-2.77.3}/PKG-INFO +1 -1
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/__init__.py +1 -1
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/openrouter_decision_client.py +9 -1
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/repositories/llm_query_repo.py +4 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/configuration_service.py +6 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/context_builder_service.py +16 -0
- iatoolkit-2.77.3/src/iatoolkit/services/decision_attachments.py +248 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/decision_service.py +53 -2
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/llm_client_service.py +11 -1
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/query_service.py +24 -3
- {iatoolkit-2.77.1 → iatoolkit-2.77.3/src/iatoolkit.egg-info}/PKG-INFO +1 -1
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit.egg-info/SOURCES.txt +1 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/LICENSE +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/LICENSE_COMMUNITY.md +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/pyproject.toml +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/readme.md +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/setup.cfg +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/base_company.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/cli_commands.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/__init__.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/access.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/bridge_validation.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/company_paths.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/decision_types.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/exceptions.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/interfaces/__init__.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/interfaces/asset_storage.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/interfaces/database_provider.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/interfaces/history_compaction_trigger.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/interfaces/memory_compilation_trigger.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/interfaces/secret_provider.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/interfaces/signup_policy_resolver.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/interfaces/web_search_provider.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/memory_tools.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/model_registry.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/routes.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/search_terms.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/secret_resolver.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/session_manager.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/text_splitter.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/url_safety.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/util.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/company_registry.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/__init__.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/llm_capabilities.yaml +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_agents/history_compaction.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_agents/memory_compiler.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_agents/memory_correction.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_agents/memory_document_ingest.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_agents/memory_image_ingest.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_agents/memory_meeting_ingest.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_agents/memory_question_review.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_agents_pack.yaml +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts/channel_business_identity.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts/channel_conversation_continuity.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts/channel_response_style.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts/chat_state_rules.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts/chat_user_profile.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts/core_identity.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts/email_output.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts/file_download_output.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts/format_styles.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts/html_structures.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts/links_documents.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts/memory_usage.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts/multimodal_basics.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts/output_basics.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts/presentation_formatting.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts/query_main.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts/sql_aggregation_scope.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts/sql_casting.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts/sql_core.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts/sql_jsonb.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts/sql_mysql_casting.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts/sql_mysql_json.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts/sql_redshift_basics.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts/sql_redshift_types.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts/tool_html_passthrough.prompt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_prompts_pack.yaml +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_tools_pack.yaml +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/core.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/__init__.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/brevo_mail_app.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/call_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/connectors/__init__.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/connectors/file_connector.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/connectors/file_connector_factory.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/connectors/google_cloud_storage_connector.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/connectors/google_drive_connector.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/connectors/local_file_connector.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/connectors/s3_connector.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/google_auth_client.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/google_chat_app.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/inference_embeddings_client.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/inference_handler.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/jina_embeddings_client.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/llm_gateway_resolver.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/llm_providers/__init__.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/llm_providers/anthropic_adapter.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/llm_providers/deepseek_adapter.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/llm_providers/gemini_adapter.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/llm_providers/openai_adapter.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/llm_providers/openai_compatible_chat_adapter.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/llm_providers/openrouter_adapter.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/llm_proxy.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/llm_response.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/infra/redis_session_manager.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/locales/en.yaml +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/locales/es.yaml +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/repositories/__init__.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/repositories/advisory_lock.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/repositories/api_key_repo.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/repositories/bridge_repo.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/repositories/database_manager.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/repositories/document_repo.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/repositories/env_secret_provider.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/repositories/filesystem_asset_repository.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/repositories/knowledge_wiki_repo.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/repositories/mcp_token_repo.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/repositories/memory_question_repo.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/repositories/memory_repo.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/repositories/memory_usage_repo.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/repositories/models.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/repositories/profile_repo.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/repositories/prompt_resource_repo.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/repositories/sql_dataset_repo.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/repositories/sql_source_repo.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/repositories/vs_repo.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/runtime_logging.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/__init__.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/api_key_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/attachment_policy_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/auth_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/branding_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/company_context_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/dispatcher_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/embedding_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/excel_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/file_processor_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/history_compaction_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/history_manager_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/http_tool_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/i18n_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/inference_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/jwt_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/knowledge_base_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/language_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/license_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/mail_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/markdown_wiki_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/mcp_token_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/memory_compiler_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/memory_lookup_policy_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/memory_question_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/memory_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/memory_sources.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/memory_usage_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/memory_wiki_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/noop_history_compaction_trigger.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/noop_memory_compilation_trigger.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/operator_identity_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/parsers/__init__.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/parsers/contracts.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/parsers/image_normalizer.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/parsers/parsing_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/parsers/pdf_ocr_detection.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/parsers/provider_factory.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/parsers/provider_resolver.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/parsers/providers/__init__.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/parsers/providers/basic_provider.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/parsers/providers/docling_provider.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/parsers/validator.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/pdf_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/profile_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/prompt_resource_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/prompt_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/resource_access_scope.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/signup_guard_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/signup_policy_resolver.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/sql_catalog_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/sql_dataset_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/sql_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/sql_source_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/storage_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/structured_output_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/system_agent_catalog.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/system_agent_runner.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/system_prompt_catalog.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/system_tools.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/telemetry_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/tenant_wiki_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/tool_output_contract.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/tool_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/user_feedback_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/user_session_context_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/visual_kb_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/visual_tool_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/warmup_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/web_search/__init__.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/web_search/provider_factory.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/web_search/providers/__init__.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/web_search/providers/brave_provider.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/web_search_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/services/wiki_page_index_service.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/static/images/favicon.ico +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/static/images/favicon.png +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/static/images/fernando.jpeg +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/static/images/iatoolkit_core.png +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/static/images/iatoolkit_logo.png +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/static/js/chat_feedback_button.js +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/static/js/chat_filepond.js +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/static/js/chat_header_navigation.js +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/static/js/chat_help_content.js +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/static/js/chat_history_button.js +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/static/js/chat_logout_button.js +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/static/js/chat_main.js +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/static/js/chat_memory_button.js +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/static/js/chat_model_selector.js +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/static/js/chat_onboarding_button.js +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/static/js/chat_prompt_manager.js +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/static/styles/account.css +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/static/styles/chat_iatoolkit.css +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/static/styles/chat_modal.css +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/static/styles/chat_public.css +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/static/styles/documents.css +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/static/styles/landing_page.css +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/static/styles/llm_output.css +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/static/styles/onboarding.css +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/static/styles/wiki_page.css +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/templates/_chat_header.html +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/templates/_company_header.html +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/templates/_login_widget.html +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/templates/account.html +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/templates/base.html +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/templates/change_password.html +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/templates/chat.html +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/templates/chat_memory_modal.html +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/templates/chat_modals.html +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/templates/error.html +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/templates/forgot_password.html +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/templates/home_hosted_default.html +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/templates/login_global.html +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/templates/memory/wiki_schema.md +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/templates/pdf/base.html +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/templates/pdf/letter.html +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/templates/pdf/report.html +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/templates/pdf/simple.html +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/templates/signup.html +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/templates/wiki_page.html +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/__init__.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/account_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/api_key_api_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/base_login_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/categories_api_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/change_password_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/chat_context_preview_api_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/chat_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/configuration_api_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/connectors_api_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/embedding_api_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/forgot_password_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/help_content_api_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/history_api_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/home_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/init_context_api_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/llmquery_api_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/load_document_api_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/login_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/logout_api_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/memory_api_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/profile_api_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/prompt_api_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/prompt_context_preview_api_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/prompt_resource_api_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/rag_api_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/root_redirect_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/signup_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/static_page_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/tool_api_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/user_feedback_api_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/users_api_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/verify_user_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/views/wiki_page_view.py +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit.egg-info/dependency_links.txt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit.egg-info/requires.txt +0 -0
- {iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit.egg-info/top_level.txt +0 -0
|
@@ -66,12 +66,15 @@ class OpenRouterDecisionClient:
|
|
|
66
66
|
read_timeout_seconds: float = 60.0,
|
|
67
67
|
retry_attempts: int = DEFAULT_RETRY_ATTEMPTS,
|
|
68
68
|
user: str | None = None,
|
|
69
|
+
assets: dict[str, dict[str, str]] | None = None,
|
|
69
70
|
) -> DecisionResult:
|
|
70
71
|
payload: dict[str, Any] = {
|
|
71
72
|
"model": model,
|
|
72
73
|
"state": state,
|
|
73
74
|
"questions": {name: question.to_wire() for name, question in questions.items()},
|
|
74
75
|
}
|
|
76
|
+
if assets:
|
|
77
|
+
payload["assets"] = assets
|
|
75
78
|
if provider_policy:
|
|
76
79
|
payload["provider"] = dict(provider_policy)
|
|
77
80
|
if user:
|
|
@@ -109,7 +112,12 @@ class OpenRouterDecisionClient:
|
|
|
109
112
|
return DecisionResult.from_response(body, requested_model=model, latency_ms=latency_ms)
|
|
110
113
|
|
|
111
114
|
message = self._error_message(response)
|
|
112
|
-
|
|
115
|
+
# With images, a 5xx is how an oversized request answers (measured: a
|
|
116
|
+
# bare 500); sending it again only repeats it. Rate limits still retry.
|
|
117
|
+
retryable = response.status_code in RETRYABLE_STATUS and (
|
|
118
|
+
not assets or response.status_code == 429
|
|
119
|
+
)
|
|
120
|
+
if retryable and attempt < attempts:
|
|
113
121
|
logging.warning(
|
|
114
122
|
"Decision model '%s' answered %s (attempt %s/%s): %s",
|
|
115
123
|
model, response.status_code, attempt, attempts, message,
|
|
@@ -14,6 +14,7 @@ from iatoolkit.repositories.models import (
|
|
|
14
14
|
from injector import inject
|
|
15
15
|
from iatoolkit.repositories.database_manager import DatabaseManager
|
|
16
16
|
from sqlalchemy import or_, and_
|
|
17
|
+
from sqlalchemy.orm.attributes import flag_modified
|
|
17
18
|
from iatoolkit.repositories.models import CONTEXT_INIT_REQUEST_SOURCE
|
|
18
19
|
from typing import List
|
|
19
20
|
|
|
@@ -233,6 +234,9 @@ class LLMQueryRepo:
|
|
|
233
234
|
or dict(getattr(prompt, "runtime_policy", None) or {})
|
|
234
235
|
)
|
|
235
236
|
prompt.decision_spec = getattr(new_prompt, "decision_spec", None)
|
|
237
|
+
# The order of the questions is part of the agent, and dicts compare
|
|
238
|
+
# equal regardless of it: without this a reorder is never written.
|
|
239
|
+
flag_modified(prompt, "decision_spec")
|
|
236
240
|
else:
|
|
237
241
|
if new_prompt.active is None:
|
|
238
242
|
new_prompt.active = True
|
|
@@ -658,6 +658,12 @@ class ConfigurationService:
|
|
|
658
658
|
else:
|
|
659
659
|
if decisions_cfg.get("provider") is not None and not isinstance(decisions_cfg.get("provider"), dict):
|
|
660
660
|
add_error("llm.decisions.provider", "Must be a dictionary (e.g. {zdr: true}).")
|
|
661
|
+
raw_max_images = decisions_cfg.get("max_images")
|
|
662
|
+
if raw_max_images is not None and (
|
|
663
|
+
isinstance(raw_max_images, bool) or not isinstance(raw_max_images, int)
|
|
664
|
+
or not 1 <= raw_max_images <= 20
|
|
665
|
+
):
|
|
666
|
+
add_error("llm.decisions.max_images", "Must be an integer between 1 and 20.")
|
|
661
667
|
raw_path = decisions_cfg.get("path")
|
|
662
668
|
if raw_path is not None and (not isinstance(raw_path, str) or not raw_path.strip().startswith("/")):
|
|
663
669
|
add_error("llm.decisions.path", "Must be a path starting with '/'.")
|
|
@@ -577,6 +577,22 @@ class ContextBuilderService:
|
|
|
577
577
|
images=[],
|
|
578
578
|
) or "").strip()
|
|
579
579
|
|
|
580
|
+
def prepare_decision_attachments(self, company: Company, files: list, *, mode: str | None,
|
|
581
|
+
max_images: int):
|
|
582
|
+
"""A decision agent's attachments (BL-008, Etapa 2): images to send and text for the case."""
|
|
583
|
+
from iatoolkit.services.decision_attachments import prepare_decision_attachments
|
|
584
|
+
|
|
585
|
+
company_short_name = str(getattr(company, "short_name", "") or "default")
|
|
586
|
+
return prepare_decision_attachments(
|
|
587
|
+
files,
|
|
588
|
+
mode=mode,
|
|
589
|
+
max_images=max_images,
|
|
590
|
+
decode=self.util.normalize_base64_payload,
|
|
591
|
+
extract_text=lambda filename, content: self.parsing_service.extract_text_for_context(
|
|
592
|
+
filename=filename, content=content, company_short_name=company_short_name,
|
|
593
|
+
),
|
|
594
|
+
)
|
|
595
|
+
|
|
580
596
|
def build_user_turn_prompt(self,
|
|
581
597
|
company: Company,
|
|
582
598
|
user_identifier: str,
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
+
# Product: IAToolkit
|
|
3
|
+
#
|
|
4
|
+
# IAToolkit is open source software.
|
|
5
|
+
|
|
6
|
+
"""Attachments of a decision agent (BL-008, Etapa 2): images and PDFs.
|
|
7
|
+
|
|
8
|
+
Decision models take images only as PNG or JPEG `assets`, and each one limits
|
|
9
|
+
what it accepts: Jev refuses anything over ~1568 px on its longest side, and
|
|
10
|
+
every model tested answered a bare 500 to a 4 MB image. A PDF is not an asset
|
|
11
|
+
at all. So nothing goes out as it came in:
|
|
12
|
+
|
|
13
|
+
- every image, and every PDF page sent as an image, is normalized to a JPEG of
|
|
14
|
+
at most 1568 px, which fits every model, keeps the request small, lowers the
|
|
15
|
+
cost (image tokens grow with resolution) and accepts any format Pillow reads;
|
|
16
|
+
- a PDF's text, when the agent asks for it, joins the case as text, through
|
|
17
|
+
the same extractor the chat agents use.
|
|
18
|
+
|
|
19
|
+
What reaches the model is decided by the agent's attachment mode, the same
|
|
20
|
+
setting chat agents have:
|
|
21
|
+
|
|
22
|
+
| mode | image | PDF |
|
|
23
|
+
|-------------------------|---------------|-----------------------------|
|
|
24
|
+
| `extracted_only` | refused | its text joins the case |
|
|
25
|
+
| `native_only` | sent as image | its pages are sent as images|
|
|
26
|
+
| `native_plus_extracted` | sent as image | text and pages |
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
import hashlib
|
|
32
|
+
import io
|
|
33
|
+
from dataclasses import dataclass, field
|
|
34
|
+
from typing import Any, Callable
|
|
35
|
+
|
|
36
|
+
from iatoolkit.common.exceptions import IAToolkitException
|
|
37
|
+
|
|
38
|
+
MODE_EXTRACTED = "extracted_only"
|
|
39
|
+
MODE_NATIVE = "native_only"
|
|
40
|
+
MODE_BOTH = "native_plus_extracted"
|
|
41
|
+
MODES = (MODE_EXTRACTED, MODE_NATIVE, MODE_BOTH)
|
|
42
|
+
|
|
43
|
+
#: Longest side of every image sent. Jev's limit, measured (1568 passes, 2048
|
|
44
|
+
#: fails); every other model tested accepts it.
|
|
45
|
+
MAX_IMAGE_SIDE = 1568
|
|
46
|
+
JPEG_QUALITY = 85
|
|
47
|
+
#: Bytes of one image after normalization. 2.7 MB passed and 4 MB failed with a
|
|
48
|
+
#: 500; a 1568 px JPEG is far below this, so it only guards odd inputs.
|
|
49
|
+
MAX_IMAGE_BYTES = 2_500_000
|
|
50
|
+
DEFAULT_MAX_IMAGES = 5
|
|
51
|
+
|
|
52
|
+
_IMAGE_EXTENSIONS = (".png", ".jpg", ".jpeg", ".webp", ".gif", ".bmp", ".tif", ".tiff", ".heic", ".heif")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass(frozen=True)
|
|
56
|
+
class DecisionImage:
|
|
57
|
+
"""One image as sent: a normalized JPEG, named after where it came from."""
|
|
58
|
+
name: str
|
|
59
|
+
data: bytes
|
|
60
|
+
source: str
|
|
61
|
+
page: int | None = None
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def sha256(self) -> str:
|
|
65
|
+
return hashlib.sha256(self.data).hexdigest()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass
|
|
69
|
+
class PreparedAttachments:
|
|
70
|
+
images: list[DecisionImage] = field(default_factory=list)
|
|
71
|
+
#: Extracted text, ready to follow the rendered case.
|
|
72
|
+
text: str = ""
|
|
73
|
+
#: What was received and what was made of it, for the record (never the bytes).
|
|
74
|
+
manifest: list[dict[str, Any]] = field(default_factory=list)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _refuse(message: str) -> IAToolkitException:
|
|
78
|
+
return IAToolkitException(IAToolkitException.ErrorType.INVALID_PARAMETER, message)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def normalize_image(data: bytes, name: str = "image") -> bytes:
|
|
82
|
+
"""Any image Pillow reads, as a JPEG of at most MAX_IMAGE_SIDE px."""
|
|
83
|
+
from PIL import Image, ImageOps
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
image = Image.open(io.BytesIO(data))
|
|
87
|
+
image.load()
|
|
88
|
+
except Exception as exc:
|
|
89
|
+
raise _refuse(f"The attachment '{name}' is not an image this platform can read ({exc}).") from exc
|
|
90
|
+
|
|
91
|
+
image = ImageOps.exif_transpose(image)
|
|
92
|
+
if image.mode in ("RGBA", "LA", "P"):
|
|
93
|
+
# JPEG has no transparency: flatten on white, as the page it came from.
|
|
94
|
+
rgba = image.convert("RGBA")
|
|
95
|
+
background = Image.new("RGB", rgba.size, "white")
|
|
96
|
+
background.paste(rgba, mask=rgba.split()[-1])
|
|
97
|
+
image = background
|
|
98
|
+
elif image.mode != "RGB":
|
|
99
|
+
image = image.convert("RGB")
|
|
100
|
+
image.thumbnail((MAX_IMAGE_SIDE, MAX_IMAGE_SIDE))
|
|
101
|
+
|
|
102
|
+
quality = JPEG_QUALITY
|
|
103
|
+
while True:
|
|
104
|
+
buffer = io.BytesIO()
|
|
105
|
+
image.save(buffer, "JPEG", quality=quality, optimize=True)
|
|
106
|
+
encoded = buffer.getvalue()
|
|
107
|
+
if len(encoded) <= MAX_IMAGE_BYTES or quality <= 40:
|
|
108
|
+
break
|
|
109
|
+
quality -= 15
|
|
110
|
+
if len(encoded) > MAX_IMAGE_BYTES:
|
|
111
|
+
raise _refuse(f"The image '{name}' is still over {MAX_IMAGE_BYTES // 1_000_000} MB after resizing.")
|
|
112
|
+
return encoded
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def render_pdf_pages(data: bytes, name: str) -> list[bytes]:
|
|
116
|
+
"""Every page of a PDF as a normalized JPEG."""
|
|
117
|
+
import fitz
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
document = fitz.open(stream=data, filetype="pdf")
|
|
121
|
+
except Exception as exc:
|
|
122
|
+
raise _refuse(f"The PDF '{name}' could not be opened ({exc}).") from exc
|
|
123
|
+
pages = []
|
|
124
|
+
with document:
|
|
125
|
+
for page in document:
|
|
126
|
+
longest = max(page.rect.width, page.rect.height) or 1
|
|
127
|
+
zoom = min(MAX_IMAGE_SIDE / longest, 4.0)
|
|
128
|
+
pixmap = page.get_pixmap(matrix=fitz.Matrix(zoom, zoom))
|
|
129
|
+
pages.append(normalize_image(pixmap.tobytes("png"), name))
|
|
130
|
+
return pages
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def pdf_page_count(data: bytes) -> int:
|
|
134
|
+
import fitz
|
|
135
|
+
|
|
136
|
+
with fitz.open(stream=data, filetype="pdf") as document:
|
|
137
|
+
return document.page_count
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _is_image(filename: str) -> bool:
|
|
141
|
+
return filename.lower().endswith(_IMAGE_EXTENSIONS)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _is_pdf(filename: str) -> bool:
|
|
145
|
+
return filename.lower().endswith(".pdf")
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def prepare_decision_attachments(
|
|
149
|
+
files: list[dict] | None,
|
|
150
|
+
*,
|
|
151
|
+
mode: str | None,
|
|
152
|
+
max_images: int = DEFAULT_MAX_IMAGES,
|
|
153
|
+
decode: Callable[[Any], bytes],
|
|
154
|
+
extract_text: Callable[[str, bytes], str],
|
|
155
|
+
) -> PreparedAttachments:
|
|
156
|
+
"""What a decision agent sends for its attachments, or an error that says why not.
|
|
157
|
+
|
|
158
|
+
`decode` turns a file's payload (base64, possibly a data URL) into bytes;
|
|
159
|
+
`extract_text` is the extractor chat agents use for documents.
|
|
160
|
+
"""
|
|
161
|
+
prepared = PreparedAttachments()
|
|
162
|
+
if not files:
|
|
163
|
+
return prepared
|
|
164
|
+
mode = str(mode or MODE_EXTRACTED).strip().lower()
|
|
165
|
+
if mode not in MODES:
|
|
166
|
+
mode = MODE_EXTRACTED
|
|
167
|
+
native = mode in (MODE_NATIVE, MODE_BOTH)
|
|
168
|
+
extracted = mode in (MODE_EXTRACTED, MODE_BOTH)
|
|
169
|
+
text_parts: list[str] = []
|
|
170
|
+
|
|
171
|
+
for document in files:
|
|
172
|
+
filename = str(document.get("file_id") or document.get("filename") or document.get("name") or "").strip()
|
|
173
|
+
payload = document.get("base64") or document.get("content")
|
|
174
|
+
if not filename or not payload:
|
|
175
|
+
raise _refuse("An attachment has no name or no content.")
|
|
176
|
+
data = decode(payload)
|
|
177
|
+
if isinstance(data, str):
|
|
178
|
+
data = data.encode("utf-8")
|
|
179
|
+
entry: dict[str, Any] = {
|
|
180
|
+
"name": filename,
|
|
181
|
+
"bytes": len(data),
|
|
182
|
+
"sha256": hashlib.sha256(data).hexdigest(),
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if _is_image(filename):
|
|
186
|
+
if not native:
|
|
187
|
+
raise _refuse(
|
|
188
|
+
f"'{filename}' is an image, and this agent's attachment mode only sends extracted text. "
|
|
189
|
+
"Change it to native to send images."
|
|
190
|
+
)
|
|
191
|
+
_check_room(prepared, 1, max_images, filename)
|
|
192
|
+
prepared.images.append(DecisionImage(
|
|
193
|
+
name=f"img_{len(prepared.images) + 1}",
|
|
194
|
+
data=normalize_image(data, filename),
|
|
195
|
+
source=filename,
|
|
196
|
+
))
|
|
197
|
+
entry["sent_as"] = "image"
|
|
198
|
+
entry["images"] = 1
|
|
199
|
+
|
|
200
|
+
elif _is_pdf(filename):
|
|
201
|
+
entry["pages"] = pdf_page_count(data)
|
|
202
|
+
sent_as = []
|
|
203
|
+
if extracted:
|
|
204
|
+
text = (extract_text(filename, data) or "").strip()
|
|
205
|
+
if text:
|
|
206
|
+
text_parts.append(f"Documento adjunto '{filename}':\n{text}")
|
|
207
|
+
sent_as.append("text")
|
|
208
|
+
elif not native:
|
|
209
|
+
raise _refuse(
|
|
210
|
+
f"The PDF '{filename}' has no text to extract (it looks scanned). "
|
|
211
|
+
"Change the agent's attachment mode to native to send its pages as images."
|
|
212
|
+
)
|
|
213
|
+
if native:
|
|
214
|
+
_check_room(prepared, entry["pages"], max_images, filename)
|
|
215
|
+
for page_number, page in enumerate(render_pdf_pages(data, filename), start=1):
|
|
216
|
+
prepared.images.append(DecisionImage(
|
|
217
|
+
name=f"img_{len(prepared.images) + 1}",
|
|
218
|
+
data=page,
|
|
219
|
+
source=filename,
|
|
220
|
+
page=page_number,
|
|
221
|
+
))
|
|
222
|
+
sent_as.append("pages")
|
|
223
|
+
entry["images"] = entry["pages"]
|
|
224
|
+
entry["sent_as"] = "+".join(sent_as)
|
|
225
|
+
|
|
226
|
+
else:
|
|
227
|
+
if not extracted:
|
|
228
|
+
raise _refuse(
|
|
229
|
+
f"'{filename}' is neither an image nor a PDF; in native mode a decision agent sends only those."
|
|
230
|
+
)
|
|
231
|
+
text = (extract_text(filename, data) or "").strip()
|
|
232
|
+
if not text:
|
|
233
|
+
raise _refuse(f"No text could be extracted from '{filename}'.")
|
|
234
|
+
text_parts.append(f"Documento adjunto '{filename}':\n{text}")
|
|
235
|
+
entry["sent_as"] = "text"
|
|
236
|
+
|
|
237
|
+
prepared.manifest.append(entry)
|
|
238
|
+
|
|
239
|
+
prepared.text = "\n\n".join(text_parts)
|
|
240
|
+
return prepared
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _check_room(prepared: PreparedAttachments, needed: int, max_images: int, filename: str) -> None:
|
|
244
|
+
if len(prepared.images) + needed > max_images:
|
|
245
|
+
raise _refuse(
|
|
246
|
+
f"'{filename}' would take the images sent to {len(prepared.images) + needed}; "
|
|
247
|
+
f"a decision agent sends at most {max_images} (llm.decisions.max_images)."
|
|
248
|
+
)
|
|
@@ -12,6 +12,7 @@ endpoint but none of its adapters.
|
|
|
12
12
|
|
|
13
13
|
from __future__ import annotations
|
|
14
14
|
|
|
15
|
+
import base64
|
|
15
16
|
import json
|
|
16
17
|
import logging
|
|
17
18
|
from typing import Any
|
|
@@ -25,6 +26,7 @@ from iatoolkit.common.decision_types import (
|
|
|
25
26
|
parse_decision_spec,
|
|
26
27
|
)
|
|
27
28
|
from iatoolkit.common.exceptions import IAToolkitException
|
|
29
|
+
from iatoolkit.services.decision_attachments import DecisionImage
|
|
28
30
|
from iatoolkit.infra.llm_proxy import LLMProxy
|
|
29
31
|
from iatoolkit.infra.openrouter_decision_client import OpenRouterDecisionClient
|
|
30
32
|
from iatoolkit.services.configuration_service import ConfigurationService
|
|
@@ -98,6 +100,7 @@ class DecisionService:
|
|
|
98
100
|
decision_spec: dict,
|
|
99
101
|
user_identifier: str | None = None,
|
|
100
102
|
telemetry_execution: Any = None,
|
|
103
|
+
images: list[DecisionImage] | None = None,
|
|
101
104
|
) -> DecisionResult:
|
|
102
105
|
try:
|
|
103
106
|
questions = parse_decision_spec(decision_spec)
|
|
@@ -116,6 +119,10 @@ class DecisionService:
|
|
|
116
119
|
"The case to decide on is empty: the agent template rendered no text.",
|
|
117
120
|
)
|
|
118
121
|
self._warn_if_too_long(company_short_name, model_key, model_config, state_text)
|
|
122
|
+
images = list(images or [])
|
|
123
|
+
if images:
|
|
124
|
+
self._check_takes_images(model_key, model_config)
|
|
125
|
+
wire_state, assets = self.build_state(model_key, state_text, images)
|
|
119
126
|
|
|
120
127
|
client_config = self.llm_proxy.resolve_client_config(
|
|
121
128
|
company_short_name, DECISION_PROVIDER, model_config=model_config
|
|
@@ -131,7 +138,11 @@ class DecisionService:
|
|
|
131
138
|
name="iatoolkit.decision",
|
|
132
139
|
span_type="llm",
|
|
133
140
|
event={
|
|
134
|
-
"input": {
|
|
141
|
+
"input": {
|
|
142
|
+
"state": state_text,
|
|
143
|
+
"questions": {n: q.to_wire() for n, q in questions.items()},
|
|
144
|
+
"images": [{"name": i.name, "source": i.source, "page": i.page} for i in images],
|
|
145
|
+
},
|
|
135
146
|
"metadata": {"model": wire_model, "company": company_short_name},
|
|
136
147
|
},
|
|
137
148
|
)
|
|
@@ -143,8 +154,9 @@ class DecisionService:
|
|
|
143
154
|
api_key=client_config.get("api_key") or "",
|
|
144
155
|
url=url,
|
|
145
156
|
model=wire_model,
|
|
146
|
-
state=
|
|
157
|
+
state=wire_state,
|
|
147
158
|
questions=questions,
|
|
159
|
+
assets=assets,
|
|
148
160
|
provider_policy=provider_policy if isinstance(provider_policy, dict) else None,
|
|
149
161
|
default_headers=client_config.get("default_headers") or {},
|
|
150
162
|
connect_timeout_seconds=client_config.get("connect_timeout_seconds") or 10.0,
|
|
@@ -176,6 +188,45 @@ class DecisionService:
|
|
|
176
188
|
)
|
|
177
189
|
return result
|
|
178
190
|
|
|
191
|
+
@staticmethod
|
|
192
|
+
def _check_takes_images(model: str, model_config: dict) -> None:
|
|
193
|
+
capabilities = model_config.get("capabilities") if isinstance(model_config.get("capabilities"), dict) else {}
|
|
194
|
+
modalities = {str(item).strip().lower() for item in capabilities.get("input_modalities") or []}
|
|
195
|
+
if "image" not in modalities:
|
|
196
|
+
raise IAToolkitException(
|
|
197
|
+
IAToolkitException.ErrorType.INVALID_PARAMETER,
|
|
198
|
+
f"Decision model '{model}' does not read images; choose one that does, "
|
|
199
|
+
"or set the agent's attachment mode to extracted text.",
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
@staticmethod
|
|
203
|
+
def build_state(model: str, state_text: str, images: list[DecisionImage]) -> tuple[Any, dict | None]:
|
|
204
|
+
"""The `state` and `assets` for a case with images (measured on 9 models).
|
|
205
|
+
|
|
206
|
+
Every model that reads images takes them as whole top-level items of an
|
|
207
|
+
array state, referenced by their asset key: `[case, "img_1", ...]`. Only
|
|
208
|
+
Jev also reads a reference inside text or a nested object. ScaleDown
|
|
209
|
+
takes no assets at all and reads one image as a document instead.
|
|
210
|
+
"""
|
|
211
|
+
if not images:
|
|
212
|
+
return state_text, None
|
|
213
|
+
if model.startswith("scaledown/"):
|
|
214
|
+
if len(images) > 1:
|
|
215
|
+
raise IAToolkitException(
|
|
216
|
+
IAToolkitException.ErrorType.INVALID_PARAMETER,
|
|
217
|
+
f"Decision model '{model}' reads one image per case; {len(images)} were sent.",
|
|
218
|
+
)
|
|
219
|
+
return {
|
|
220
|
+
"text": state_text,
|
|
221
|
+
"document": base64.b64encode(images[0].data).decode("ascii"),
|
|
222
|
+
"document_mime_type": "image/jpeg",
|
|
223
|
+
}, None
|
|
224
|
+
assets = {
|
|
225
|
+
image.name: {"data_base64": base64.b64encode(image.data).decode("ascii"), "format": "jpg"}
|
|
226
|
+
for image in images
|
|
227
|
+
}
|
|
228
|
+
return [state_text, *[image.name for image in images]], assets
|
|
229
|
+
|
|
179
230
|
@staticmethod
|
|
180
231
|
def _warn_if_too_long(company_short_name: str, model: str, model_config: dict, state_text: str) -> None:
|
|
181
232
|
try:
|
|
@@ -146,7 +146,9 @@ class llmClient:
|
|
|
146
146
|
task_id: Optional[int] = None,
|
|
147
147
|
execution_metadata: Optional[Dict[str, Any]] = None,
|
|
148
148
|
telemetry_request: Optional[Dict[str, Any]] = None,
|
|
149
|
-
execution_trace: Optional[Dict[str, Any]] = None
|
|
149
|
+
execution_trace: Optional[Dict[str, Any]] = None,
|
|
150
|
+
images: Optional[list] = None,
|
|
151
|
+
attachments: Optional[List[Dict[str, Any]]] = None) -> dict:
|
|
150
152
|
"""Runs a decision agent's model (BL-008) and records it like any agent execution.
|
|
151
153
|
|
|
152
154
|
Same table, same `stats` keys as a chat turn — `model`, tokens, response
|
|
@@ -159,6 +161,12 @@ class llmClient:
|
|
|
159
161
|
telemetry_execution = self.telemetry_service.start_execution(telemetry_request)
|
|
160
162
|
telemetry_execution.record_input({"state": state, "questions": (decision_spec or {}).get("questions")})
|
|
161
163
|
base_stats: dict = {"model": model, "kind": "decision"}
|
|
164
|
+
if attachments:
|
|
165
|
+
# What was attached and how it was sent; never the bytes.
|
|
166
|
+
base_stats["attachments"] = {
|
|
167
|
+
"count": len(attachments),
|
|
168
|
+
"images_sent": len(images or []),
|
|
169
|
+
}
|
|
162
170
|
for key, value in (execution_metadata or {}).items():
|
|
163
171
|
if value is not None:
|
|
164
172
|
base_stats[key] = value
|
|
@@ -171,6 +179,7 @@ class llmClient:
|
|
|
171
179
|
decision_spec=decision_spec,
|
|
172
180
|
user_identifier=user_identifier,
|
|
173
181
|
telemetry_execution=telemetry_execution,
|
|
182
|
+
images=images,
|
|
174
183
|
)
|
|
175
184
|
except Exception as e:
|
|
176
185
|
error_message = getattr(e, "message", None) or str(e)
|
|
@@ -226,6 +235,7 @@ class llmClient:
|
|
|
226
235
|
"decision": result.to_dict(),
|
|
227
236
|
"state": state,
|
|
228
237
|
"decision_spec": decision_spec,
|
|
238
|
+
"attachments": list(attachments or []),
|
|
229
239
|
},
|
|
230
240
|
function_calls=[],
|
|
231
241
|
stats=stats,
|
|
@@ -1387,9 +1387,6 @@ class QueryService:
|
|
|
1387
1387
|
prompt_output_contract: dict,
|
|
1388
1388
|
execution_trace: dict | None) -> dict:
|
|
1389
1389
|
company_short_name = company.short_name
|
|
1390
|
-
if files:
|
|
1391
|
-
return {"error": True,
|
|
1392
|
-
"error_message": "Decision agents do not take attachments yet: send the case in the agent's fields."}
|
|
1393
1390
|
if not str(model or "").strip() and not str(prompt_output_contract.get("llm_model") or "").strip():
|
|
1394
1391
|
return {"error": True,
|
|
1395
1392
|
"error_message": f"Agent '{prompt_name}' has no decision model: choose one in its settings."}
|
|
@@ -1409,6 +1406,17 @@ class QueryService:
|
|
|
1409
1406
|
)
|
|
1410
1407
|
question = json.dumps({'prompt': prompt_name, 'data': safe_client_data}, default=str)
|
|
1411
1408
|
|
|
1409
|
+
# Attachments follow the agent's attachment mode: extracted text joins the
|
|
1410
|
+
# case, images (and PDF pages, in native mode) travel as images.
|
|
1411
|
+
prepared = self.context_builder.prepare_decision_attachments(
|
|
1412
|
+
company,
|
|
1413
|
+
files or [],
|
|
1414
|
+
mode=prompt_output_contract.get("attachment_mode"),
|
|
1415
|
+
max_images=self._decision_max_images(company_short_name),
|
|
1416
|
+
)
|
|
1417
|
+
if prepared.text:
|
|
1418
|
+
state = f"{state}\n\n{prepared.text}" if state else prepared.text
|
|
1419
|
+
|
|
1412
1420
|
execution_metadata: dict = {}
|
|
1413
1421
|
request_source = str(safe_client_data.get("source") or "").strip().lower() or None
|
|
1414
1422
|
if request_source:
|
|
@@ -1435,8 +1443,21 @@ class QueryService:
|
|
|
1435
1443
|
execution_metadata=execution_metadata,
|
|
1436
1444
|
telemetry_request=telemetry_request,
|
|
1437
1445
|
execution_trace=execution_trace,
|
|
1446
|
+
images=prepared.images,
|
|
1447
|
+
attachments=prepared.manifest,
|
|
1438
1448
|
)
|
|
1439
1449
|
|
|
1450
|
+
def _decision_max_images(self, company_short_name: str) -> int:
|
|
1451
|
+
from iatoolkit.services.decision_attachments import DEFAULT_MAX_IMAGES
|
|
1452
|
+
|
|
1453
|
+
llm_config = self.configuration_service.get_configuration(company_short_name, "llm") or {}
|
|
1454
|
+
decisions = llm_config.get("decisions") if isinstance(llm_config, dict) else None
|
|
1455
|
+
raw = decisions.get("max_images") if isinstance(decisions, dict) else None
|
|
1456
|
+
try:
|
|
1457
|
+
return int(raw) if raw is not None else DEFAULT_MAX_IMAGES
|
|
1458
|
+
except (TypeError, ValueError):
|
|
1459
|
+
return DEFAULT_MAX_IMAGES
|
|
1460
|
+
|
|
1440
1461
|
def llm_query(self,
|
|
1441
1462
|
company_short_name: str,
|
|
1442
1463
|
user_identifier: str,
|
|
@@ -130,6 +130,7 @@ src/iatoolkit/services/branding_service.py
|
|
|
130
130
|
src/iatoolkit/services/company_context_service.py
|
|
131
131
|
src/iatoolkit/services/configuration_service.py
|
|
132
132
|
src/iatoolkit/services/context_builder_service.py
|
|
133
|
+
src/iatoolkit/services/decision_attachments.py
|
|
133
134
|
src/iatoolkit/services/decision_service.py
|
|
134
135
|
src/iatoolkit/services/dispatcher_service.py
|
|
135
136
|
src/iatoolkit/services/embedding_service.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/interfaces/history_compaction_trigger.py
RENAMED
|
File without changes
|
{iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/interfaces/memory_compilation_trigger.py
RENAMED
|
File without changes
|
|
File without changes
|
{iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/interfaces/signup_policy_resolver.py
RENAMED
|
File without changes
|
{iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/common/interfaces/web_search_provider.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_agents/history_compaction.prompt
RENAMED
|
File without changes
|
{iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_agents/memory_compiler.prompt
RENAMED
|
File without changes
|
{iatoolkit-2.77.1 → iatoolkit-2.77.3}/src/iatoolkit/config/system_agents/memory_correction.prompt
RENAMED
|
File without changes
|