unique_toolkit 1.4.3__tar.gz → 1.5.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 (145) hide show
  1. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/CHANGELOG.md +6 -0
  2. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/PKG-INFO +7 -1
  3. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/pyproject.toml +1 -1
  4. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/history_manager/history_manager.py +19 -0
  5. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/history_manager/loop_token_reducer.py +20 -5
  6. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/a2a/evaluation/config.py +1 -0
  7. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/a2a/evaluation/evaluator.py +43 -37
  8. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/a2a/postprocessing/postprocessor.py +75 -70
  9. unique_toolkit-1.5.0/unique_toolkit/agentic/tools/a2a/postprocessing/test/test_postprocessor_reference_functions.py +248 -0
  10. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/LICENSE +0 -0
  11. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/README.md +0 -0
  12. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/__init__.py +0 -0
  13. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/_common/_base_service.py +0 -0
  14. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/_common/_time_utils.py +0 -0
  15. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/_common/api_calling/human_verification_manager.py +0 -0
  16. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/_common/base_model_type_attribute.py +0 -0
  17. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/_common/chunk_relevancy_sorter/config.py +0 -0
  18. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/_common/chunk_relevancy_sorter/exception.py +0 -0
  19. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/_common/chunk_relevancy_sorter/schemas.py +0 -0
  20. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/_common/chunk_relevancy_sorter/service.py +0 -0
  21. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/_common/chunk_relevancy_sorter/tests/test_service.py +0 -0
  22. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/_common/default_language_model.py +0 -0
  23. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/_common/endpoint_builder.py +0 -0
  24. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/_common/endpoint_requestor.py +0 -0
  25. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/_common/exception.py +0 -0
  26. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/_common/feature_flags/schema.py +0 -0
  27. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/_common/pydantic/rjsf_tags.py +0 -0
  28. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/_common/pydantic_helpers.py +0 -0
  29. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/_common/string_utilities.py +0 -0
  30. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/_common/token/image_token_counting.py +0 -0
  31. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/_common/token/token_counting.py +0 -0
  32. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/_common/utils/__init__.py +0 -0
  33. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/_common/utils/structured_output/__init__.py +0 -0
  34. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/_common/utils/structured_output/schema.py +0 -0
  35. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/_common/utils/write_configuration.py +0 -0
  36. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/_common/validate_required_values.py +0 -0
  37. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/_common/validators.py +0 -0
  38. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/__init__.py +0 -0
  39. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/debug_info_manager/debug_info_manager.py +0 -0
  40. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/evaluation/config.py +0 -0
  41. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/evaluation/context_relevancy/prompts.py +0 -0
  42. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/evaluation/context_relevancy/schema.py +0 -0
  43. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/evaluation/context_relevancy/service.py +0 -0
  44. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/evaluation/evaluation_manager.py +0 -0
  45. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/evaluation/exception.py +0 -0
  46. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/evaluation/hallucination/constants.py +0 -0
  47. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/evaluation/hallucination/hallucination_evaluation.py +0 -0
  48. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/evaluation/hallucination/prompts.py +0 -0
  49. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/evaluation/hallucination/service.py +0 -0
  50. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/evaluation/hallucination/utils.py +0 -0
  51. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/evaluation/output_parser.py +0 -0
  52. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/evaluation/schemas.py +0 -0
  53. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/evaluation/tests/test_context_relevancy_service.py +0 -0
  54. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/evaluation/tests/test_output_parser.py +0 -0
  55. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/history_manager/history_construction_with_contents.py +0 -0
  56. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/history_manager/utils.py +0 -0
  57. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/postprocessor/postprocessor_manager.py +0 -0
  58. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/reference_manager/reference_manager.py +0 -0
  59. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/short_term_memory_manager/persistent_short_term_memory_manager.py +0 -0
  60. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/thinking_manager/thinking_manager.py +0 -0
  61. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/__init__.py +0 -0
  62. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/a2a/__init__.py +0 -0
  63. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/a2a/config.py +0 -0
  64. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/a2a/evaluation/__init__.py +0 -0
  65. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/a2a/evaluation/summarization_user_message.j2 +0 -0
  66. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/a2a/manager.py +0 -0
  67. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/a2a/memory.py +0 -0
  68. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/a2a/postprocessing/__init__.py +0 -0
  69. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/a2a/postprocessing/display.py +0 -0
  70. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/a2a/postprocessing/test/test_display.py +0 -0
  71. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/a2a/schema.py +0 -0
  72. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/a2a/service.py +0 -0
  73. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/agent_chunks_hanlder.py +0 -0
  74. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/config.py +0 -0
  75. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/factory.py +0 -0
  76. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/mcp/__init__.py +0 -0
  77. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/mcp/manager.py +0 -0
  78. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/mcp/models.py +0 -0
  79. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/mcp/tool_wrapper.py +0 -0
  80. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/schemas.py +0 -0
  81. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/test/test_mcp_manager.py +0 -0
  82. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/test/test_tool_progress_reporter.py +0 -0
  83. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/tool.py +0 -0
  84. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/tool_manager.py +0 -0
  85. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/tool_progress_reporter.py +0 -0
  86. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/utils/__init__.py +0 -0
  87. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/utils/execution/__init__.py +0 -0
  88. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/utils/execution/execution.py +0 -0
  89. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/utils/source_handling/__init__.py +0 -0
  90. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/utils/source_handling/schema.py +0 -0
  91. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/utils/source_handling/source_formatting.py +0 -0
  92. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/agentic/tools/utils/source_handling/tests/test_source_formatting.py +0 -0
  93. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/app/__init__.py +0 -0
  94. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/app/dev_util.py +0 -0
  95. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/app/init_logging.py +0 -0
  96. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/app/init_sdk.py +0 -0
  97. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/app/performance/async_tasks.py +0 -0
  98. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/app/performance/async_wrapper.py +0 -0
  99. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/app/schemas.py +0 -0
  100. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/app/unique_settings.py +0 -0
  101. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/app/verification.py +0 -0
  102. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/chat/__init__.py +0 -0
  103. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/chat/constants.py +0 -0
  104. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/chat/functions.py +0 -0
  105. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/chat/schemas.py +0 -0
  106. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/chat/service.py +0 -0
  107. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/chat/state.py +0 -0
  108. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/chat/utils.py +0 -0
  109. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/content/__init__.py +0 -0
  110. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/content/constants.py +0 -0
  111. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/content/functions.py +0 -0
  112. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/content/schemas.py +0 -0
  113. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/content/service.py +0 -0
  114. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/content/utils.py +0 -0
  115. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/embedding/__init__.py +0 -0
  116. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/embedding/constants.py +0 -0
  117. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/embedding/functions.py +0 -0
  118. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/embedding/schemas.py +0 -0
  119. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/embedding/service.py +0 -0
  120. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/embedding/utils.py +0 -0
  121. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/framework_utilities/__init__.py +0 -0
  122. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/framework_utilities/langchain/client.py +0 -0
  123. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/framework_utilities/langchain/history.py +0 -0
  124. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/framework_utilities/openai/__init__.py +0 -0
  125. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/framework_utilities/openai/client.py +0 -0
  126. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/framework_utilities/openai/message_builder.py +0 -0
  127. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/framework_utilities/utils.py +0 -0
  128. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/language_model/__init__.py +0 -0
  129. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/language_model/builder.py +0 -0
  130. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/language_model/constants.py +0 -0
  131. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/language_model/functions.py +0 -0
  132. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/language_model/infos.py +0 -0
  133. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/language_model/prompt.py +0 -0
  134. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/language_model/reference.py +0 -0
  135. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/language_model/schemas.py +0 -0
  136. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/language_model/service.py +0 -0
  137. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/language_model/utils.py +0 -0
  138. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/protocols/support.py +0 -0
  139. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/short_term_memory/__init__.py +0 -0
  140. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/short_term_memory/constants.py +0 -0
  141. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/short_term_memory/functions.py +0 -0
  142. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/short_term_memory/schemas.py +0 -0
  143. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/short_term_memory/service.py +0 -0
  144. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/smart_rules/__init__.py +0 -0
  145. {unique_toolkit-1.4.3 → unique_toolkit-1.5.0}/unique_toolkit/smart_rules/compile.py +0 -0
