unique_toolkit 1.38.2__py3-none-any.whl → 1.38.4__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.
Files changed (25) hide show
  1. unique_toolkit/agentic/evaluation/evaluation_manager.py +4 -4
  2. unique_toolkit/agentic/loop_runner/__init__.py +15 -5
  3. unique_toolkit/agentic/loop_runner/_iteration_handler_utils.py +95 -0
  4. unique_toolkit/agentic/loop_runner/middleware/__init__.py +0 -8
  5. unique_toolkit/agentic/loop_runner/middleware/planning/planning.py +1 -1
  6. unique_toolkit/agentic/loop_runner/runners/__init__.py +14 -1
  7. unique_toolkit/agentic/loop_runner/runners/basic.py +8 -58
  8. unique_toolkit/agentic/loop_runner/runners/qwen/__init__.py +15 -0
  9. unique_toolkit/agentic/loop_runner/{middleware/qwen_forced_tool_call → runners/qwen}/helpers.py +15 -0
  10. unique_toolkit/agentic/loop_runner/runners/qwen/qwen_runner.py +118 -0
  11. unique_toolkit/agentic/postprocessor/postprocessor_manager.py +1 -1
  12. unique_toolkit/agentic/responses_api/postprocessors/generated_files.py +1 -1
  13. unique_toolkit/agentic/short_term_memory_manager/persistent_short_term_memory_manager.py +1 -1
  14. unique_toolkit/agentic/tools/a2a/evaluation/evaluator.py +1 -1
  15. unique_toolkit/agentic/tools/tool_manager.py +4 -4
  16. unique_toolkit/agentic/tools/utils/__init__.py +0 -19
  17. unique_toolkit/chat/responses_api.py +1 -1
  18. {unique_toolkit-1.38.2.dist-info → unique_toolkit-1.38.4.dist-info}/METADATA +7 -1
  19. {unique_toolkit-1.38.2.dist-info → unique_toolkit-1.38.4.dist-info}/RECORD +22 -22
  20. unique_toolkit/agentic/loop_runner/middleware/qwen_forced_tool_call/__init__.py +0 -13
  21. unique_toolkit/agentic/loop_runner/middleware/qwen_forced_tool_call/qwen_forced_tool_call.py +0 -50
  22. unique_toolkit/agentic/tools/utils/execution/__init__.py +0 -1
  23. /unique_toolkit/{agentic/tools/utils/execution → _common}/execution.py +0 -0
  24. {unique_toolkit-1.38.2.dist-info → unique_toolkit-1.38.4.dist-info}/LICENSE +0 -0
  25. {unique_toolkit-1.38.2.dist-info → unique_toolkit-1.38.4.dist-info}/WHEEL +0 -0
@@ -2,15 +2,15 @@ import asyncio
2
2
  from abc import ABC
3
3
  from logging import Logger
4
4
 
5
+ from unique_toolkit._common.execution import (
6
+ Result,
7
+ SafeTaskExecutor,
8
+ )
5
9
  from unique_toolkit.agentic.evaluation.schemas import (
6
10
  EvaluationAssessmentMessage,
7
11
  EvaluationMetricName,
8
12
  EvaluationMetricResult,
9
13
  )
