unique_toolkit 1.1.9__tar.gz → 1.2.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.
Files changed (139) hide show
  1. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/CHANGELOG.md +3 -0
  2. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/PKG-INFO +4 -1
  3. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/pyproject.toml +1 -1
  4. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/postprocessor/postprocessor_manager.py +3 -2
  5. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/a2a/config.py +20 -2
  6. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/a2a/manager.py +4 -4
  7. unique_toolkit-1.2.0/unique_toolkit/agentic/tools/a2a/postprocessing/__init__.py +5 -0
  8. unique_toolkit-1.2.0/unique_toolkit/agentic/tools/a2a/postprocessing/display.py +113 -0
  9. unique_toolkit-1.2.0/unique_toolkit/agentic/tools/a2a/postprocessing/postprocessor.py +204 -0
  10. unique_toolkit-1.2.0/unique_toolkit/agentic/tools/a2a/postprocessing/test/test_display.py +412 -0
  11. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/a2a/service.py +94 -25
  12. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/config.py +10 -1
  13. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/tool_manager.py +7 -1
  14. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/LICENSE +0 -0
  15. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/README.md +0 -0
  16. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/__init__.py +0 -0
  17. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/_common/_base_service.py +0 -0
  18. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/_common/_time_utils.py +0 -0
  19. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/_common/api_calling/human_verification_manager.py +0 -0
  20. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/_common/base_model_type_attribute.py +0 -0
  21. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/_common/chunk_relevancy_sorter/config.py +0 -0
  22. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/_common/chunk_relevancy_sorter/exception.py +0 -0
  23. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/_common/chunk_relevancy_sorter/schemas.py +0 -0
  24. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/_common/chunk_relevancy_sorter/service.py +0 -0
  25. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/_common/chunk_relevancy_sorter/tests/test_service.py +0 -0
  26. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/_common/default_language_model.py +0 -0
  27. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/_common/endpoint_builder.py +0 -0
  28. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/_common/endpoint_requestor.py +0 -0
  29. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/_common/exception.py +0 -0
  30. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/_common/feature_flags/schema.py +0 -0
  31. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/_common/pydantic_helpers.py +0 -0
  32. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/_common/string_utilities.py +0 -0
  33. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/_common/token/image_token_counting.py +0 -0
  34. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/_common/token/token_counting.py +0 -0
  35. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/_common/utils/__init__.py +0 -0
  36. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/_common/utils/structured_output/__init__.py +0 -0
  37. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/_common/utils/structured_output/schema.py +0 -0
  38. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/_common/utils/write_configuration.py +0 -0
  39. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/_common/validate_required_values.py +0 -0
  40. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/_common/validators.py +0 -0
  41. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/__init__.py +0 -0
  42. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/debug_info_manager/debug_info_manager.py +0 -0
  43. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/evaluation/config.py +0 -0
  44. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/evaluation/context_relevancy/prompts.py +0 -0
  45. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/evaluation/context_relevancy/schema.py +0 -0
  46. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/evaluation/context_relevancy/service.py +0 -0
  47. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/evaluation/evaluation_manager.py +0 -0
  48. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/evaluation/exception.py +0 -0
  49. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/evaluation/hallucination/constants.py +0 -0
  50. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/evaluation/hallucination/hallucination_evaluation.py +0 -0
  51. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/evaluation/hallucination/prompts.py +0 -0
  52. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/evaluation/hallucination/service.py +0 -0
  53. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/evaluation/hallucination/utils.py +0 -0
  54. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/evaluation/output_parser.py +0 -0
  55. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/evaluation/schemas.py +0 -0
  56. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/evaluation/tests/test_context_relevancy_service.py +0 -0
  57. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/evaluation/tests/test_output_parser.py +0 -0
  58. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/history_manager/history_construction_with_contents.py +0 -0
  59. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/history_manager/history_manager.py +0 -0
  60. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/history_manager/loop_token_reducer.py +0 -0
  61. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/history_manager/utils.py +0 -0
  62. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/reference_manager/reference_manager.py +0 -0
  63. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/short_term_memory_manager/persistent_short_term_memory_manager.py +0 -0
  64. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/thinking_manager/thinking_manager.py +0 -0
  65. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/__init__.py +0 -0
  66. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/a2a/__init__.py +0 -0
  67. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/a2a/memory.py +0 -0
  68. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/a2a/schema.py +0 -0
  69. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/agent_chunks_hanlder.py +0 -0
  70. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/factory.py +0 -0
  71. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/mcp/__init__.py +0 -0
  72. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/mcp/manager.py +0 -0
  73. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/mcp/models.py +0 -0
  74. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/mcp/tool_wrapper.py +0 -0
  75. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/schemas.py +0 -0
  76. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/test/test_mcp_manager.py +0 -0
  77. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/test/test_tool_progress_reporter.py +0 -0
  78. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/tool.py +0 -0
  79. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/tool_progress_reporter.py +0 -0
  80. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/utils/__init__.py +0 -0
  81. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/utils/execution/__init__.py +0 -0
  82. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/utils/execution/execution.py +0 -0
  83. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/utils/source_handling/__init__.py +0 -0
  84. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/utils/source_handling/schema.py +0 -0
  85. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/utils/source_handling/source_formatting.py +0 -0
  86. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/agentic/tools/utils/source_handling/tests/test_source_formatting.py +0 -0
  87. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/app/__init__.py +0 -0
  88. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/app/dev_util.py +0 -0
  89. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/app/init_logging.py +0 -0
  90. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/app/init_sdk.py +0 -0
  91. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/app/performance/async_tasks.py +0 -0
  92. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/app/performance/async_wrapper.py +0 -0
  93. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/app/schemas.py +0 -0
  94. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/app/unique_settings.py +0 -0
  95. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/app/verification.py +0 -0
  96. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/chat/__init__.py +0 -0
  97. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/chat/constants.py +0 -0
  98. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/chat/functions.py +0 -0
  99. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/chat/schemas.py +0 -0
  100. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/chat/service.py +0 -0
  101. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/chat/state.py +0 -0
  102. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/chat/utils.py +0 -0
  103. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/content/__init__.py +0 -0
  104. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/content/constants.py +0 -0
  105. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/content/functions.py +0 -0
  106. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/content/schemas.py +0 -0
  107. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/content/service.py +0 -0
  108. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/content/utils.py +0 -0
  109. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/embedding/__init__.py +0 -0
  110. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/embedding/constants.py +0 -0
  111. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/embedding/functions.py +0 -0
  112. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/embedding/schemas.py +0 -0
  113. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/embedding/service.py +0 -0
  114. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/embedding/utils.py +0 -0
  115. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/framework_utilities/__init__.py +0 -0
  116. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/framework_utilities/langchain/client.py +0 -0
  117. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/framework_utilities/langchain/history.py +0 -0
  118. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/framework_utilities/openai/__init__.py +0 -0
  119. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/framework_utilities/openai/client.py +0 -0
  120. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/framework_utilities/openai/message_builder.py +0 -0
  121. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/framework_utilities/utils.py +0 -0
  122. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/language_model/__init__.py +0 -0
  123. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/language_model/builder.py +0 -0
  124. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/language_model/constants.py +0 -0
  125. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/language_model/functions.py +0 -0
  126. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/language_model/infos.py +0 -0
  127. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/language_model/prompt.py +0 -0
  128. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/language_model/reference.py +0 -0
  129. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/language_model/schemas.py +0 -0
  130. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/language_model/service.py +0 -0
  131. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/language_model/utils.py +0 -0
  132. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/protocols/support.py +0 -0
  133. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/short_term_memory/__init__.py +0 -0
  134. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/short_term_memory/constants.py +0 -0
  135. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/short_term_memory/functions.py +0 -0
  136. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/short_term_memory/schemas.py +0 -0
  137. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/short_term_memory/service.py +0 -0
  138. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/smart_rules/__init__.py +0 -0
  139. {unique_toolkit-1.1.9 → unique_toolkit-1.2.0}/unique_toolkit/smart_rules/compile.py +0 -0
