unique_toolkit 1.35.4__py3-none-any.whl → 1.37.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.
- unique_toolkit/_common/referencing.py +3 -3
- unique_toolkit/agentic/loop_runner/__init__.py +6 -0
- unique_toolkit/agentic/loop_runner/middleware/__init__.py +13 -1
- unique_toolkit/agentic/loop_runner/middleware/qwen_forced_tool_call/__init__.py +13 -0
- unique_toolkit/agentic/loop_runner/middleware/qwen_forced_tool_call/helpers.py +33 -0
- unique_toolkit/agentic/loop_runner/middleware/qwen_forced_tool_call/qwen_forced_tool_call.py +50 -0
- unique_toolkit/agentic/loop_runner/runners/basic.py +8 -6
- unique_toolkit/agentic/tools/a2a/postprocessing/config.py +1 -1
- unique_toolkit/agentic/tools/a2a/prompts.py +2 -0
- unique_toolkit/agentic/tools/a2a/tool/config.py +22 -4
- unique_toolkit/agentic/tools/a2a/tool/service.py +119 -69
- unique_toolkit/agentic/tools/a2a/tool/test/test_service_utils.py +829 -0
- {unique_toolkit-1.35.4.dist-info → unique_toolkit-1.37.0.dist-info}/METADATA +7 -1
- {unique_toolkit-1.35.4.dist-info → unique_toolkit-1.37.0.dist-info}/RECORD +16 -12
- {unique_toolkit-1.35.4.dist-info → unique_toolkit-1.37.0.dist-info}/LICENSE +0 -0
- {unique_toolkit-1.35.4.dist-info → unique_toolkit-1.37.0.dist-info}/WHEEL +0 -0
|
@@ -15,7 +15,7 @@ def _iter_ref_numbers(text: str) -> Generator[int, None, None]:
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
@functools.cache
|
|
18
|
-
def
|
|
18
|
+
def get_detection_pattern_for_ref(ref_number: int) -> re.Pattern[str]:
|
|
19
19
|
return re.compile(rf"<sup>\s*{ref_number}\s*</sup>")
|
|
20
20
|
|
|
21
21
|
|
|
@@ -35,11 +35,11 @@ def replace_ref_number(text: str, ref_number: int, replacement: int | str) -> st
|
|
|
35
35
|
if isinstance(replacement, int):
|
|
36
36
|
replacement = get_reference_pattern(replacement)
|
|
37
37
|
|
|
38
|
-
return
|
|
38
|
+
return get_detection_pattern_for_ref(ref_number).sub(replacement, text)
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
def remove_ref_number(text: str, ref_number: int) -> str:
|
|
42
|
-
return
|
|
42
|
+
return get_detection_pattern_for_ref(ref_number).sub("", text)
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
def remove_all_refs(text: str) -> str:
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
from unique_toolkit.agentic.loop_runner.base import LoopIterationRunner
|
|
2
2
|
from unique_toolkit.agentic.loop_runner.middleware import (
|
|
3
|
+
QWEN_FORCED_TOOL_CALL_PROMPT_INSTRUCTION,
|
|
3
4
|
PlanningConfig,
|
|
4
5
|
PlanningMiddleware,
|
|
5
6
|
PlanningSchemaConfig,
|
|
7
|
+
QwenForcedToolCallMiddleware,
|
|
8
|
+
is_qwen_model,
|
|
6
9
|
)
|
|
7
10
|
from unique_toolkit.agentic.loop_runner.runners import (
|
|
8
11
|
BasicLoopIterationRunner,
|
|
@@ -11,9 +14,12 @@ from unique_toolkit.agentic.loop_runner.runners import (
|
|
|
11
14
|
|
|
12
15
|
__all__ = [
|
|
13
16
|
"LoopIterationRunner",
|
|
17
|
+
"QWEN_FORCED_TOOL_CALL_PROMPT_INSTRUCTION",
|
|
14
18
|
"PlanningConfig",
|
|
15
19
|
"PlanningMiddleware",
|
|
16
20
|
"PlanningSchemaConfig",
|
|
21
|
+
"QwenForcedToolCallMiddleware",
|
|
22
|
+
"is_qwen_model",
|
|
17
23
|
"BasicLoopIterationRunnerConfig",
|
|
18
24
|
"BasicLoopIterationRunner",
|
|
19
25
|
]
|
|
@@ -3,5 +3,17 @@ from unique_toolkit.agentic.loop_runner.middleware.planning import (
|
|
|
3
3
|
PlanningMiddleware,
|
|
4
4
|
PlanningSchemaConfig,
|
|
5
5
|
)
|
|
6
|
+
from unique_toolkit.agentic.loop_runner.middleware.qwen_forced_tool_call import (
|
|
7
|
+
QWEN_FORCED_TOOL_CALL_PROMPT_INSTRUCTION,
|
|
8
|
+
QwenForcedToolCallMiddleware,
|
|
9
|
+
is_qwen_model,
|
|
10
|
+
)
|
|
6
11
|
|
|
7
|
-
__all__ = [
|
|
12
|
+
__all__ = [
|
|
13
|
+
"PlanningConfig",
|
|
14
|
+
"PlanningMiddleware",
|
|
15
|
+
"PlanningSchemaConfig",
|
|
16
|
+
"QWEN_FORCED_TOOL_CALL_PROMPT_INSTRUCTION",
|
|
17
|
+
"QwenForcedToolCallMiddleware",
|
|
18
|
+
"is_qwen_model",
|
|
19
|
+
]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from unique_toolkit.agentic.loop_runner.middleware.qwen_forced_tool_call.helpers import (
|
|
2
|
+
is_qwen_model,
|
|
3
|
+
)
|
|
4
|
+
from unique_toolkit.agentic.loop_runner.middleware.qwen_forced_tool_call.qwen_forced_tool_call import (
|
|
5
|
+
QWEN_FORCED_TOOL_CALL_PROMPT_INSTRUCTION,
|
|
6
|
+
QwenForcedToolCallMiddleware,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"QwenForcedToolCallMiddleware",
|
|
11
|
+
"QWEN_FORCED_TOOL_CALL_PROMPT_INSTRUCTION",
|
|
12
|
+
"is_qwen_model",
|
|
13
|
+
]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from unique_toolkit.language_model.infos import LanguageModelInfo
|
|
2
|
+
from unique_toolkit.language_model.schemas import (
|
|
3
|
+
LanguageModelMessageRole,
|
|
4
|
+
LanguageModelMessages,
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def is_qwen_model(*, model: str | LanguageModelInfo | None) -> bool:
|
|
9
|
+
"""Check if the model is a Qwen model."""
|
|
10
|
+
if isinstance(model, LanguageModelInfo):
|
|
11
|
+
name = model.name
|
|
12
|
+
# name is an Enum with a .value attribute
|
|
13
|
+
return "qwen" in str(getattr(name, "value", name)).lower()
|
|
14
|
+
elif isinstance(model, str):
|
|
15
|
+
return "qwen" in model.lower()
|
|
16
|
+
return False
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def append_qwen_forced_tool_call_instruction(
|
|
20
|
+
*,
|
|
21
|
+
messages: LanguageModelMessages,
|
|
22
|
+
forced_tool_call_instruction: str,
|
|
23
|
+
) -> LanguageModelMessages:
|
|
24
|
+
"""Append tool call instruction to the last user message for Qwen models."""
|
|
25
|
+
messages_list = list(messages)
|
|
26
|
+
for i in range(len(messages_list) - 1, -1, -1):
|
|
27
|
+
msg = messages_list[i]
|
|
28
|
+
if msg.role == LanguageModelMessageRole.USER and isinstance(msg.content, str):
|
|
29
|
+
messages_list[i] = msg.model_copy(
|
|
30
|
+
update={"content": msg.content + "\n" + forced_tool_call_instruction}
|
|
31
|
+
)
|
|
32
|
+
break
|
|
33
|
+
return LanguageModelMessages(root=messages_list)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Unpack
|
|
3
|
+
|
|
4
|
+
from unique_toolkit.agentic.loop_runner.base import (
|
|
5
|
+
LoopIterationRunner,
|
|
6
|
+
_LoopIterationRunnerKwargs,
|
|
7
|
+
)
|
|
8
|
+
from unique_toolkit.agentic.loop_runner.middleware.qwen_forced_tool_call.helpers import (
|
|
9
|
+
append_qwen_forced_tool_call_instruction,
|
|
10
|
+
)
|
|
11
|
+
from unique_toolkit.chat.service import LanguageModelStreamResponse
|
|
12
|
+
|
|
13
|
+
_LOGGER = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
QWEN_FORCED_TOOL_CALL_PROMPT_INSTRUCTION = (
|
|
16
|
+
"Tool Call Instruction: \nYou always have to return a tool call. "
|
|
17
|
+
"You must start the response with <tool_call> and end with </tool_call>. "
|
|
18
|
+
"Do NOT provide natural language explanations, summaries, or any text outside the <tool_call> block."
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class QwenForcedToolCallMiddleware(LoopIterationRunner):
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
*,
|
|
26
|
+
loop_runner: LoopIterationRunner,
|
|
27
|
+
qwen_forced_tool_call_prompt_instruction: str,
|
|
28
|
+
) -> None:
|
|
29
|
+
self._qwen_forced_tool_call_prompt_instruction = (
|
|
30
|
+
qwen_forced_tool_call_prompt_instruction
|
|
31
|
+
)
|
|
32
|
+
self._loop_runner = loop_runner
|
|
33
|
+
|
|
34
|
+
async def __call__(
|
|
35
|
+
self, **kwargs: Unpack[_LoopIterationRunnerKwargs]
|
|
36
|
+
) -> LanguageModelStreamResponse:
|
|
37
|
+
tool_choices = kwargs.get("tool_choices") or []
|
|
38
|
+
iteration_index = kwargs["iteration_index"]
|
|
39
|
+
|
|
40
|
+
# For Qwen models, append tool call instruction to the last user message. These models ignore the parameter tool_choice.
|
|
41
|
+
if len(tool_choices) > 0 and iteration_index == 0 and kwargs.get("messages"):
|
|
42
|
+
_LOGGER.info(
|
|
43
|
+
"Appending tool call instruction to the last user message for Qwen models to force tool calls."
|
|
44
|
+
)
|
|
45
|
+
kwargs["messages"] = append_qwen_forced_tool_call_instruction(
|
|
46
|
+
messages=kwargs["messages"],
|
|
47
|
+
forced_tool_call_instruction=self._qwen_forced_tool_call_prompt_instruction,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
return await self._loop_runner(**kwargs)
|
|
@@ -56,13 +56,15 @@ class BasicLoopIterationRunner(LoopIterationRunner):
|
|
|
56
56
|
|
|
57
57
|
responses: list[LanguageModelStreamResponse] = []
|
|
58
58
|
|
|
59
|
+
available_tools = {t.name: t for t in kwargs.get("tools") or []}
|
|
60
|
+
|
|
59
61
|
for opt in tool_choices:
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
)
|
|
62
|
+
func_name = opt.get("function", {}).get("name")
|
|
63
|
+
limited_tool = available_tools.get(func_name) if func_name else None
|
|
64
|
+
stream_kwargs = {"loop_runner_kwargs": kwargs, "tool_choice": opt}
|
|
65
|
+
if limited_tool:
|
|
66
|
+
stream_kwargs["tools"] = [limited_tool]
|
|
67
|
+
responses.append(await stream_response(**stream_kwargs))
|
|
66
68
|
|
|
67
69
|
# Merge responses and refs:
|
|
68
70
|
tool_calls = []
|
|
@@ -65,7 +65,7 @@ class SubAgentDisplayConfig(BaseModel):
|
|
|
65
65
|
)
|
|
66
66
|
force_include_references: bool = Field(
|
|
67
67
|
default=False,
|
|
68
|
-
description="If set, the sub agent references will be added to the main agent response references even
|
|
68
|
+
description="If set, the sub agent references will be added to the main agent response references even if not mentioned in the main agent response text.",
|
|
69
69
|
)
|
|
70
70
|
|
|
71
71
|
answer_substrings_config: list[SubAgentAnswerSubstringConfig] = Field(
|
|
@@ -37,6 +37,8 @@ References: <sup><name>SubAgentName 4</name>2</sup><sup><name>SubAgentName 4</na
|
|
|
37
37
|
|
|
38
38
|
6. Fact repetition: If you reuse a fact from SubAgentName, you MUST reference it again inline with the correct format.
|
|
39
39
|
|
|
40
|
+
7. You can ONLY use references if they are present in the subagent response! You must NOT create any references!
|
|
41
|
+
|
|
40
42
|
Reminder:
|
|
41
43
|
Inline = directly next to the fact, inside the same sentence or bullet.
|
|
42
44
|
""".strip()
|
|
@@ -13,6 +13,7 @@ class SubAgentSystemReminderType(StrEnum):
|
|
|
13
13
|
FIXED = "fixed"
|
|
14
14
|
REGEXP = "regexp"
|
|
15
15
|
REFERENCE = "reference"
|
|
16
|
+
NO_REFERENCE = "no_reference"
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
T = TypeVar("T", bound=SubAgentSystemReminderType)
|
|
@@ -31,12 +32,24 @@ The reminder to add to the tool response. The reminder can be a Jinja template a
|
|
|
31
32
|
""".strip()
|
|
32
33
|
|
|
33
34
|
|
|
35
|
+
class NoReferenceSystemReminderConfig(SystemReminderConfig):
|
|
36
|
+
"""A system reminder that is only added if the sub agent response does not contain any references."""
|
|
37
|
+
|
|
38
|
+
type: Literal[SubAgentSystemReminderType.NO_REFERENCE] = (
|
|
39
|
+
SubAgentSystemReminderType.NO_REFERENCE
|
|
40
|
+
)
|
|
41
|
+
reminder: str = Field(
|
|
42
|
+
default="Do NOT create any references from this sub agent in your response! The sub agent response does not contain any references.",
|
|
43
|
+
description=_SYSTEM_REMINDER_FIELD_DESCRIPTION,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
34
47
|
class ReferenceSystemReminderConfig(SystemReminderConfig):
|
|
35
48
|
type: Literal[SubAgentSystemReminderType.REFERENCE] = (
|
|
36
49
|
SubAgentSystemReminderType.REFERENCE
|
|
37
50
|
)
|
|
38
51
|
reminder: str = Field(
|
|
39
|
-
default="Rememeber to properly reference EACH fact from sub agent {{ display_name }}'s response with the correct format INLINE.",
|
|
52
|
+
default="Rememeber to properly reference EACH fact from sub agent {{ display_name }}'s response with the correct format INLINE. You MUST COPY THE REFERENCE AS PRESENT IN THE SUBAGENT RESPONSE.",
|
|
40
53
|
description=_SYSTEM_REMINDER_FIELD_DESCRIPTION,
|
|
41
54
|
)
|
|
42
55
|
|
|
@@ -69,6 +82,13 @@ class RegExpDetectedSystemReminderConfig(SystemReminderConfig):
|
|
|
69
82
|
)
|
|
70
83
|
|
|
71
84
|
|
|
85
|
+
SystemReminderConfigType = (
|
|
86
|
+
FixedSystemReminderConfig
|
|
87
|
+
| RegExpDetectedSystemReminderConfig
|
|
88
|
+
| ReferenceSystemReminderConfig
|
|
89
|
+
| NoReferenceSystemReminderConfig
|
|
90
|
+
)
|
|
91
|
+
|
|
72
92
|
DEFAULT_PARAM_DESCRIPTION_SUB_AGENT_USER_MESSAGE = """
|
|
73
93
|
This is the message that will be sent to the sub-agent.
|
|
74
94
|
""".strip()
|
|
@@ -147,9 +167,7 @@ class SubAgentToolConfig(BaseToolConfig):
|
|
|
147
167
|
|
|
148
168
|
system_reminders_config: list[
|
|
149
169
|
Annotated[
|
|
150
|
-
|
|
151
|
-
| RegExpDetectedSystemReminderConfig
|
|
152
|
-
| ReferenceSystemReminderConfig,
|
|
170
|
+
SystemReminderConfigType,
|
|
153
171
|
Field(discriminator="type"),
|
|
154
172
|
]
|
|
155
173
|
] = Field(
|
|
@@ -12,7 +12,7 @@ from unique_sdk.utils.chat_in_space import send_message_and_wait_for_completion
|
|
|
12
12
|
|
|
13
13
|
from unique_toolkit._common.referencing import (
|
|
14
14
|
get_all_ref_numbers,
|
|
15
|
-
|
|
15
|
+
get_detection_pattern_for_ref,
|
|
16
16
|
replace_ref_number,
|
|
17
17
|
)
|
|
18
18
|
from unique_toolkit._common.utils.jinja.render import render_template
|
|
@@ -29,6 +29,7 @@ from unique_toolkit.agentic.tools.a2a.tool.config import (
|
|
|
29
29
|
RegExpDetectedSystemReminderConfig,
|
|
30
30
|
SubAgentSystemReminderType,
|
|
31
31
|
SubAgentToolConfig,
|
|
32
|
+
SystemReminderConfigType,
|
|
32
33
|
)
|
|
33
34
|
from unique_toolkit.agentic.tools.factory import ToolFactory
|
|
34
35
|
from unique_toolkit.agentic.tools.schemas import ToolCallResponse
|
|
@@ -201,15 +202,39 @@ class SubAgentTool(Tool[SubAgentToolConfig]):
|
|
|
201
202
|
if response["text"] is None:
|
|
202
203
|
raise ValueError("No response returned from sub agent")
|
|
203
204
|
|
|
205
|
+
has_refs = False
|
|
206
|
+
content = ""
|
|
207
|
+
content_chunks = None
|
|
204
208
|
if self.config.returns_content_chunks:
|
|
205
|
-
content = ""
|
|
206
209
|
content_chunks = _ContentChunkList.validate_json(response["text"])
|
|
207
210
|
else:
|
|
208
|
-
|
|
211
|
+
has_refs = self.config.use_sub_agent_references and _response_has_refs(
|
|
212
|
+
response
|
|
213
|
+
)
|
|
214
|
+
content = response["text"]
|
|
215
|
+
if has_refs:
|
|
216
|
+
refs = response["references"]
|
|
217
|
+
assert refs is not None # Checked in _response_has_refs
|
|
218
|
+
content = _prepare_sub_agent_response_refs(
|
|
219
|
+
response=content,
|
|
220
|
+
name=self.name,
|
|
221
|
+
sequence_number=sequence_number,
|
|
222
|
+
refs=refs,
|
|
223
|
+
)
|
|
224
|
+
content = _remove_extra_refs(content, refs=refs)
|
|
225
|
+
else:
|
|
226
|
+
content = _remove_extra_refs(content, refs=[])
|
|
227
|
+
|
|
228
|
+
system_reminders = []
|
|
229
|
+
if not self.config.returns_content_chunks:
|
|
230
|
+
system_reminders = _get_sub_agent_system_reminders(
|
|
209
231
|
response=response["text"],
|
|
232
|
+
configs=self.config.system_reminders_config,
|
|
233
|
+
name=self.name,
|
|
234
|
+
display_name=self.display_name(),
|
|
210
235
|
sequence_number=sequence_number,
|
|
236
|
+
has_refs=has_refs,
|
|
211
237
|
)
|
|
212
|
-
content_chunks = None
|
|
213
238
|
|
|
214
239
|
await self._notify_progress(
|
|
215
240
|
tool_call=tool_call,
|
|
@@ -223,58 +248,11 @@ class SubAgentTool(Tool[SubAgentToolConfig]):
|
|
|
223
248
|
content=_format_response(
|
|
224
249
|
tool_name=self.name,
|
|
225
250
|
text=content,
|
|
226
|
-
system_reminders=
|
|
251
|
+
system_reminders=system_reminders,
|
|
227
252
|
),
|
|
228
253
|
content_chunks=content_chunks,
|
|
229
254
|
)
|
|
230
255
|
|
|
231
|
-
def _get_system_reminders(self, message: unique_sdk.Space.Message) -> list[str]:
|
|
232
|
-
reminders = []
|
|
233
|
-
for reminder_config in self.config.system_reminders_config:
|
|
234
|
-
if reminder_config.type == SubAgentSystemReminderType.FIXED:
|
|
235
|
-
reminders.append(
|
|
236
|
-
render_template(
|
|
237
|
-
reminder_config.reminder,
|
|
238
|
-
display_name=self.display_name(),
|
|
239
|
-
tool_name=self.name,
|
|
240
|
-
)
|
|
241
|
-
)
|
|
242
|
-
elif (
|
|
243
|
-
reminder_config.type == SubAgentSystemReminderType.REFERENCE
|
|
244
|
-
and self.config.use_sub_agent_references
|
|
245
|
-
and message["references"] is not None
|
|
246
|
-
and len(message["references"]) > 0
|
|
247
|
-
):
|
|
248
|
-
reminders.append(
|
|
249
|
-
render_template(
|
|
250
|
-
reminder_config.reminder,
|
|
251
|
-
display_name=self.display_name(),
|
|
252
|
-
tool_name=self.name,
|
|
253
|
-
)
|
|
254
|
-
)
|
|
255
|
-
elif (
|
|
256
|
-
reminder_config.type == SubAgentSystemReminderType.REGEXP
|
|
257
|
-
and message["text"] is not None
|
|
258
|
-
):
|
|
259
|
-
reminder_config = cast(
|
|
260
|
-
RegExpDetectedSystemReminderConfig, reminder_config
|
|
261
|
-
)
|
|
262
|
-
text_matches = [
|
|
263
|
-
match.group(0)
|
|
264
|
-
for match in reminder_config.regexp.finditer(message["text"])
|
|
265
|
-
]
|
|
266
|
-
if len(text_matches) > 0:
|
|
267
|
-
reminders.append(
|
|
268
|
-
render_template(
|
|
269
|
-
reminder_config.reminder,
|
|
270
|
-
display_name=self.display_name(),
|
|
271
|
-
tool_name=self.name,
|
|
272
|
-
text_matches=text_matches,
|
|
273
|
-
)
|
|
274
|
-
)
|
|
275
|
-
|
|
276
|
-
return reminders
|
|
277
|
-
|
|
278
256
|
async def _get_chat_id(self) -> str | None:
|
|
279
257
|
if not self.config.reuse_chat:
|
|
280
258
|
return None
|
|
@@ -290,23 +268,6 @@ class SubAgentTool(Tool[SubAgentToolConfig]):
|
|
|
290
268
|
|
|
291
269
|
return None
|
|
292
270
|
|
|
293
|
-
def _prepare_response_references(self, response: str, sequence_number: int) -> str:
|
|
294
|
-
if not self.config.use_sub_agent_references:
|
|
295
|
-
# Remove all references from the response
|
|
296
|
-
response = remove_all_refs(response)
|
|
297
|
-
return response
|
|
298
|
-
|
|
299
|
-
for ref_number in get_all_ref_numbers(response):
|
|
300
|
-
reference = self.get_sub_agent_reference_format(
|
|
301
|
-
name=self.name,
|
|
302
|
-
sequence_number=sequence_number,
|
|
303
|
-
reference_number=ref_number,
|
|
304
|
-
)
|
|
305
|
-
response = replace_ref_number(
|
|
306
|
-
text=response, ref_number=ref_number, replacement=reference
|
|
307
|
-
)
|
|
308
|
-
return response
|
|
309
|
-
|
|
310
271
|
async def _save_chat_id(self, chat_id: str) -> None:
|
|
311
272
|
if not self.config.reuse_chat:
|
|
312
273
|
return
|
|
@@ -390,4 +351,93 @@ def _format_response(tool_name: str, text: str, system_reminders: list[str]) ->
|
|
|
390
351
|
return json.dumps(response, indent=2)
|
|
391
352
|
|
|
392
353
|
|
|
354
|
+
def _response_has_refs(response: unique_sdk.Space.Message) -> bool:
|
|
355
|
+
if (
|
|
356
|
+
response["text"] is None
|
|
357
|
+
or response["references"] is None
|
|
358
|
+
or len(response["references"]) == 0
|
|
359
|
+
):
|
|
360
|
+
return False
|
|
361
|
+
|
|
362
|
+
for ref in response["references"]:
|
|
363
|
+
if (
|
|
364
|
+
re.search(
|
|
365
|
+
get_detection_pattern_for_ref(ref["sequenceNumber"]), response["text"]
|
|
366
|
+
)
|
|
367
|
+
is not None
|
|
368
|
+
):
|
|
369
|
+
return True
|
|
370
|
+
|
|
371
|
+
return False
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def _remove_extra_refs(response: str, refs: list[unique_sdk.Space.Reference]) -> str:
|
|
375
|
+
text_ref_numbers = set(get_all_ref_numbers(response))
|
|
376
|
+
extra_ref_numbers = text_ref_numbers - set(ref["sequenceNumber"] for ref in refs)
|
|
377
|
+
|
|
378
|
+
for ref_num in extra_ref_numbers:
|
|
379
|
+
response = get_detection_pattern_for_ref(ref_num).sub("", response)
|
|
380
|
+
|
|
381
|
+
return response
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def _prepare_sub_agent_response_refs(
|
|
385
|
+
response: str,
|
|
386
|
+
name: str,
|
|
387
|
+
sequence_number: int,
|
|
388
|
+
refs: list[unique_sdk.Space.Reference],
|
|
389
|
+
) -> str:
|
|
390
|
+
for ref in refs:
|
|
391
|
+
ref_number = ref["sequenceNumber"]
|
|
392
|
+
reference = SubAgentTool.get_sub_agent_reference_format(
|
|
393
|
+
name=name, sequence_number=sequence_number, reference_number=ref_number
|
|
394
|
+
)
|
|
395
|
+
response = replace_ref_number(
|
|
396
|
+
text=response, ref_number=ref_number, replacement=reference
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
return response
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def _get_sub_agent_system_reminders(
|
|
403
|
+
response: str,
|
|
404
|
+
configs: list[SystemReminderConfigType],
|
|
405
|
+
name: str,
|
|
406
|
+
display_name: str,
|
|
407
|
+
sequence_number: int,
|
|
408
|
+
has_refs: bool,
|
|
409
|
+
) -> list[str]:
|
|
410
|
+
reminders = []
|
|
411
|
+
|
|
412
|
+
for reminder_config in configs:
|
|
413
|
+
render_kwargs = {}
|
|
414
|
+
render_kwargs["display_name"] = display_name
|
|
415
|
+
render_kwargs["tool_name"] = name
|
|
416
|
+
template = None
|
|
417
|
+
|
|
418
|
+
if reminder_config.type == SubAgentSystemReminderType.FIXED:
|
|
419
|
+
template = reminder_config.reminder
|
|
420
|
+
elif (
|
|
421
|
+
reminder_config.type == SubAgentSystemReminderType.REFERENCE and has_refs
|
|
422
|
+
) or (
|
|
423
|
+
reminder_config.type == SubAgentSystemReminderType.NO_REFERENCE
|
|
424
|
+
and not has_refs
|
|
425
|
+
):
|
|
426
|
+
render_kwargs["tool_name"] = f"{name} {sequence_number}"
|
|
427
|
+
template = reminder_config.reminder
|
|
428
|
+
elif reminder_config.type == SubAgentSystemReminderType.REGEXP:
|
|
429
|
+
reminder_config = cast(RegExpDetectedSystemReminderConfig, reminder_config)
|
|
430
|
+
text_matches = [
|
|
431
|
+
match.group(0) for match in reminder_config.regexp.finditer(response)
|
|
432
|
+
]
|
|
433
|
+
if len(text_matches) > 0:
|
|
434
|
+
template = reminder_config.reminder
|
|
435
|
+
render_kwargs["text_matches"] = text_matches
|
|
436
|
+
|
|
437
|
+
if template is not None:
|
|
438
|
+
reminders.append(render_template(template, **render_kwargs))
|
|
439
|
+
|
|
440
|
+
return reminders
|
|
441
|
+
|
|
442
|
+
|
|
393
443
|
ToolFactory.register_tool(SubAgentTool, SubAgentToolConfig)
|