unique_toolkit 1.29.4__tar.gz → 1.30.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/CHANGELOG.md +3 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/PKG-INFO +4 -1
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/pyproject.toml +8 -1
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/a2a/postprocessing/_display_utils.py +37 -3
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/a2a/postprocessing/config.py +21 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/a2a/postprocessing/display.py +40 -3
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/a2a/postprocessing/test/test_display_utils.py +388 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/app/__init__.py +3 -0
- unique_toolkit-1.30.0/unique_toolkit/app/fast_api_factory.py +131 -0
- unique_toolkit-1.30.0/unique_toolkit/app/webhook.py +77 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/LICENSE +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/README.md +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/_base_service.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/_time_utils.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/api_calling/human_verification_manager.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/base_model_type_attribute.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/chunk_relevancy_sorter/config.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/chunk_relevancy_sorter/exception.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/chunk_relevancy_sorter/schemas.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/chunk_relevancy_sorter/service.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/chunk_relevancy_sorter/tests/test_service.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/default_language_model.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/docx_generator/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/docx_generator/config.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/docx_generator/schemas.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/docx_generator/service.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/docx_generator/template/Doc Template.docx +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/endpoint_builder.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/endpoint_requestor.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/exception.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/experimental/endpoint_builder.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/experimental/endpoint_requestor.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/feature_flags/schema.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/pydantic/rjsf_tags.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/pydantic_helpers.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/referencing.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/string_utilities.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/tests/test_referencing.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/tests/test_string_utilities.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/token/image_token_counting.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/token/token_counting.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/utils/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/utils/files.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/utils/image/encode.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/utils/jinja/helpers.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/utils/jinja/render.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/utils/jinja/schema.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/utils/jinja/utils.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/utils/structured_output/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/utils/structured_output/schema.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/utils/write_configuration.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/validate_required_values.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/_common/validators.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/debug_info_manager/debug_info_manager.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/debug_info_manager/test/test_debug_info_manager.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/evaluation/config.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/evaluation/context_relevancy/prompts.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/evaluation/context_relevancy/schema.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/evaluation/context_relevancy/service.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/evaluation/evaluation_manager.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/evaluation/exception.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/evaluation/hallucination/constants.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/evaluation/hallucination/hallucination_evaluation.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/evaluation/hallucination/prompts.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/evaluation/hallucination/service.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/evaluation/hallucination/utils.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/evaluation/output_parser.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/evaluation/schemas.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/evaluation/tests/test_context_relevancy_service.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/evaluation/tests/test_output_parser.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/history_manager/history_construction_with_contents.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/history_manager/history_manager.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/history_manager/loop_token_reducer.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/history_manager/utils.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/message_log_manager/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/message_log_manager/service.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/postprocessor/postprocessor_manager.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/reference_manager/reference_manager.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/responses_api/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/responses_api/postprocessors/code_display.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/responses_api/postprocessors/generated_files.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/responses_api/stream_handler.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/short_term_memory_manager/persistent_short_term_memory_manager.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/thinking_manager/thinking_manager.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/a2a/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/a2a/config.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/a2a/evaluation/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/a2a/evaluation/_utils.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/a2a/evaluation/config.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/a2a/evaluation/evaluator.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/a2a/evaluation/summarization_user_message.j2 +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/a2a/manager.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/a2a/postprocessing/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/a2a/postprocessing/_ref_utils.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/a2a/postprocessing/references.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/a2a/postprocessing/test/test_ref_utils.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/a2a/prompts.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/a2a/response_watcher/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/a2a/response_watcher/service.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/a2a/tool/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/a2a/tool/_memory.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/a2a/tool/_schema.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/a2a/tool/config.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/a2a/tool/service.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/agent_chunks_hanlder.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/config.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/factory.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/mcp/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/mcp/manager.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/mcp/models.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/mcp/tool_wrapper.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/openai_builtin/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/openai_builtin/base.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/openai_builtin/code_interpreter/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/openai_builtin/code_interpreter/config.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/openai_builtin/code_interpreter/service.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/openai_builtin/manager.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/schemas.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/test/test_mcp_manager.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/test/test_tool_manager.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/test/test_tool_progress_reporter.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/tool.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/tool_manager.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/tool_progress_reporter.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/utils/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/utils/execution/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/utils/execution/execution.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/utils/source_handling/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/utils/source_handling/schema.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/utils/source_handling/source_formatting.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/agentic/tools/utils/source_handling/tests/test_source_formatting.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/app/dev_util.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/app/init_logging.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/app/init_sdk.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/app/performance/async_tasks.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/app/performance/async_wrapper.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/app/schemas.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/app/unique_settings.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/app/verification.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/chat/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/chat/constants.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/chat/deprecated/service.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/chat/functions.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/chat/rendering.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/chat/responses_api.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/chat/schemas.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/chat/service.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/chat/state.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/chat/utils.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/content/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/content/constants.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/content/functions.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/content/schemas.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/content/service.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/content/smart_rules.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/content/utils.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/embedding/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/embedding/constants.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/embedding/functions.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/embedding/schemas.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/embedding/service.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/embedding/utils.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/framework_utilities/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/framework_utilities/langchain/client.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/framework_utilities/langchain/history.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/framework_utilities/openai/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/framework_utilities/openai/client.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/framework_utilities/openai/message_builder.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/framework_utilities/utils.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/language_model/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/language_model/_responses_api_utils.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/language_model/builder.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/language_model/constants.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/language_model/default_language_model.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/language_model/functions.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/language_model/infos.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/language_model/prompt.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/language_model/reference.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/language_model/schemas.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/language_model/service.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/language_model/utils.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/protocols/support.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/services/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/services/chat_service.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/services/knowledge_base.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/short_term_memory/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/short_term_memory/constants.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/short_term_memory/functions.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/short_term_memory/schemas.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/short_term_memory/service.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/smart_rules/__init__.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/smart_rules/compile.py +0 -0
- {unique_toolkit-1.29.4 → unique_toolkit-1.30.0}/unique_toolkit/test_utilities/events.py +0 -0
|
@@ -5,6 +5,9 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.30.0] - 2025-11-26
|
|
9
|
+
- Add option to only display parts of sub agent responses.
|
|
10
|
+
|
|
8
11
|
## [1.29.4] - 2025-11-25
|
|
9
12
|
- Add display name to openai builtin tools
|
|
10
13
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: unique_toolkit
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.30.0
|
|
4
4
|
Summary:
|
|
5
5
|
License: Proprietary
|
|
6
6
|
Author: Cedric Klinkert
|
|
@@ -121,6 +121,9 @@ All notable changes to this project will be documented in this file.
|
|
|
121
121
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
122
122
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
123
123
|
|
|
124
|
+
## [1.30.0] - 2025-11-26
|
|
125
|
+
- Add option to only display parts of sub agent responses.
|
|
126
|
+
|
|
124
127
|
## [1.29.4] - 2025-11-25
|
|
125
128
|
- Add display name to openai builtin tools
|
|
126
129
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "unique_toolkit"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.30.0"
|
|
4
4
|
description = ""
|
|
5
5
|
authors = [
|
|
6
6
|
"Cedric Klinkert <cedric.klinkert@unique.ch>",
|
|
@@ -45,6 +45,13 @@ openai = "^1.97.0"
|
|
|
45
45
|
langchain = "^0.3.27"
|
|
46
46
|
langchain-openai = "^0.3.28"
|
|
47
47
|
|
|
48
|
+
[tool.poetry.group.fastapi]
|
|
49
|
+
optional = true
|
|
50
|
+
|
|
51
|
+
[tool.poetry.group.fastapi.dependencies]
|
|
52
|
+
fastapi = "^0.119.0"
|
|
53
|
+
uvicorn = "^0.37.0"
|
|
54
|
+
|
|
48
55
|
[tool.poetry.group.dev.dependencies]
|
|
49
56
|
ruff = "0.12.10"
|
|
50
57
|
pytest = "^7.4.3"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import re
|
|
2
|
-
from typing import Literal
|
|
2
|
+
from typing import Literal, NamedTuple
|
|
3
3
|
|
|
4
4
|
from unique_toolkit.agentic.tools.a2a.postprocessing.config import (
|
|
5
5
|
SubAgentDisplayConfig,
|
|
@@ -126,7 +126,7 @@ def _get_display_template(
|
|
|
126
126
|
if add_block_border:
|
|
127
127
|
template = _wrap_with_block_border(template)
|
|
128
128
|
|
|
129
|
-
return template
|
|
129
|
+
return template.strip()
|
|
130
130
|
|
|
131
131
|
|
|
132
132
|
def _get_display_removal_re(
|
|
@@ -150,10 +150,40 @@ def _get_display_removal_re(
|
|
|
150
150
|
return re.compile(pattern, flags=re.DOTALL)
|
|
151
151
|
|
|
152
152
|
|
|
153
|
+
class SubAgentAnswerPart(NamedTuple):
|
|
154
|
+
matching_text: str # Matching text as found in the answer
|
|
155
|
+
formatted_text: str # Formatted text to be displayed
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def get_sub_agent_answer_parts(
|
|
159
|
+
answer: str,
|
|
160
|
+
display_config: SubAgentDisplayConfig,
|
|
161
|
+
) -> list[SubAgentAnswerPart]:
|
|
162
|
+
if display_config.mode == SubAgentResponseDisplayMode.HIDDEN:
|
|
163
|
+
return []
|
|
164
|
+
|
|
165
|
+
if len(display_config.answer_substrings_config) == 0:
|
|
166
|
+
return [SubAgentAnswerPart(matching_text=answer, formatted_text=answer)]
|
|
167
|
+
|
|
168
|
+
substrings = []
|
|
169
|
+
for config in display_config.answer_substrings_config:
|
|
170
|
+
match = re.search(config.regexp, answer)
|
|
171
|
+
if match is not None:
|
|
172
|
+
text = match.group(0)
|
|
173
|
+
substrings.append(
|
|
174
|
+
SubAgentAnswerPart(
|
|
175
|
+
matching_text=text,
|
|
176
|
+
formatted_text=config.display_template.format(text),
|
|
177
|
+
)
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
return substrings
|
|
181
|
+
|
|
182
|
+
|
|
153
183
|
def get_sub_agent_answer_display(
|
|
154
184
|
display_name: str,
|
|
155
185
|
display_config: SubAgentDisplayConfig,
|
|
156
|
-
answer: str,
|
|
186
|
+
answer: str | list[str],
|
|
157
187
|
assistant_id: str,
|
|
158
188
|
) -> str:
|
|
159
189
|
template = _get_display_template(
|
|
@@ -162,6 +192,10 @@ def get_sub_agent_answer_display(
|
|
|
162
192
|
add_block_border=display_config.add_block_border,
|
|
163
193
|
display_title_template=display_config.display_title_template,
|
|
164
194
|
)
|
|
195
|
+
|
|
196
|
+
if isinstance(answer, list):
|
|
197
|
+
answer = display_config.answer_substrings_separator.join(answer)
|
|
198
|
+
|
|
165
199
|
return template.format(
|
|
166
200
|
display_name=display_name, answer=answer, assistant_id=assistant_id
|
|
167
201
|
)
|
|
@@ -13,6 +13,18 @@ class SubAgentResponseDisplayMode(StrEnum):
|
|
|
13
13
|
PLAIN = "plain"
|
|
14
14
|
|
|
15
15
|
|
|
16
|
+
class SubAgentAnswerSubstringConfig(BaseModel):
|
|
17
|
+
model_config = get_configuration_dict()
|
|
18
|
+
|
|
19
|
+
regexp: str = Field(
|
|
20
|
+
description="The regular expression to use to extract the substring. The first capture group will always be used.",
|
|
21
|
+
)
|
|
22
|
+
display_template: str = Field(
|
|
23
|
+
default="{}",
|
|
24
|
+
description="The template to use to display the substring. It should contain exactly one empty placeholder '{}' for the substring.",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
16
28
|
class SubAgentDisplayConfig(BaseModel):
|
|
17
29
|
model_config = get_configuration_dict()
|
|
18
30
|
|
|
@@ -47,3 +59,12 @@ class SubAgentDisplayConfig(BaseModel):
|
|
|
47
59
|
default=False,
|
|
48
60
|
description="If set, the sub agent references will be added to the main agent response references even in not mentioned in the main agent response text.",
|
|
49
61
|
)
|
|
62
|
+
|
|
63
|
+
answer_substrings_config: list[SubAgentAnswerSubstringConfig] = Field(
|
|
64
|
+
default=[],
|
|
65
|
+
description="If set, only parts of the answer matching the provided regular expressions will be displayed.",
|
|
66
|
+
)
|
|
67
|
+
answer_substrings_separator: str = Field(
|
|
68
|
+
default="\n",
|
|
69
|
+
description="The separator to use between the substrings.",
|
|
70
|
+
)
|
|
@@ -10,6 +10,7 @@ from unique_toolkit._common.pydantic_helpers import get_configuration_dict
|
|
|
10
10
|
from unique_toolkit.agentic.postprocessor.postprocessor_manager import Postprocessor
|
|
11
11
|
from unique_toolkit.agentic.tools.a2a.postprocessing._display_utils import (
|
|
12
12
|
get_sub_agent_answer_display,
|
|
13
|
+
get_sub_agent_answer_parts,
|
|
13
14
|
remove_sub_agent_answer_from_text,
|
|
14
15
|
)
|
|
15
16
|
from unique_toolkit.agentic.tools.a2a.postprocessing._ref_utils import (
|
|
@@ -44,6 +45,15 @@ class SubAgentResponsesPostprocessorConfig(BaseModel):
|
|
|
44
45
|
default=1, description="Time to sleep before updating the main agent message."
|
|
45
46
|
)
|
|
46
47
|
|
|
48
|
+
remove_duplicate_answers: bool = Field(
|
|
49
|
+
default=False,
|
|
50
|
+
description="If set, duplicate answers will only be displayed once. If sub agent is configured to display only substrings, this will remove duplicate substrings across different responses.",
|
|
51
|
+
)
|
|
52
|
+
answer_separator: str = Field(
|
|
53
|
+
default="",
|
|
54
|
+
description="The separator to use between the different sub agent answers.",
|
|
55
|
+
)
|
|
56
|
+
|
|
47
57
|
|
|
48
58
|
class SubAgentResponsesDisplayPostprocessor(Postprocessor):
|
|
49
59
|
def __init__(
|
|
@@ -96,6 +106,8 @@ class SubAgentResponsesDisplayPostprocessor(Postprocessor):
|
|
|
96
106
|
answers_displayed_before = []
|
|
97
107
|
answers_displayed_after = []
|
|
98
108
|
|
|
109
|
+
all_answers_displayed = set()
|
|
110
|
+
|
|
99
111
|
for assistant_id, responses in displayed_sub_agent_responses.items():
|
|
100
112
|
for response in responses:
|
|
101
113
|
message = response.message
|
|
@@ -105,6 +117,9 @@ class SubAgentResponsesDisplayPostprocessor(Postprocessor):
|
|
|
105
117
|
loop_response=loop_response, response=message
|
|
106
118
|
)
|
|
107
119
|
|
|
120
|
+
if tool_info.display_config.mode == SubAgentResponseDisplayMode.HIDDEN:
|
|
121
|
+
continue
|
|
122
|
+
|
|
108
123
|
display_name = tool_info.display_name
|
|
109
124
|
if len(responses) > 1:
|
|
110
125
|
display_name += f" {response.sequence_number}"
|
|
@@ -116,13 +131,33 @@ class SubAgentResponsesDisplayPostprocessor(Postprocessor):
|
|
|
116
131
|
response.sequence_number,
|
|
117
132
|
)
|
|
118
133
|
|
|
119
|
-
|
|
134
|
+
message_text = message["text"] or ""
|
|
135
|
+
|
|
136
|
+
answer_parts = get_sub_agent_answer_parts(
|
|
137
|
+
answer=message_text,
|
|
138
|
+
display_config=tool_info.display_config,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
if len(answer_parts) == 0:
|
|
120
142
|
continue
|
|
121
143
|
|
|
144
|
+
answer_display_texts = []
|
|
145
|
+
if self._config.remove_duplicate_answers:
|
|
146
|
+
for answer_part in answer_parts:
|
|
147
|
+
if answer_part.matching_text in all_answers_displayed:
|
|
148
|
+
continue
|
|
149
|
+
|
|
150
|
+
all_answers_displayed.add(answer_part.matching_text)
|
|
151
|
+
answer_display_texts.append(answer_part.formatted_text)
|
|
152
|
+
else:
|
|
153
|
+
answer_display_texts = [
|
|
154
|
+
answer_part.formatted_text for answer_part in answer_parts
|
|
155
|
+
]
|
|
156
|
+
|
|
122
157
|
answer = get_sub_agent_answer_display(
|
|
123
158
|
display_name=display_name,
|
|
124
159
|
display_config=tool_info.display_config,
|
|
125
|
-
answer=
|
|
160
|
+
answer=answer_display_texts,
|
|
126
161
|
assistant_id=assistant_id,
|
|
127
162
|
)
|
|
128
163
|
|
|
@@ -135,6 +170,7 @@ class SubAgentResponsesDisplayPostprocessor(Postprocessor):
|
|
|
135
170
|
text=loop_response.message.text,
|
|
136
171
|
answers_before=answers_displayed_before,
|
|
137
172
|
answers_after=answers_displayed_after,
|
|
173
|
+
sep=self._config.answer_separator,
|
|
138
174
|
)
|
|
139
175
|
|
|
140
176
|
return True
|
|
@@ -182,4 +218,5 @@ def _get_final_answer_display(
|
|
|
182
218
|
|
|
183
219
|
if len(answers_after) > 0:
|
|
184
220
|
text = text + sep + sep.join(answers_after)
|
|
185
|
-
|
|
221
|
+
|
|
222
|
+
return text.strip()
|
|
@@ -16,9 +16,11 @@ from unique_toolkit.agentic.tools.a2a.postprocessing._display_utils import (
|
|
|
16
16
|
_wrap_with_details_tag,
|
|
17
17
|
_wrap_with_quote_border,
|
|
18
18
|
get_sub_agent_answer_display,
|
|
19
|
+
get_sub_agent_answer_parts,
|
|
19
20
|
remove_sub_agent_answer_from_text,
|
|
20
21
|
)
|
|
21
22
|
from unique_toolkit.agentic.tools.a2a.postprocessing.config import (
|
|
23
|
+
SubAgentAnswerSubstringConfig,
|
|
22
24
|
SubAgentDisplayConfig,
|
|
23
25
|
SubAgentResponseDisplayMode,
|
|
24
26
|
)
|
|
@@ -1333,3 +1335,389 @@ def test_remove_sub_agent_answer__no_op_when_assistant_not_found() -> None:
|
|
|
1333
1335
|
assert result == original_text
|
|
1334
1336
|
assert "Present answer" in result
|
|
1335
1337
|
assert "Present Agent" in result
|
|
1338
|
+
|
|
1339
|
+
|
|
1340
|
+
# Test get_sub_agent_answer_parts
|
|
1341
|
+
|
|
1342
|
+
|
|
1343
|
+
@pytest.mark.ai
|
|
1344
|
+
def test_get_sub_agent_answer_parts__returns_empty__when_hidden_mode() -> None:
|
|
1345
|
+
"""
|
|
1346
|
+
Purpose: Verify empty list returned for HIDDEN display mode.
|
|
1347
|
+
Why this matters: Hidden mode should not extract any answer parts.
|
|
1348
|
+
Setup summary: Set mode to HIDDEN, assert empty list.
|
|
1349
|
+
"""
|
|
1350
|
+
# Arrange
|
|
1351
|
+
answer = "Some answer text"
|
|
1352
|
+
config = SubAgentDisplayConfig(
|
|
1353
|
+
mode=SubAgentResponseDisplayMode.HIDDEN,
|
|
1354
|
+
)
|
|
1355
|
+
|
|
1356
|
+
# Act
|
|
1357
|
+
result = get_sub_agent_answer_parts(answer=answer, display_config=config)
|
|
1358
|
+
|
|
1359
|
+
# Assert
|
|
1360
|
+
assert result == []
|
|
1361
|
+
|
|
1362
|
+
|
|
1363
|
+
@pytest.mark.ai
|
|
1364
|
+
def test_get_sub_agent_answer_parts__returns_full_answer__when_no_config() -> None:
|
|
1365
|
+
"""
|
|
1366
|
+
Purpose: Verify full answer returned when no substring config provided.
|
|
1367
|
+
Why this matters: Default behavior should return entire answer.
|
|
1368
|
+
Setup summary: Provide answer without substring config, assert full answer.
|
|
1369
|
+
"""
|
|
1370
|
+
# Arrange
|
|
1371
|
+
answer = "This is the complete answer"
|
|
1372
|
+
config = SubAgentDisplayConfig(
|
|
1373
|
+
mode=SubAgentResponseDisplayMode.PLAIN,
|
|
1374
|
+
answer_substrings_config=[],
|
|
1375
|
+
)
|
|
1376
|
+
|
|
1377
|
+
# Act
|
|
1378
|
+
result = get_sub_agent_answer_parts(answer=answer, display_config=config)
|
|
1379
|
+
|
|
1380
|
+
# Assert
|
|
1381
|
+
assert len(result) == 1
|
|
1382
|
+
assert result[0].matching_text == answer
|
|
1383
|
+
assert result[0].formatted_text == answer
|
|
1384
|
+
|
|
1385
|
+
|
|
1386
|
+
@pytest.mark.ai
|
|
1387
|
+
def test_get_sub_agent_answer_parts__extracts_single_match__with_one_regexp() -> None:
|
|
1388
|
+
"""
|
|
1389
|
+
Purpose: Verify single substring extracted with one regexp config.
|
|
1390
|
+
Why this matters: Core functionality for extracting specific answer parts.
|
|
1391
|
+
Setup summary: Provide answer with single regexp config, assert match extracted.
|
|
1392
|
+
"""
|
|
1393
|
+
# Arrange
|
|
1394
|
+
answer = "The price is $42.99 for the item"
|
|
1395
|
+
config = SubAgentDisplayConfig(
|
|
1396
|
+
mode=SubAgentResponseDisplayMode.PLAIN,
|
|
1397
|
+
answer_substrings_config=[
|
|
1398
|
+
SubAgentAnswerSubstringConfig(regexp=r"\$\d+\.\d+"),
|
|
1399
|
+
],
|
|
1400
|
+
)
|
|
1401
|
+
|
|
1402
|
+
# Act
|
|
1403
|
+
result = get_sub_agent_answer_parts(answer=answer, display_config=config)
|
|
1404
|
+
|
|
1405
|
+
# Assert
|
|
1406
|
+
assert len(result) == 1
|
|
1407
|
+
assert result[0].matching_text == "$42.99"
|
|
1408
|
+
assert result[0].formatted_text == "$42.99"
|
|
1409
|
+
|
|
1410
|
+
|
|
1411
|
+
@pytest.mark.ai
|
|
1412
|
+
def test_get_sub_agent_answer_parts__extracts_multiple_matches__with_multiple_regexps() -> (
|
|
1413
|
+
None
|
|
1414
|
+
):
|
|
1415
|
+
"""
|
|
1416
|
+
Purpose: Verify multiple substrings extracted with multiple regexp configs.
|
|
1417
|
+
Why this matters: Supports extracting different types of information.
|
|
1418
|
+
Setup summary: Provide answer with multiple regexp configs, assert all matches.
|
|
1419
|
+
"""
|
|
1420
|
+
# Arrange
|
|
1421
|
+
answer = "Contact John at john@example.com or call 555-1234"
|
|
1422
|
+
config = SubAgentDisplayConfig(
|
|
1423
|
+
mode=SubAgentResponseDisplayMode.PLAIN,
|
|
1424
|
+
answer_substrings_config=[
|
|
1425
|
+
SubAgentAnswerSubstringConfig(
|
|
1426
|
+
regexp=r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b"
|
|
1427
|
+
),
|
|
1428
|
+
SubAgentAnswerSubstringConfig(regexp=r"\d{3}-\d{4}"),
|
|
1429
|
+
],
|
|
1430
|
+
)
|
|
1431
|
+
|
|
1432
|
+
# Act
|
|
1433
|
+
result = get_sub_agent_answer_parts(answer=answer, display_config=config)
|
|
1434
|
+
|
|
1435
|
+
# Assert
|
|
1436
|
+
assert len(result) == 2
|
|
1437
|
+
assert result[0].matching_text == "john@example.com"
|
|
1438
|
+
assert result[1].matching_text == "555-1234"
|
|
1439
|
+
|
|
1440
|
+
|
|
1441
|
+
@pytest.mark.ai
|
|
1442
|
+
def test_get_sub_agent_answer_parts__applies_display_template__to_matched_text() -> (
|
|
1443
|
+
None
|
|
1444
|
+
):
|
|
1445
|
+
"""
|
|
1446
|
+
Purpose: Verify display template is applied to format matched text.
|
|
1447
|
+
Why this matters: Allows customization of how extracted parts are displayed.
|
|
1448
|
+
Setup summary: Provide template with placeholder, assert formatted output.
|
|
1449
|
+
"""
|
|
1450
|
+
# Arrange
|
|
1451
|
+
answer = "The temperature is 72 degrees"
|
|
1452
|
+
config = SubAgentDisplayConfig(
|
|
1453
|
+
mode=SubAgentResponseDisplayMode.PLAIN,
|
|
1454
|
+
answer_substrings_config=[
|
|
1455
|
+
SubAgentAnswerSubstringConfig(
|
|
1456
|
+
regexp=r"\d+",
|
|
1457
|
+
display_template="Temperature: {}°F",
|
|
1458
|
+
),
|
|
1459
|
+
],
|
|
1460
|
+
)
|
|
1461
|
+
|
|
1462
|
+
# Act
|
|
1463
|
+
result = get_sub_agent_answer_parts(answer=answer, display_config=config)
|
|
1464
|
+
|
|
1465
|
+
# Assert
|
|
1466
|
+
assert len(result) == 1
|
|
1467
|
+
assert result[0].matching_text == "72"
|
|
1468
|
+
assert result[0].formatted_text == "Temperature: 72°F"
|
|
1469
|
+
|
|
1470
|
+
|
|
1471
|
+
@pytest.mark.ai
|
|
1472
|
+
def test_get_sub_agent_answer_parts__returns_empty_list__when_no_matches() -> None:
|
|
1473
|
+
"""
|
|
1474
|
+
Purpose: Verify empty list returned when regexp doesn't match answer.
|
|
1475
|
+
Why this matters: Handles cases where expected pattern not present.
|
|
1476
|
+
Setup summary: Provide regexp that doesn't match, assert empty list.
|
|
1477
|
+
"""
|
|
1478
|
+
# Arrange
|
|
1479
|
+
answer = "This is plain text without numbers"
|
|
1480
|
+
config = SubAgentDisplayConfig(
|
|
1481
|
+
mode=SubAgentResponseDisplayMode.PLAIN,
|
|
1482
|
+
answer_substrings_config=[
|
|
1483
|
+
SubAgentAnswerSubstringConfig(regexp=r"\d+"),
|
|
1484
|
+
],
|
|
1485
|
+
)
|
|
1486
|
+
|
|
1487
|
+
# Act
|
|
1488
|
+
result = get_sub_agent_answer_parts(answer=answer, display_config=config)
|
|
1489
|
+
|
|
1490
|
+
# Assert
|
|
1491
|
+
assert result == []
|
|
1492
|
+
|
|
1493
|
+
|
|
1494
|
+
@pytest.mark.ai
|
|
1495
|
+
def test_get_sub_agent_answer_parts__extracts_first_match_only__for_each_regexp() -> (
|
|
1496
|
+
None
|
|
1497
|
+
):
|
|
1498
|
+
"""
|
|
1499
|
+
Purpose: Verify only first match per regexp is extracted.
|
|
1500
|
+
Why this matters: Function uses re.search which finds first occurrence.
|
|
1501
|
+
Setup summary: Provide answer with multiple numbers, assert only first extracted.
|
|
1502
|
+
"""
|
|
1503
|
+
# Arrange
|
|
1504
|
+
answer = "First number is 42 and second is 99"
|
|
1505
|
+
config = SubAgentDisplayConfig(
|
|
1506
|
+
mode=SubAgentResponseDisplayMode.PLAIN,
|
|
1507
|
+
answer_substrings_config=[
|
|
1508
|
+
SubAgentAnswerSubstringConfig(regexp=r"\d+"),
|
|
1509
|
+
],
|
|
1510
|
+
)
|
|
1511
|
+
|
|
1512
|
+
# Act
|
|
1513
|
+
result = get_sub_agent_answer_parts(answer=answer, display_config=config)
|
|
1514
|
+
|
|
1515
|
+
# Assert
|
|
1516
|
+
assert len(result) == 1
|
|
1517
|
+
assert result[0].matching_text == "42"
|
|
1518
|
+
|
|
1519
|
+
|
|
1520
|
+
@pytest.mark.ai
|
|
1521
|
+
def test_get_sub_agent_answer_parts__handles_empty_answer__with_no_config() -> None:
|
|
1522
|
+
"""
|
|
1523
|
+
Purpose: Verify empty answer returned as single part when no config.
|
|
1524
|
+
Why this matters: Edge case handling for empty content.
|
|
1525
|
+
Setup summary: Provide empty answer, assert single empty part.
|
|
1526
|
+
"""
|
|
1527
|
+
# Arrange
|
|
1528
|
+
answer = ""
|
|
1529
|
+
config = SubAgentDisplayConfig(
|
|
1530
|
+
mode=SubAgentResponseDisplayMode.PLAIN,
|
|
1531
|
+
answer_substrings_config=[],
|
|
1532
|
+
)
|
|
1533
|
+
|
|
1534
|
+
# Act
|
|
1535
|
+
result = get_sub_agent_answer_parts(answer=answer, display_config=config)
|
|
1536
|
+
|
|
1537
|
+
# Assert
|
|
1538
|
+
assert len(result) == 1
|
|
1539
|
+
assert result[0].matching_text == ""
|
|
1540
|
+
assert result[0].formatted_text == ""
|
|
1541
|
+
|
|
1542
|
+
|
|
1543
|
+
@pytest.mark.ai
|
|
1544
|
+
def test_get_sub_agent_answer_parts__handles_empty_answer__with_regexp_config() -> None:
|
|
1545
|
+
"""
|
|
1546
|
+
Purpose: Verify empty list returned for empty answer with regexp config.
|
|
1547
|
+
Why this matters: No matches possible in empty string.
|
|
1548
|
+
Setup summary: Provide empty answer with regexp, assert empty list.
|
|
1549
|
+
"""
|
|
1550
|
+
# Arrange
|
|
1551
|
+
answer = ""
|
|
1552
|
+
config = SubAgentDisplayConfig(
|
|
1553
|
+
mode=SubAgentResponseDisplayMode.PLAIN,
|
|
1554
|
+
answer_substrings_config=[
|
|
1555
|
+
SubAgentAnswerSubstringConfig(regexp=r"\d+"),
|
|
1556
|
+
],
|
|
1557
|
+
)
|
|
1558
|
+
|
|
1559
|
+
# Act
|
|
1560
|
+
result = get_sub_agent_answer_parts(answer=answer, display_config=config)
|
|
1561
|
+
|
|
1562
|
+
# Assert
|
|
1563
|
+
assert result == []
|
|
1564
|
+
|
|
1565
|
+
|
|
1566
|
+
@pytest.mark.ai
|
|
1567
|
+
def test_get_sub_agent_answer_parts__handles_multiline_answer__with_regexp() -> None:
|
|
1568
|
+
"""
|
|
1569
|
+
Purpose: Verify regexp matching works across multiple lines.
|
|
1570
|
+
Why this matters: Answers can span multiple lines.
|
|
1571
|
+
Setup summary: Provide multiline answer with pattern, assert match found.
|
|
1572
|
+
"""
|
|
1573
|
+
# Arrange
|
|
1574
|
+
answer = "Line 1\nThe code is ABC123\nLine 3"
|
|
1575
|
+
config = SubAgentDisplayConfig(
|
|
1576
|
+
mode=SubAgentResponseDisplayMode.PLAIN,
|
|
1577
|
+
answer_substrings_config=[
|
|
1578
|
+
SubAgentAnswerSubstringConfig(regexp=r"[A-Z]{3}\d{3}"),
|
|
1579
|
+
],
|
|
1580
|
+
)
|
|
1581
|
+
|
|
1582
|
+
# Act
|
|
1583
|
+
result = get_sub_agent_answer_parts(answer=answer, display_config=config)
|
|
1584
|
+
|
|
1585
|
+
# Assert
|
|
1586
|
+
assert len(result) == 1
|
|
1587
|
+
assert result[0].matching_text == "ABC123"
|
|
1588
|
+
|
|
1589
|
+
|
|
1590
|
+
@pytest.mark.ai
|
|
1591
|
+
def test_get_sub_agent_answer_parts__handles_special_regex_chars__in_answer() -> None:
|
|
1592
|
+
"""
|
|
1593
|
+
Purpose: Verify regexp can match content with special regex characters.
|
|
1594
|
+
Why this matters: Answers may contain special characters.
|
|
1595
|
+
Setup summary: Provide answer with special chars, use proper escaping in regexp.
|
|
1596
|
+
"""
|
|
1597
|
+
# Arrange
|
|
1598
|
+
answer = "The expression is: [test] (value)"
|
|
1599
|
+
config = SubAgentDisplayConfig(
|
|
1600
|
+
mode=SubAgentResponseDisplayMode.PLAIN,
|
|
1601
|
+
answer_substrings_config=[
|
|
1602
|
+
SubAgentAnswerSubstringConfig(regexp=r"\[test\]"),
|
|
1603
|
+
],
|
|
1604
|
+
)
|
|
1605
|
+
|
|
1606
|
+
# Act
|
|
1607
|
+
result = get_sub_agent_answer_parts(answer=answer, display_config=config)
|
|
1608
|
+
|
|
1609
|
+
# Assert
|
|
1610
|
+
assert len(result) == 1
|
|
1611
|
+
assert result[0].matching_text == "[test]"
|
|
1612
|
+
|
|
1613
|
+
|
|
1614
|
+
@pytest.mark.ai
|
|
1615
|
+
def test_get_sub_agent_answer_parts__skips_non_matching_configs__returns_matches_only() -> (
|
|
1616
|
+
None
|
|
1617
|
+
):
|
|
1618
|
+
"""
|
|
1619
|
+
Purpose: Verify only matching regexp configs produce results.
|
|
1620
|
+
Why this matters: Should not fail on partial matches, only return what matches.
|
|
1621
|
+
Setup summary: Provide multiple configs where only some match, assert partial results.
|
|
1622
|
+
"""
|
|
1623
|
+
# Arrange
|
|
1624
|
+
answer = "Value is 42"
|
|
1625
|
+
config = SubAgentDisplayConfig(
|
|
1626
|
+
mode=SubAgentResponseDisplayMode.PLAIN,
|
|
1627
|
+
answer_substrings_config=[
|
|
1628
|
+
SubAgentAnswerSubstringConfig(regexp=r"\d+"), # Matches
|
|
1629
|
+
SubAgentAnswerSubstringConfig(regexp=r"[A-Z]{3}"), # Doesn't match
|
|
1630
|
+
SubAgentAnswerSubstringConfig(regexp=r"Value"), # Matches
|
|
1631
|
+
],
|
|
1632
|
+
)
|
|
1633
|
+
|
|
1634
|
+
# Act
|
|
1635
|
+
result = get_sub_agent_answer_parts(answer=answer, display_config=config)
|
|
1636
|
+
|
|
1637
|
+
# Assert
|
|
1638
|
+
assert len(result) == 2
|
|
1639
|
+
assert result[0].matching_text == "42"
|
|
1640
|
+
assert result[1].matching_text == "Value"
|
|
1641
|
+
|
|
1642
|
+
|
|
1643
|
+
@pytest.mark.ai
|
|
1644
|
+
def test_get_sub_agent_answer_parts__preserves_order__of_configs_not_matches() -> None:
|
|
1645
|
+
"""
|
|
1646
|
+
Purpose: Verify results follow config order, not match order in text.
|
|
1647
|
+
Why this matters: Predictable output order based on configuration.
|
|
1648
|
+
Setup summary: Provide configs in specific order, assert results match config order.
|
|
1649
|
+
"""
|
|
1650
|
+
# Arrange
|
|
1651
|
+
answer = "first 123 then abc"
|
|
1652
|
+
config = SubAgentDisplayConfig(
|
|
1653
|
+
mode=SubAgentResponseDisplayMode.PLAIN,
|
|
1654
|
+
answer_substrings_config=[
|
|
1655
|
+
SubAgentAnswerSubstringConfig(regexp=r"[a-z]{3,}"), # Matches "first"
|
|
1656
|
+
SubAgentAnswerSubstringConfig(regexp=r"\d+"), # Matches "123"
|
|
1657
|
+
],
|
|
1658
|
+
)
|
|
1659
|
+
|
|
1660
|
+
# Act
|
|
1661
|
+
result = get_sub_agent_answer_parts(answer=answer, display_config=config)
|
|
1662
|
+
|
|
1663
|
+
# Assert
|
|
1664
|
+
assert len(result) == 2
|
|
1665
|
+
# Results follow config order, not text order
|
|
1666
|
+
assert result[0].matching_text == "first"
|
|
1667
|
+
assert result[1].matching_text == "123"
|
|
1668
|
+
|
|
1669
|
+
|
|
1670
|
+
@pytest.mark.ai
|
|
1671
|
+
def test_get_sub_agent_answer_parts__handles_complex_template__with_multiple_placeholders() -> (
|
|
1672
|
+
None
|
|
1673
|
+
):
|
|
1674
|
+
"""
|
|
1675
|
+
Purpose: Verify complex display templates with formatting work correctly.
|
|
1676
|
+
Why this matters: Supports rich formatting of extracted content.
|
|
1677
|
+
Setup summary: Provide template with additional text, assert formatted correctly.
|
|
1678
|
+
"""
|
|
1679
|
+
# Arrange
|
|
1680
|
+
answer = "User score: 95"
|
|
1681
|
+
config = SubAgentDisplayConfig(
|
|
1682
|
+
mode=SubAgentResponseDisplayMode.PLAIN,
|
|
1683
|
+
answer_substrings_config=[
|
|
1684
|
+
SubAgentAnswerSubstringConfig(
|
|
1685
|
+
regexp=r"\d+",
|
|
1686
|
+
display_template="**Score: {}%**",
|
|
1687
|
+
),
|
|
1688
|
+
],
|
|
1689
|
+
)
|
|
1690
|
+
|
|
1691
|
+
# Act
|
|
1692
|
+
result = get_sub_agent_answer_parts(answer=answer, display_config=config)
|
|
1693
|
+
|
|
1694
|
+
# Assert
|
|
1695
|
+
assert len(result) == 1
|
|
1696
|
+
assert result[0].matching_text == "95"
|
|
1697
|
+
assert result[0].formatted_text == "**Score: 95%**"
|
|
1698
|
+
|
|
1699
|
+
|
|
1700
|
+
@pytest.mark.ai
|
|
1701
|
+
def test_get_sub_agent_answer_parts__works_with_details_modes__extracts_normally() -> (
|
|
1702
|
+
None
|
|
1703
|
+
):
|
|
1704
|
+
"""
|
|
1705
|
+
Purpose: Verify extraction works regardless of display mode (except HIDDEN).
|
|
1706
|
+
Why this matters: Substring extraction independent of display mode.
|
|
1707
|
+
Setup summary: Use DETAILS modes, assert extraction still works.
|
|
1708
|
+
"""
|
|
1709
|
+
# Arrange
|
|
1710
|
+
answer = "Result: SUCCESS"
|
|
1711
|
+
config = SubAgentDisplayConfig(
|
|
1712
|
+
mode=SubAgentResponseDisplayMode.DETAILS_CLOSED,
|
|
1713
|
+
answer_substrings_config=[
|
|
1714
|
+
SubAgentAnswerSubstringConfig(regexp=r"SUCCESS"),
|
|
1715
|
+
],
|
|
1716
|
+
)
|
|
1717
|
+
|
|
1718
|
+
# Act
|
|
1719
|
+
result = get_sub_agent_answer_parts(answer=answer, display_config=config)
|
|
1720
|
+
|
|
1721
|
+
# Assert
|
|
1722
|
+
assert len(result) == 1
|
|
1723
|
+
assert result[0].matching_text == "SUCCESS"
|