@@ -5,6 +5,12 @@ 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.5.0] - 2025-10-01
9
+ - Allow history manager to fetch only ui visible text
10
+
11
+ ## [1.4.4] - 2025-09-30
12
+ - Fix bugs with display of sub-agent answers and evaluations when multiple sub-agent from the same assistant run concurrently.
13
+
8
14
  ## [1.4.3] - 2025-09-30
9
15
  - Fix bug with sub-agent post-processing reference numbers.
10
16
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: unique_toolkit
3
- Version: 1.4.3
3
+ Version: 1.5.0
4
4
  Summary:
5
5
  License: Proprietary
6
6
  Author: Cedric Klinkert
@@ -118,6 +118,12 @@ 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.5.0] - 2025-10-01
122
+ - Allow history manager to fetch only ui visible text
123
+
124
+ ## [1.4.4] - 2025-09-30
125
+ - Fix bugs with display of sub-agent answers and evaluations when multiple sub-agent from the same assistant run concurrently.
126
+
121
127
  ## [1.4.3] - 2025-09-30
122
128
  - Fix bug with sub-agent post-processing reference numbers.
123
129
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "unique_toolkit"
3
- version = "1.4.3"
3
+ version = "1.5.0"
4
4
  description = ""
5
5
  authors = [
6
6
  "Cedric Klinkert <cedric.klinkert@unique.ch>",
@@ -215,3 +215,22 @@ class HistoryManager:
215
215
  remove_from_text=remove_from_text,
216
216
  )