10
- from unique_toolkit.agentic.tools.utils.execution.execution import (
11
- Result,
12
- SafeTaskExecutor,
13
- )
14
14
  from unique_toolkit.chat.schemas import (
15
15
  ChatMessageAssessmentStatus,
16
16
  ChatMessageAssessmentType,
@@ -1,25 +1,35 @@
1
+ from unique_toolkit.agentic.loop_runner._iteration_handler_utils import (
2
+ handle_forced_tools_iteration,
3
+ handle_last_iteration,
4
+ handle_normal_iteration,
5
+ )
1
6
  from unique_toolkit.agentic.loop_runner.base import LoopIterationRunner
2
7
  from unique_toolkit.agentic.loop_runner.middleware import (
3
- QWEN_FORCED_TOOL_CALL_PROMPT_INSTRUCTION,
4
8
  PlanningConfig,
5
9
  PlanningMiddleware,
6
10
  PlanningSchemaConfig,
7
- QwenForcedToolCallMiddleware,
8
- is_qwen_model,
9
11
  )
10
12
  from unique_toolkit.agentic.loop_runner.runners import (
13
+ QWEN_FORCED_TOOL_CALL_INSTRUCTION,
14
+ QWEN_LAST_ITERATION_INSTRUCTION,
11
15
  BasicLoopIterationRunner,
12
16
  BasicLoopIterationRunnerConfig,
17
+ QwenLoopIterationRunner,
18
+ is_qwen_model,
13
19
  )
14
20
 
15
21
  __all__ = [
16
22
  "LoopIterationRunner",
17
- "QWEN_FORCED_TOOL_CALL_PROMPT_INSTRUCTION",
18
23
  "PlanningConfig",
19
24
  "PlanningMiddleware",
20
25
  "PlanningSchemaConfig",
21
- "QwenForcedToolCallMiddleware",
26
+ "QwenLoopIterationRunner",
22
27
  "is_qwen_model",
23
28
  "BasicLoopIterationRunnerConfig",
24
29
  "BasicLoopIterationRunner",
30
+ "handle_forced_tools_iteration",
31
+ "handle_last_iteration",
32
+ "handle_normal_iteration",
33
+ "QWEN_FORCED_TOOL_CALL_INSTRUCTION",
34
+ "QWEN_LAST_ITERATION_INSTRUCTION",
25
35
  ]
@@ -0,0 +1,95 @@
1
+ import logging
2
+ from typing import Protocol, Unpack, cast
3
+
4
+ from unique_toolkit.agentic.loop_runner._stream_handler_utils import stream_response
5
+ from unique_toolkit.agentic.loop_runner.base import (
6
+ _LoopIterationRunnerKwargs,
7
+ )
8
+ from unique_toolkit.chat.functions import LanguageModelStreamResponse
9
+
10
+ _LOGGER = logging.getLogger(__name__)
11
+
12
+
13
+ class PrepareForcedToolIterationKwargs(Protocol):
14
+ def __call__(
15
+ self,
16
+ func_name: str | None,
17
+ per_choice_kwargs: _LoopIterationRunnerKwargs,
18
+ ) -> _LoopIterationRunnerKwargs: ...
19
+
20
+
21
+ async def handle_last_iteration(
22
+ **kwargs: Unpack[_LoopIterationRunnerKwargs],
23
+ ) -> LanguageModelStreamResponse:
24
+ _LOGGER.info("Reached last iteration, removing tools and producing final response")
25
+
26
+ return await stream_response(
27
+ loop_runner_kwargs=kwargs,
28
+ tools=None,
29
+ )
30
+
31
+
32
+ async def handle_normal_iteration(
33
+ **kwargs: Unpack[_LoopIterationRunnerKwargs],
34
+ ) -> LanguageModelStreamResponse:
35
+ _LOGGER.info("Running loop iteration %d", kwargs["iteration_index"])
36
+
37
+ return await stream_response(loop_runner_kwargs=kwargs)
38
+
39
+
40
+ async def handle_forced_tools_iteration(
41
+ **kwargs: Unpack[_LoopIterationRunnerKwargs],
42
+ ) -> LanguageModelStreamResponse:
43
+ return await run_forced_tools_iteration(loop_runner_kwargs=kwargs)
44
+
45
+
46
+ async def run_forced_tools_iteration(
47
+ *,
48
+ loop_runner_kwargs: _LoopIterationRunnerKwargs,
49
+ prepare_loop_runner_kwargs: PrepareForcedToolIterationKwargs | None = None,
50
+ ) -> LanguageModelStreamResponse:
51
+ """
52
+ Execute a "forced tools" iteration by running one stream_response per tool choice,
53
+ then merging tool calls and references into a single response.
54
+
55
+ Some models (e.g. Qwen) need per-tool-choice message rewriting; this can be done
56
+ via prepare_loop_runner_kwargs (called once per tool choice, on a copy of kwargs).
57
+ """
58
+ assert "tool_choices" in loop_runner_kwargs
59
+
60
+ tool_choices = loop_runner_kwargs["tool_choices"]
61
+ assert len(tool_choices) > 0, (
62
+ "run_forced_tools_iteration requires at least one tool choice"
63
+ )
64
+ _LOGGER.info("Forcing tools calls: %s", tool_choices)
65
+
66
+ responses: list[LanguageModelStreamResponse] = []
67
+
68
+ available_tools = {t.name: t for t in loop_runner_kwargs.get("tools") or []}
69
+
70
+ for opt in tool_choices:
71
+ func_name = opt.get("function", {}).get("name")
72
+
73
+ per_choice_kwargs = cast(_LoopIterationRunnerKwargs, dict(loop_runner_kwargs))
74
+ if prepare_loop_runner_kwargs:
75
+ per_choice_kwargs = prepare_loop_runner_kwargs(func_name, per_choice_kwargs)
76
+
77
+ limited_tool = available_tools.get(func_name) if func_name else None
78
+ stream_kwargs = {"loop_runner_kwargs": per_choice_kwargs, "tool_choice": opt}
79
+ if limited_tool:
80
+ stream_kwargs["tools"] = [limited_tool]
81
+ responses.append(await stream_response(**stream_kwargs))
82
+
83
+ # Merge responses and refs:
84
+ tool_calls = []
85
+ references = []
86
+ for r in responses:
87
+ if r.tool_calls:
88
+ tool_calls.extend(r.tool_calls)
89
+ references.extend(r.message.references)
90
+
91
+ response = responses[0]
92
+ response.tool_calls = tool_calls if len(tool_calls) > 0 else None
93
+ response.message.references = references
94
+
95
+ return response
@@ -3,17 +3,9 @@ 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
- )
11
6
 
12
7
  __all__ = [
13
8
  "PlanningConfig",
14
9
  "PlanningMiddleware",
15
10
  "PlanningSchemaConfig",
16
- "QWEN_FORCED_TOOL_CALL_PROMPT_INSTRUCTION",
17
- "QwenForcedToolCallMiddleware",
18
- "is_qwen_model",
19
11
  ]
@@ -5,6 +5,7 @@ from typing import Unpack
5
5
  from pydantic import BaseModel, Field
6
6
 
7
7
  from unique_toolkit import LanguageModelService
8
+ from unique_toolkit._common.execution import failsafe_async
8
9
  from unique_toolkit._common.pydantic_helpers import get_configuration_dict
9
10
  from unique_toolkit.agentic.history_manager.history_manager import HistoryManager
