unique_toolkit 0.7.40__py3-none-any.whl → 0.7.42__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/app/schemas.py +4 -0
- unique_toolkit/tools/tool_definitions.py +145 -0
- unique_toolkit/tools/tool_definitionsV2.py +137 -0
- unique_toolkit/tools/tool_factory.py +31 -0
- unique_toolkit/tools/tool_progress_reporter.py +225 -0
- {unique_toolkit-0.7.40.dist-info → unique_toolkit-0.7.42.dist-info}/METADATA +7 -1
- {unique_toolkit-0.7.40.dist-info → unique_toolkit-0.7.42.dist-info}/RECORD +9 -5
- {unique_toolkit-0.7.40.dist-info → unique_toolkit-0.7.42.dist-info}/LICENSE +0 -0
- {unique_toolkit-0.7.40.dist-info → unique_toolkit-0.7.42.dist-info}/WHEEL +0 -0
unique_toolkit/app/schemas.py
CHANGED
|
@@ -116,6 +116,10 @@ class ChatEventPayload(BaseModel):
|
|
|
116
116
|
default_factory=list,
|
|
117
117
|
description="A list containing the tool names the user has chosen to be activated.",
|
|
118
118
|
)
|
|
119
|
+
disabled_tools: list[str] = Field(
|
|
120
|
+
default_factory=list,
|
|
121
|
+
description="A list containing the tool names of tools that are disabled at the company level",
|
|
122
|
+
)
|
|
119
123
|
tool_parameters: dict[str, Any] = Field(
|
|
120
124
|
default_factory=dict,
|
|
121
125
|
description="Parameters extracted from module selection function calling the tool.",
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from enum import StrEnum
|
|
3
|
+
from typing import Any, Generic, List, Self, TypeVar
|
|
4
|
+
from unique_toolkit.language_model import LanguageModelToolDescription
|
|
5
|
+
from typing_extensions import deprecated
|
|
6
|
+
# import baseModel from pedantic
|
|
7
|
+
from unique_toolkit.language_model import LanguageModelFunction
|
|
8
|
+
from pydantic import BaseModel, Field, model_validator, root_validator
|
|
9
|
+
|
|
10
|
+
from unique_toolkit.unique_toolkit.app.schemas import ChatEvent
|
|
11
|
+
from unique_toolkit.unique_toolkit.tools.tool_progress_reporter import ToolProgressReporter
|
|
12
|
+
|
|
13
|
+
class ToolSelectionPolicy(StrEnum):
|
|
14
|
+
"""Determine the usage policy of tools."""
|
|
15
|
+
|
|
16
|
+
FORCED_BY_DEFAULT = "ForcedByDefault"
|
|
17
|
+
ON_BY_DEFAULT = "OnByDefault"
|
|
18
|
+
BY_USER = "ByUser"
|
|
19
|
+
|
|
20
|
+
class UqToolName(StrEnum):
|
|
21
|
+
WEB_SEARCH = "WebSearch"
|
|
22
|
+
INTERNAL_SEARCH = "InternalSearch"
|
|
23
|
+
DOCUMENT_SUMMARIZER = "DocumentSummarizer"
|
|
24
|
+
CHART_GENERATOR = "ChartGenerator"
|
|
25
|
+
DOCUMENT_GENERATOR = "DocumentGenerator"
|
|
26
|
+
DOCUMENT_PARSER = "DocumentParser"
|
|
27
|
+
IMAGE_CONTENT = "ImageContent"
|
|
28
|
+
TABLE_SEARCH = "TableSearch"
|
|
29
|
+
BAR_CHART = "BarChart"
|
|
30
|
+
LINE_CHART = "LineChart"
|
|
31
|
+
PIE_CHART = "PieChart"
|
|
32
|
+
BASE_TOOL = "BaseTool"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class BaseToolConfig(BaseModel):
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
ConfigType = TypeVar("ConfigType", bound=BaseToolConfig)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ToolSettings(Generic[ConfigType]):
|
|
43
|
+
configuration: ConfigType
|
|
44
|
+
display_name: str
|
|
45
|
+
icon: str
|
|
46
|
+
selection_policy: ToolSelectionPolicy = Field(
|
|
47
|
+
default=ToolSelectionPolicy.BY_USER,
|
|
48
|
+
)
|
|
49
|
+
is_exclusive: bool = Field(default=False)
|
|
50
|
+
is_enabled: bool = Field(default=True)
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def from_service_dict(cls, service_dict: dict[str, Any]) -> Self | None:
|
|
54
|
+
try:
|
|
55
|
+
return cls(**service_dict)
|
|
56
|
+
except (ValueError, TypeError) as e:
|
|
57
|
+
print(e)
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class ToolCallResponse(BaseModel):
|
|
62
|
+
id: str
|
|
63
|
+
name: str
|
|
64
|
+
debug_info: dict = {}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class ToolPromptInstructions(BaseModel):
|
|
68
|
+
system_prompt: str = Field(
|
|
69
|
+
default="",
|
|
70
|
+
description=("Helps the LLM understand how to use the tool. "
|
|
71
|
+
"This is injected into the system prompt."
|
|
72
|
+
"This might not be needed for every tool but some of the work better with user prompt "
|
|
73
|
+
"instructions while others work better with system prompt instructions."),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
user_prompt: str = Field(
|
|
77
|
+
default="",
|
|
78
|
+
description=("Helps the LLM understand how to use the tool. "
|
|
79
|
+
"This is injected into the user prompt. "
|
|
80
|
+
"This might not be needed for every tool but some of the work better with user prompt "
|
|
81
|
+
"instructions while others work better with system prompt instructions.")
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
system_prompt_tool_chosen: str = Field(
|
|
85
|
+
default="",
|
|
86
|
+
description=("Once the tool is chosen, this is injected into the system prompt"
|
|
87
|
+
" to help the LLM understand how work with the tools results."),
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
user_prompt_tool_chosen: str = Field(
|
|
91
|
+
default="",
|
|
92
|
+
description=("Once the tool is chosen, this is injected into the user prompt "
|
|
93
|
+
"to help the LLM understand how to work with the tools results."),
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class Tool(ABC, Generic[ConfigType]):
|
|
98
|
+
name: str
|
|
99
|
+
|
|
100
|
+
def tool_description(self) -> LanguageModelToolDescription:
|
|
101
|
+
raise NotImplementedError
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def get_prompt_instructions(self) -> ToolPromptInstructions:
|
|
105
|
+
return ToolPromptInstructions(
|
|
106
|
+
system_prompt="",
|
|
107
|
+
user_prompt="",
|
|
108
|
+
system_prompt_tool_chosen="",
|
|
109
|
+
user_prompt_tool_chosen="",
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def is_exclusive(self) -> bool:
|
|
114
|
+
return self.settings.is_exclusive
|
|
115
|
+
|
|
116
|
+
def is_enabled(self) -> bool:
|
|
117
|
+
return self.settings.is_enabled
|
|
118
|
+
|
|
119
|
+
def display_name(self) -> str:
|
|
120
|
+
return self.settings.display_name
|
|
121
|
+
|
|
122
|
+
def icon(self) -> str:
|
|
123
|
+
return self.settings.icon
|
|
124
|
+
|
|
125
|
+
def tool_selection_policy(self) -> ToolSelectionPolicy:
|
|
126
|
+
return self.settings.selection_policy
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@abstractmethod
|
|
130
|
+
async def run(self, tool_call: LanguageModelFunction) -> ToolCallResponse:
|
|
131
|
+
raise NotImplementedError
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def __init__(
|
|
136
|
+
self,
|
|
137
|
+
settings: ToolSettings[ConfigType],
|
|
138
|
+
event: ChatEvent,
|
|
139
|
+
tool_progress_reporter: ToolProgressReporter
|
|
140
|
+
):
|
|
141
|
+
self.settings = settings
|
|
142
|
+
self.tool_progress_reporter = tool_progress_reporter
|
|
143
|
+
self.event = event
|
|
144
|
+
|
|
145
|
+
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from enum import StrEnum
|
|
3
|
+
from typing import Any, Generic, List, Self, TypeVar
|
|
4
|
+
from unique_toolkit.language_model import LanguageModelToolDescription
|
|
5
|
+
from typing_extensions import deprecated
|
|
6
|
+
# import baseModel from pedantic
|
|
7
|
+
from unique_toolkit.language_model import LanguageModelFunction
|
|
8
|
+
from pydantic import BaseModel, Field, model_validator, root_validator
|
|
9
|
+
|
|
10
|
+
from unique_toolkit.unique_toolkit.app.schemas import ChatEvent
|
|
11
|
+
from unique_toolkit.unique_toolkit.tools.tool_definitions import BaseToolConfig, Tool, ToolCallResponse, ToolPromptInstructions, ToolSettings
|
|
12
|
+
from unique_toolkit.unique_toolkit.tools.tool_progress_reporter import ToolProgressReporter
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class BaseToolConfigV2(BaseToolConfig):
|
|
16
|
+
class ToolCallConfig(BaseModel):
|
|
17
|
+
description: str = Field(
|
|
18
|
+
default="Base",
|
|
19
|
+
description="The tool description must be set by subclasses",
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
parameters: type[BaseModel] = Field(
|
|
23
|
+
default=BaseModel,
|
|
24
|
+
description="The tool parameters configuration must be set by subclasses",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
class PromptInstructionsConfig(BaseModel):
|
|
28
|
+
system_prompt: str = Field(
|
|
29
|
+
default="",
|
|
30
|
+
description=("Helps the LLM understand how to use the tool. "
|
|
31
|
+
"This is injected into the system prompt."
|
|
32
|
+
"This might not be needed for every tool but some of the work better with user prompt "
|
|
33
|
+
"instructions while others work better with system prompt instructions."),
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
user_prompt: str = Field(
|
|
37
|
+
default="",
|
|
38
|
+
description=("Helps the LLM understand how to use the tool. "
|
|
39
|
+
"This is injected into the user prompt. "
|
|
40
|
+
"This might not be needed for every tool but some of the work better with user prompt "
|
|
41
|
+
"instructions while others work better with system prompt instructions.")
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
system_prompt_tool_chosen: str = Field(
|
|
45
|
+
default="",
|
|
46
|
+
description=("Once the tool is chosen, this is injected into the system prompt"
|
|
47
|
+
" to help the LLM understand how work with the tools results."),
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
user_prompt_tool_chosen: str = Field(
|
|
51
|
+
default="",
|
|
52
|
+
description=("Once the tool is chosen, this is injected into the user prompt "
|
|
53
|
+
"to help the LLM understand how to work with the tools results."),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
tool_call: ToolCallConfig = Field(
|
|
57
|
+
default_factory=ToolCallConfig,
|
|
58
|
+
description="Configuration for the tool, including description and parameters",
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
prompts: PromptInstructionsConfig = Field(
|
|
62
|
+
default_factory=PromptInstructionsConfig,
|
|
63
|
+
description="Configuration for prompts related to the tool",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# This makes sure that the settings are all present in all subclasses and that they define a default value.
|
|
67
|
+
@model_validator(mode="after")
|
|
68
|
+
def validate_tool_description(cls):
|
|
69
|
+
if cls.__class__ is BaseToolConfig:
|
|
70
|
+
return cls # Skip validation for the base class
|
|
71
|
+
if cls.tool_call.description == "Base":
|
|
72
|
+
raise ValueError(
|
|
73
|
+
f"Subclass {cls.__class__.__name__} must define a default value for 'tool_description'."
|
|
74
|
+
)
|
|
75
|
+
if cls.tool_call.parameters == BaseModel:
|
|
76
|
+
raise ValueError(
|
|
77
|
+
f"Subclass {cls.__class__.__name__} must define a default value for 'tool_parameters_config'."
|
|
78
|
+
)
|
|
79
|
+
if cls.prompts.system_prompt == "":
|
|
80
|
+
raise ValueError(
|
|
81
|
+
f"Subclass {cls.__class__.__name__} must define a default value for 'system_prompt_base_instructions'."
|
|
82
|
+
)
|
|
83
|
+
if cls.prompts.user_prompt == "":
|
|
84
|
+
raise ValueError(
|
|
85
|
+
f"Subclass {cls.__class__.__name__} must define a default value for 'user_prompt_base_instructions'."
|
|
86
|
+
)
|
|
87
|
+
if cls.prompts.system_prompt_tool_chosen == "":
|
|
88
|
+
raise ValueError(
|
|
89
|
+
f"Subclass {cls.__class__.__name__} must define a default value for 'system_prompt_tool_chosen_instructions'."
|
|
90
|
+
)
|
|
91
|
+
if cls.prompts.user_prompt_tool_chosen == "":
|
|
92
|
+
raise ValueError(
|
|
93
|
+
f"Subclass {cls.__class__.__name__} must define a default value for 'user_prompt_tool_chosen_instructions'."
|
|
94
|
+
)
|
|
95
|
+
return cls
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
ConfigTypeV2 = TypeVar("ConfigTypeV2", bound=BaseToolConfigV2)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class ToolV2(Tool[ConfigTypeV2]):
|
|
103
|
+
name: str
|
|
104
|
+
|
|
105
|
+
def tool_description(self) -> LanguageModelToolDescription:
|
|
106
|
+
return LanguageModelToolDescription(
|
|
107
|
+
name=self.name,
|
|
108
|
+
description=self.settings.configuration.tool_call.description,
|
|
109
|
+
parameters=self.settings.configuration.tool_call.parameters,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
def get_prompt_instructions(self) -> ToolPromptInstructions:
|
|
113
|
+
return ToolPromptInstructions(
|
|
114
|
+
system_prompt=self.settings.configuration.prompts.system_prompt,
|
|
115
|
+
user_prompt=self.settings.configuration.prompts.user_prompt,
|
|
116
|
+
system_prompt_tool_chosen=self.settings.configuration.prompts.system_prompt_tool_chosen,
|
|
117
|
+
user_prompt_tool_chosen=self.settings.configuration.prompts.user_prompt_tool_chosen
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@abstractmethod
|
|
122
|
+
async def run(self, tool_call: LanguageModelFunction) -> ToolCallResponse:
|
|
123
|
+
raise NotImplementedError
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def __init__(
|
|
128
|
+
self,
|
|
129
|
+
settings: ToolSettings[ConfigTypeV2],
|
|
130
|
+
event: ChatEvent,
|
|
131
|
+
tool_progress_reporter: ToolProgressReporter
|
|
132
|
+
):
|
|
133
|
+
self.settings = settings
|
|
134
|
+
self.tool_progress_reporter = tool_progress_reporter
|
|
135
|
+
self.event = event
|
|
136
|
+
|
|
137
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from typing import Callable
|
|
2
|
+
|
|
3
|
+
from unique_toolkit.unique_toolkit.tools.tool_definitions import BaseToolConfig, Tool
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ToolFactory:
|
|
8
|
+
tool_map: dict[str, type[Tool]] = {}
|
|
9
|
+
tool_config_map: dict[str, Callable] = {}
|
|
10
|
+
|
|
11
|
+
@classmethod
|
|
12
|
+
def register_tool(
|
|
13
|
+
cls,
|
|
14
|
+
tool: type[Tool],
|
|
15
|
+
tool_config: type[BaseToolConfig],
|
|
16
|
+
):
|
|
17
|
+
cls.tool_map[tool.name] = tool
|
|
18
|
+
cls.tool_config_map[tool.name] = tool_config
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def build_tool(cls, tool_name: str, *args, **kwargs) -> Tool:
|
|
22
|
+
tool = cls.tool_map[tool_name](*args, **kwargs)
|
|
23
|
+
return tool
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def build_tool_config(
|
|
27
|
+
cls, tool_name: str, **kwargs
|
|
28
|
+
) -> BaseToolConfig:
|
|
29
|
+
if tool_name not in cls.tool_config_map:
|
|
30
|
+
raise ValueError(f"Tool {tool_name} not found")
|
|
31
|
+
return cls.tool_config_map[tool_name](**kwargs)
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from functools import wraps
|
|
5
|
+
from typing import Protocol
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
from unique_toolkit.chat.service import ChatService
|
|
9
|
+
from unique_toolkit.content.schemas import ContentReference
|
|
10
|
+
from unique_toolkit.language_model.schemas import (
|
|
11
|
+
LanguageModelFunction,
|
|
12
|
+
LanguageModelStreamResponse,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
ARROW = "→ "
|
|
16
|
+
DUMMY_REFERENCE_PLACEHOLDER = "<sup></sup>"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ProgressState(Enum):
|
|
20
|
+
STARTED = "⚪"
|
|
21
|
+
RUNNING = "🟡"
|
|
22
|
+
FAILED = "🔴"
|
|
23
|
+
FINISHED = "🟢"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ToolExecutionStatus(BaseModel):
|
|
27
|
+
name: str
|
|
28
|
+
message: str
|
|
29
|
+
state: ProgressState
|
|
30
|
+
references: list[ContentReference] = []
|
|
31
|
+
timestamp: datetime = datetime.now()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ToolProgressReporter:
|
|
35
|
+
def __init__(self, chat_service: ChatService):
|
|
36
|
+
self.chat_service = chat_service
|
|
37
|
+
self.tool_statuses: dict[str, ToolExecutionStatus] = {}
|
|
38
|
+
self._progress_start_text = ""
|
|
39
|
+
self._requires_new_assistant_message = False
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def requires_new_assistant_message(self):
|
|
43
|
+
return self._requires_new_assistant_message
|
|
44
|
+
|
|
45
|
+
@requires_new_assistant_message.setter
|
|
46
|
+
def requires_new_assistant_message(self, value: bool):
|
|
47
|
+
self._requires_new_assistant_message = value
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def tool_statuses_is_empty(self):
|
|
51
|
+
return len(self.tool_statuses) == 0
|
|
52
|
+
|
|
53
|
+
def empty_tool_statuses_if_stream_has_text(
|
|
54
|
+
self, stream_response: LanguageModelStreamResponse
|
|
55
|
+
):
|
|
56
|
+
if stream_response.message.text:
|
|
57
|
+
self.tool_statuses = {}
|
|
58
|
+
|
|
59
|
+
async def notify_from_tool_call(
|
|
60
|
+
self,
|
|
61
|
+
tool_call: LanguageModelFunction,
|
|
62
|
+
name: str,
|
|
63
|
+
message: str,
|
|
64
|
+
state: ProgressState,
|
|
65
|
+
references: list[ContentReference] = [],
|
|
66
|
+
requires_new_assistant_message: bool = False,
|
|
67
|
+
):
|
|
68
|
+
"""
|
|
69
|
+
Notifies about a tool call execution status and updates the assistant message.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
tool_call (LanguageModelFunction): The tool call being executed
|
|
73
|
+
name (str): Name of the tool being executed
|
|
74
|
+
message (str): Status message to display
|
|
75
|
+
state (ProgressState): Current execution state of the tool
|
|
76
|
+
references (list[ContentReference], optional): List of content references. Defaults to [].
|
|
77
|
+
requires_new_assistant_message (bool, optional): Whether a new assistant message is needed when tool call is finished.
|
|
78
|
+
Defaults to False. If yes, the agentic steps will remain in chat history and will be overwritten by the stream response.
|
|
79
|
+
|
|
80
|
+
Raises:
|
|
81
|
+
AssertionError: If tool_call.id is None
|
|
82
|
+
"""
|
|
83
|
+
assert tool_call.id is not None
|
|
84
|
+
self.tool_statuses[tool_call.id] = ToolExecutionStatus(
|
|
85
|
+
name=name,
|
|
86
|
+
message=message,
|
|
87
|
+
state=state,
|
|
88
|
+
references=references,
|
|
89
|
+
)
|
|
90
|
+
self.requires_new_assistant_message = (
|
|
91
|
+
self.requires_new_assistant_message
|
|
92
|
+
or requires_new_assistant_message
|
|
93
|
+
)
|
|
94
|
+
await self.publish()
|
|
95
|
+
|
|
96
|
+
async def publish(self):
|
|
97
|
+
messages = []
|
|
98
|
+
all_references = []
|
|
99
|
+
for item in sorted(
|
|
100
|
+
self.tool_statuses.values(), key=lambda x: x.timestamp
|
|
101
|
+
):
|
|
102
|
+
references = item.references
|
|
103
|
+
start_number = len(all_references) + 1
|
|
104
|
+
message = self._replace_placeholders(item.message, start_number)
|
|
105
|
+
references = self._correct_reference_sequence(
|
|
106
|
+
references, start_number
|
|
107
|
+
)
|
|
108
|
+
all_references.extend(references)
|
|
109
|
+
|
|
110
|
+
messages.append(
|
|
111
|
+
f"{ARROW}**{item.name} {item.state.value}**: {message}"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
await self.chat_service.modify_assistant_message_async(
|
|
115
|
+
content=self._progress_start_text + "\n\n" + "\n\n".join(messages),
|
|
116
|
+
references=all_references,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
@staticmethod
|
|
120
|
+
def _replace_placeholders(message: str, start_number: int = 1) -> str:
|
|
121
|
+
counter = start_number
|
|
122
|
+
|
|
123
|
+
def replace_match(match):
|
|
124
|
+
nonlocal counter
|
|
125
|
+
result = f"<sup>{counter}</sup>"
|
|
126
|
+
counter += 1
|
|
127
|
+
return result
|
|
128
|
+
|
|
129
|
+
return re.sub(r"<sup></sup>", replace_match, message)
|
|
130
|
+
|
|
131
|
+
@staticmethod
|
|
132
|
+
def _correct_reference_sequence(
|
|
133
|
+
references: list[ContentReference], start_number: int = 1
|
|
134
|
+
) -> list[ContentReference]:
|
|
135
|
+
for i, reference in enumerate(references, start_number):
|
|
136
|
+
reference.sequence_number = i
|
|
137
|
+
return references
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class ToolWithToolProgressReporter(Protocol):
|
|
141
|
+
tool_progress_reporter: ToolProgressReporter
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def track_tool_progress(
|
|
145
|
+
message: str,
|
|
146
|
+
on_start_state: ProgressState = ProgressState.RUNNING,
|
|
147
|
+
on_success_state: ProgressState = ProgressState.RUNNING,
|
|
148
|
+
on_success_message: str | None = None,
|
|
149
|
+
on_error_message: str = "Unexpected error occurred",
|
|
150
|
+
requires_new_assistant_message: bool = False,
|
|
151
|
+
):
|
|
152
|
+
"""
|
|
153
|
+
Decorator to add progress reporting and status tracking steps to tool functions. Can be used with async and sync functions.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
name (str): Display name for the tool progress status
|
|
157
|
+
message (str): Message to show during tool execution
|
|
158
|
+
on_error_message (str, optional): Message to show if tool execution fails. Defaults to empty string.
|
|
159
|
+
on_success_state (ProgressState, optional): State to set after successful execution. Defaults to RUNNING.
|
|
160
|
+
requires_new_assistant_message (bool, optional): Whether to create a new assistant message. Defaults to False.
|
|
161
|
+
|
|
162
|
+
The decorator will:
|
|
163
|
+
1. Show a RUNNING status when the tool starts executing
|
|
164
|
+
2. Update the status to on_success_state if execution succeeds
|
|
165
|
+
3. Update the status to FAILED if execution fails
|
|
166
|
+
4. Include any references from the tool result in the status update if the result has a 'references' attribute or item.
|
|
167
|
+
5. Create a new assistant message if requires_new_assistant_message is True
|
|
168
|
+
|
|
169
|
+
The decorated function must be a method of a class that implements ToolWithToolProgressReporter.
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
def decorator(func):
|
|
173
|
+
@wraps(func) # Preserve the original function's metadata
|
|
174
|
+
async def async_wrapper(
|
|
175
|
+
self: ToolWithToolProgressReporter,
|
|
176
|
+
tool_call: LanguageModelFunction,
|
|
177
|
+
notification_tool_name: str,
|
|
178
|
+
*args,
|
|
179
|
+
**kwargs,
|
|
180
|
+
):
|
|
181
|
+
try:
|
|
182
|
+
# Start status
|
|
183
|
+
await self.tool_progress_reporter.notify_from_tool_call(
|
|
184
|
+
tool_call=tool_call,
|
|
185
|
+
name=notification_tool_name,
|
|
186
|
+
message=message,
|
|
187
|
+
state=on_start_state,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
# Execute the tool function
|
|
191
|
+
result = await func(
|
|
192
|
+
self, tool_call, notification_tool_name, *args, **kwargs
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# Success status
|
|
196
|
+
await self.tool_progress_reporter.notify_from_tool_call(
|
|
197
|
+
tool_call=tool_call,
|
|
198
|
+
name=notification_tool_name,
|
|
199
|
+
message=on_success_message or message,
|
|
200
|
+
state=on_success_state,
|
|
201
|
+
references=_get_references_from_results(result),
|
|
202
|
+
requires_new_assistant_message=requires_new_assistant_message,
|
|
203
|
+
)
|
|
204
|
+
return result
|
|
205
|
+
|
|
206
|
+
except Exception as e:
|
|
207
|
+
# Failure status
|
|
208
|
+
await self.tool_progress_reporter.notify_from_tool_call(
|
|
209
|
+
tool_call=tool_call,
|
|
210
|
+
name=notification_tool_name,
|
|
211
|
+
message=on_error_message,
|
|
212
|
+
state=ProgressState.FAILED,
|
|
213
|
+
requires_new_assistant_message=requires_new_assistant_message,
|
|
214
|
+
)
|
|
215
|
+
raise e
|
|
216
|
+
|
|
217
|
+
return async_wrapper
|
|
218
|
+
|
|
219
|
+
return decorator
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _get_references_from_results(result):
|
|
223
|
+
if isinstance(result, dict):
|
|
224
|
+
return result.get("references", [])
|
|
225
|
+
return getattr(result, "references", [])
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: unique_toolkit
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.42
|
|
4
4
|
Summary:
|
|
5
5
|
License: Proprietary
|
|
6
6
|
Author: Martin Fadler
|
|
@@ -113,6 +113,12 @@ All notable changes to this project will be documented in this file.
|
|
|
113
113
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
114
114
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
115
115
|
|
|
116
|
+
## [0.7.42] - 2025-08-01
|
|
117
|
+
- Added tool definitions
|
|
118
|
+
|
|
119
|
+
## [0.7.41] - 2025-07-31
|
|
120
|
+
- Add new chat event attribute indicating tools disabled on a company level
|
|
121
|
+
|
|
116
122
|
## [0.7.40] - 2025-07-30
|
|
117
123
|
- Remove `GEMINI_2_5_FLASH_PREVIEW_0417` model
|
|
118
124
|
|
|
@@ -10,7 +10,7 @@ unique_toolkit/app/init_logging.py,sha256=Sh26SRxOj8i8dzobKhYha2lLrkrMTHfB1V4jR3
|
|
|
10
10
|
unique_toolkit/app/init_sdk.py,sha256=Nv4Now4pMfM0AgRhbtatLpm_39rKxn0WmRLwmPhRl-8,1285
|
|
11
11
|
unique_toolkit/app/performance/async_tasks.py,sha256=H0l3OAcosLwNHZ8d2pd-Di4wHIXfclEvagi5kfqLFPA,1941
|
|
12
12
|
unique_toolkit/app/performance/async_wrapper.py,sha256=yVVcRDkcdyfjsxro-N29SBvi-7773wnfDplef6-y8xw,1077
|
|
13
|
-
unique_toolkit/app/schemas.py,sha256=
|
|
13
|
+
unique_toolkit/app/schemas.py,sha256=cRh5Ye077MqERIK2OKj86wMVhzpkGJ37gEAaan1agyY,5223
|
|
14
14
|
unique_toolkit/app/sse_client.py,sha256=jtOhB2g_oE-vJBqtVuWnWyYUJYv3oOhN2U8j8wuHJ5Y,668
|
|
15
15
|
unique_toolkit/app/unique_settings.py,sha256=fi3V9Dru1G1YK7Pxju_KBGiNJHkHdx7JzpDglcvMZro,1820
|
|
16
16
|
unique_toolkit/app/verification.py,sha256=GxFFwcJMy25fCA_Xe89wKW7bgqOu8PAs5y8QpHF0GSc,3861
|
|
@@ -70,7 +70,11 @@ unique_toolkit/short_term_memory/schemas.py,sha256=OhfcXyF6ACdwIXW45sKzjtZX_gkcJ
|
|
|
70
70
|
unique_toolkit/short_term_memory/service.py,sha256=8sW7cFJRd4vcRfotJSWb0uHZYl-e5hQWp3G1SddL5Bg,8110
|
|
71
71
|
unique_toolkit/smart_rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
72
72
|
unique_toolkit/smart_rules/compile.py,sha256=cxWjb2dxEI2HGsakKdVCkSNi7VK9mr08w5sDcFCQyWI,9553
|
|
73
|
-
unique_toolkit
|
|
74
|
-
unique_toolkit
|
|
75
|
-
unique_toolkit
|
|
76
|
-
unique_toolkit
|
|
73
|
+
unique_toolkit/tools/tool_definitions.py,sha256=YYu53vXMJBeJtuSU1L_FJBsiN52LSA5LIDt9O-1HBgE,4500
|
|
74
|
+
unique_toolkit/tools/tool_definitionsV2.py,sha256=yjLmP85pFGd1QtIVMC3oLQPSQ2NckBj9hIihjIr2FZg,5728
|
|
75
|
+
unique_toolkit/tools/tool_factory.py,sha256=ux11jd7Oobb-6eBeS51T-tviH14k6HKqsKmljA7h6qA,879
|
|
76
|
+
unique_toolkit/tools/tool_progress_reporter.py,sha256=AyPdgxpd48qotJyPB8qJ7h7ghiv2w2EK8nlyqQVFRt4,8048
|
|
77
|
+
unique_toolkit-0.7.42.dist-info/LICENSE,sha256=GlN8wHNdh53xwOPg44URnwag6TEolCjoq3YD_KrWgss,193
|
|
78
|
+
unique_toolkit-0.7.42.dist-info/METADATA,sha256=k8Y-jEXJg9MGNXVzPkS1-nmaJC_BQPt-4cmcHJNEz0Q,25619
|
|
79
|
+
unique_toolkit-0.7.42.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
80
|
+
unique_toolkit-0.7.42.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|