217
217
  return messages
218
+
219
+ async def get_user_visible_chat_history(
220
+ self, assistant_message_text: str | None = None
221
+ ) -> LanguageModelMessages:
222
+ """Get the user visible chat history.
223
+
224
+ Args:
225
+ assistant_message_text (str | None): The latest assistant message to append to the history, as this is not extracted from the history.
226
+ If None, the history will be returned without the latest assistant message.
227
+
228
+ Returns:
229
+ LanguageModelMessages: The user visible chat history.
230
+ """
231
+ history = await self._token_reducer._get_history_from_db()
232
+ if assistant_message_text:
233
+ history.append(
234
+ LanguageModelAssistantMessage(content=assistant_message_text)
235
+ )
236
+ return LanguageModelMessages(history)
@@ -113,6 +113,21 @@ class LoopTokenReducer:
113
113
 
114
114
  return messages
115
115
 
116
+ async def get_user_visible_chat_history(
117
+ self, message: LanguageModelAssistantMessage
118
+ ) -> LanguageModelMessages:
119
+ """Get the user visible chat history.
120
+
121
+ Args:
122
+ message (LanguageModelAssistantMessage): The latest assistant message to append to the history, as this is not extracted from the history.
123
+
124
+ Returns:
125
+ LanguageModelMessages: The user visible chat history.
126
+ """
127
+ history = await self._get_history_from_db()
128
+ history.append(message)
129
+ return LanguageModelMessages(history)
130
+
116
131
  def _exceeds_token_limit(self, token_count: int) -> bool:
117
132
  """Check if token count exceeds the maximum allowed limit and if at least one tool call has more than one source."""
118
133
  # At least one tool call should have more than one chunk as answer
@@ -223,7 +238,7 @@ class LoopTokenReducer:
223
238
  return history
224
239
 
225
240
  async def _get_history_from_db(
226
- self, remove_from_text: Callable[[str], Awaitable[str]]
241
+ self, remove_from_text: Callable[[str], Awaitable[str]] | None = None
227
242
  ) -> list[LanguageModelMessage]:
