unique_toolkit 1.7.0__tar.gz → 1.8.1__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.
Files changed (156) hide show
  1. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/CHANGELOG.md +7 -0
  2. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/PKG-INFO +8 -1
  3. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/pyproject.toml +1 -1
  4. unique_toolkit-1.8.1/unique_toolkit/agentic/tools/a2a/__init__.py +20 -0
  5. unique_toolkit-1.8.1/unique_toolkit/agentic/tools/a2a/config.py +17 -0
  6. unique_toolkit-1.8.1/unique_toolkit/agentic/tools/a2a/evaluation/__init__.py +13 -0
  7. unique_toolkit-1.8.1/unique_toolkit/agentic/tools/a2a/evaluation/_utils.py +66 -0
  8. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/a2a/evaluation/config.py +19 -3
  9. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/a2a/evaluation/evaluator.py +82 -89
  10. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/a2a/manager.py +2 -2
  11. unique_toolkit-1.8.1/unique_toolkit/agentic/tools/a2a/postprocessing/__init__.py +13 -0
  12. unique_toolkit-1.7.0/unique_toolkit/agentic/tools/a2a/postprocessing/display.py → unique_toolkit-1.8.1/unique_toolkit/agentic/tools/a2a/postprocessing/_display.py +16 -7
  13. unique_toolkit-1.8.1/unique_toolkit/agentic/tools/a2a/postprocessing/_utils.py +19 -0
  14. unique_toolkit-1.8.1/unique_toolkit/agentic/tools/a2a/postprocessing/config.py +24 -0
  15. unique_toolkit-1.8.1/unique_toolkit/agentic/tools/a2a/postprocessing/postprocessor.py +230 -0
  16. unique_toolkit-1.8.1/unique_toolkit/agentic/tools/a2a/postprocessing/test/test_consolidate_references.py +665 -0
  17. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/a2a/postprocessing/test/test_display.py +54 -75
  18. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/a2a/postprocessing/test/test_postprocessor_reference_functions.py +53 -45
  19. unique_toolkit-1.8.1/unique_toolkit/agentic/tools/a2a/tool/__init__.py +4 -0
  20. unique_toolkit-1.7.0/unique_toolkit/agentic/tools/a2a/memory.py → unique_toolkit-1.8.1/unique_toolkit/agentic/tools/a2a/tool/_memory.py +1 -1
  21. unique_toolkit-1.7.0/unique_toolkit/agentic/tools/a2a/schema.py → unique_toolkit-1.8.1/unique_toolkit/agentic/tools/a2a/tool/_schema.py +0 -6
  22. unique_toolkit-1.8.1/unique_toolkit/agentic/tools/a2a/tool/config.py +63 -0
  23. {unique_toolkit-1.7.0/unique_toolkit/agentic/tools/a2a → unique_toolkit-1.8.1/unique_toolkit/agentic/tools/a2a/tool}/service.py +108 -65
  24. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/config.py +2 -2
  25. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/tool_manager.py +1 -2
  26. unique_toolkit-1.7.0/unique_toolkit/agentic/tools/a2a/__init__.py +0 -4
  27. unique_toolkit-1.7.0/unique_toolkit/agentic/tools/a2a/config.py +0 -57
  28. unique_toolkit-1.7.0/unique_toolkit/agentic/tools/a2a/evaluation/__init__.py +0 -6
  29. unique_toolkit-1.7.0/unique_toolkit/agentic/tools/a2a/postprocessing/__init__.py +0 -5
  30. unique_toolkit-1.7.0/unique_toolkit/agentic/tools/a2a/postprocessing/postprocessor.py +0 -231
  31. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/LICENSE +0 -0
  32. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/README.md +0 -0
  33. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/__init__.py +0 -0
  34. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/_common/_base_service.py +0 -0
  35. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/_common/_time_utils.py +0 -0
  36. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/_common/api_calling/human_verification_manager.py +0 -0
  37. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/_common/base_model_type_attribute.py +0 -0
  38. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/_common/chunk_relevancy_sorter/config.py +0 -0
  39. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/_common/chunk_relevancy_sorter/exception.py +0 -0
  40. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/_common/chunk_relevancy_sorter/schemas.py +0 -0
  41. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/_common/chunk_relevancy_sorter/service.py +0 -0
  42. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/_common/chunk_relevancy_sorter/tests/test_service.py +0 -0
  43. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/_common/default_language_model.py +0 -0
  44. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/_common/endpoint_builder.py +0 -0
  45. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/_common/endpoint_requestor.py +0 -0
  46. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/_common/exception.py +0 -0
  47. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/_common/feature_flags/schema.py +0 -0
  48. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/_common/pydantic/rjsf_tags.py +0 -0
  49. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/_common/pydantic_helpers.py +0 -0
  50. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/_common/string_utilities.py +0 -0
  51. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/_common/token/image_token_counting.py +0 -0
  52. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/_common/token/token_counting.py +0 -0
  53. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/_common/utils/__init__.py +0 -0
  54. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/_common/utils/structured_output/__init__.py +0 -0
  55. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/_common/utils/structured_output/schema.py +0 -0
  56. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/_common/utils/write_configuration.py +0 -0
  57. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/_common/validate_required_values.py +0 -0
  58. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/_common/validators.py +0 -0
  59. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/__init__.py +0 -0
  60. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/debug_info_manager/debug_info_manager.py +0 -0
  61. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/evaluation/config.py +0 -0
  62. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/evaluation/context_relevancy/prompts.py +0 -0
  63. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/evaluation/context_relevancy/schema.py +0 -0
  64. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/evaluation/context_relevancy/service.py +0 -0
  65. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/evaluation/evaluation_manager.py +0 -0
  66. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/evaluation/exception.py +0 -0
  67. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/evaluation/hallucination/constants.py +0 -0
  68. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/evaluation/hallucination/hallucination_evaluation.py +0 -0
  69. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/evaluation/hallucination/prompts.py +0 -0
  70. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/evaluation/hallucination/service.py +0 -0
  71. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/evaluation/hallucination/utils.py +0 -0
  72. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/evaluation/output_parser.py +0 -0
  73. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/evaluation/schemas.py +0 -0
  74. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/evaluation/tests/test_context_relevancy_service.py +0 -0
  75. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/evaluation/tests/test_output_parser.py +0 -0
  76. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/history_manager/history_construction_with_contents.py +0 -0
  77. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/history_manager/history_manager.py +0 -0
  78. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/history_manager/loop_token_reducer.py +0 -0
  79. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/history_manager/utils.py +0 -0
  80. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/postprocessor/postprocessor_manager.py +0 -0
  81. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/reference_manager/reference_manager.py +0 -0
  82. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/short_term_memory_manager/persistent_short_term_memory_manager.py +0 -0
  83. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/thinking_manager/thinking_manager.py +0 -0
  84. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/__init__.py +0 -0
  85. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/a2a/evaluation/summarization_user_message.j2 +0 -0
  86. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/agent_chunks_hanlder.py +0 -0
  87. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/factory.py +0 -0
  88. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/mcp/__init__.py +0 -0
  89. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/mcp/manager.py +0 -0
  90. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/mcp/models.py +0 -0
  91. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/mcp/tool_wrapper.py +0 -0
  92. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/schemas.py +0 -0
  93. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/test/test_mcp_manager.py +0 -0
  94. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/test/test_tool_progress_reporter.py +0 -0
  95. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/tool.py +0 -0
  96. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/tool_progress_reporter.py +0 -0
  97. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/utils/__init__.py +0 -0
  98. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/utils/execution/__init__.py +0 -0
  99. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/utils/execution/execution.py +0 -0
  100. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/utils/source_handling/__init__.py +0 -0
  101. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/utils/source_handling/schema.py +0 -0
  102. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/utils/source_handling/source_formatting.py +0 -0
  103. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/agentic/tools/utils/source_handling/tests/test_source_formatting.py +0 -0
  104. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/app/__init__.py +0 -0
  105. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/app/dev_util.py +0 -0
  106. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/app/init_logging.py +0 -0
  107. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/app/init_sdk.py +0 -0
  108. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/app/performance/async_tasks.py +0 -0
  109. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/app/performance/async_wrapper.py +0 -0
  110. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/app/schemas.py +0 -0
  111. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/app/unique_settings.py +0 -0
  112. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/app/verification.py +0 -0
  113. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/chat/__init__.py +0 -0
  114. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/chat/constants.py +0 -0
  115. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/chat/functions.py +0 -0
  116. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/chat/schemas.py +0 -0
  117. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/chat/service.py +0 -0
  118. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/chat/state.py +0 -0
  119. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/chat/utils.py +0 -0
  120. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/content/__init__.py +0 -0
  121. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/content/constants.py +0 -0
  122. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/content/functions.py +0 -0
  123. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/content/schemas.py +0 -0
  124. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/content/service.py +0 -0
  125. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/content/utils.py +0 -0
  126. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/embedding/__init__.py +0 -0
  127. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/embedding/constants.py +0 -0
  128. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/embedding/functions.py +0 -0
  129. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/embedding/schemas.py +0 -0
  130. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/embedding/service.py +0 -0
  131. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/embedding/utils.py +0 -0
  132. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/framework_utilities/__init__.py +0 -0
  133. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/framework_utilities/langchain/client.py +0 -0
  134. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/framework_utilities/langchain/history.py +0 -0
  135. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/framework_utilities/openai/__init__.py +0 -0
  136. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/framework_utilities/openai/client.py +0 -0
  137. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/framework_utilities/openai/message_builder.py +0 -0
  138. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/framework_utilities/utils.py +0 -0
  139. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/language_model/__init__.py +0 -0
  140. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/language_model/builder.py +0 -0
  141. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/language_model/constants.py +0 -0
  142. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/language_model/functions.py +0 -0
  143. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/language_model/infos.py +0 -0
  144. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/language_model/prompt.py +0 -0
  145. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/language_model/reference.py +0 -0
  146. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/language_model/schemas.py +0 -0
  147. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/language_model/service.py +0 -0
  148. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/language_model/utils.py +0 -0
  149. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/protocols/support.py +0 -0
  150. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/short_term_memory/__init__.py +0 -0
  151. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/short_term_memory/constants.py +0 -0
  152. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/short_term_memory/functions.py +0 -0
  153. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/short_term_memory/schemas.py +0 -0
  154. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/short_term_memory/service.py +0 -0
  155. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/smart_rules/__init__.py +0 -0
  156. {unique_toolkit-1.7.0 → unique_toolkit-1.8.1}/unique_toolkit/smart_rules/compile.py +0 -0
