unique_toolkit 1.7.0__py3-none-any.whl → 1.8.1__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.
- unique_toolkit/agentic/tools/a2a/__init__.py +19 -3
- unique_toolkit/agentic/tools/a2a/config.py +12 -52
- unique_toolkit/agentic/tools/a2a/evaluation/__init__.py +10 -3
- unique_toolkit/agentic/tools/a2a/evaluation/_utils.py +66 -0
- unique_toolkit/agentic/tools/a2a/evaluation/config.py +19 -3
- unique_toolkit/agentic/tools/a2a/evaluation/evaluator.py +82 -89
- unique_toolkit/agentic/tools/a2a/manager.py +2 -2
- unique_toolkit/agentic/tools/a2a/postprocessing/__init__.py +9 -1
- unique_toolkit/agentic/tools/a2a/postprocessing/{display.py → _display.py} +16 -7
- unique_toolkit/agentic/tools/a2a/postprocessing/_utils.py +19 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/config.py +24 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/postprocessor.py +109 -110
- unique_toolkit/agentic/tools/a2a/postprocessing/test/test_consolidate_references.py +665 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/test/test_display.py +54 -75
- unique_toolkit/agentic/tools/a2a/postprocessing/test/test_postprocessor_reference_functions.py +53 -45
- unique_toolkit/agentic/tools/a2a/tool/__init__.py +4 -0
- unique_toolkit/agentic/tools/a2a/{memory.py → tool/_memory.py} +1 -1
- unique_toolkit/agentic/tools/a2a/{schema.py → tool/_schema.py} +0 -6
- unique_toolkit/agentic/tools/a2a/tool/config.py +63 -0
- unique_toolkit/agentic/tools/a2a/{service.py → tool/service.py} +108 -65
- unique_toolkit/agentic/tools/config.py +2 -2
- unique_toolkit/agentic/tools/tool_manager.py +1 -2
- {unique_toolkit-1.7.0.dist-info → unique_toolkit-1.8.1.dist-info}/METADATA +8 -1
- {unique_toolkit-1.7.0.dist-info → unique_toolkit-1.8.1.dist-info}/RECORD +26 -20
- {unique_toolkit-1.7.0.dist-info → unique_toolkit-1.8.1.dist-info}/LICENSE +0 -0
- {unique_toolkit-1.7.0.dist-info → unique_toolkit-1.8.1.dist-info}/WHEEL +0 -0
@@ -1,4 +1,20 @@
|
|
1
|
-
from unique_toolkit.agentic.tools.a2a.config import
|
2
|
-
from unique_toolkit.agentic.tools.a2a.
|
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
|
3
11
|
|
4
|
-
__all__ = [
|
12
|
+
__all__ = [
|
13
|
+
"SubAgentToolConfig",
|
14
|
+
"SubAgentTool",
|
15
|
+
"SubAgentResponsesPostprocessor",
|
16
|
+
"A2AManager",
|
17
|
+
"ExtendedSubAgentToolConfig",
|
18
|
+
"SubAgentEvaluationServiceConfig",
|
19
|
+
"SubAgentEvaluationService",
|
20
|
+
]
|
@@ -1,57 +1,17 @@
|
|
1
|
-
from
|
1
|
+
from pydantic import Field
|
2
2
|
|
3
|
-
from
|
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
|
4
6
|
|
5
|
-
from unique_toolkit.agentic.tools.config import get_configuration_dict
|
6
|
-
from unique_toolkit.agentic.tools.schemas import BaseToolConfig
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
NEVER mention any references from sub-agent answers in your response.
|
14
|
-
"""
|
15
|
-
|
16
|
-
|
17
|
-
class ResponseDisplayMode(StrEnum):
|
18
|
-
HIDDEN = "hidden"
|
19
|
-
DETAILS_OPEN = "details_open"
|
20
|
-
DETAILS_CLOSED = "details_closed"
|
21
|
-
|
22
|
-
|
23
|
-
class SubAgentToolDisplayConfig(BaseModel):
|
24
|
-
model_config = get_configuration_dict()
|
25
|
-
|
26
|
-
mode: ResponseDisplayMode = ResponseDisplayMode.HIDDEN
|
27
|
-
remove_from_history: bool = True
|
28
|
-
|
29
|
-
|
30
|
-
class SubAgentEvaluationConfig(BaseModel):
|
31
|
-
model_config = get_configuration_dict()
|
32
|
-
display_evalution: bool = True
|
33
|
-
|
34
|
-
|
35
|
-
class SubAgentToolConfig(BaseToolConfig):
|
36
|
-
model_config = get_configuration_dict()
|
37
|
-
|
38
|
-
assistant_id: str = ""
|
39
|
-
chat_id: str | None = None
|
40
|
-
reuse_chat: bool = True
|
41
|
-
|
42
|
-
tool_description_for_system_prompt: str = ""
|
43
|
-
tool_description: str = ""
|
44
|
-
param_description_sub_agent_user_message: str = (
|
45
|
-
DEFAULT_PARAM_DESCRIPTION_SUB_AGENT_USER_MESSAGE
|
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.",
|
46
13
|
)
|
47
|
-
|
48
|
-
|
14
|
+
evaluation_config: SubAgentEvaluationConfig = Field(
|
15
|
+
default_factory=SubAgentEvaluationConfig,
|
16
|
+
description="Configuration for handling assessments of the sub-agent response.",
|
49
17
|
)
|
50
|
-
tool_description_for_user_prompt: str = ""
|
51
|
-
tool_format_information_for_user_prompt: str = ""
|
52
|
-
|
53
|
-
poll_interval: float = 1.0
|
54
|
-
max_wait: float = 120.0
|
55
|
-
|
56
|
-
response_display_config: SubAgentToolDisplayConfig = SubAgentToolDisplayConfig()
|
57
|
-
evaluation_config: SubAgentEvaluationConfig = SubAgentEvaluationConfig()
|
@@ -1,6 +1,13 @@
|
|
1
|
-
from unique_toolkit.agentic.tools.a2a.evaluation.config import
|
1
|
+
from unique_toolkit.agentic.tools.a2a.evaluation.config import (
|
2
|
+
SubAgentEvaluationConfig,
|
3
|
+
SubAgentEvaluationServiceConfig,
|
4
|
+
)
|
2
5
|
from unique_toolkit.agentic.tools.a2a.evaluation.evaluator import (
|
3
|
-
|
6
|
+
SubAgentEvaluationService,
|
4
7
|
)
|
5
8
|
|
6
|
-
__all__ = [
|
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
|
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.
|
16
|
-
|
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:
|
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
|
44
|
+
class SubAgentEvaluationService(Evaluation):
|
38
45
|
DISPLAY_NAME = "Sub Agents"
|
39
46
|
|
40
47
|
def __init__(
|
41
48
|
self,
|
42
|
-
config:
|
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
|
-
|
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
|
-
|
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",
|
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 =
|
113
|
-
|
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" {
|
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
|
-
|
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,
|
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][
|
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.
|
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
|
|
@@ -1,5 +1,13 @@
|
|
1
|
+
from unique_toolkit.agentic.tools.a2a.postprocessing.config import (
|
2
|
+
SubAgentDisplayConfig,
|
3
|
+
SubAgentResponseDisplayMode,
|
4
|
+
)
|
1
5
|
from unique_toolkit.agentic.tools.a2a.postprocessing.postprocessor import (
|
2
6
|
SubAgentResponsesPostprocessor,
|
3
7
|
)
|
4
8
|
|
5
|
-
__all__ = [
|
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
|
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
|
-
|
88
|
-
|
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
|
93
|
-
display_name: 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
|
106
|
-
display_mode:
|
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
|
+
)
|