228
243
  """
229
244
  Get the history of the conversation. The function will retrieve a subset of the full history based on the configuration.
@@ -242,10 +257,10 @@ class LoopTokenReducer:
242
257
  else FileContentSerialization.FILE_NAME
243
258
  ),
244
259
  )
245
-
246
- full_history.root = await self._clean_messages(
247
- full_history.root, remove_from_text
248
- )
260
+ if remove_from_text is not None:
261
+ full_history.root = await self._clean_messages(
262
+ full_history.root, remove_from_text
263
+ )
249
264
 
250
265
  limited_history_messages = self._limit_to_token_window(
251
266
  full_history.root, self._max_history_tokens
@@ -15,6 +15,7 @@ Your task is to give a brief summary (1-10 sentences) of the received assessment
15
15
  1. You must NOT in ANY case state a fact that is not stated in the given assessments.
16
16
  2. You must focus first and foremost on the failing assessments, labeled `RED` below.
17
17
  3. You must mention each agent's name when summarizing its list of assessments.
18
+ 4. You must NOT use any markdown formatting in your response as this will FAIL to render in the chat frontend.
18
19
  """.strip()
19
20
 
20
21
  with open(Path(__file__).parent / "summarization_user_message.j2", "r") as file:
@@ -27,7 +27,7 @@ logger = logging.getLogger(__name__)
27
27
 
28
28
 
29
29
  class _SubAgentToolInfo(TypedDict):
30
- assessment: list[unique_sdk.Space.Assessment] | None
30
+ assessments: list[list[unique_sdk.Space.Assessment]]
31
31
  display_name: str
32
32
 
33
33
 
@@ -53,7 +53,7 @@ class SubAgentsEvaluation(Evaluation):
53
53
  if sub_agent_tool.config.evaluation_config.display_evalution:
54
54
  sub_agent_tool.subscribe(self)
55
55
  self._assistant_id_to_tool_info[sub_agent_tool.config.assistant_id] = {
56
- "assessment": None,
56
+ "assessments": [],
57
57
  "display_name": sub_agent_tool.display_name(),
58
58
  }
59
59
 
@@ -80,47 +80,53 @@ class SubAgentsEvaluation(Evaluation):
80
80
  label_comparison_dict[ChatMessageAssessmentLabel.RED] = 0
81
81
 
82
82
  for assistant_id, tool_info in self._assistant_id_to_tool_info.items():
83
- assessments = tool_info["assessment"] or []
84
- valid_assessments = []
85
- for assessment in assessments:
86
- if (
87
- assessment["label"] is None
88
- or assessment["label"] not in ChatMessageAssessmentLabel
89
- ):
90
- logger.warning(
91
- "Unkown assistant label %s for assistant %s will be ignored",
92
- assessment["label"],
93
- assistant_id,
94
- )
95
- continue
96
- if assessment["status"] != ChatMessageAssessmentStatus.DONE:
97
- logger.warning(
98
- "Assessment %s for assistant %s is not done (status: %s) will be ignored",
99
- assessment["label"],
100
- assistant_id,
83
+ 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)
105
+
106
+ if len(valid_assessments) == 0:
107
+ logger.info(
108
+ "No valid assessment found for assistant %s", assistant_id
101
109
  )
102
110
  continue
103
- valid_assessments.append(assessment)
104
-
105
- if len(valid_assessments) == 0:
106
- logger.info("No valid assessment found for assistant %s", assistant_id)
107
- continue
108
111
 