@@ -5,6 +5,13 @@ 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.8.1] - 2026-10-03
9
+ - Fix bug where sub agent evaluation config variable `include_evaluation` did not include aliases for previous names.
10
+
11
+ ## [1.8.0] - 2026-10-03
12
+ - Sub Agents now block when executing the same sub-agent multiple times with `reuse_chat` set to `True`.
13
+ - Sub Agents tool, evaluation and post-processing refactored and tests added.
14
+
8
15
  ## [1.7.0] - 2025-10-01
9
16
  - Add functionality to remove text in `get_user_visible_chat_history`
10
17
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: unique_toolkit
3
- Version: 1.7.0
3
+ Version: 1.8.1
4
4
  Summary:
5
5
  License: Proprietary
6
6
  Author: Cedric Klinkert
@@ -118,6 +118,13 @@ All notable changes to this project will be documented in this file.
118
118
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
119
119
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
120
120
 
121
+ ## [1.8.1] - 2026-10-03
122
+ - Fix bug where sub agent evaluation config variable `include_evaluation` did not include aliases for previous names.
123
+
124
+ ## [1.8.0] - 2026-10-03
125
+ - Sub Agents now block when executing the same sub-agent multiple times with `reuse_chat` set to `True`.
126
+ - Sub Agents tool, evaluation and post-processing refactored and tests added.
127
+
121
128
  ## [1.7.0] - 2025-10-01