10
11
  from unique_toolkit.agentic.loop_runner.base import (
@@ -15,7 +16,6 @@ from unique_toolkit.agentic.loop_runner.middleware.planning.schema import (
15
16
  PlanningSchemaConfig,
16
17
  get_planning_schema,
17
18
  )
18
- from unique_toolkit.agentic.tools.utils import failsafe_async
19
19
  from unique_toolkit.chat.service import LanguageModelStreamResponse
20
20
  from unique_toolkit.language_model import (
21
21
  LanguageModelAssistantMessage,
@@ -2,5 +2,18 @@ from unique_toolkit.agentic.loop_runner.runners.basic import (
2
2
  BasicLoopIterationRunner,
3
3
  BasicLoopIterationRunnerConfig,
4
4
  )
5
+ from unique_toolkit.agentic.loop_runner.runners.qwen import (
6
+ QWEN_FORCED_TOOL_CALL_INSTRUCTION,
7
+ QWEN_LAST_ITERATION_INSTRUCTION,
8
+ QwenLoopIterationRunner,
9
+ is_qwen_model,
10
+ )
5
11
 
6
- __all__ = ["BasicLoopIterationRunnerConfig", "BasicLoopIterationRunner"]
12
+ __all__ = [
13
+ "BasicLoopIterationRunnerConfig",
14
+ "BasicLoopIterationRunner",
15
+ "QwenLoopIterationRunner",
16
+ "QWEN_FORCED_TOOL_CALL_INSTRUCTION",
17
+ "QWEN_LAST_ITERATION_INSTRUCTION",
18
+ "is_qwen_model",
19
+ ]
@@ -4,7 +4,11 @@ from typing import Unpack, override
4
4
  from pydantic import BaseModel
5
5
 
6
6
  from unique_toolkit._common.pydantic_helpers import get_configuration_dict
7
- from unique_toolkit.agentic.loop_runner._stream_handler_utils import stream_response
7
+ from unique_toolkit.agentic.loop_runner._iteration_handler_utils import (
8
+ handle_forced_tools_iteration,
9
+ handle_last_iteration,
10
+ handle_normal_iteration,
11
+ )
8
12
  from unique_toolkit.agentic.loop_runner.base import (
9
13
  LoopIterationRunner,
10
14
  _LoopIterationRunnerKwargs,
@@ -26,60 +30,6 @@ class BasicLoopIterationRunner(LoopIterationRunner):
26
30
  def __init__(self, config: BasicLoopIterationRunnerConfig) -> None:
27
31
  self._config = config
28
32
 
29
- async def _handle_last_iteration(
30
- self, **kwargs: Unpack[_LoopIterationRunnerKwargs]
31
- ) -> LanguageModelStreamResponse:
32
- _LOGGER.info(
33
- "Reached last iteration, removing tools and producing final response"
34
- )
35
-
36
- return await stream_response(
37
- loop_runner_kwargs=kwargs,
38
- tools=None,
39
- )
40
-
41
- async def _handle_normal_iteration(
42
- self, **kwargs: Unpack[_LoopIterationRunnerKwargs]
43
- ) -> LanguageModelStreamResponse:
44
- _LOGGER.info("Running loop iteration %d", kwargs["iteration_index"])
45
-
46
- return await stream_response(loop_runner_kwargs=kwargs)
47
-
48
- async def _handle_forced_tools_iteration(
49
- self,
50
- **kwargs: Unpack[_LoopIterationRunnerKwargs],
51
- ) -> LanguageModelStreamResponse:
52
- assert "tool_choices" in kwargs
53
-
54
- tool_choices = kwargs["tool_choices"]
55
- _LOGGER.info("Forcing tools calls: %s", tool_choices)
56
-
57
- responses: list[LanguageModelStreamResponse] = []
58
-
59
- available_tools = {t.name: t for t in kwargs.get("tools") or []}
60
-
61
- for opt in tool_choices:
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))
68
-
69
- # Merge responses and refs:
70
- tool_calls = []
71
- references = []
72
- for r in responses:
73
- if r.tool_calls:
74
- tool_calls.extend(r.tool_calls)
75
- references.extend(r.message.references)
76
-
77
- response = responses[0]
78
- response.tool_calls = tool_calls if len(tool_calls) > 0 else None
79
- response.message.references = references
80
-
81
- return response
82
-
83
33
  @override
84
34
  async def __call__(
85
35
  self,
@@ -89,8 +39,8 @@ class BasicLoopIterationRunner(LoopIterationRunner):
89
39
  iteration_index = kwargs["iteration_index"]
90
40
 
91
41
  if len(tool_choices) > 0 and iteration_index == 0:
92
- return await self._handle_forced_tools_iteration(**kwargs)
42
+ return await handle_forced_tools_iteration(**kwargs)
93
43
  elif iteration_index == self._config.max_loop_iterations - 1:
94
- return await self._handle_last_iteration(**kwargs)
44
+ return await handle_last_iteration(**kwargs)
95
45
  else:
96
- return await self._handle_normal_iteration(**kwargs)
46
+ return await handle_normal_iteration(**kwargs)
@@ -0,0 +1,15 @@
1
+ from unique_toolkit.agentic.loop_runner.runners.qwen.helpers import (
2
+ is_qwen_model,
3
+ )
4
+ from unique_toolkit.agentic.loop_runner.runners.qwen.qwen_runner import (
5
+ QWEN_FORCED_TOOL_CALL_INSTRUCTION,
6
+ QWEN_LAST_ITERATION_INSTRUCTION,
7
+ QwenLoopIterationRunner,
8
+ )
9
+
10
+ __all__ = [
11
+ "QwenLoopIterationRunner",
12
+ "is_qwen_model",
13
+ "QWEN_FORCED_TOOL_CALL_INSTRUCTION",
14
+ "QWEN_LAST_ITERATION_INSTRUCTION",
15
+ ]
@@ -1,5 +1,6 @@
1
1
  from unique_toolkit.language_model.infos import LanguageModelInfo
2
2
  from unique_toolkit.language_model.schemas import (
3
+ LanguageModelAssistantMessage,
3
4
  LanguageModelMessageRole,
4
5
  LanguageModelMessages,
5
6
  )
@@ -31,3 +32,17 @@ def append_qwen_forced_tool_call_instruction(
31
32
  )
32
33
  break
33
34
  return LanguageModelMessages(root=messages_list)
35
+
36
+
37
+ def append_qwen_last_iteration_assistant_message(
38
+ *,
39
+ messages: LanguageModelMessages,
40
+ last_iteration_instruction: str,
41
+ ) -> LanguageModelMessages:
42
+ """Append an assistant message at the end to indicate no further tool calls are allowed."""
43
+ messages_list = list(messages)
44
+ assistant_message = LanguageModelAssistantMessage(
45
+ content=last_iteration_instruction,
46
+ )
47
+ messages_list.append(assistant_message)
48
+ return LanguageModelMessages(root=messages_list)
@@ -0,0 +1,118 @@
1
+ import logging
2
+ from typing import Unpack
3
+
4
+ from unique_toolkit.agentic.loop_runner._iteration_handler_utils import (
5
+ handle_last_iteration,
6
+ handle_normal_iteration,
7
+ run_forced_tools_iteration,
8
+ )
9
+ from unique_toolkit.agentic.loop_runner.base import (
10
+ LoopIterationRunner,
11
+ _LoopIterationRunnerKwargs,
12
+ )
13
+ from unique_toolkit.agentic.loop_runner.runners.qwen.helpers import (
14
+ append_qwen_forced_tool_call_instruction,
15
+ append_qwen_last_iteration_assistant_message,
16
+ )
17
+ from unique_toolkit.chat.service import ChatService, LanguageModelStreamResponse
18
+
19
+ _LOGGER = logging.getLogger(__name__)
20
+
21
+ QWEN_FORCED_TOOL_CALL_INSTRUCTION = (
22
+ "**Tool Call Instruction:** \nYou MUST call the tool {TOOL_NAME}. "
23
+ "You must start the response with <tool_call>. "
24
+ "Do NOT provide natural language explanations, summaries, or any text outside the <tool_call> block."
25
+ )
26
+
27
+ QWEN_LAST_ITERATION_INSTRUCTION = "The maximum number of loop iteration have been reached. Not further tool calls are allowed. Based on the found information, an answer should be generated"
28
+
29
+
30
+ class QwenLoopIterationRunner(LoopIterationRunner):
31
+ def __init__(
32
+ self,
33
+ *,
34
+ qwen_forced_tool_call_instruction: str,
35
+ qwen_last_iteration_instruction: str,
36
+ max_loop_iterations: int,
37
+ chat_service: ChatService,
38
+ ) -> None:
39
+ self._qwen_forced_tool_call_instruction = qwen_forced_tool_call_instruction
40
+ self._qwen_last_iteration_instruction = qwen_last_iteration_instruction
41
+ self._max_loop_iterations = max_loop_iterations
42
+ self._chat_service = chat_service
43
+
44
+ async def __call__(
45
+ self, **kwargs: Unpack[_LoopIterationRunnerKwargs]
46
+ ) -> LanguageModelStreamResponse:
47
+ tool_choices = kwargs.get("tool_choices") or []
48
+ iteration_index = kwargs["iteration_index"]
49
+
50
+ if len(tool_choices) > 0 and iteration_index == 0:
51
+ return await self._qwen_handle_forced_tools_iteration(**kwargs)
52
+ elif iteration_index == self._max_loop_iterations - 1:
53
+ return await self._qwen_handle_last_iteration(**kwargs)
54
+ else:
55
+ return await self._qwen_handle_normal_iteration(**kwargs)
56
+
57
+ async def _qwen_handle_forced_tools_iteration(
58
+ self, **kwargs: Unpack[_LoopIterationRunnerKwargs]
59
+ ) -> LanguageModelStreamResponse:
60
+ # For Qwen models, append tool call instruction to the last user message. These models ignore the parameter tool_choice.
61
+ # As the message has to be modified for each tool call instruction, the function from the basic runner cant be used.
62
+ original_messages = kwargs["messages"].model_copy(deep=True)
63
+
64
+ def _prepare(
65
+ func_name: str | None,
66
+ per_choice_kwargs: _LoopIterationRunnerKwargs,
67
+ ) -> _LoopIterationRunnerKwargs:
68
+ prompt_instruction = self._qwen_forced_tool_call_instruction.format(
69
+ TOOL_NAME=func_name or ""
70
+ )
71
+ per_choice_kwargs["messages"] = append_qwen_forced_tool_call_instruction(
72
+ messages=original_messages,
73
+ forced_tool_call_instruction=prompt_instruction,
74
+ )
75
+ return per_choice_kwargs
76
+
77
+ response = await run_forced_tools_iteration(
78
+ loop_runner_kwargs=kwargs,
79
+ prepare_loop_runner_kwargs=_prepare,
80
+ )
81
+ return self._process_response(response)
82
+
83
+ async def _qwen_handle_last_iteration(
84
+ self, **kwargs: Unpack[_LoopIterationRunnerKwargs]
85
+ ) -> LanguageModelStreamResponse:
86
+ # For Qwen models, append an assistant message with instructions to not call any tool in this iteration.
87
+ _LOGGER.info(
88
+ "Reached last iteration, removing tools. Appending assistant message with instructions to not call any tool in this iteration."
89
+ )
90
+ kwargs["messages"] = append_qwen_last_iteration_assistant_message(
91
+ messages=kwargs["messages"],
92
+ last_iteration_instruction=self._qwen_last_iteration_instruction,
93
+ )
94
+
95
+ response = await handle_last_iteration(
96
+ **kwargs,
97
+ )
98
+
99
+ return self._process_response(response)
100
+
101
+ async def _qwen_handle_normal_iteration(
102
+ self, **kwargs: Unpack[_LoopIterationRunnerKwargs]
103
+ ) -> LanguageModelStreamResponse:
104
+ response = await handle_normal_iteration(**kwargs)
105
+ return self._process_response(response)
106
+
107
+ def _process_response(
108
+ self, response: LanguageModelStreamResponse
109
+ ) -> LanguageModelStreamResponse:
110
+ # Check if content of response is </tool_call>
111
+ if "</tool_call>" == response.message.text.strip():
112
+ _LOGGER.warning(
113
+ "Response contains only <tool_call>. This is not allowed. Returning empty response."
114
+ )
115
+ self._chat_service.modify_assistant_message(content="")
116
+ response.message.text = ""
117
+
118
+ return response
@@ -2,7 +2,7 @@ import asyncio
2
2
  from abc import ABC
3
3
  from logging import Logger
4
4
 
5
- from unique_toolkit.agentic.tools.utils.execution.execution import SafeTaskExecutor
5
+ from unique_toolkit._common.execution import SafeTaskExecutor
6
6
  from unique_toolkit.chat.service import ChatService
7
7
  from unique_toolkit.language_model.schemas import (
8
8
  LanguageModelStreamResponse,
@@ -9,6 +9,7 @@ from openai.types.responses.response_output_text import AnnotationContainerFileC
9
9
  from pydantic import BaseModel, Field, RootModel
10
10
 
11
11
  from unique_toolkit import ChatService
12
+ from unique_toolkit._common.execution import failsafe_async
12
13
  from unique_toolkit.agentic.postprocessor.postprocessor_manager import (
13
14
  ResponsesApiPostprocessor,
14
15
  )
@@ -16,7 +17,6 @@ from unique_toolkit.agentic.short_term_memory_manager.persistent_short_term_memo
16
17
  PersistentShortMemoryManager,
17
18
  )
18
19
  from unique_toolkit.agentic.tools.config import get_configuration_dict
19
- from unique_toolkit.agentic.tools.utils import failsafe_async
20
20
  from unique_toolkit.content.schemas import ContentReference
21
21
  from unique_toolkit.content.service import ContentService
22
22
  from unique_toolkit.language_model.schemas import ResponsesLanguageModelStreamResponse
@@ -5,7 +5,7 @@ from typing import Generic, Type, TypeVar
5
5
 
6
6
  from pydantic import BaseModel
7
7
 
8
- from unique_toolkit.agentic.tools.utils.execution.execution import SafeTaskExecutor
8
+ from unique_toolkit._common.execution import SafeTaskExecutor
9
9
  from unique_toolkit.short_term_memory.schemas import ShortTermMemory
10
10
  from unique_toolkit.short_term_memory.service import ShortTermMemoryService
11
11
 
@@ -5,6 +5,7 @@ import unique_sdk
5
5
  from jinja2 import Template
6
6
  from pydantic import BaseModel
7
7
 
8
+ from unique_toolkit._common.execution import failsafe
8
9
  from unique_toolkit.agentic.evaluation.evaluation_manager import Evaluation
9
10
  from unique_toolkit.agentic.evaluation.schemas import (
10
11
  EvaluationAssessmentMessage,
@@ -24,7 +25,6 @@ from unique_toolkit.agentic.tools.a2a.response_watcher import (
24
25
  SubAgentResponse,
25
26
  SubAgentResponseWatcher,
26
27
  )
27
- from unique_toolkit.agentic.tools.utils import failsafe
28
28
  from unique_toolkit.chat.schemas import (
29
29
  ChatMessageAssessmentLabel,
30
30
  ChatMessageAssessmentStatus,
@@ -11,6 +11,10 @@ from openai.types.responses import (
11
11
  )
12
12
  from pydantic import BaseModel, Field
13
13
 
14
+ from unique_toolkit._common.execution import (
15
+ Result,
16
+ SafeTaskExecutor,
17
+ )
14
18
  from unique_toolkit.agentic.evaluation.schemas import EvaluationMetricName
15
19
  from unique_toolkit.agentic.tools.a2a import A2AManager, SubAgentTool
16
20
  from unique_toolkit.agentic.tools.config import ToolBuildConfig
@@ -24,10 +28,6 @@ from unique_toolkit.agentic.tools.openai_builtin.manager import OpenAIBuiltInToo
24
28
  from unique_toolkit.agentic.tools.schemas import ToolCallResponse, ToolPrompts
25
29
  from unique_toolkit.agentic.tools.tool import Tool
26
30
  from unique_toolkit.agentic.tools.tool_progress_reporter import ToolProgressReporter
27
- from unique_toolkit.agentic.tools.utils.execution.execution import (
28
- Result,
29
- SafeTaskExecutor,
30
- )
31
31
  from unique_toolkit.app.schemas import ChatEvent
32
32
  from unique_toolkit.language_model.schemas import (
33
33
  LanguageModelFunction,
@@ -1,19 +0,0 @@
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
- ]
@@ -14,7 +14,7 @@ from openai.types.responses import (
14
14
  from openai.types.shared_params import Metadata, Reasoning
15
15
  from pydantic import BaseModel, TypeAdapter, ValidationError
16
16
 
17
- from unique_toolkit.agentic.tools.utils.execution.execution import (
17
+ from unique_toolkit._common.execution import (
18
18
  failsafe,
19
19
  )
20
20
  from unique_toolkit.content.schemas import ContentChunk
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: unique_toolkit
3
- Version: 1.38.2
3
+ Version: 1.38.4
4
4
  Summary:
5
5
  License: Proprietary
6
6
  Author: Cedric Klinkert
@@ -121,6 +121,12 @@ 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.38.4] - 2025-12-17
125
+ - Improving handling of tool calls with Qwen models
126
+
127
+ ## [1.38.3] - 2025-12-17
128
+ - Move the failsafe exception to root folder of unique_toolkit from agentic tools
129
+
124
130
  ## [1.38.2] - 2025-12-17
125
131
  - Fixing bug that language model infos were not loaded correctly
126
132
 
@@ -17,6 +17,7 @@ unique_toolkit/_common/docx_generator/template/Doc Template.docx,sha256=USnCg8h6
17
17
  unique_toolkit/_common/endpoint_builder.py,sha256=pEDwgeDzt67qbyaM98u8X7UAy29mQIw9Qufjz2bxgEA,11410
18
18
  unique_toolkit/_common/endpoint_requestor.py,sha256=TBLYUVpfdYIPpeKfEVYfH8BrNxnn4yAcSUjfdVDkT78,15443
19
19
  unique_toolkit/_common/exception.py,sha256=ho0uBcPeZXU2w15IrSBhO5w7KUgxp1HcKAQrf2uin-w,1243
20
+ unique_toolkit/_common/execution.py,sha256=ocPGGfUwa851207HNTLYiBJ1pNzJp4VhMZ49OPP33gU,8022
20
21
  unique_toolkit/_common/experimental/endpoint_builder.py,sha256=pEDwgeDzt67qbyaM98u8X7UAy29mQIw9Qufjz2bxgEA,11410
21
22
  unique_toolkit/_common/experimental/endpoint_requestor.py,sha256=YnDr8wASAEjZjLAeBmOWuFn4wUIZslTHBN_aApWeJBA,16079
22
23
  unique_toolkit/_common/feature_flags/schema.py,sha256=X32VqH4VMK7bhEfSd8Wbddl8FVs7Gh7ucuIEbmqc4Kw,268
@@ -47,7 +48,7 @@ unique_toolkit/agentic/evaluation/config.py,sha256=zcW7m63Yt5G39hN2If8slBl6Eu3jT
47
48
  unique_toolkit/agentic/evaluation/context_relevancy/prompts.py,sha256=EdHFUOB581yVxcOL8482KUv_LzaRjuiem71EF8udYMc,1331
48
49
  unique_toolkit/agentic/evaluation/context_relevancy/schema.py,sha256=lZd0TPzH43ifgWWGg3WO6b1AQX8aK2R9y51yH0d1DHM,2919
49
50
  unique_toolkit/agentic/evaluation/context_relevancy/service.py,sha256=2NM1_PCP6fXsRm0r-MGrUg5Z3WO00FBCqmiU8f5Kagg,9661
50
- unique_toolkit/agentic/evaluation/evaluation_manager.py,sha256=IPx4BVUgkjFOP1BGLi0BlB6UujpXlZ0KGuSXDRemQhY,8143
51
+ unique_toolkit/agentic/evaluation/evaluation_manager.py,sha256=wDN_Uuut9kEGek8JY3QeInKpF-ukbvOSKOVd7DHFT3Q,8121
51
52
  unique_toolkit/agentic/evaluation/exception.py,sha256=7lcVbCyoN4Md1chNJDFxpUYyWbVrcr9dcc3TxWykJTc,115
52
53
  unique_toolkit/agentic/evaluation/hallucination/constants.py,sha256=SoGmoYti2J33tSmmOC1BSF6Pkh8DQvbQAU9xIZFQZRs,2070
53
54
  unique_toolkit/agentic/evaluation/hallucination/hallucination_evaluation.py,sha256=x5ta2Fum4fE5ySgIXPKlnbTtmV140z0IazSATd0-REg,4092
@@ -62,27 +63,28 @@ unique_toolkit/agentic/history_manager/history_construction_with_contents.py,sha
62
63
  unique_toolkit/agentic/history_manager/history_manager.py,sha256=7V7_173XkAjc8otBACF0G3dbqRs34FSlURbBPrE95Wk,9537
63
64
  unique_toolkit/agentic/history_manager/loop_token_reducer.py,sha256=3c-uonDovtanEJUpAO4zlA4-n9MS_Ws_V0Yb6G7hPM0,20172
64
65
  unique_toolkit/agentic/history_manager/utils.py,sha256=VIn_UmcR3jHtpux0qp5lQQzczgAm8XYSeQiPo87jC3A,3143
65
- unique_toolkit/agentic/loop_runner/__init__.py,sha256=Kg-6Zgt--iIYk-biAkvnuwbButxPRPoVlrpppUblm0s,721
66
+ unique_toolkit/agentic/loop_runner/__init__.py,sha256=3Ddm4WXa5KBTwScvmn6INmBBcuFVfJGriOE6VUfGVkM,1037
67
+ unique_toolkit/agentic/loop_runner/_iteration_handler_utils.py,sha256=czwTbMMmikppRjAA2wpx-UZGub8KrpSoWfojklr-5sE,3261
66
68
  unique_toolkit/agentic/loop_runner/_stream_handler_utils.py,sha256=FTGc5y8wkDnwnRVSYEdandgKz-FiySOsrTFFMadwP6E,1706
67
69
  unique_toolkit/agentic/loop_runner/base.py,sha256=3g4PalzV00o8kcRwHds2c2rtxW4idD7_7vS2Z7GkMvQ,1370
68
- unique_toolkit/agentic/loop_runner/middleware/__init__.py,sha256=r9c_Ml2g7obglnEC7gDihSTUrZ6s1sNGCMuMXR0Yl90,520
70
+ unique_toolkit/agentic/loop_runner/middleware/__init__.py,sha256=5yhFZ14_C1qAt-Mb3u3nZ1h6gxuSZ5Ts90-rbk2jjUM,232
69
71
  unique_toolkit/agentic/loop_runner/middleware/planning/__init__.py,sha256=Y9MlihNA8suNREixW98RF45bj0EMtD_tQuDrO2MEML4,304
70
- unique_toolkit/agentic/loop_runner/middleware/planning/planning.py,sha256=5d8kyipuFyI_1SQG49f165eOwSHeSG1qjbJQ7laeTsk,3218
72
+ unique_toolkit/agentic/loop_runner/middleware/planning/planning.py,sha256=s6SAP3BCCExgwnyRj_bZTaWgTOiNVju5qcJA0WFUUoE,3216
71
73
  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
75
- unique_toolkit/agentic/loop_runner/runners/__init__.py,sha256=raaNpHcTfXkYURy0ysyacispSdQzYPDoG17PyR57uK4,205
76
- unique_toolkit/agentic/loop_runner/runners/basic.py,sha256=SQzwkLEiraU8neXvPEc_uOBzC17PkCpOEsFrZ79YGCY,3379
74
+ unique_toolkit/agentic/loop_runner/runners/__init__.py,sha256=9aUSdHgwAfwEmvE_xL7luxmaL2HaU-fG_x-HLbHls9k,536
75
+ unique_toolkit/agentic/loop_runner/runners/basic.py,sha256=PKg7AXp5_2OJpJX8b7_5u0jBKffX5mg8X6DFk98sSwA,1535
76
+ unique_toolkit/agentic/loop_runner/runners/qwen/__init__.py,sha256=b2zAQIjtPy_BBQeDHsdfDLTKigI11w7tXHgUGNnFIiU,419
77
+ unique_toolkit/agentic/loop_runner/runners/qwen/helpers.py,sha256=JBnxeYKu8HiUq3VHjnb8XdHCe1c3cJ3OwagckF4UvnU,1763
78
+ unique_toolkit/agentic/loop_runner/runners/qwen/qwen_runner.py,sha256=7tYfTkNKwxYVn6C1Htya5QEIK2BfWkxCPNkdMoaZGq0,4920
77
79
  unique_toolkit/agentic/message_log_manager/__init__.py,sha256=3-KY_sGkPbNoSnrzwPY0FQIJNnsz4NHXvocXgGRUeuE,169
78
80
  unique_toolkit/agentic/message_log_manager/service.py,sha256=AiuIq2dKQg9Y8bEYgGcve1X8-WRRdqPZXaZXXLJxfFM,3057
79
- unique_toolkit/agentic/postprocessor/postprocessor_manager.py,sha256=s6HFhA61TE05aAay15NFTWI1JvdSlxmGpEVfpBbGFyM,7684
81
+ unique_toolkit/agentic/postprocessor/postprocessor_manager.py,sha256=CoKzVFeLIr1eRP3ZLnmUJ8KNsFLyvK5iuvUilbcGAm0,7662
80
82
  unique_toolkit/agentic/reference_manager/reference_manager.py,sha256=x51CT0D8HHu2LzgXdHGy0leOYpjnsxVbPZ2nc28G9mA,4005
81
83
  unique_toolkit/agentic/responses_api/__init__.py,sha256=9WTO-ef7fGE9Y1QtZJFm8Q_jkwK8Srtl-HWvpAD2Wxs,668
82
84
  unique_toolkit/agentic/responses_api/postprocessors/code_display.py,sha256=h6ZqPR0kPQnxM0ynshYQTa1BrcN8XGbUz9p03m8rOj0,2339
83
- unique_toolkit/agentic/responses_api/postprocessors/generated_files.py,sha256=ZdxJQQlD5K2Arn_NJVQNzIUwp-q5SmIE17o-UquTa0A,11691
85
+ unique_toolkit/agentic/responses_api/postprocessors/generated_files.py,sha256=Bywzf0SVwdzvWbNJbsMU4T43IclIqlUO_adCo7xaU7U,11689
84
86
  unique_toolkit/agentic/responses_api/stream_handler.py,sha256=Y1IM0uiPBdlab5UuOTCsHTaVX-fd9MxfS3xkwhdFie4,647
85
- unique_toolkit/agentic/short_term_memory_manager/persistent_short_term_memory_manager.py,sha256=uF3HSoZF0hBfuNhIE9N8KRtuwDfpoeXUFVrv_cyZ3Sw,5839
87
+ unique_toolkit/agentic/short_term_memory_manager/persistent_short_term_memory_manager.py,sha256=g8I64dKkpwWIXfwpxD1-rLte00hh_PoQ9-fXUAcNQCo,5817
86
88
  unique_toolkit/agentic/thinking_manager/thinking_manager.py,sha256=41QWFsdRrbWlQHBfYCFv726UDom4WbcvaRfjCmoUOQI,4183
87
89
  unique_toolkit/agentic/tools/__init__.py,sha256=-ToY9-Xiz0K7qCUydH1h1yG6n4h1hQS8sBuSVPNEq2Y,43
88
90
  unique_toolkit/agentic/tools/a2a/__init__.py,sha256=41DntFL-YKOQFBxObfrhlKmMj2BD0Zx611U1qSSyL9E,1329
@@ -90,7 +92,7 @@ unique_toolkit/agentic/tools/a2a/config.py,sha256=6diTTSiS2prY294LfYozB-db2wmJ6j
90
92
  unique_toolkit/agentic/tools/a2a/evaluation/__init__.py,sha256=Efso468EQ4UANv140qcrRPCdX98-OQJfnrLqhgJ9pfE,412
91
93
  unique_toolkit/agentic/tools/a2a/evaluation/_utils.py,sha256=FO5_us6mC4t_X4OVtNei1Ife4SjMjCiFOsPhUHUsY-s,1878
92
94
  unique_toolkit/agentic/tools/a2a/evaluation/config.py,sha256=Ra5rzJArS3r8C7RTmzKB8cLEYh4w-u3pe_XLgul3GOA,2355
93
- unique_toolkit/agentic/tools/a2a/evaluation/evaluator.py,sha256=AJvXu0UJKHe72nRmFZQjmBqxglSNDpYk_Tup3jJ8irg,9388
95
+ unique_toolkit/agentic/tools/a2a/evaluation/evaluator.py,sha256=74ceLV-2zye0MANxhsEt7oG8KSxYT1_HMnlwUAi7tXw,9386
94
96
  unique_toolkit/agentic/tools/a2a/evaluation/summarization_user_message.j2,sha256=acP1YqD_sCy6DT0V2EIfhQTmaUKeqpeWNJ7RGgceo8I,271
95
97
  unique_toolkit/agentic/tools/a2a/manager.py,sha256=pk06UUXKQdIUY-PyykYiItubBjmIydOaqWvBBDwhMN4,1939
96
98
  unique_toolkit/agentic/tools/a2a/postprocessing/__init__.py,sha256=aVtUBPN7kDrqA6Bze34AbqQpcBBqpvfyJG-xF65w7R0,659
@@ -128,11 +130,9 @@ unique_toolkit/agentic/tools/schemas.py,sha256=TXshRvivr2hD-McXHumO0bp-Z0mz_GnAm
128
130
  unique_toolkit/agentic/tools/test/test_mcp_manager.py,sha256=VpB4k4Dh0lQWakilJMQSzO8sBXapuEC26cub_lorl-M,19221
129
131
  unique_toolkit/agentic/tools/test/test_tool_progress_reporter.py,sha256=XHNezB8itj9KzpQgD0cwRtp2AgRUfUkQsHc3bTyPj6c,15801
130
132
  unique_toolkit/agentic/tools/tool.py,sha256=YmhGZ-NkPlgQ4X1hA6IVod10LNM5YErzbd0Zn6ANA98,6529
131
- unique_toolkit/agentic/tools/tool_manager.py,sha256=ZwzCcjzHYZgB1FxrpKRmarepfaZknIhLilxeyKoe4BY,17541
133
+ unique_toolkit/agentic/tools/tool_manager.py,sha256=zwpIxPUl1SamqGUUwgl0X4H-IR0ZpGjaqvszDLYy7ig,17519
132
134
  unique_toolkit/agentic/tools/tool_progress_reporter.py,sha256=GaR0oqDUJZvBB9WCUVYYh0Zvs6U-LMygJCCrqPlitgA,10296
133
- unique_toolkit/agentic/tools/utils/__init__.py,sha256=s75sjY5nrJchjLGs3MwSIqhDW08fFXIaX7eRQjFIA4s,346
134
- unique_toolkit/agentic/tools/utils/execution/__init__.py,sha256=OHiKpqBnfhBiEQagKVWJsZlHv8smPp5OI4dFIexzibw,37
135
- unique_toolkit/agentic/tools/utils/execution/execution.py,sha256=ocPGGfUwa851207HNTLYiBJ1pNzJp4VhMZ49OPP33gU,8022
135
+ unique_toolkit/agentic/tools/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
136
136
  unique_toolkit/agentic/tools/utils/source_handling/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
137
137
  unique_toolkit/agentic/tools/utils/source_handling/schema.py,sha256=iHBKuks6tUy8tvian4Pd0B6_-8__SehVVNcxIUAUjEA,882
138
138
  unique_toolkit/agentic/tools/utils/source_handling/source_formatting.py,sha256=uZ0QXqrPWgId3ZA67dvjHQ6xrW491LK1xxx_sVJmFHg,9160
@@ -153,7 +153,7 @@ unique_toolkit/chat/constants.py,sha256=05kq6zjqUVB2d6_P7s-90nbljpB3ryxwCI-CAz0r
153
153
  unique_toolkit/chat/deprecated/service.py,sha256=CYwzXi7OB0RjHd73CO2jq8SlpdBmDYLatzPFkb5sA0k,6529
154
154
  unique_toolkit/chat/functions.py,sha256=VdehD26NnsEhBsV5tD0tgUFD4LuEqIYgz8wWSsxYFgA,46078
155
155
  unique_toolkit/chat/rendering.py,sha256=c8YiV9oADRrJQ5A_QBJ4_UFc0NZ-2vVaa7tupoMusso,880
156
- unique_toolkit/chat/responses_api.py,sha256=MCI1MR_4wlo9xY1ifH2daNz4JvjX18uPdryQlemaeLw,14488
156
+ unique_toolkit/chat/responses_api.py,sha256=K_3lY8PUmey2io_enx_WY3hxE7TmMZueeIBG2hYa8KY,14466
157
157
  unique_toolkit/chat/schemas.py,sha256=Zh903Xt9PhugvwzoC_k5KXQWAqllWHvj9DmqDVom2yk,6890
158
158
  unique_toolkit/chat/service.py,sha256=6D00OL4QrGafbOhTaC5zNXaNgg7gS5W_2ePVa4LhqpE,4439
159
159
  unique_toolkit/chat/state.py,sha256=Cjgwv_2vhDFbV69xxsn7SefhaoIAEqLx3ferdVFCnOg,1445
@@ -212,7 +212,7 @@ unique_toolkit/short_term_memory/service.py,sha256=5PeVBu1ZCAfyDb2HLVvlmqSbyzBBu
212
212
  unique_toolkit/smart_rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
213
213
  unique_toolkit/smart_rules/compile.py,sha256=Ozhh70qCn2yOzRWr9d8WmJeTo7AQurwd3tStgBMPFLA,1246
214
214
  unique_toolkit/test_utilities/events.py,sha256=_mwV2bs5iLjxS1ynDCjaIq-gjjKhXYCK-iy3dRfvO3g,6410
215
- unique_toolkit-1.38.2.dist-info/LICENSE,sha256=GlN8wHNdh53xwOPg44URnwag6TEolCjoq3YD_KrWgss,193
216
- unique_toolkit-1.38.2.dist-info/METADATA,sha256=2ZglK0rXlPIPEkPJIOui1aQlBND2oCzd-C72_7z1gN0,46611
217
- unique_toolkit-1.38.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
218
- unique_toolkit-1.38.2.dist-info/RECORD,,
215
+ unique_toolkit-1.38.4.dist-info/LICENSE,sha256=GlN8wHNdh53xwOPg44URnwag6TEolCjoq3YD_KrWgss,193
216
+ unique_toolkit-1.38.4.dist-info/METADATA,sha256=fRZxzhXh974Ju0N6ugjU2PqqrdDbf22IL3sjyzHoElI,46797
217
+ unique_toolkit-1.38.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
218
+ unique_toolkit-1.38.4.dist-info/RECORD,,
@@ -1,13 +0,0 @@
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
- ]
@@ -1,50 +0,0 @@
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)
@@ -1 +0,0 @@
1
- """Execution utilities for tools."""