109
- assessments = sorted(
110
- valid_assessments, key=lambda x: label_comparison_dict[x["label"]]
111
- )
112
-
113
- for assessment in assessments:
114
- value = min(
115
- value, assessment["label"], key=lambda x: label_comparison_dict[x]
112
+ assessments = sorted(
113
+ valid_assessments, key=lambda x: label_comparison_dict[x["label"]]
116
114
  )
117
115
 
118
- sub_agents_display_data.append(
119
- {
116
+ for assessment in assessments:
117
+ value = min(
118
+ value,
119
+ assessment["label"],
120
+ key=lambda x: label_comparison_dict[x],
121
+ )
122
+ data = {
120
123
  "name": tool_info["display_name"],
121
124
  "assessments": assessments,
122
125
  }
123
- )
126
+ if len(sub_agent_assessments) > 1:
127
+ data["name"] += f" {i}"
128
+
129
+ sub_agents_display_data.append(data)
124
130
 
125
131
  if len(sub_agents_display_data) == 0:
126
132
  logger.warning("No valid sub agent assessments found")
@@ -200,10 +206,10 @@ class SubAgentsEvaluation(Evaluation):
200
206
  )
201
207
  return
202
208
 
203
- self._assistant_id_to_tool_info[sub_agent_assistant_id]["assessment"] = (
209
+ self._assistant_id_to_tool_info[sub_agent_assistant_id]["assessments"].append(
204
210
  response[
205
211
  "assessment"
206
212
  ].copy() # Shallow copy as we don't modify individual assessments
207
213
  if response["assessment"] is not None
208
- else None
214
+ else []
209
215
  )
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
  import re
3
- from typing import NotRequired, TypedDict, override
3
+ from typing import TypedDict, override
4
4
 
5
5
  import unique_sdk
6
6
 
@@ -30,7 +30,7 @@ class _SubAgentMessageInfo(TypedDict):
30
30
  class _SubAgentToolInfo(TypedDict):
31
31
  display_name: str
32
32
  display_config: SubAgentToolDisplayConfig
33
- response: NotRequired[_SubAgentMessageInfo]
33
+ responses: list[_SubAgentMessageInfo]
34
34
 
35
35
 
36
36
  class SubAgentResponsesPostprocessor(Postprocessor):
@@ -57,6 +57,7 @@ class SubAgentResponsesPostprocessor(Postprocessor):
57
57
  _SubAgentToolInfo(
58
58
  display_config=sub_agent_tool.config.response_display_config,
59
59
  display_name=sub_agent_tool.display_name(),
60
+ responses=[],
60
61
  )
61
62
  )
62
63
 
@@ -81,7 +82,7 @@ class SubAgentResponsesPostprocessor(Postprocessor):
81
82
  for assistant_id, tool_info in self._assistant_id_to_tool_info.items():
82
83
  display_mode = tool_info["display_config"].mode
83
84
 
84
- if "response" not in tool_info:
85
+ if len(tool_info["responses"]) == 0:
85
86
  logger.warning(
86
87
  "No response from assistant %s",
87
88
  assistant_id,
@@ -89,7 +90,7 @@ class SubAgentResponsesPostprocessor(Postprocessor):
89
90
  continue
90
91
 
91
92
  if display_mode != ResponseDisplayMode.HIDDEN:
92
- displayed[assistant_id] = tool_info["response"]
93
+ displayed[assistant_id] = tool_info["responses"]
93
94
 
94
95
  existing_refs = {
95
96
  ref.source_id: ref.sequence_number
@@ -98,33 +99,38 @@ class SubAgentResponsesPostprocessor(Postprocessor):
98
99
  _consolidate_references_in_place(displayed, existing_refs)
99
100
 
100
101
  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"],
102
+ for assistant_id, messages in reversed(displayed.items()):
103
+ for i in reversed(range(len(messages))):
104
+ message = messages[i]
105
+ tool_info = self._assistant_id_to_tool_info[assistant_id]
106
+ display_mode = tool_info["display_config"].mode
107
+ display_name = tool_info["display_name"]
108
+ if len(messages) > 1:
109
+ display_name += f" {i + 1}"
110
+
111
+ loop_response.message.text = (
112
+ build_sub_agent_answer_display(
113
+ display_name=display_name,
114
+ assistant_id=assistant_id,
115
+ display_mode=display_mode,
116
+ answer=message["text"],
117
+ )
118
+ + loop_response.message.text
111
119
  )
112
- + loop_response.message.text
113
- )
114
-
115
- assert self._sub_agent_message is not None
116
120
 
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"],
121
+ assert self._sub_agent_message is not None
122
+
123
+ loop_response.message.references.extend(
124
+ ContentReference(
125
+ message_id=self._sub_agent_message["id"],
126
+ source_id=ref["sourceId"],
127
+ url=ref["url"],
128
+ source=ref["source"],
129
+ name=ref["name"],
130
+ sequence_number=ref["sequenceNumber"],
131
+ )
132
+ for ref in message["references"]
125
133
  )
126
- for ref in message["references"]
127
- )
128
134
 
129
135
  return modified
130
136
 
@@ -150,59 +156,58 @@ class SubAgentResponsesPostprocessor(Postprocessor):
150
156
  )
