unique_toolkit 1.36.0__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/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-1.36.0.dist-info → unique_toolkit-1.37.0.dist-info}/METADATA +4 -1
- {unique_toolkit-1.36.0.dist-info → unique_toolkit-1.37.0.dist-info}/RECORD +10 -7
- {unique_toolkit-1.36.0.dist-info → unique_toolkit-1.37.0.dist-info}/LICENSE +0 -0
- {unique_toolkit-1.36.0.dist-info → unique_toolkit-1.37.0.dist-info}/WHEEL +0 -0
|
@@ -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 = []
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: unique_toolkit
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.37.0
|
|
4
4
|
Summary:
|
|
5
5
|
License: Proprietary
|
|
6
6
|
Author: Cedric Klinkert
|
|
@@ -121,6 +121,9 @@ All notable changes to this project will be documented in this file.
|
|
|
121
121
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
122
122
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
123
123
|
|
|
124
|
+
## [1.37.0] - 2025-12-15
|
|
125
|
+
- Adding a prompt appendix to enforce forced tool calls when using Qwen models
|
|
126
|
+
|
|
124
127
|
## [1.36.0] - 2025-12-11
|
|
125
128
|
- Add support for a sub agent tool system reminder when no references are present in the sub agent response.
|
|
126
129
|
|
|
@@ -62,15 +62,18 @@ unique_toolkit/agentic/history_manager/history_construction_with_contents.py,sha
|
|
|
62
62
|
unique_toolkit/agentic/history_manager/history_manager.py,sha256=7V7_173XkAjc8otBACF0G3dbqRs34FSlURbBPrE95Wk,9537
|
|
63
63
|
unique_toolkit/agentic/history_manager/loop_token_reducer.py,sha256=3c-uonDovtanEJUpAO4zlA4-n9MS_Ws_V0Yb6G7hPM0,20172
|
|
64
64
|
unique_toolkit/agentic/history_manager/utils.py,sha256=VIn_UmcR3jHtpux0qp5lQQzczgAm8XYSeQiPo87jC3A,3143
|
|
65
|
-
unique_toolkit/agentic/loop_runner/__init__.py,sha256=
|
|
65
|
+
unique_toolkit/agentic/loop_runner/__init__.py,sha256=Kg-6Zgt--iIYk-biAkvnuwbButxPRPoVlrpppUblm0s,721
|
|
66
66
|
unique_toolkit/agentic/loop_runner/_stream_handler_utils.py,sha256=FTGc5y8wkDnwnRVSYEdandgKz-FiySOsrTFFMadwP6E,1706
|
|
67
67
|
unique_toolkit/agentic/loop_runner/base.py,sha256=3g4PalzV00o8kcRwHds2c2rtxW4idD7_7vS2Z7GkMvQ,1370
|
|
68
|
-
unique_toolkit/agentic/loop_runner/middleware/__init__.py,sha256=
|
|
68
|
+
unique_toolkit/agentic/loop_runner/middleware/__init__.py,sha256=r9c_Ml2g7obglnEC7gDihSTUrZ6s1sNGCMuMXR0Yl90,520
|
|
69
69
|
unique_toolkit/agentic/loop_runner/middleware/planning/__init__.py,sha256=Y9MlihNA8suNREixW98RF45bj0EMtD_tQuDrO2MEML4,304
|
|
70
70
|
unique_toolkit/agentic/loop_runner/middleware/planning/planning.py,sha256=5d8kyipuFyI_1SQG49f165eOwSHeSG1qjbJQ7laeTsk,3218
|
|
71
71
|
unique_toolkit/agentic/loop_runner/middleware/planning/schema.py,sha256=76C36CWCLfDAYYqtaQlhXsmkWM1fCqf8j-l5afQREKA,2869
|
|
72
|
+
unique_toolkit/agentic/loop_runner/middleware/qwen_forced_tool_call/__init__.py,sha256=lP8N8XLvV1irvGC6Q0FedAlBx-T2UPKotDRwkdx7neA,417
|
|
73
|
+
unique_toolkit/agentic/loop_runner/middleware/qwen_forced_tool_call/helpers.py,sha256=DGKW9i7mCXNTejO2fotCmqSzI2b5k89ybkJA0QQ75qU,1234
|
|
74
|
+
unique_toolkit/agentic/loop_runner/middleware/qwen_forced_tool_call/qwen_forced_tool_call.py,sha256=dp08YgL4UwVDTsJB-z7eiJn7zWEbqG8eiWfMOcvKZdI,1955
|
|
72
75
|
unique_toolkit/agentic/loop_runner/runners/__init__.py,sha256=raaNpHcTfXkYURy0ysyacispSdQzYPDoG17PyR57uK4,205
|
|
73
|
-
unique_toolkit/agentic/loop_runner/runners/basic.py,sha256=
|
|
76
|
+
unique_toolkit/agentic/loop_runner/runners/basic.py,sha256=SQzwkLEiraU8neXvPEc_uOBzC17PkCpOEsFrZ79YGCY,3379
|
|
74
77
|
unique_toolkit/agentic/message_log_manager/__init__.py,sha256=3-KY_sGkPbNoSnrzwPY0FQIJNnsz4NHXvocXgGRUeuE,169
|
|
75
78
|
unique_toolkit/agentic/message_log_manager/service.py,sha256=AiuIq2dKQg9Y8bEYgGcve1X8-WRRdqPZXaZXXLJxfFM,3057
|
|
76
79
|
unique_toolkit/agentic/postprocessor/postprocessor_manager.py,sha256=s6HFhA61TE05aAay15NFTWI1JvdSlxmGpEVfpBbGFyM,7684
|
|
@@ -209,7 +212,7 @@ unique_toolkit/short_term_memory/service.py,sha256=5PeVBu1ZCAfyDb2HLVvlmqSbyzBBu
|
|
|
209
212
|
unique_toolkit/smart_rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
210
213
|
unique_toolkit/smart_rules/compile.py,sha256=Ozhh70qCn2yOzRWr9d8WmJeTo7AQurwd3tStgBMPFLA,1246
|
|
211
214
|
unique_toolkit/test_utilities/events.py,sha256=_mwV2bs5iLjxS1ynDCjaIq-gjjKhXYCK-iy3dRfvO3g,6410
|
|
212
|
-
unique_toolkit-1.
|
|
213
|
-
unique_toolkit-1.
|
|
214
|
-
unique_toolkit-1.
|
|
215
|
-
unique_toolkit-1.
|
|
215
|
+
unique_toolkit-1.37.0.dist-info/LICENSE,sha256=GlN8wHNdh53xwOPg44URnwag6TEolCjoq3YD_KrWgss,193
|
|
216
|
+
unique_toolkit-1.37.0.dist-info/METADATA,sha256=P8N25tqQqezFmyaOYAXtauCGKHdeGaxMa9kyfd-x1_Q,46284
|
|
217
|
+
unique_toolkit-1.37.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
218
|
+
unique_toolkit-1.37.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|