unique_toolkit 1.17.3__py3-none-any.whl → 1.18.0__py3-none-any.whl

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.

Potentially problematic release.


This version of unique_toolkit might be problematic. Click here for more details.

@@ -3,6 +3,7 @@ from typing import override
3
3
 
4
4
  import unique_sdk
5
5
  from jinja2 import Template
6
+ from pydantic import BaseModel
6
7
  from typing_extensions import TypedDict
7
8
 
8
9
  from unique_toolkit.agentic.evaluation.evaluation_manager import Evaluation
@@ -21,6 +22,7 @@ from unique_toolkit.agentic.tools.a2a.evaluation.config import (
21
22
  SubAgentEvaluationServiceConfig,
22
23
  )
23
24
  from unique_toolkit.agentic.tools.a2a.tool import SubAgentTool
25
+ from unique_toolkit.agentic.tools.utils import failsafe
24
26
  from unique_toolkit.chat.schemas import (
25
27
  ChatMessageAssessmentLabel,
26
28
  ChatMessageAssessmentStatus,
@@ -38,7 +40,27 @@ class _SubAgentToolInfo(TypedDict):
38
40
  display_name: str
39
41
 
40
42
 
41
- NO_ASSESSMENTS_FOUND = "NO_ASSESSMENTS_FOUND"
43
+ _NO_ASSESSMENTS_FOUND = "NO_ASSESSMENTS_FOUND"
44
+
45
+
46
+ class _SingleAssessmentData(BaseModel):
47
+ name: str
48
+ explanation: str
49
+
50
+
51
+ def _format_single_assessment_found(name: str, explanation: str) -> str:
52
+ return _SingleAssessmentData(name=name, explanation=explanation).model_dump_json()
53
+
54
+
55
+ @failsafe(failure_return_value=False)
56
+ def _is_single_assessment_found(value: str) -> bool:
57
+ _ = _SingleAssessmentData.model_validate_json(value)
58
+ return True
59
+
60
+
61
+ def _parse_single_assessment_found(value: str) -> tuple[str, str]:
62
+ data = _SingleAssessmentData.model_validate_json(value)
63
+ return data.name, data.explanation
42
64
 
43
65
 
44
66
  class SubAgentEvaluationService(Evaluation):
@@ -99,15 +121,36 @@ class SubAgentEvaluationService(Evaluation):
99
121
 
100
122
  sub_agents_display_data.append(data)
101
123
 
124
+ # No valid assessments found
102
125
  if len(sub_agents_display_data) == 0:
103
126
  logger.warning("No valid sub agent assessments found")
104
127
 
105
128
  return EvaluationMetricResult(
106
129
  name=self.get_name(),
107
- value=NO_ASSESSMENTS_FOUND,
130
+ # This is a trick to be able to indicate to `evaluation_metric_to_assessment`
131
+ # that no valid assessments were found
132
+ value=_NO_ASSESSMENTS_FOUND,
108
133
  reason="No sub agents assessments found",
109
134
  )
110
135
 
136
+ # Only one valid assessment found, no need to perform summarization
137
+ if (
138
+ len(sub_agents_display_data) == 1
139
+ and len(sub_agents_display_data[0]["assessments"]) == 1
140
+ ):
141
+ assessment = sub_agents_display_data[0]["assessments"][0]
142
+ explanation = assessment["explanation"] or ""
143
+ name = sub_agents_display_data[0]["name"]
144
+ label = assessment["label"]
145
+
146
+ return EvaluationMetricResult(
147
+ name=self.get_name(),
148
+ value=label,
149
+ # This is a trick to be able to pass the display name to the UI in `evaluation_metric_to_assessment`
150
+ reason=_format_single_assessment_found(name, explanation),
151
+ is_positive=label == ChatMessageAssessmentLabel.GREEN,
152
+ )
153
+
111
154
  reason = await self._get_reason(sub_agents_display_data)
112
155
 
113
156
  return EvaluationMetricResult(
@@ -121,7 +164,7 @@ class SubAgentEvaluationService(Evaluation):
121
164
  async def evaluation_metric_to_assessment(
122
165
  self, evaluation_result: EvaluationMetricResult
123
166
  ) -> EvaluationAssessmentMessage:
124
- if evaluation_result.value == NO_ASSESSMENTS_FOUND:
167
+ if evaluation_result.value == _NO_ASSESSMENTS_FOUND:
125
168
  return EvaluationAssessmentMessage(
126
169
  status=ChatMessageAssessmentStatus.DONE,
127
170
  explanation="No valid sub agents assessments found to consolidate.",
@@ -130,6 +173,16 @@ class SubAgentEvaluationService(Evaluation):
130
173
  type=self.get_assessment_type(),
131
174
  )
132
175
 
176
+ if _is_single_assessment_found(evaluation_result.reason):
177
+ name, reason = _parse_single_assessment_found(evaluation_result.reason)
178
+ return EvaluationAssessmentMessage(
179
+ status=ChatMessageAssessmentStatus.DONE,
180
+ explanation=reason,
181
+ title=name,
182
+ label=evaluation_result.value, # type: ignore
183
+ type=self.get_assessment_type(),
184
+ )
185
+
133
186
  return EvaluationAssessmentMessage(
134
187
  status=ChatMessageAssessmentStatus.DONE,
135
188
  explanation=evaluation_result.reason,
@@ -182,12 +235,6 @@ class SubAgentEvaluationService(Evaluation):
182
235
  )
183
236
 
184
237
  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
238
  messages = (
192
239
  MessagesBuilder()
193
240
  .system_message_append(self._config.summarization_system_message)
@@ -94,13 +94,17 @@ def _get_display_template(
94
94
  assistant_id_placeholder, "{%s}" % answer_placeholder, sep="\n\n"
95
95
  ) # Double line break is needed for markdown formatting
96
96
 
97
+ template = _add_line_break(template, before=True, after=False)
98
+
97
99
  if add_quote_border:
98
100
  template = _wrap_with_quote_border(template)
99
101
 
100
102
  match mode:
101
103
  case SubAgentResponseDisplayMode.DETAILS_OPEN:
102
104
  template = _wrap_with_details_tag(
103
- template, "open", display_name_placeholder
105
+ template,
106
+ "open",
107
+ display_name_placeholder,
104
108
  )
105
109
  case SubAgentResponseDisplayMode.DETAILS_CLOSED:
106
110
  template = _wrap_with_details_tag(
@@ -1,9 +1,12 @@
1
+ import asyncio
1
2
  import logging
2
3
  import re
3
4
  from typing import TypedDict, override
4
5
 
5
6
  import unique_sdk
7
+ from pydantic import BaseModel, Field
6
8
 
9
+ from unique_toolkit._common.pydantic_helpers import get_configuration_dict
7
10
  from unique_toolkit.agentic.postprocessor.postprocessor_manager import Postprocessor
8
11
  from unique_toolkit.agentic.tools.a2a.postprocessing._display import (
9
12
  _build_sub_agent_answer_display,
@@ -37,12 +40,21 @@ class _SubAgentToolInfo(TypedDict):
37
40
  responses: dict[int, _SubAgentMessageInfo]
38
41
 
39
42
 
43
+ class SubAgentResponsesPostprocessorConfig(BaseModel):
44
+ model_config = get_configuration_dict()
45
+
46
+ sleep_time_before_update: float = Field(
47
+ default=0.5, description="Time to sleep before updating the main agent message."
48
+ )
49
+
50
+
40
51
  class SubAgentResponsesPostprocessor(Postprocessor):
41
52
  def __init__(
42
53
  self,
43
54
  user_id: str,
44
55
  company_id: str,
45
56
  main_agent_chat_id: str,
57
+ config: SubAgentResponsesPostprocessorConfig | None = None,
46
58
  ) -> None:
47
59
  super().__init__(name=self.__class__.__name__)
48
60
 
@@ -52,6 +64,7 @@ class SubAgentResponsesPostprocessor(Postprocessor):
52
64
 
53
65
  self._assistant_id_to_tool_info: dict[str, _SubAgentToolInfo] = {}
54
66
  self._main_agent_message: SpaceMessage | None = None
67
+ self._config = config or SubAgentResponsesPostprocessorConfig()
55
68
 
56
69
  @override
57
70
  async def run(self, loop_response: LanguageModelStreamResponse) -> None:
@@ -60,6 +73,9 @@ class SubAgentResponsesPostprocessor(Postprocessor):
60
73
  company_id=self._company_id,
61
74
  chat_id=self._main_agent_chat_id,
62
75
  )
76
+ await asyncio.sleep(
77
+ self._config.sleep_time_before_update
78
+ ) # Frontend rendering issues
63
79
 
64
80
  @override
65
81
  def apply_postprocessing_to_response(
@@ -125,7 +141,7 @@ class SubAgentResponsesPostprocessor(Postprocessor):
125
141
  )
126
142
 
127
143
  loop_response.message.text = (
128
- "\n\n".join(answers) + "<br>\n\n" + loop_response.message.text.strip()
144
+ "<br>\n\n".join(answers) + "<br>\n\n" + loop_response.message.text.strip()
129
145
  )
130
146
 
131
147
  return True
@@ -1,3 +1,5 @@
1
+ from typing import Literal
2
+
1
3
  from pydantic import Field
2
4
 
3
5
  from unique_toolkit._common.pydantic_helpers import get_configuration_dict
@@ -27,6 +29,10 @@ class SubAgentToolConfig(BaseToolConfig):
27
29
  default=True,
28
30
  description="Whether this sub agent's references should be used in the main agent's response.",
29
31
  )
32
+ forced_tools: list[str] | None = Field(
33
+ default=None,
34
+ description="The list of tool names that will be forced to be called for this sub-agent.",
35
+ )
30
36
 
31
37
  tool_description_for_system_prompt: str = Field(
32
38
  default="",
@@ -61,3 +67,7 @@ class SubAgentToolConfig(BaseToolConfig):
61
67
  default=120.0,
62
68
  description="Maximum time in seconds to wait for the sub-agent response before timing out.",
63
69
  )
70
+ stop_condition: Literal["stoppedStreamingAt", "completedAt"] = Field(
71
+ default="completedAt",
72
+ description="The condition that will be used to stop the polling for the sub-agent response.",
73
+ )
@@ -282,8 +282,9 @@ class SubAgentTool(Tool[SubAgentToolConfig]):
282
282
  text=tool_user_message,
283
283
  chat_id=chat_id,
284
284
  poll_interval=self.config.poll_interval,
285
+ tool_choices=self.config.forced_tools,
285
286
  max_wait=self.config.max_wait,
286
- stop_condition="completedAt",
287
+ stop_condition=self.config.stop_condition,
287
288
  )
288
289
  except TimeoutError as e:
289
290
  await self._notify_progress(
@@ -1 +1,19 @@
1
1
  """Utilities for tools."""
2
+
3
+ from unique_toolkit.agentic.tools.utils.execution.execution import (
4
+ Result,
5
+ SafeTaskExecutor,
6
+ failsafe,
7
+ failsafe_async,
8
+ safe_execute,
9
+ safe_execute_async,
10
+ )
11
+
12
+ __all__ = [
13
+ "failsafe",
14
+ "failsafe_async",
15
+ "safe_execute",
16
+ "safe_execute_async",
17
+ "SafeTaskExecutor",
18
+ "Result",
19
+ ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: unique_toolkit
3
- Version: 1.17.3
3
+ Version: 1.18.0
4
4
  Summary:
5
5
  License: Proprietary
6
6
  Author: Cedric Klinkert
@@ -118,6 +118,11 @@ 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.18.0] - 2025-10-27
122
+ - Temporary fix to rendering of sub agent responses.
123
+ - Add config option `stop_condition` to `SubAgentToolConfig`
124
+ - Add config option `tool_choices` to `SubAgentToolConfig`
125
+ - Make sub agent evaluation use the name of the sub agent name if only one assessment is valid
121
126
 
122
127
  ## [1.17.3] - 2025-10-27
123
128
  - Update Hallucination check citation regex parsing pattern
@@ -61,14 +61,14 @@ unique_toolkit/agentic/tools/a2a/config.py,sha256=6diTTSiS2prY294LfYozB-db2wmJ6j
61
61
  unique_toolkit/agentic/tools/a2a/evaluation/__init__.py,sha256=_cR8uBwLbG7lyXoRskTpItzacgs4n23e2LeqClrytuc,354
62
62
  unique_toolkit/agentic/tools/a2a/evaluation/_utils.py,sha256=GtcPAMWkwGwJ--hBxn35ow9jN0VKYx8h2qMUXR8DCho,1877
63
63
  unique_toolkit/agentic/tools/a2a/evaluation/config.py,sha256=Ra5rzJArS3r8C7RTmzKB8cLEYh4w-u3pe_XLgul3GOA,2355
64
- unique_toolkit/agentic/tools/a2a/evaluation/evaluator.py,sha256=K4GkVOQwAUofjMF1-ofIGV3XPY1vOnOA8aw6CducRc0,7248
64
+ unique_toolkit/agentic/tools/a2a/evaluation/evaluator.py,sha256=-XDqkC2fLDFMkl8KZIefb1fl9dvlwug-uzcWmhil0vk,9144
65
65
  unique_toolkit/agentic/tools/a2a/evaluation/summarization_user_message.j2,sha256=acP1YqD_sCy6DT0V2EIfhQTmaUKeqpeWNJ7RGgceo8I,271
66
66
  unique_toolkit/agentic/tools/a2a/manager.py,sha256=FkO9jY7o8Td0t-HBkkatmxwhJGSJXmYkFYKFhPdbpMo,1674
67
67
  unique_toolkit/agentic/tools/a2a/postprocessing/__init__.py,sha256=R90CSecxJrKH7TbwiMYPyTwsXUUmouL8fbEUlL4ee9Q,362
68
- unique_toolkit/agentic/tools/a2a/postprocessing/_display.py,sha256=z6U62NaL_i02mlE2PiaNKmhaB71xjEb-lxM7kgv3Nuk,5085
68
+ unique_toolkit/agentic/tools/a2a/postprocessing/_display.py,sha256=rdWk-6M6V27v7G3dqaaDj1vXcgsFvDrHswFuSKQfd2I,5186
69
69
  unique_toolkit/agentic/tools/a2a/postprocessing/_utils.py,sha256=JsWwylR2Ao_L0wk1UlhqeN2fTxPnrbhoi1klYHVBnLk,750
70
70
  unique_toolkit/agentic/tools/a2a/postprocessing/config.py,sha256=hqo3vQZX63yXoLT7wN13vf0rC7qKiozKKfdkicYCQno,1061
71
- unique_toolkit/agentic/tools/a2a/postprocessing/postprocessor.py,sha256=xVclK3X6f-IU9W1jK_OO2yMRpPjVMmeLeQSTTlZQOKM,9703
71
+ unique_toolkit/agentic/tools/a2a/postprocessing/postprocessor.py,sha256=lEjJ1u-CTpoyDT7OFaBBOklONUCUU-CGsosHJRYUfkI,10335
72
72
  unique_toolkit/agentic/tools/a2a/postprocessing/test/test_consolidate_references.py,sha256=0hlTbtqClyMqHNnZRRxETgjVhYrtRu3xUnZx0JSE-uo,28030
73
73
  unique_toolkit/agentic/tools/a2a/postprocessing/test/test_display.py,sha256=Hn2GOsVhpCkpFV9Asvy_IyiYvCd88rxDzi0E_zt2kWc,37553
74
74
  unique_toolkit/agentic/tools/a2a/postprocessing/test/test_postprocessor_reference_functions.py,sha256=GxSkkY-Xgd61Bk8sIRfEtkT9hqL1VgPLWrq-6XoB0rA,11360
@@ -76,8 +76,8 @@ unique_toolkit/agentic/tools/a2a/prompts.py,sha256=0ILHL_RAcT04gFm2d470j4Gho7PoJ
76
76
  unique_toolkit/agentic/tools/a2a/tool/__init__.py,sha256=JIJKZBTLTA39OWhxoUd6uairxmqINur1Ex6iXDk9ef8,197
77
77
  unique_toolkit/agentic/tools/a2a/tool/_memory.py,sha256=w8bxjokrqHQZgApd55b5rHXF-DpgJwaKTg4CvLBLamc,1034
78
78
  unique_toolkit/agentic/tools/a2a/tool/_schema.py,sha256=wMwyunViTnxaURvenkATEvyfXn5LvLaP0HxbYqdZGls,158
79
- unique_toolkit/agentic/tools/a2a/tool/config.py,sha256=07W8cZFc32yRvIeUFEQXi8R1cSGqlXL4pZb7UlB3Rvs,2490
80
- unique_toolkit/agentic/tools/a2a/tool/service.py,sha256=6Eru-jqlt13lIBVC26LhH0Oxb3qVdDQIUWRoChf8ysw,10525
79
+ unique_toolkit/agentic/tools/a2a/tool/config.py,sha256=NcrS0hzIeYvjv1oMobAAPlZrbTPPWhcPhi0jUHLFBTI,2903
80
+ unique_toolkit/agentic/tools/a2a/tool/service.py,sha256=Rutos0nmY4PhvO_ETlHr5mRdQFa2UuzSJWC2rmiAJng,10593
81
81
  unique_toolkit/agentic/tools/agent_chunks_hanlder.py,sha256=x32Dp1Z8cVW5i-XzXbaMwX2KHPcNGmqEU-FB4AV9ZGo,1909
82
82
  unique_toolkit/agentic/tools/config.py,sha256=QD83iy2xAJFyoPggYyLWq-MaSGSq00yS9iI31My1NBc,5387
83
83
  unique_toolkit/agentic/tools/factory.py,sha256=A1Aliwx037UAk9ADiDsg0zjCWWnvzV_PxwJNoPTvW6c,1434
@@ -97,7 +97,7 @@ unique_toolkit/agentic/tools/test/test_tool_progress_reporter.py,sha256=dod5QPqg
97
97
  unique_toolkit/agentic/tools/tool.py,sha256=m56VLxiHuKU2_J5foZp00xhm5lTxWEW7zRLGbIE9ssU,6744
98
98
  unique_toolkit/agentic/tools/tool_manager.py,sha256=DtxJobe_7QKFe6CjnMhCP-mnKO6MjnZeDXsO3jBoC9w,16283
99
99
  unique_toolkit/agentic/tools/tool_progress_reporter.py,sha256=ixud9VoHey1vlU1t86cW0-WTvyTwMxNSWBon8I11SUk,7955
100
- unique_toolkit/agentic/tools/utils/__init__.py,sha256=iD1YYzf9LcJFv95Z8BqCAFSewNBabybZRZyvPKGfvro,27
100
+ unique_toolkit/agentic/tools/utils/__init__.py,sha256=s75sjY5nrJchjLGs3MwSIqhDW08fFXIaX7eRQjFIA4s,346
101
101
  unique_toolkit/agentic/tools/utils/execution/__init__.py,sha256=OHiKpqBnfhBiEQagKVWJsZlHv8smPp5OI4dFIexzibw,37
102
102
  unique_toolkit/agentic/tools/utils/execution/execution.py,sha256=vjG2Y6awsGNtlvyQAGCTthQ5thWHYnn-vzZXaYLb3QE,7922
103
103
  unique_toolkit/agentic/tools/utils/source_handling/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -165,7 +165,7 @@ unique_toolkit/short_term_memory/service.py,sha256=5PeVBu1ZCAfyDb2HLVvlmqSbyzBBu
165
165
  unique_toolkit/smart_rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
166
166
  unique_toolkit/smart_rules/compile.py,sha256=Ozhh70qCn2yOzRWr9d8WmJeTo7AQurwd3tStgBMPFLA,1246
167
167
  unique_toolkit/test_utilities/events.py,sha256=_mwV2bs5iLjxS1ynDCjaIq-gjjKhXYCK-iy3dRfvO3g,6410
168
- unique_toolkit-1.17.3.dist-info/LICENSE,sha256=GlN8wHNdh53xwOPg44URnwag6TEolCjoq3YD_KrWgss,193
169
- unique_toolkit-1.17.3.dist-info/METADATA,sha256=WpWG0ZZEMzpUBMiEOmvdBoGE-q0mfGKX_2RkyQXxwW4,38235
170
- unique_toolkit-1.17.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
171
- unique_toolkit-1.17.3.dist-info/RECORD,,
168
+ unique_toolkit-1.18.0.dist-info/LICENSE,sha256=GlN8wHNdh53xwOPg44URnwag6TEolCjoq3YD_KrWgss,193
169
+ unique_toolkit-1.18.0.dist-info/METADATA,sha256=6XAUmwya1a8ILqkVKETFxYEaCF4p4Q3GfhRmgWO6tcI,38528
170
+ unique_toolkit-1.18.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
171
+ unique_toolkit-1.18.0.dist-info/RECORD,,