@@ -6,6 +6,9 @@ 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
8
 
9
+ ## [1.2.0] - 2025-09-24
10
+ - Add ability to display sub agent responses in the chat.
11
+
9
12
  ## [1.1.9] - 2025-09-24
10
13
  - Fix bug in `LanguageModelFunction` to extend support mistral tool calling.
11
14
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: unique_toolkit
3
- Version: 1.1.9
3
+ Version: 1.2.0
4
4
  Summary:
5
5
  License: Proprietary
6
6
  Author: Cedric Klinkert
@@ -119,6 +119,9 @@ 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
121
 
122
+ ## [1.2.0] - 2025-09-24
123
+ - Add ability to display sub agent responses in the chat.
124
+
122
125
  ## [1.1.9] - 2025-09-24
123
126
  - Fix bug in `LanguageModelFunction` to extend support mistral tool calling.
124
127
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "unique_toolkit"
3
- version = "1.1.9"
3
+ version = "1.2.0"
4
4
  description = ""
5
5
  authors = [
6
6
  "Cedric Klinkert <cedric.klinkert@unique.ch>",
@@ -16,10 +16,10 @@ class Postprocessor(ABC):
16
16
  def get_name(self) -> str:
17
17
  return self.name
18
18
 
19
- async def run(self, loop_response: LanguageModelStreamResponse) -> str:
19
+ async def run(self, loop_response: LanguageModelStreamResponse) -> None:
20
20
  raise NotImplementedError("Subclasses must implement this method.")
21
21
 
22
- async def apply_postprocessing_to_response(
22
+ def apply_postprocessing_to_response(
23
23
  self, loop_response: LanguageModelStreamResponse
24
24
  ) -> bool:
25
25
  raise NotImplementedError(
@@ -102,6 +102,7 @@ class PostprocessorManager:
102
102
  self._chat_service.modify_assistant_message(
103
103
  content=loop_response.message.text,
104
104
  message_id=loop_response.message.id,
105
+ references=loop_response.message.references,
105
106
  )
106
107
 
107
108
  async def execute_postprocessors(
@@ -1,3 +1,7 @@
1
+ from enum import StrEnum
2
+
3
+ from pydantic import BaseModel
4
+
1
5
  from unique_toolkit.agentic.tools.config import get_configuration_dict
2
6
  from unique_toolkit.agentic.tools.schemas import BaseToolConfig
3
7
 
@@ -6,22 +10,36 @@ This is the message that will be sent to the sub-agent.
6
10
  """.strip()
7
11
 
8
12
 
13
+ class ResponseDisplayMode(StrEnum):
14
+ HIDDEN = "hidden"
15
+ DETAILS_OPEN = "details_open"
16
+ DETAILS_CLOSED = "details_closed"
17
+
18
+
19
+ class SubAgentToolDisplayConfig(BaseModel):
20
+ model_config = get_configuration_dict()
21
+
22
+ mode: ResponseDisplayMode = ResponseDisplayMode.HIDDEN
23
+ remove_from_history: bool = True
24
+
25
+
9
26
  class SubAgentToolConfig(BaseToolConfig):
10
27
  model_config = get_configuration_dict()
11
28
 
12
- name: str = "default_name"
13
29
  assistant_id: str = ""
14
30
  chat_id: str | None = None
15
31
  reuse_chat: bool = True
32
+
16
33
  tool_description_for_system_prompt: str = ""
17
34
  tool_description: str = ""
18
35
  param_description_sub_agent_user_message: str = (
19
36
  DEFAULT_PARAM_DESCRIPTION_SUB_AGENT_USER_MESSAGE
20
37
  )
21
38
  tool_format_information_for_system_prompt: str = ""
22
-
23
39
  tool_description_for_user_prompt: str = ""
24
40
  tool_format_information_for_user_prompt: str = ""
25
41
 
26
42
  poll_interval: float = 1.0
27
43
  max_wait: float = 120.0
44
+
45
+ response_display_config: SubAgentToolDisplayConfig = SubAgentToolDisplayConfig()
@@ -3,8 +3,6 @@ from logging import Logger
3
3
  from unique_toolkit.agentic.tools.a2a.config import SubAgentToolConfig
4
4
  from unique_toolkit.agentic.tools.a2a.service import SubAgentTool, ToolProgressReporter
5
5
  from unique_toolkit.agentic.tools.config import ToolBuildConfig
6
- from unique_toolkit.agentic.tools.schemas import BaseToolConfig
7
- from unique_toolkit.agentic.tools.tool import Tool
8
6
  from unique_toolkit.app.schemas import ChatEvent
9
7
 
10
8
 
@@ -19,7 +17,7 @@ class A2AManager:
19
17
 
20
18
  def get_all_sub_agents(
21
19
  self, tool_configs: list[ToolBuildConfig], event: ChatEvent
22
- ) -> tuple[list[ToolBuildConfig], list[Tool[BaseToolConfig]]]:
20
+ ) -> tuple[list[ToolBuildConfig], list[SubAgentTool]]:
23
21
  sub_agents = []
24
22
 
25
23
  for tool_config in tool_configs:
@@ -32,13 +30,15 @@ class A2AManager:
32
30
  )
33
31
  continue
34
32
 
35
- sub_agent_tool_config: SubAgentToolConfig = tool_config.configuration
33
+ sub_agent_tool_config = tool_config.configuration
36
34
 
37
35
  sub_agents.append(
38
36
  SubAgentTool(
39
37
  configuration=sub_agent_tool_config,
40
38
  event=event,
41
39
  tool_progress_reporter=self._tool_progress_reporter,
40
+ name=tool_config.name,
41
+ display_name=tool_config.display_name,
42
42
  )
43
43
  )
44
44
 
@@ -0,0 +1,5 @@
1
+ from unique_toolkit.agentic.tools.a2a.postprocessing.postprocessor import (
2
+ SubAgentResponsesPostprocessor,
3
+ )
4
+
5
+ __all__ = ["SubAgentResponsesPostprocessor"]
@@ -0,0 +1,113 @@
1
+ import re
2
+ from abc import ABC, abstractmethod
3
+ from typing import Literal, override
4
+
5
+ from unique_toolkit.agentic.tools.a2a.config import ResponseDisplayMode
6
+
7
+
8
+ class _ResponseDisplayHandler(ABC):
9
+ @abstractmethod
10
+ def build_response_display(
11
+ self, display_name: str, assistant_id: str, answer: str
12
+ ) -> str:
13
+ raise NotImplementedError()
14
+
15
+ @abstractmethod
16
+ def remove_response_display(self, assistant_id: str, text: str) -> str:
17
+ raise NotImplementedError()
18
+
19
+
20
+ class _DetailsResponseDisplayHandler(_ResponseDisplayHandler):
21
+ def __init__(self, mode: Literal["open", "closed"]) -> None:
22
+ self._mode = mode
23
+
24
+ DETAILS_CLOSED_TEMPLATE = (
25
+ "<details><summary>{display_name}</summary>\n"
26
+ "\n"
27
+ '<div style="display: none;">{assistant_id}</div>\n'
28
+ "\n"
29
+ "{answer}\n"
30
+ "</details>\n"
31
+ "<br>\n"
32
+ "\n"
33
+ )
34
+
35
+ DETAILS_OPEN_TEMPLATE = (
36
+ "<details open><summary>{display_name}</summary>\n"
37
+ "\n"
38
+ '<div style="display: none;">{assistant_id}</div>\n'
39
+ "\n"
40
+ "{answer}\n"
41
+ "\n"
42
+ "</details>\n"
43
+ "<br>\n"
44
+ "\n"
45
+ )
46
+
47
+ def _get_detect_re(self, assistant_id: str) -> str:
48
+ if self._mode == "open":
49
+ return (
50
+ r"(?s)<details open>\s*"
51
+ r"<summary>(.*?)</summary>\s*"
52
+ rf"<div style=\"display: none;\">{re.escape(assistant_id)}</div>\s*"
53
+ r"(.*?)\s*"
54
+ r"</details>\s*"
55
+ r"<br>\s*"
56
+ )
57
+ else:
58
+ return (
59
+ r"(?s)<details>\s*"
60
+ r"<summary>(.*?)</summary>\s*"
61
+ rf"<div style=\"display: none;\">{re.escape(assistant_id)}</div>\s*"
62
+ r"(.*?)\s*"
63
+ r"</details>\s*"
64
+ r"<br>\s*"
65
+ )
66
+
67
+ def _get_template(self) -> str:
68
+ if self._mode == "open":
69
+ return self.DETAILS_OPEN_TEMPLATE
70
+ else:
71
+ return self.DETAILS_CLOSED_TEMPLATE
72
+
73
+ @override
74
+ def build_response_display(
75
+ self, display_name: str, assistant_id: str, answer: str
76
+ ) -> str:
77
+ return self._get_template().format(
78
+ assistant_id=assistant_id, display_name=display_name, answer=answer
79
+ )
80
+
81
+ @override
82
+ def remove_response_display(self, assistant_id: str, text: str) -> str:
83
+ return re.sub(self._get_detect_re(assistant_id=assistant_id), "", text)
84
+
85
+
86
+ _DISPLAY_HANDLERS = {
87
+ ResponseDisplayMode.DETAILS_OPEN: _DetailsResponseDisplayHandler(mode="open"),
88
+ ResponseDisplayMode.DETAILS_CLOSED: _DetailsResponseDisplayHandler(mode="closed"),
89
+ }
90
+
91
+
92
+ def build_sub_agent_answer_display(
93
+ display_name: str, display_mode: ResponseDisplayMode, answer: str, assistant_id: str
94
+ ) -> str:
95
+ if display_mode not in _DISPLAY_HANDLERS:
96
+ return ""
97
+
98
+ display_f = _DISPLAY_HANDLERS[display_mode]
99
+
100
+ return display_f.build_response_display(
101
+ display_name=display_name, answer=answer, assistant_id=assistant_id
102
+ )
103
+
104
+
105
+ def remove_sub_agent_answer_from_text(
106
+ display_mode: ResponseDisplayMode, text: str, assistant_id: str
107
+ ) -> str:
108
+ if display_mode not in _DISPLAY_HANDLERS:
109
+ return text
110
+
111
+ display_f = _DISPLAY_HANDLERS[display_mode]
112
+
113
+ return display_f.remove_response_display(assistant_id=assistant_id, text=text)
@@ -0,0 +1,204 @@
1
+ import logging
2
+ import re
3
+ from typing import NotRequired, TypedDict, override
4
+
5
+ import unique_sdk
6
+
7
+ from unique_toolkit.agentic.postprocessor.postprocessor_manager import Postprocessor
8
+ from unique_toolkit.agentic.tools.a2a.config import (
9
+ ResponseDisplayMode,
10
+ SubAgentToolDisplayConfig,
11
+ )
12
+ from unique_toolkit.agentic.tools.a2a.postprocessing.display import (
13
+ build_sub_agent_answer_display,
14
+ remove_sub_agent_answer_from_text,
15
+ )
16
+ from unique_toolkit.agentic.tools.a2a.service import SubAgentTool
17
+ from unique_toolkit.content.schemas import ContentReference
18
+ from unique_toolkit.language_model.schemas import LanguageModelStreamResponse
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+ SpaceMessage = unique_sdk.Space.Message
23
+
24
+
25
+ class _SubAgentMessageInfo(TypedDict):
26
+ text: str | None
27
+ references: list[unique_sdk.Space.Reference]
28
+
29
+
30
+ class _SubAgentToolInfo(TypedDict):
31
+ display_name: str
32
+ display_config: SubAgentToolDisplayConfig
33
+ response: NotRequired[_SubAgentMessageInfo]
34
+
35
+
36
+ class SubAgentResponsesPostprocessor(Postprocessor):
37
+ def __init__(
38
+ self,
39
+ user_id: str,
40
+ company_id: str,
41
+ agent_chat_id: str,
42
+ sub_agent_tools: list[SubAgentTool],
43
+ ):
44
+ super().__init__(name=self.__class__.__name__)
45
+
46
+ self._user_id = user_id
47
+ self._company_id = company_id
48
+
49
+ self._agent_chat_id = agent_chat_id
50
+
51
+ self._assistant_id_to_tool_info: dict[str, _SubAgentToolInfo] = {}
52
+
53
+ for sub_agent_tool in sub_agent_tools:
54
+ sub_agent_tool.subscribe(self)
55
+
56
+ self._assistant_id_to_tool_info[sub_agent_tool.config.assistant_id] = (
57
+ _SubAgentToolInfo(
58
+ display_config=sub_agent_tool.config.response_display_config,
59
+ display_name=sub_agent_tool.display_name(),
60
+ )
61
+ )
62
+
63
+ self._sub_agent_message = None
64
+
65
+ @override
66
+ async def run(self, loop_response: LanguageModelStreamResponse) -> None:
67
+ self._sub_agent_message = await unique_sdk.Space.get_latest_message_async(
68
+ user_id=self._user_id,
69
+ company_id=self._company_id,
70
+ chat_id=self._agent_chat_id,
71
+ )
72
+
73
+ @override
74
+ def apply_postprocessing_to_response(
75
+ self, loop_response: LanguageModelStreamResponse
76
+ ) -> bool:
77
+ logger.info("Adding sub agent responses to the response")
78
+
79
+ # Get responses to display
80
+ displayed = {}
81
+ for assistant_id, tool_info in self._assistant_id_to_tool_info.items():
82
+ display_mode = tool_info["display_config"].mode
83
+
84
+ if "response" not in tool_info:
85
+ logger.warning(
86
+ "No response from assistant %s",
87
+ assistant_id,
88
+ )
89
+ continue
90
+
91
+ if display_mode != ResponseDisplayMode.HIDDEN:
92
+ displayed[assistant_id] = tool_info["response"]
93
+
94
+ existing_refs = {
95
+ ref.source_id: ref.sequence_number
96
+ for ref in loop_response.message.references
97
+ }
98
+ _consolidate_references_in_place(displayed, existing_refs)
99
+
100
+ modified = len(displayed) > 0
101
+ for assistant_id, message in reversed(displayed.items()):
102
+ tool_info = self._assistant_id_to_tool_info[assistant_id]
103
+ display_mode = tool_info["display_config"].mode
104
+ display_name = tool_info["display_name"]
105
+ loop_response.message.text = (
106
+ build_sub_agent_answer_display(
107
+ display_name=display_name,
108
+ assistant_id=assistant_id,
109
+ display_mode=display_mode,
110
+ answer=message["text"],
111
+ )
112
+ + loop_response.message.text
113
+ )
114
+
115
+ assert self._sub_agent_message is not None
116
+
117
+ loop_response.message.references.extend(
118
+ ContentReference(
119
+ message_id=self._sub_agent_message["id"],
120
+ source_id=ref["sourceId"],
121
+ url=ref["url"],
122
+ source=ref["source"],
123
+ name=ref["name"],
124
+ sequence_number=ref["sequenceNumber"],
125
+ )
126
+ for ref in message["references"]
127
+ )
128
+
129
+ return modified
130
+
131
+ @override
132
+ async def remove_from_text(self, text) -> str:
133
+ for assistant_id, tool_info in self._assistant_id_to_tool_info.items():
134
+ display_config = tool_info["display_config"]
135
+ if display_config.remove_from_history:
136
+ text = remove_sub_agent_answer_from_text(
137
+ display_mode=display_config.mode,
138
+ text=text,
139
+ assistant_id=assistant_id,
140
+ )
141
+ return text
142
+
143
+ def notify_sub_agent_response(
144
+ self, sub_agent_assistant_id: str, response: SpaceMessage
145
+ ) -> None:
146
+ if sub_agent_assistant_id not in self._assistant_id_to_tool_info:
147
+ logger.warning(
148
+ "Unknown assistant id %s received, message will be ignored.",
149
+ sub_agent_assistant_id,
150
+ )
151
+ return
152
+
153
+ self._assistant_id_to_tool_info[sub_agent_assistant_id]["response"] = {
154
+ "text": response["text"],
155
+ "references": [
156
+ {
157
+ "name": ref["name"],
158
+ "url": ref["url"],
159
+ "sequenceNumber": ref["sequenceNumber"],
160
+ "originalIndex": [],
161
+ "sourceId": ref["sourceId"],
162
+ "source": ref["source"],
163
+ }
164
+ for ref in response["references"] or []
165
+ ],
166
+ }
167
+
168
+
169
+ def _consolidate_references_in_place(
170
+ messages: dict[str, _SubAgentMessageInfo], existing_refs: dict[str, int]
171
+ ) -> None:
172
+ start_index = max(existing_refs.values(), default=0) + 1
173
+
174
+ for assistant_id, message in messages.items():
175
+ references = message["references"]
176
+ if len(references) == 0 or message["text"] is None:
177
+ logger.info(
178
+ "Message from assistant %s does not contain any references",
179
+ assistant_id,
180
+ )
181
+ continue
182
+
183
+ references = list(sorted(references, key=lambda ref: ref["sequenceNumber"]))
184
+
185
+ message_new_refs = []
186
+ for reference in references:
187
+ source_id = reference["sourceId"]
188
+
189
+ if source_id not in existing_refs:
190
+ message_new_refs.append(reference)
191
+ existing_refs[source_id] = start_index
192
+ start_index += 1
193
+
194
+ reference_num = existing_refs[source_id]
195
+
196
+ seq_num = reference["sequenceNumber"]
197
+ message["text"] = re.sub(
198
+ rf"<sup>{seq_num}</sup>",
199
+ f"<sup>{reference_num}</sup>",
200
+ message["text"],
201
+ )
202
+ reference["sequenceNumber"] = reference_num
203
+
204
+ message["references"] = message_new_refs