122
129
  - Add functionality to remove text in `get_user_visible_chat_history`
123
130
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "unique_toolkit"
3
- version = "1.7.0"
3
+ version = "1.8.1"
4
4
  description = ""
5
5
  authors = [
6
6
  "Cedric Klinkert <cedric.klinkert@unique.ch>",
@@ -0,0 +1,20 @@
1
+ from unique_toolkit.agentic.tools.a2a.config import ExtendedSubAgentToolConfig
2
+ from unique_toolkit.agentic.tools.a2a.evaluation import (
3
+ SubAgentEvaluationService,
4
+ SubAgentEvaluationServiceConfig,
5
+ )
6
+ from unique_toolkit.agentic.tools.a2a.manager import A2AManager
7
+ from unique_toolkit.agentic.tools.a2a.postprocessing import (
8
+ SubAgentResponsesPostprocessor,
9
+ )
10
+ from unique_toolkit.agentic.tools.a2a.tool import SubAgentTool, SubAgentToolConfig
11
+
12
+ __all__ = [
13
+ "SubAgentToolConfig",
14
+ "SubAgentTool",
15
+ "SubAgentResponsesPostprocessor",
16
+ "A2AManager",
17
+ "ExtendedSubAgentToolConfig",
18
+ "SubAgentEvaluationServiceConfig",
19
+ "SubAgentEvaluationService",
20
+ ]
@@ -0,0 +1,17 @@
1
+ from pydantic import Field
2
+
3
+ from unique_toolkit.agentic.tools.a2a.evaluation import SubAgentEvaluationConfig
4
+ from unique_toolkit.agentic.tools.a2a.postprocessing import SubAgentDisplayConfig
5
+ from unique_toolkit.agentic.tools.a2a.tool import SubAgentToolConfig
6
+
7
+
8
+ # SubAgentToolConfig with display and evaluation configs
9
+ class ExtendedSubAgentToolConfig(SubAgentToolConfig):
10
+ response_display_config: SubAgentDisplayConfig = Field(
11
+ default_factory=SubAgentDisplayConfig,
12
+ description="Configuration for how to display the sub-agent response.",
13
+ )
14
+ evaluation_config: SubAgentEvaluationConfig = Field(
15
+ default_factory=SubAgentEvaluationConfig,
16
+ description="Configuration for handling assessments of the sub-agent response.",
17
+ )
@@ -0,0 +1,13 @@
1
+ from unique_toolkit.agentic.tools.a2a.evaluation.config import (
2
+ SubAgentEvaluationConfig,
3
+ SubAgentEvaluationServiceConfig,
4
+ )
5
+ from unique_toolkit.agentic.tools.a2a.evaluation.evaluator import (
6
+ SubAgentEvaluationService,
7
+ )
8
+
9
+ __all__ = [
10
+ "SubAgentEvaluationService",
11
+ "SubAgentEvaluationServiceConfig",
12
+ "SubAgentEvaluationConfig",
13
+ ]
@@ -0,0 +1,66 @@
1
+ import logging
2
+
3
+ import unique_sdk
4
+
5
+ from unique_toolkit.chat.schemas import (
6
+ ChatMessageAssessmentLabel,
7
+ ChatMessageAssessmentStatus,
8
+ )
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ _ASSESSMENT_LABEL_COMPARISON_DICT: dict[str, int] = {
13
+ ChatMessageAssessmentLabel.RED: 0,
14
+ ChatMessageAssessmentLabel.YELLOW: 1,
15
+ ChatMessageAssessmentLabel.GREEN: 2,
16
+ }
17
+
18
+
19
+ def _sort_assessments(
20
+ assessments: list[unique_sdk.Space.Assessment],
21
+ ) -> list[unique_sdk.Space.Assessment]:
22
+ return sorted(
23
+ assessments,
24
+ key=lambda x: _ASSESSMENT_LABEL_COMPARISON_DICT[x["label"]], # type: ignore (should be checked before sorting)
25
+ )
26
+
27
+
28
+ def _worst_label(
29
+ *labels: str,
30
+ ) -> str:
31
+ return min(
32
+ labels,
33
+ key=lambda x: _ASSESSMENT_LABEL_COMPARISON_DICT[x],
34
+ )
35
+
36
+
37
+ def _get_valid_assessments(
38
+ assessments: list[unique_sdk.Space.Assessment],
39
+ display_name: str,
40
+ sequence_number: int,
41
+ ) -> list[unique_sdk.Space.Assessment]:
42
+ valid_assessments = []
43
+ for assessment in assessments:
44
+ if (
45
+ assessment["label"] is None
46
+ or assessment["label"] not in ChatMessageAssessmentLabel
47
+ ):
48
+ logger.warning(
49
+ "Unkown assistant label %s for assistant %s (sequence number: %s) will be ignored",
50
+ assessment["label"],
51
+ display_name,
52
+ sequence_number,
53
+ )
54
+ continue
55
+ if assessment["status"] != ChatMessageAssessmentStatus.DONE:
56
+ logger.warning(
57
+ "Assessment %s for assistant %s (sequence number: %s) is not done (status: %s) will be ignored",
58
+ assessment["label"],
59
+ display_name,
60
+ sequence_number,
61
+ assessment["status"],
62
+ )
63
+ continue
64
+ valid_assessments.append(assessment)
65
+
66
+ return valid_assessments
@@ -1,8 +1,9 @@
1
1
  from pathlib import Path
2
2
 
3
- from pydantic import BaseModel, Field
3
+ from pydantic import AliasChoices, BaseModel, Field
4
4
 
5
5
  from unique_toolkit._common.default_language_model import DEFAULT_GPT_4o
6
+ from unique_toolkit._common.pydantic_helpers import get_configuration_dict
6
7
  from unique_toolkit._common.validators import LMI, get_LMI_default_field
7
8
  from unique_toolkit.chat.schemas import (
8
9
  ChatMessageAssessmentType,
@@ -22,12 +23,13 @@ with open(Path(__file__).parent / "summarization_user_message.j2", "r") as file:
22
23
  DEFAULT_SUMMARIZATION_USER_MESSAGE_TEMPLATE = file.read().strip()
23
24
 
24
25
 
25
- class SubAgentEvaluationConfig(BaseModel):
26
+ class SubAgentEvaluationServiceConfig(BaseModel):
27
+ model_config = get_configuration_dict()
28
+
26
29
  assessment_type: ChatMessageAssessmentType = Field(
27
30
  default=ChatMessageAssessmentType.COMPLIANCE,
28
31
  description="The type of assessment to use in the display.",
29
32
  )
30
-
31
33
  summarization_model: LMI = get_LMI_default_field(DEFAULT_GPT_4o)
32
34
  summarization_system_message: str = Field(
33
35
  default=DEFAULT_EVALUATION_SYSTEM_MESSAGE_TEMPLATE,
@@ -37,3 +39,17 @@ class SubAgentEvaluationConfig(BaseModel):
37
39
  default=DEFAULT_SUMMARIZATION_USER_MESSAGE_TEMPLATE,
38
40
  description="The user message template for the summarization model.",
39
41
  )
42
+
43
+
44
+ class SubAgentEvaluationConfig(BaseModel):
45
+ model_config = get_configuration_dict()
46
+
47
+ include_evaluation: bool = Field(
48
+ default=True,
49
+ description="Whether to include the evaluation in the response.",
50
+ validation_alias=AliasChoices(
51
+ "includeEvaluation",
52
+ "displayEvalution", # typo in old config name
53
+ "display_evalution",
54
+ ),
55
+ )
@@ -1,5 +1,4 @@
1
1
  import logging
2
- from collections import defaultdict
3
2
  from typing import override
4
3
 
5
4
  import unique_sdk
@@ -12,8 +11,16 @@ from unique_toolkit.agentic.evaluation.schemas import (
12
11
  EvaluationMetricName,
13
12
  EvaluationMetricResult,
14
13
  )
15
- from unique_toolkit.agentic.tools.a2a.evaluation.config import SubAgentEvaluationConfig
16
- from unique_toolkit.agentic.tools.a2a.service import SubAgentTool
14
+ from unique_toolkit.agentic.tools.a2a.evaluation._utils import (
15
+ _get_valid_assessments,
16
+ _sort_assessments,
17
+ _worst_label,
18
+ )
19
+ from unique_toolkit.agentic.tools.a2a.evaluation.config import (
20
+ SubAgentEvaluationConfig,
21
+ SubAgentEvaluationServiceConfig,
22
+ )
23
+ from unique_toolkit.agentic.tools.a2a.tool import SubAgentTool
17
24
  from unique_toolkit.chat.schemas import (
18
25
  ChatMessageAssessmentLabel,
19
26
  ChatMessageAssessmentStatus,
@@ -27,20 +34,19 @@ logger = logging.getLogger(__name__)
27
34
 
28
35
 
29
36
  class _SubAgentToolInfo(TypedDict):
30
- assessments: list[list[unique_sdk.Space.Assessment]]
37
+ assessments: dict[int, list[unique_sdk.Space.Assessment]]
31
38
  display_name: str
32
39
 
33
40
 
34
41
  NO_ASSESSMENTS_FOUND = "NO_ASSESSMENTS_FOUND"
35
42
 
36
43
 
37
- class SubAgentsEvaluation(Evaluation):
44
+ class SubAgentEvaluationService(Evaluation):
38
45
  DISPLAY_NAME = "Sub Agents"
39
46
 
40
47
  def __init__(
41
48
  self,
42
- config: SubAgentEvaluationConfig,
43
- sub_agent_tools: list[SubAgentTool],
49
+ config: SubAgentEvaluationServiceConfig,
44
50
  language_model_service: LanguageModelService,
45
51
  ):
46
52
  super().__init__(EvaluationMetricName.SUB_AGENT)
@@ -49,14 +55,6 @@ class SubAgentsEvaluation(Evaluation):
49
55
  self._assistant_id_to_tool_info: dict[str, _SubAgentToolInfo] = {}
50
56
  self._language_model_service = language_model_service
51
57
 
52
- for sub_agent_tool in sub_agent_tools:
53
- if sub_agent_tool.config.evaluation_config.display_evalution:
54
- sub_agent_tool.subscribe(self)
55
- self._assistant_id_to_tool_info[sub_agent_tool.config.assistant_id] = {
56
- "assessments": [],
57
- "display_name": sub_agent_tool.display_name(),
58
- }
59
-
60
58
  @override
61
59
  def get_assessment_type(self) -> ChatMessageAssessmentType:
62
60
  return self._config.assessment_type
@@ -71,102 +69,46 @@ class SubAgentsEvaluation(Evaluation):
71
69
 
72
70
  value = ChatMessageAssessmentLabel.GREEN
73
71
 
74
- # Use a dict in order to compare labels (RED being the worst)
75
- label_comparison_dict = defaultdict(
76
- lambda: 3
77
- ) # Unkown labels are highest in the sorting
78
- label_comparison_dict[ChatMessageAssessmentLabel.GREEN] = 2
79
- label_comparison_dict[ChatMessageAssessmentLabel.YELLOW] = 1
80
- label_comparison_dict[ChatMessageAssessmentLabel.RED] = 0
81
-
82
- for assistant_id, tool_info in self._assistant_id_to_tool_info.items():
72
+ for tool_info in self._assistant_id_to_tool_info.values():
83
73
  sub_agent_assessments = tool_info["assessments"] or []
84
- for i, assessments in enumerate(sub_agent_assessments, start=1):
85
- valid_assessments = []
86
- for assessment in assessments:
87
- if (
88
- assessment["label"] is None
89
- or assessment["label"] not in ChatMessageAssessmentLabel
90
- ):
91
- logger.warning(
92
- "Unkown assistant label %s for assistant %s will be ignored",
93
- assessment["label"],
94
- assistant_id,
95
- )
96
- continue
97
- if assessment["status"] != ChatMessageAssessmentStatus.DONE:
98
- logger.warning(
99
- "Assessment %s for assistant %s is not done (status: %s) will be ignored",
100
- assessment["label"],
101
- assistant_id,
102
- )
103
- continue
104
- valid_assessments.append(assessment)
74
+ display_name = tool_info["display_name"]
105
75
 
76
+ for sequence_number in sorted(sub_agent_assessments):
77
+ assessments = sub_agent_assessments[sequence_number]
78
+
79
+ valid_assessments = _get_valid_assessments(
80
+ assessments, display_name, sequence_number
81
+ )
106
82
  if len(valid_assessments) == 0:
107
83
  logger.info(
108
- "No valid assessment found for assistant %s", assistant_id
84
+ "No valid assessment found for assistant %s (sequence number: %s)",
85
+ display_name,
86
+ sequence_number,
109
87
  )
110
88
  continue
111
89
 
112
- assessments = sorted(
113
- valid_assessments, key=lambda x: label_comparison_dict[x["label"]]
114
- )
90
+ assessments = _sort_assessments(valid_assessments)
91
+ value = _worst_label(value, assessments[0]["label"]) # type: ignore
115
92
 
116
- for assessment in assessments:
117
- value = min(
118
- value,
119
- assessment["label"],
120
- key=lambda x: label_comparison_dict[x],
121
- )
122
93
  data = {
123
94
  "name": tool_info["display_name"],
124
95
  "assessments": assessments,
125
96
  }
126
97
  if len(sub_agent_assessments) > 1:
127
- data["name"] += f" {i}"
98
+ data["name"] += f" {sequence_number}"
128
99
 
129
100
  sub_agents_display_data.append(data)
130
101
 
131
102
  if len(sub_agents_display_data) == 0:
132
103
  logger.warning("No valid sub agent assessments found")
104
+
133
105
  return EvaluationMetricResult(
134
106
  name=self.get_name(),
135
107
  value=NO_ASSESSMENTS_FOUND,
136
108
  reason="No sub agents assessments found",
137
109
  )
138
110
 
139
- should_summarize = False
140
- reason = ""
141
-
142
- if len(sub_agents_display_data) > 1:
143
- should_summarize = True
144
- elif len(sub_agents_display_data) == 1:
145
- if len(sub_agents_display_data[0]["assessments"]) > 1:
146
- should_summarize = True
147
- else:
148
- reason = (
149
- sub_agents_display_data[0]["assessments"][0]["explanation"] or ""
150
- )
151
-
152
- if should_summarize:
153
- messages = (
154
- MessagesBuilder()
155
- .system_message_append(self._config.summarization_system_message)
156
- .user_message_append(
157
- Template(self._config.summarization_user_message_template).render(
158
- sub_agents=sub_agents_display_data,
159
- )
160
- )
161
- .build()
162
- )
163
-
164
- reason = await self._language_model_service.complete_async(
165
- messages=messages,
166
- model_name=self._config.summarization_model.name,
167
- temperature=0.0,
168
- )
169
- reason = str(reason.choices[0].message.content)
111
+ reason = await self._get_reason(sub_agents_display_data)
170
112
 
171
113
  return EvaluationMetricResult(
172
114
  name=self.get_name(),
@@ -196,8 +138,30 @@ class SubAgentsEvaluation(Evaluation):
196
138
  type=self.get_assessment_type(),
197
139
  )
198
140
 
141
+ def register_sub_agent_tool(
142
+ self, tool: SubAgentTool, evaluation_config: SubAgentEvaluationConfig
143
+ ) -> None:
144
+ if not evaluation_config.include_evaluation:
145
+ logger.warning(
146
+ "Sub agent tool %s has evaluation config `include_evaluation` set to False, responses will be ignored.",
147
+ tool.config.assistant_id,
148
+ )
149
+ return
150
+
151
+ if tool.config.assistant_id not in self._assistant_id_to_tool_info:
152
+ tool.subscribe(self)
153
+ self._assistant_id_to_tool_info[tool.config.assistant_id] = (
154
+ _SubAgentToolInfo(
155
+ display_name=tool.display_name(),
156
+ assessments={},
157
+ )
158
+ )
159
+
199
160
  def notify_sub_agent_response(
200
- self, sub_agent_assistant_id: str, response: unique_sdk.Space.Message
161
+ self,
162
+ response: unique_sdk.Space.Message,
163
+ sub_agent_assistant_id: str,
164
+ sequence_number: int,
201
165
  ) -> None:
202
166
  if sub_agent_assistant_id not in self._assistant_id_to_tool_info:
203
167
  logger.warning(
@@ -206,10 +170,39 @@ class SubAgentsEvaluation(Evaluation):
206
170
  )
207
171
  return
208
172
 
209
- self._assistant_id_to_tool_info[sub_agent_assistant_id]["assessments"].append(
173
+ sub_agent_assessments = self._assistant_id_to_tool_info[sub_agent_assistant_id][
174
+ "assessments"
175
+ ]
176
+ sub_agent_assessments[sequence_number] = (
210
177
  response[
211
178
  "assessment"
212
179
  ].copy() # Shallow copy as we don't modify individual assessments
213
180
  if response["assessment"] is not None
214
181
  else []
215
182
  )
183
+
184
+ async def _get_reason(self, sub_agents_display_data: list[dict]) -> str:
185
+ if (
186
+ len(sub_agents_display_data) == 1
187
+ and len(sub_agents_display_data[0]["assessments"]) == 1
188
+ ):
189
+ return sub_agents_display_data[0]["assessments"][0]["explanation"] or ""
190
+
191
+ messages = (
192
+ MessagesBuilder()
193
+ .system_message_append(self._config.summarization_system_message)
194
+ .user_message_append(
195
+ Template(self._config.summarization_user_message_template).render(
196
+ sub_agents=sub_agents_display_data,
197
+ )
198
+ )
199
+ .build()
200
+ )
201
+
202
+ reason = await self._language_model_service.complete_async(
203
+ messages=messages,
204
+ model_name=self._config.summarization_model.name,
205
+ temperature=0.0,
206
+ )
207
+
208
+ return str(reason.choices[0].message.content)
@@ -1,8 +1,8 @@
1
1
  from logging import Logger
2
2
 
3
- from unique_toolkit.agentic.tools.a2a.config import SubAgentToolConfig
4
- from unique_toolkit.agentic.tools.a2a.service import SubAgentTool, ToolProgressReporter
3
+ from unique_toolkit.agentic.tools.a2a.tool import SubAgentTool, SubAgentToolConfig
5
4
  from unique_toolkit.agentic.tools.config import ToolBuildConfig
5
+ from unique_toolkit.agentic.tools.tool_progress_reporter import ToolProgressReporter
6
6
  from unique_toolkit.app.schemas import ChatEvent
7
7
 
8
8
 
@@ -0,0 +1,13 @@
1
+ from unique_toolkit.agentic.tools.a2a.postprocessing.config import (
2
+ SubAgentDisplayConfig,
3
+ SubAgentResponseDisplayMode,
4
+ )
5
+ from unique_toolkit.agentic.tools.a2a.postprocessing.postprocessor import (
6
+ SubAgentResponsesPostprocessor,
7
+ )
8
+
9
+ __all__ = [
10
+ "SubAgentResponsesPostprocessor",
11
+ "SubAgentResponseDisplayMode",
12
+ "SubAgentDisplayConfig",
13
+ ]
@@ -2,7 +2,9 @@ import re
2
2
  from abc import ABC, abstractmethod
3
3
  from typing import Literal, override
4
4
 
5
- from unique_toolkit.agentic.tools.a2a.config import ResponseDisplayMode
5
+ from unique_toolkit.agentic.tools.a2a.postprocessing.config import (
6
+ SubAgentResponseDisplayMode,
7
+ )
6
8
 
7
9
 
8
10
  class _ResponseDisplayHandler(ABC):
@@ -84,13 +86,20 @@ class _DetailsResponseDisplayHandler(_ResponseDisplayHandler):
84
86
 
85
87
 
86
88
  _DISPLAY_HANDLERS = {
87
- ResponseDisplayMode.DETAILS_OPEN: _DetailsResponseDisplayHandler(mode="open"),
88
- ResponseDisplayMode.DETAILS_CLOSED: _DetailsResponseDisplayHandler(mode="closed"),
89
+ SubAgentResponseDisplayMode.DETAILS_OPEN: _DetailsResponseDisplayHandler(
90
+ mode="open"
91
+ ),
92
+ SubAgentResponseDisplayMode.DETAILS_CLOSED: _DetailsResponseDisplayHandler(
93
+ mode="closed"
94
+ ),
89
95
  }
90
96
 
91
97
 
92
- def build_sub_agent_answer_display(
93
- display_name: str, display_mode: ResponseDisplayMode, answer: str, assistant_id: str
98
+ def _build_sub_agent_answer_display(
99
+ display_name: str,
100
+ display_mode: SubAgentResponseDisplayMode,
101
+ answer: str,
102
+ assistant_id: str,
94
103
  ) -> str:
95
104
  if display_mode not in _DISPLAY_HANDLERS:
96
105
  return ""
@@ -102,8 +111,8 @@ def build_sub_agent_answer_display(
102
111
  )
103
112
 
104
113
 
105
- def remove_sub_agent_answer_from_text(
106
- display_mode: ResponseDisplayMode, text: str, assistant_id: str
114
+ def _remove_sub_agent_answer_from_text(
115
+ display_mode: SubAgentResponseDisplayMode, text: str, assistant_id: str
107
116
  ) -> str:
108
117
  if display_mode not in _DISPLAY_HANDLERS:
109
118
  return text
@@ -0,0 +1,19 @@
1
+ def _replace_references_in_text_non_overlapping(
2
+ text: str, ref_map: dict[int, int]
3
+ ) -> str:
4
+ for orig, repl in ref_map.items():
5
+ text = text.replace(f"<sup>{orig}</sup>", f"<sup>{repl}</sup>")
6
+ return text
7
+
8
+
9
+ def _replace_references_in_text(text: str, ref_map: dict[int, int]) -> str:
10
+ # 2 phase replacement, since the map keys and values can overlap
11
+ max_ref = max(max(ref_map.keys(), default=0), max(ref_map.values(), default=0)) + 1
12
+ unique_refs = range(max_ref, max_ref + len(ref_map))
13
+
14
+ text = _replace_references_in_text_non_overlapping(
15
+ text, dict(zip(ref_map.keys(), unique_refs))
16
+ )
17
+ return _replace_references_in_text_non_overlapping(
18
+ text, dict(zip(unique_refs, ref_map.values()))
19
+ )
@@ -0,0 +1,24 @@
1
+ from enum import StrEnum
2
+
3
+ from pydantic import BaseModel, Field
4
+
5
+ from unique_toolkit._common.pydantic_helpers import get_configuration_dict
6
+
7
+
8
+ class SubAgentResponseDisplayMode(StrEnum):
9
+ HIDDEN = "hidden"
10
+ DETAILS_OPEN = "details_open"
11
+ DETAILS_CLOSED = "details_closed"
12
+
13
+
14
+ class SubAgentDisplayConfig(BaseModel):
15
+ model_config = get_configuration_dict()
16
+
17
+ mode: SubAgentResponseDisplayMode = Field(
18
+ default=SubAgentResponseDisplayMode.HIDDEN,
19
+ description="Controls how to display the sub agent response.",
20
+ )
21
+ remove_from_history: bool = Field(
22
+ default=True,
23
+ description="If set, sub agent responses will be removed from the history on subsequent calls to the assistant.",
24
+ )