151
157
  return
152
158
 
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
- }
159
+ self._assistant_id_to_tool_info[sub_agent_assistant_id]["responses"].append(
160
+ {
161
+ "text": response["text"],
162
+ "references": [
163
+ {
164
+ "name": ref["name"],
165
+ "url": ref["url"],
166
+ "sequenceNumber": ref["sequenceNumber"],
167
+ "originalIndex": [],
168
+ "sourceId": ref["sourceId"],
169
+ "source": ref["source"],
170
+ }
171
+ for ref in response["references"] or []
172
+ ],
173
+ }
174
+ )
167
175
 
168
176
 
169
177
  def _consolidate_references_in_place(
170
- messages: dict[str, _SubAgentMessageInfo], existing_refs: dict[str, int]
178
+ messages: dict[str, list[_SubAgentMessageInfo]], existing_refs: dict[str, int]
171
179
  ) -> None:
172
180
  start_index = max(existing_refs.values(), default=0) + 1
173
181
 
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
- ref_map = {}
186
-
187
- message_new_refs = []
188
- for reference in references:
189
- source_id = reference["sourceId"]
182
+ for assistant_id, assistant_messages in messages.items():
183
+ for message in assistant_messages:
184
+ references = message["references"]
185
+ if len(references) == 0 or message["text"] is None:
186
+ logger.info(
187
+ "Message from assistant %s does not contain any references",
188
+ assistant_id,
189
+ )
190
+ continue
190
191
 
191
- if source_id not in existing_refs:
192
- message_new_refs.append(reference)
192
+ references = list(sorted(references, key=lambda ref: ref["sequenceNumber"]))
193
193
 
194
- existing_refs[source_id] = start_index
195
- start_index += 1
194
+ ref_map = {}
196
195
 
197
- reference_num = existing_refs[source_id]
198
- seq_num = reference["sequenceNumber"]
196
+ message_new_refs = []
197
+ for reference in references:
198
+ source_id = reference["sourceId"]
199
199
 
200
- ref_map[seq_num] = reference_num
200
+ if source_id not in existing_refs:
201
+ message_new_refs.append(reference)
202
+ existing_refs[source_id] = start_index
203
+ start_index += 1
201
204
 
202
- reference["sequenceNumber"] = reference_num
205
+ reference_num = existing_refs[source_id]
206
+ ref_map[reference["sequenceNumber"]] = reference_num
207
+ reference["sequenceNumber"] = reference_num
203
208
 
204
- message["text"] = _replace_references_in_text(message["text"], ref_map)
205
- message["references"] = message_new_refs
209
+ message["text"] = _replace_references_in_text(message["text"], ref_map)
210
+ message["references"] = message_new_refs
206
211
 
207
212
 
208
213
  def _replace_references_in_text_non_overlapping(
@@ -215,7 +220,7 @@ def _replace_references_in_text_non_overlapping(
215
220
 
216
221
  def _replace_references_in_text(text: str, ref_map: dict[int, int]) -> str:
217
222
  # 2 phase replacement, since the map keys and values can overlap
218
- max_ref = max(ref_map.keys(), default=0) + 1
223
+ max_ref = max(max(ref_map.keys(), default=0), max(ref_map.values(), default=0)) + 1
219
224
  unique_refs = range(max_ref, max_ref + len(ref_map))
220
225
 
221
226
  text = _replace_references_in_text_non_overlapping(