unique_toolkit 0.8.24__py3-none-any.whl → 0.8.26__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/__init__.py +8 -1
- unique_toolkit/_common/endpoint_builder.py +145 -0
- unique_toolkit/app/schemas.py +11 -0
- unique_toolkit/app/unique_settings.py +102 -1
- unique_toolkit/chat/service.py +4 -0
- unique_toolkit/framework_utilities/__init__.py +1 -0
- unique_toolkit/framework_utilities/langchain/client.py +3 -1
- unique_toolkit/framework_utilities/openai/__init__.py +6 -0
- unique_toolkit/framework_utilities/openai/client.py +4 -1
- unique_toolkit/history_manager/history_manager.py +7 -0
- unique_toolkit/tools/mcp/__init__.py +2 -2
- unique_toolkit/tools/mcp/manager.py +40 -55
- unique_toolkit/tools/mcp/models.py +2 -13
- unique_toolkit/tools/mcp/tool_wrapper.py +39 -55
- unique_toolkit/tools/schemas.py +4 -1
- unique_toolkit/tools/tool.py +10 -4
- unique_toolkit/tools/tool_manager.py +1 -3
- {unique_toolkit-0.8.24.dist-info → unique_toolkit-0.8.26.dist-info}/METADATA +11 -1
- {unique_toolkit-0.8.24.dist-info → unique_toolkit-0.8.26.dist-info}/RECORD +21 -18
- {unique_toolkit-0.8.24.dist-info → unique_toolkit-0.8.26.dist-info}/LICENSE +0 -0
- {unique_toolkit-0.8.24.dist-info → unique_toolkit-0.8.26.dist-info}/WHEEL +0 -0
unique_toolkit/__init__.py
CHANGED
|
@@ -2,7 +2,12 @@
|
|
|
2
2
|
from unique_toolkit.chat import ChatService
|
|
3
3
|
from unique_toolkit.content import ContentService
|
|
4
4
|
from unique_toolkit.embedding import EmbeddingService
|
|
5
|
-
from unique_toolkit.language_model import
|
|
5
|
+
from unique_toolkit.language_model import (
|
|
6
|
+
LanguageModelMessages,
|
|
7
|
+
LanguageModelName,
|
|
8
|
+
LanguageModelService,
|
|
9
|
+
LanguageModelToolDescription,
|
|
10
|
+
)
|
|
6
11
|
from unique_toolkit.short_term_memory import ShortTermMemoryService
|
|
7
12
|
|
|
8
13
|
# You can add other classes you frequently use here as well
|
|
@@ -10,6 +15,8 @@ from unique_toolkit.short_term_memory import ShortTermMemoryService
|
|
|
10
15
|
__all__ = [
|
|
11
16
|
"LanguageModelService",
|
|
12
17
|
"LanguageModelMessages",
|
|
18
|
+
"LanguageModelName",
|
|
19
|
+
"LanguageModelToolDescription",
|
|
13
20
|
"ChatService",
|
|
14
21
|
"ContentService",
|
|
15
22
|
"EmbeddingService",
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides a minimal framework for building endpoint classes such that a client can use
|
|
3
|
+
the endpoints without having to know the details of the endpoints.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from collections.abc import Callable
|
|
7
|
+
from string import Formatter, Template
|
|
8
|
+
from typing import (
|
|
9
|
+
Any,
|
|
10
|
+
Generic,
|
|
11
|
+
ParamSpec,
|
|
12
|
+
Protocol,
|
|
13
|
+
TypeVar,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from pydantic import BaseModel
|
|
17
|
+
|
|
18
|
+
# Type variables
|
|
19
|
+
ResponseType = TypeVar("ResponseType", bound=BaseModel)
|
|
20
|
+
PathParamsType = TypeVar("PathParamsType", bound=BaseModel)
|
|
21
|
+
RequestBodyType = TypeVar("RequestBodyType", bound=BaseModel)
|
|
22
|
+
|
|
23
|
+
# ParamSpecs for function signatures
|
|
24
|
+
RequestConstructorSpec = ParamSpec("RequestConstructorSpec")
|
|
25
|
+
PathParamsSpec = ParamSpec("PathParamsSpec")
|
|
26
|
+
RequestBodySpec = ParamSpec("RequestBodySpec")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# Necessary for typing of make_endpoint_class
|
|
30
|
+
class EndpointClassProtocol(Protocol, Generic[PathParamsSpec, RequestBodySpec]):
|
|
31
|
+
@staticmethod
|
|
32
|
+
def create_url(
|
|
33
|
+
*args: PathParamsSpec.args, **kwargs: PathParamsSpec.kwargs
|
|
34
|
+
) -> str: ...
|
|
35
|
+
|
|
36
|
+
@staticmethod
|
|
37
|
+
def create_payload(
|
|
38
|
+
*args: RequestBodySpec.args, **kwargs: RequestBodySpec.kwargs
|
|
39
|
+
) -> dict[str, Any]: ...
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# Model for any client to implement
|
|
43
|
+
class Client(Protocol):
|
|
44
|
+
def request(
|
|
45
|
+
self,
|
|
46
|
+
endpoint: EndpointClassProtocol,
|
|
47
|
+
) -> dict[str, Any]: ...
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def build_endpoint_class(
|
|
51
|
+
*,
|
|
52
|
+
url_template: Template,
|
|
53
|
+
path_params_model: Callable[PathParamsSpec, PathParamsType],
|
|
54
|
+
payload_model: Callable[RequestBodySpec, RequestBodyType],
|
|
55
|
+
response_model: type[ResponseType],
|
|
56
|
+
dump_options: dict | None = None,
|
|
57
|
+
) -> type[EndpointClassProtocol[PathParamsSpec, RequestBodySpec]]:
|
|
58
|
+
"""Generate a class with static methods for endpoint handling.
|
|
59
|
+
|
|
60
|
+
Uses separate models for path parameters and request body for clean API design.
|
|
61
|
+
|
|
62
|
+
Returns a class with static methods:
|
|
63
|
+
- create_url: Creates URL from path parameters
|
|
64
|
+
- create_payload: Creates request body payload
|
|
65
|
+
"""
|
|
66
|
+
if not dump_options:
|
|
67
|
+
dump_options = {
|
|
68
|
+
"exclude_unset": True,
|
|
69
|
+
"by_alias": True,
|
|
70
|
+
"exclude_defaults": True,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
class EndpointClass(EndpointClassProtocol):
|
|
74
|
+
@staticmethod
|
|
75
|
+
def create_url(
|
|
76
|
+
*args: PathParamsSpec.args, **kwargs: PathParamsSpec.kwargs
|
|
77
|
+
) -> str:
|
|
78
|
+
"""Create URL from path parameters."""
|
|
79
|
+
path_model = path_params_model(*args, **kwargs)
|
|
80
|
+
path_dict = path_model.model_dump(**dump_options)
|
|
81
|
+
|
|
82
|
+
# Extract expected path parameters from template
|
|
83
|
+
template_params = [
|
|
84
|
+
fname
|
|
85
|
+
for _, fname, _, _ in Formatter().parse(url_template.template)
|
|
86
|
+
if fname is not None
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
# Verify all required path parameters are present
|
|
90
|
+
missing_params = [
|
|
91
|
+
param for param in template_params if param not in path_dict
|
|
92
|
+
]
|
|
93
|
+
if missing_params:
|
|
94
|
+
raise ValueError(f"Missing path parameters: {missing_params}")
|
|
95
|
+
|
|
96
|
+
return url_template.substitute(**path_dict)
|
|
97
|
+
|
|
98
|
+
@staticmethod
|
|
99
|
+
def create_payload(
|
|
100
|
+
*args: RequestBodySpec.args, **kwargs: RequestBodySpec.kwargs
|
|
101
|
+
) -> dict[str, Any]:
|
|
102
|
+
"""Create request body payload."""
|
|
103
|
+
request_model = payload_model(*args, **kwargs)
|
|
104
|
+
return request_model.model_dump(**dump_options)
|
|
105
|
+
|
|
106
|
+
@staticmethod
|
|
107
|
+
def handle_response(response: dict[str, Any]) -> ResponseType:
|
|
108
|
+
return response_model.model_validate(response)
|
|
109
|
+
|
|
110
|
+
return EndpointClass
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
if __name__ == "__main__":
|
|
114
|
+
# Example models
|
|
115
|
+
class GetUserPathParams(BaseModel):
|
|
116
|
+
"""Path parameters for the user endpoint."""
|
|
117
|
+
|
|
118
|
+
user_id: int
|
|
119
|
+
|
|
120
|
+
class GetUserRequestBody(BaseModel):
|
|
121
|
+
"""Request body/query parameters for the user endpoint."""
|
|
122
|
+
|
|
123
|
+
include_profile: bool = False
|
|
124
|
+
|
|
125
|
+
class UserResponse(BaseModel):
|
|
126
|
+
"""Response model for user data."""
|
|
127
|
+
|
|
128
|
+
id: int
|
|
129
|
+
name: str
|
|
130
|
+
|
|
131
|
+
# Example usage of make_endpoint_class
|
|
132
|
+
UserEndpoint = build_endpoint_class(
|
|
133
|
+
url_template=Template("/users/${user_id}"),
|
|
134
|
+
path_params_model=GetUserPathParams,
|
|
135
|
+
payload_model=GetUserRequestBody,
|
|
136
|
+
response_model=UserResponse,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# Create URL from path parameters
|
|
140
|
+
url = UserEndpoint.create_url(user_id=123)
|
|
141
|
+
print(f"URL: {url}")
|
|
142
|
+
|
|
143
|
+
# Create payload from request body parameters
|
|
144
|
+
payload = UserEndpoint.create_payload(include_profile=True)
|
|
145
|
+
print(f"Payload: {payload}")
|
unique_toolkit/app/schemas.py
CHANGED
|
@@ -227,6 +227,17 @@ class ChatEvent(BaseEvent):
|
|
|
227
227
|
data = json.load(f)
|
|
228
228
|
return cls.model_validate(data)
|
|
229
229
|
|
|
230
|
+
def get_initial_debug_info(self) -> dict[str, Any]:
|
|
231
|
+
"""Get the debug information for the chat event"""
|
|
232
|
+
|
|
233
|
+
# TODO: Make sure this coincides with what is shown in the first user message
|
|
234
|
+
return {
|
|
235
|
+
"user_metadata": self.payload.user_metadata,
|
|
236
|
+
"tool_parameters": self.payload.tool_parameters,
|
|
237
|
+
"chosen_module": self.payload.name,
|
|
238
|
+
"assistant": {"id": self.payload.assistant_id},
|
|
239
|
+
}
|
|
240
|
+
|
|
230
241
|
|
|
231
242
|
@deprecated(
|
|
232
243
|
"""Use the more specific `ChatEvent` instead that has the same properties. \
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import os
|
|
1
2
|
from logging import getLogger
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
from typing import Self, TypeVar
|
|
4
5
|
from urllib.parse import urlparse, urlunparse
|
|
5
6
|
|
|
7
|
+
import unique_sdk
|
|
8
|
+
from platformdirs import user_config_dir
|
|
6
9
|
from pydantic import AliasChoices, Field, SecretStr, model_validator
|
|
7
10
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
8
11
|
|
|
@@ -100,7 +103,9 @@ class UniqueApi(BaseSettings):
|
|
|
100
103
|
parsed = urlparse(self.base_url)
|
|
101
104
|
|
|
102
105
|
path = "/public/chat"
|
|
103
|
-
if parsed.hostname and
|
|
106
|
+
if parsed.hostname and (
|
|
107
|
+
"qa.unique" in parsed.hostname or ".unique" in parsed.hostname
|
|
108
|
+
):
|
|
104
109
|
path = "/public/chat-gen2"
|
|
105
110
|
return urlunparse(parsed._replace(path=path, query=None, fragment=None))
|
|
106
111
|
|
|
@@ -142,12 +147,57 @@ class UniqueAuth(BaseSettings):
|
|
|
142
147
|
return warn_about_defaults(self)
|
|
143
148
|
|
|
144
149
|
|
|
150
|
+
class EnvFileNotFoundError(FileNotFoundError):
|
|
151
|
+
"""Raised when no environment file can be found in any of the expected locations."""
|
|
152
|
+
|
|
153
|
+
|
|
145
154
|
class UniqueSettings:
|
|
146
155
|
def __init__(self, auth: UniqueAuth, app: UniqueApp, api: UniqueApi):
|
|
147
156
|
self.app = app
|
|
148
157
|
self.auth = auth
|
|
149
158
|
self.api = api
|
|
150
159
|
|
|
160
|
+
@classmethod
|
|
161
|
+
def _find_env_file(cls, filename: str = "unique.env") -> Path:
|
|
162
|
+
"""Find environment file using cross-platform fallback locations.
|
|
163
|
+
|
|
164
|
+
Search order:
|
|
165
|
+
1. UNIQUE_ENV_FILE environment variable
|
|
166
|
+
2. Current working directory
|
|
167
|
+
3. User config directory (cross-platform via platformdirs)
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
filename: Name of the environment file (default: 'unique.env')
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
Path to the environment file.
|
|
174
|
+
|
|
175
|
+
Raises:
|
|
176
|
+
EnvFileNotFoundError: If no environment file is found in any location.
|
|
177
|
+
"""
|
|
178
|
+
locations = [
|
|
179
|
+
# 1. Explicit environment variable
|
|
180
|
+
Path(env_path) if (env_path := os.environ.get("UNIQUE_ENV_FILE")) else None,
|
|
181
|
+
# 2. Current working directory
|
|
182
|
+
Path.cwd() / filename,
|
|
183
|
+
# 3. User config directory (cross-platform)
|
|
184
|
+
Path(user_config_dir("unique", "unique-toolkit")) / filename,
|
|
185
|
+
]
|
|
186
|
+
|
|
187
|
+
for location in locations:
|
|
188
|
+
if location and location.exists() and location.is_file():
|
|
189
|
+
return location
|
|
190
|
+
|
|
191
|
+
# If no file found, provide helpful error message
|
|
192
|
+
searched_locations = [str(loc) for loc in locations if loc is not None]
|
|
193
|
+
raise EnvFileNotFoundError(
|
|
194
|
+
f"Environment file '{filename}' not found. Searched locations:\n"
|
|
195
|
+
+ "\n".join(f" - {loc}" for loc in searched_locations)
|
|
196
|
+
+ "\n\nTo fix this:\n"
|
|
197
|
+
+ f" 1. Create {filename} in one of the above locations, or\n"
|
|
198
|
+
+ f" 2. Set UNIQUE_ENV_FILE environment variable to point to your {filename} file"
|
|
199
|
+
)
|
|
200
|
+
|
|
151
201
|
@classmethod
|
|
152
202
|
def from_env(cls, env_file: Path | None = None) -> "UniqueSettings":
|
|
153
203
|
"""Initialize settings from environment variables and/or env file.
|
|
@@ -171,3 +221,54 @@ class UniqueSettings:
|
|
|
171
221
|
app = UniqueApp(_env_file=env_file_str) # type: ignore[call-arg]
|
|
172
222
|
api = UniqueApi(_env_file=env_file_str) # type: ignore[call-arg]
|
|
173
223
|
return cls(auth=auth, app=app, api=api)
|
|
224
|
+
|
|
225
|
+
@classmethod
|
|
226
|
+
def from_env_auto(cls, filename: str = "unique.env") -> "UniqueSettings":
|
|
227
|
+
"""Initialize settings by automatically finding environment file.
|
|
228
|
+
|
|
229
|
+
This method will automatically search for an environment file in standard locations
|
|
230
|
+
and fall back to environment variables only if no file is found.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
filename: Name of the environment file to search for (default: '.env')
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
UniqueSettings instance with values loaded from found env file or environment variables.
|
|
237
|
+
"""
|
|
238
|
+
try:
|
|
239
|
+
env_file = cls._find_env_file(filename)
|
|
240
|
+
return cls.from_env(env_file=env_file)
|
|
241
|
+
except EnvFileNotFoundError:
|
|
242
|
+
logger.warning(
|
|
243
|
+
f"Environment file '{filename}' not found. Falling back to environment variables only."
|
|
244
|
+
)
|
|
245
|
+
# Fall back to environment variables only
|
|
246
|
+
return cls.from_env()
|
|
247
|
+
|
|
248
|
+
def init_sdk(self) -> None:
|
|
249
|
+
"""Initialize the unique_sdk global configuration with these settings.
|
|
250
|
+
|
|
251
|
+
This method configures the global unique_sdk module with the API key,
|
|
252
|
+
app ID, and base URL from these settings.
|
|
253
|
+
"""
|
|
254
|
+
unique_sdk.api_key = self.app.key.get_secret_value()
|
|
255
|
+
unique_sdk.app_id = self.app.id.get_secret_value()
|
|
256
|
+
unique_sdk.api_base = self.api.sdk_url()
|
|
257
|
+
|
|
258
|
+
@classmethod
|
|
259
|
+
def from_env_auto_with_sdk_init(
|
|
260
|
+
cls, filename: str = "unique.env"
|
|
261
|
+
) -> "UniqueSettings":
|
|
262
|
+
"""Initialize settings and SDK in one convenient call.
|
|
263
|
+
|
|
264
|
+
This method combines from_env_auto() and init_sdk() for the most common use case.
|
|
265
|
+
|
|
266
|
+
Args:
|
|
267
|
+
filename: Name of the environment file to search for (default: '.env')
|
|
268
|
+
|
|
269
|
+
Returns:
|
|
270
|
+
UniqueSettings instance with SDK already initialized.
|
|
271
|
+
"""
|
|
272
|
+
settings = cls.from_env_auto(filename)
|
|
273
|
+
settings.init_sdk()
|
|
274
|
+
return settings
|
unique_toolkit/chat/service.py
CHANGED
|
@@ -479,6 +479,10 @@ class ChatService:
|
|
|
479
479
|
set_completed_at=set_completed_at or False,
|
|
480
480
|
)
|
|
481
481
|
|
|
482
|
+
def free_user_input(self) -> None:
|
|
483
|
+
"""Unblocks the next user input"""
|
|
484
|
+
self.modify_assistant_message(set_completed_at=True)
|
|
485
|
+
|
|
482
486
|
def get_full_history(self) -> list[ChatMessage]:
|
|
483
487
|
"""Loads the full chat history for the chat session synchronously.
|
|
484
488
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Framework utilities for integrating with external frameworks."""
|
|
@@ -23,7 +23,7 @@ else:
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
def get_client(
|
|
26
|
-
unique_settings: UniqueSettings, model: str = "AZURE_GPT_4o_2024_0806"
|
|
26
|
+
unique_settings: UniqueSettings | None = None, model: str = "AZURE_GPT_4o_2024_0806"
|
|
27
27
|
) -> ChatOpenAI:
|
|
28
28
|
"""Get a Langchain ChatOpenAI client instance.
|
|
29
29
|
|
|
@@ -36,6 +36,8 @@ def get_client(
|
|
|
36
36
|
Raises:
|
|
37
37
|
LangchainNotInstalledError: If langchain-openai package is not installed
|
|
38
38
|
"""
|
|
39
|
+
if unique_settings is None:
|
|
40
|
+
unique_settings = UniqueSettings.from_env_auto()
|
|
39
41
|
|
|
40
42
|
return ChatOpenAI(
|
|
41
43
|
base_url=unique_settings.api.openai_proxy_url(),
|
|
@@ -22,7 +22,7 @@ else:
|
|
|
22
22
|
raise OpenAINotInstalledError()
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
def get_openai_client(unique_settings: UniqueSettings) -> OpenAI:
|
|
25
|
+
def get_openai_client(unique_settings: UniqueSettings | None = None) -> OpenAI:
|
|
26
26
|
"""Get an OpenAI client instance.
|
|
27
27
|
|
|
28
28
|
Args:
|
|
@@ -34,6 +34,9 @@ def get_openai_client(unique_settings: UniqueSettings) -> OpenAI:
|
|
|
34
34
|
Raises:
|
|
35
35
|
OpenAINotInstalledError: If OpenAI package is not installed
|
|
36
36
|
"""
|
|
37
|
+
if unique_settings is None:
|
|
38
|
+
unique_settings = UniqueSettings.from_env_auto()
|
|
39
|
+
|
|
37
40
|
default_headers = get_default_headers(unique_settings.app, unique_settings.auth)
|
|
38
41
|
|
|
39
42
|
return OpenAI(
|
|
@@ -158,6 +158,13 @@ class HistoryManager:
|
|
|
158
158
|
f"Appending tool call result to history: {tool_response.name}"
|
|
159
159
|
)
|
|
160
160
|
|
|
161
|
+
if tool_response.content != "":
|
|
162
|
+
return LanguageModelToolMessage(
|
|
163
|
+
content=tool_response.content,
|
|
164
|
+
tool_call_id=tool_response.id, # type: ignore
|
|
165
|
+
name=tool_response.name,
|
|
166
|
+
)
|
|
167
|
+
|
|
161
168
|
content_chunks = (
|
|
162
169
|
tool_response.content_chunks or []
|
|
163
170
|
) # it can be that the tool response does not have content chunks
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
|
|
3
|
-
from unique_toolkit.
|
|
4
|
-
from unique_toolkit.
|
|
3
|
+
from unique_toolkit.tools.config import ToolBuildConfig, ToolIcon, ToolSelectionPolicy
|
|
4
|
+
from unique_toolkit.app.schemas import ChatEvent, McpServer
|
|
5
|
+
from unique_toolkit.tools.schemas import BaseToolConfig
|
|
6
|
+
from unique_toolkit.tools.tool import Tool
|
|
7
|
+
from unique_toolkit.tools.mcp.models import MCPToolConfig
|
|
5
8
|
from unique_toolkit.tools.mcp.tool_wrapper import MCPToolWrapper
|
|
6
9
|
from unique_toolkit.tools.tool_progress_reporter import ToolProgressReporter
|
|
7
10
|
|
|
@@ -23,60 +26,42 @@ class MCPManager:
|
|
|
23
26
|
def get_mcp_server_by_id(self, id: str):
|
|
24
27
|
return next((server for server in self._mcp_servers if server.id == id), None)
|
|
25
28
|
|
|
26
|
-
def
|
|
27
|
-
self, mcp_tool: McpTool, server: McpServer
|
|
28
|
-
) -> EnrichedMCPTool:
|
|
29
|
-
enriched_tool = type("EnrichedMcpTool", (), {})()
|
|
30
|
-
|
|
31
|
-
# Copy all attributes from the original tool
|
|
32
|
-
for attr in dir(mcp_tool):
|
|
33
|
-
if not attr.startswith("_"):
|
|
34
|
-
setattr(enriched_tool, attr, getattr(mcp_tool, attr))
|
|
35
|
-
|
|
36
|
-
# Add server-specific attributes
|
|
37
|
-
enriched_tool.server_id = server.id
|
|
38
|
-
enriched_tool.server_name = server.name
|
|
39
|
-
enriched_tool.server_system_prompt = getattr(server, "system_prompt", None)
|
|
40
|
-
enriched_tool.server_user_prompt = getattr(server, "user_prompt", None)
|
|
41
|
-
enriched_tool.mcp_source_id = server.id
|
|
42
|
-
|
|
43
|
-
return enriched_tool
|
|
44
|
-
|
|
45
|
-
def create_mcp_tool_wrapper(
|
|
46
|
-
self, mcp_tool: EnrichedMCPTool, tool_progress_reporter: ToolProgressReporter
|
|
47
|
-
) -> MCPToolWrapper:
|
|
48
|
-
"""Create MCP tool wrapper that behave like internal tools"""
|
|
49
|
-
try:
|
|
50
|
-
config = MCPToolConfig(
|
|
51
|
-
server_id=mcp_tool.server_id,
|
|
52
|
-
server_name=mcp_tool.server_name,
|
|
53
|
-
server_system_prompt=mcp_tool.server_system_prompt,
|
|
54
|
-
server_user_prompt=mcp_tool.server_user_prompt,
|
|
55
|
-
mcp_source_id=mcp_tool.mcp_source_id,
|
|
56
|
-
)
|
|
57
|
-
wrapper = MCPToolWrapper(
|
|
58
|
-
mcp_tool=mcp_tool,
|
|
59
|
-
config=config,
|
|
60
|
-
event=self._event,
|
|
61
|
-
tool_progress_reporter=tool_progress_reporter,
|
|
62
|
-
)
|
|
63
|
-
return wrapper
|
|
64
|
-
except Exception as e:
|
|
65
|
-
import traceback
|
|
66
|
-
|
|
67
|
-
logging.error(f"Error creating MCP tool wrapper for {mcp_tool.name}: {e}")
|
|
68
|
-
logging.error(f"Full traceback: {traceback.format_exc()}")
|
|
69
|
-
return None
|
|
70
|
-
|
|
71
|
-
def get_all_mcp_tools(self, selected_by_user: list[str]) -> list[MCPToolWrapper]:
|
|
29
|
+
def get_all_mcp_tools(self) -> list[Tool[BaseToolConfig]]:
|
|
72
30
|
selected_tools = []
|
|
73
31
|
for server in self._mcp_servers:
|
|
74
|
-
if hasattr(server, "tools")
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
32
|
+
if not hasattr(server, "tools"):
|
|
33
|
+
continue
|
|
34
|
+
if not server.tools:
|
|
35
|
+
continue
|
|
36
|
+
|
|
37
|
+
for tool in server.tools:
|
|
38
|
+
try:
|
|
39
|
+
config = MCPToolConfig(
|
|
40
|
+
server_id=server.id,
|
|
41
|
+
server_name=server.name,
|
|
42
|
+
server_system_prompt=server.system_prompt,
|
|
43
|
+
server_user_prompt=server.user_prompt,
|
|
44
|
+
mcp_source_id=server.id,
|
|
45
|
+
)
|
|
46
|
+
wrapper = MCPToolWrapper(
|
|
47
|
+
mcp_server=server,
|
|
48
|
+
mcp_tool=tool,
|
|
49
|
+
config=config,
|
|
50
|
+
event=self._event,
|
|
51
|
+
tool_progress_reporter=self._tool_progress_reporter,
|
|
52
|
+
)
|
|
53
|
+
wrapper.settings = ToolBuildConfig( # TODO: this must be refactored to behave like the other tools.
|
|
54
|
+
name=tool.name,
|
|
55
|
+
configuration=config,
|
|
56
|
+
display_name=tool.title or tool.name,
|
|
57
|
+
is_exclusive=False,
|
|
58
|
+
is_enabled=True,
|
|
59
|
+
icon=ToolIcon.BOOK,
|
|
60
|
+
selection_policy=ToolSelectionPolicy.BY_USER,
|
|
61
|
+
)
|
|
62
|
+
selected_tools.append(wrapper)
|
|
63
|
+
except Exception as e:
|
|
64
|
+
logging.error(
|
|
65
|
+
f"Error creating MCP tool wrapper for {tool.name}: {e}"
|
|
79
66
|
)
|
|
80
|
-
if wrapper is not None:
|
|
81
|
-
selected_tools.append(wrapper)
|
|
82
67
|
return selected_tools
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
from typing import Any, Dict, Optional
|
|
2
|
-
|
|
1
|
+
from typing import Any, Dict, Optional
|
|
3
2
|
from unique_toolkit.tools.schemas import BaseToolConfig
|
|
4
3
|
|
|
5
4
|
|
|
6
|
-
class MCPTool
|
|
5
|
+
class MCPTool:
|
|
7
6
|
"""Protocol defining the expected structure of an MCP tool."""
|
|
8
7
|
|
|
9
8
|
name: str
|
|
@@ -18,16 +17,6 @@ class MCPTool(Protocol):
|
|
|
18
17
|
is_connected: bool
|
|
19
18
|
|
|
20
19
|
|
|
21
|
-
class EnrichedMCPTool(MCPTool, Protocol):
|
|
22
|
-
"""Protocol for MCP tools enriched with server information."""
|
|
23
|
-
|
|
24
|
-
server_id: str
|
|
25
|
-
server_name: str
|
|
26
|
-
server_system_prompt: Optional[str]
|
|
27
|
-
server_user_prompt: Optional[str]
|
|
28
|
-
mcp_source_id: str
|
|
29
|
-
|
|
30
|
-
|
|
31
20
|
class MCPToolConfig(BaseToolConfig):
|
|
32
21
|
"""Configuration for MCP tools"""
|
|
33
22
|
|
|
@@ -4,15 +4,14 @@ from typing import Any, Dict
|
|
|
4
4
|
import unique_sdk
|
|
5
5
|
from pydantic import BaseModel, Field, create_model
|
|
6
6
|
|
|
7
|
-
from unique_toolkit.app.schemas import ChatEvent
|
|
7
|
+
from unique_toolkit.app.schemas import ChatEvent, McpServer, McpTool
|
|
8
8
|
from unique_toolkit.evals.schemas import EvaluationMetricName
|
|
9
9
|
from unique_toolkit.language_model import LanguageModelMessage
|
|
10
10
|
from unique_toolkit.language_model.schemas import (
|
|
11
11
|
LanguageModelFunction,
|
|
12
12
|
LanguageModelToolDescription,
|
|
13
|
-
LanguageModelToolMessage,
|
|
14
13
|
)
|
|
15
|
-
from unique_toolkit.tools.mcp.models import
|
|
14
|
+
from unique_toolkit.tools.mcp.models import MCPToolConfig
|
|
16
15
|
from unique_toolkit.tools.schemas import ToolCallResponse
|
|
17
16
|
from unique_toolkit.tools.tool import Tool
|
|
18
17
|
from unique_toolkit.tools.tool_progress_reporter import (
|
|
@@ -26,24 +25,16 @@ class MCPToolWrapper(Tool[MCPToolConfig]):
|
|
|
26
25
|
|
|
27
26
|
def __init__(
|
|
28
27
|
self,
|
|
29
|
-
|
|
28
|
+
mcp_server: McpServer,
|
|
29
|
+
mcp_tool: McpTool,
|
|
30
30
|
config: MCPToolConfig,
|
|
31
31
|
event: ChatEvent,
|
|
32
32
|
tool_progress_reporter: ToolProgressReporter | None = None,
|
|
33
33
|
):
|
|
34
34
|
self.name = mcp_tool.name
|
|
35
35
|
super().__init__(config, event, tool_progress_reporter)
|
|
36
|
-
self.
|
|
37
|
-
self.
|
|
38
|
-
self._parameters_schema = mcp_tool.input_schema
|
|
39
|
-
|
|
40
|
-
# Set the display name for user-facing messages
|
|
41
|
-
# Priority: title > annotations.title > name
|
|
42
|
-
self.display_name = (
|
|
43
|
-
getattr(mcp_tool, "title", None)
|
|
44
|
-
or (getattr(mcp_tool, "annotations", {}) or {}).get("title")
|
|
45
|
-
or mcp_tool.name
|
|
46
|
-
)
|
|
36
|
+
self._mcp_tool = mcp_tool
|
|
37
|
+
self._mcp_server = mcp_server
|
|
47
38
|
|
|
48
39
|
def tool_description(self) -> LanguageModelToolDescription:
|
|
49
40
|
"""Convert MCP tool schema to LanguageModelToolDescription"""
|
|
@@ -52,14 +43,14 @@ class MCPToolWrapper(Tool[MCPToolConfig]):
|
|
|
52
43
|
|
|
53
44
|
return LanguageModelToolDescription(
|
|
54
45
|
name=self.name,
|
|
55
|
-
description=self.
|
|
46
|
+
description=self._mcp_tool.description or "",
|
|
56
47
|
parameters=parameters_model,
|
|
57
48
|
)
|
|
58
49
|
|
|
59
50
|
def _create_parameters_model(self) -> type[BaseModel]:
|
|
60
51
|
"""Create a Pydantic model from MCP tool's input schema"""
|
|
61
|
-
properties = self.
|
|
62
|
-
required_fields = self.
|
|
52
|
+
properties = self._mcp_tool.input_schema.get("properties", {})
|
|
53
|
+
required_fields = self._mcp_tool.input_schema.get("required", [])
|
|
63
54
|
|
|
64
55
|
# Convert JSON schema properties to Pydantic fields
|
|
65
56
|
fields = {}
|
|
@@ -96,20 +87,30 @@ class MCPToolWrapper(Tool[MCPToolConfig]):
|
|
|
96
87
|
|
|
97
88
|
return type_mapping.get(json_type, str)
|
|
98
89
|
|
|
99
|
-
def display_name(self) -> str:
|
|
100
|
-
"""The display name of the tool."""
|
|
101
|
-
return self._display_name
|
|
102
|
-
|
|
103
90
|
def tool_description_for_system_prompt(self) -> str:
|
|
104
91
|
"""Return tool description for system prompt"""
|
|
105
|
-
|
|
92
|
+
# Not using jinja here to keep it simple and not import new packages.
|
|
93
|
+
description = (
|
|
94
|
+
f"**MCP Server**: {self._mcp_server.name}\n"
|
|
95
|
+
f"**Tool Name**: {self.name}\n"
|
|
96
|
+
f"{self._mcp_tool.system_prompt}"
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
return description
|
|
100
|
+
|
|
101
|
+
def tool_description_for_user_prompt(self) -> str:
|
|
102
|
+
return self._mcp_tool.user_prompt or ""
|
|
103
|
+
|
|
104
|
+
def tool_format_information_for_user_prompt(self) -> str:
|
|
105
|
+
return ""
|
|
106
106
|
|
|
107
107
|
def tool_format_information_for_system_prompt(self) -> str:
|
|
108
108
|
"""Return formatting information for system prompt"""
|
|
109
|
-
return
|
|
109
|
+
return "" # this is empty for now as it requires to add this to the MCP model of the backend.
|
|
110
110
|
|
|
111
111
|
def evaluation_check_list(self) -> list[EvaluationMetricName]:
|
|
112
112
|
"""Return evaluation check list - empty for MCP tools for now"""
|
|
113
|
+
# TODO: this is empty for now as it requires a setting in the backend for choosing a suitable validator.
|
|
113
114
|
return []
|
|
114
115
|
|
|
115
116
|
def get_evaluation_checks_based_on_tool_response(
|
|
@@ -123,35 +124,18 @@ class MCPToolWrapper(Tool[MCPToolConfig]):
|
|
|
123
124
|
self,
|
|
124
125
|
tool_response: ToolCallResponse,
|
|
125
126
|
) -> LanguageModelMessage:
|
|
126
|
-
"
|
|
127
|
-
# Convert the tool response to a message for the conversation history
|
|
128
|
-
content = (
|
|
129
|
-
tool_response.error_message
|
|
130
|
-
if tool_response.error_message
|
|
131
|
-
else "Tool executed successfully"
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
if hasattr(tool_response, "content") and tool_response.content:
|
|
135
|
-
content = str(tool_response.content)
|
|
136
|
-
elif tool_response.debug_info:
|
|
137
|
-
content = json.dumps(tool_response.debug_info)
|
|
138
|
-
|
|
139
|
-
return LanguageModelToolMessage(
|
|
140
|
-
content=content,
|
|
141
|
-
tool_call_id=tool_response.id,
|
|
142
|
-
name=tool_response.name,
|
|
143
|
-
)
|
|
127
|
+
raise NotImplementedError("function is not supported")
|
|
144
128
|
|
|
145
129
|
async def run(self, tool_call: LanguageModelFunction) -> ToolCallResponse:
|
|
146
130
|
"""Execute the MCP tool using SDK to call public API"""
|
|
147
131
|
self.logger.info(f"Running MCP tool: {self.name}")
|
|
148
132
|
|
|
149
133
|
# Notify progress if reporter is available
|
|
150
|
-
if self.
|
|
151
|
-
await self.
|
|
134
|
+
if self._tool_progress_reporter:
|
|
135
|
+
await self._tool_progress_reporter.notify_from_tool_call(
|
|
152
136
|
tool_call=tool_call,
|
|
153
|
-
name=f"**{self.display_name}**",
|
|
154
|
-
message=f"Executing MCP tool: {self.display_name}",
|
|
137
|
+
name=f"**{self.display_name()}**",
|
|
138
|
+
message=f"Executing MCP tool: {self.display_name()}",
|
|
155
139
|
state=ProgressState.RUNNING,
|
|
156
140
|
)
|
|
157
141
|
|
|
@@ -163,23 +147,23 @@ class MCPToolWrapper(Tool[MCPToolConfig]):
|
|
|
163
147
|
result = await self._call_mcp_tool_via_sdk(arguments)
|
|
164
148
|
|
|
165
149
|
# Create successful response
|
|
166
|
-
tool_response = ToolCallResponse(
|
|
150
|
+
tool_response = ToolCallResponse( # TODO: Why result here not applied directly to the body of the tool_response? like so how does it know the results in the history?
|
|
167
151
|
id=tool_call.id or "",
|
|
168
152
|
name=self.name,
|
|
169
153
|
debug_info={
|
|
170
154
|
"mcp_tool": self.name,
|
|
171
155
|
"arguments": arguments,
|
|
172
|
-
"result": result,
|
|
173
156
|
},
|
|
174
157
|
error_message="",
|
|
158
|
+
content=json.dumps(result),
|
|
175
159
|
)
|
|
176
160
|
|
|
177
161
|
# Notify completion
|
|
178
|
-
if self.
|
|
179
|
-
await self.
|
|
162
|
+
if self._tool_progress_reporter:
|
|
163
|
+
await self._tool_progress_reporter.notify_from_tool_call(
|
|
180
164
|
tool_call=tool_call,
|
|
181
|
-
name=f"**{self.display_name}**",
|
|
182
|
-
message=f"MCP tool completed: {self.display_name}",
|
|
165
|
+
name=f"**{self.display_name()}**",
|
|
166
|
+
message=f"MCP tool completed: {self.display_name()}",
|
|
183
167
|
state=ProgressState.FINISHED,
|
|
184
168
|
)
|
|
185
169
|
|
|
@@ -189,10 +173,10 @@ class MCPToolWrapper(Tool[MCPToolConfig]):
|
|
|
189
173
|
self.logger.error(f"Error executing MCP tool {self.name}: {e}")
|
|
190
174
|
|
|
191
175
|
# Notify failure
|
|
192
|
-
if self.
|
|
193
|
-
await self.
|
|
176
|
+
if self._tool_progress_reporter:
|
|
177
|
+
await self._tool_progress_reporter.notify_from_tool_call(
|
|
194
178
|
tool_call=tool_call,
|
|
195
|
-
name=f"**{self.display_name}**",
|
|
179
|
+
name=f"**{self.display_name()}**",
|
|
196
180
|
message=f"MCP tool failed: {str(e)}",
|
|
197
181
|
state=ProgressState.FAILED,
|
|
198
182
|
)
|
unique_toolkit/tools/schemas.py
CHANGED
|
@@ -14,6 +14,7 @@ from unique_toolkit.tools.utils.source_handling.schema import SourceFormatConfig
|
|
|
14
14
|
class ToolCallResponse(BaseModel):
|
|
15
15
|
id: str
|
|
16
16
|
name: str
|
|
17
|
+
content: str = ""
|
|
17
18
|
debug_info: Optional[dict] = None # TODO: Make the default {}
|
|
18
19
|
content_chunks: Optional[list[ContentChunk]] = None # TODO: Make the default []
|
|
19
20
|
reasoning_result: Optional[dict] = None # TODO: Make the default {}
|
|
@@ -133,6 +134,8 @@ class ToolPrompts(BaseModel):
|
|
|
133
134
|
name: str
|
|
134
135
|
display_name: str
|
|
135
136
|
tool_system_prompt: str
|
|
136
|
-
tool_description: str
|
|
137
137
|
tool_format_information_for_system_prompt: str
|
|
138
|
+
tool_user_prompt: str
|
|
139
|
+
tool_format_information_for_user_prompt: str
|
|
140
|
+
tool_description: str
|
|
138
141
|
input_model: dict[str, Any]
|
unique_toolkit/tools/tool.py
CHANGED
|
@@ -57,13 +57,17 @@ class Tool(ABC, Generic[ConfigType]):
|
|
|
57
57
|
else:
|
|
58
58
|
return cast("dict[str, Any]", parameters)
|
|
59
59
|
|
|
60
|
-
@abstractmethod
|
|
61
60
|
def tool_description_for_system_prompt(self) -> str:
|
|
62
|
-
|
|
61
|
+
return ""
|
|
63
62
|
|
|
64
|
-
@abstractmethod
|
|
65
63
|
def tool_format_information_for_system_prompt(self) -> str:
|
|
66
|
-
|
|
64
|
+
return ""
|
|
65
|
+
|
|
66
|
+
def tool_description_for_user_prompt(self) -> str:
|
|
67
|
+
return ""
|
|
68
|
+
|
|
69
|
+
def tool_format_information_for_user_prompt(self) -> str:
|
|
70
|
+
return ""
|
|
67
71
|
|
|
68
72
|
def tool_format_reminder_for_user_prompt(self) -> str:
|
|
69
73
|
"""A short reminder for the user prompt for formatting rules for the tool.
|
|
@@ -112,6 +116,8 @@ class Tool(ABC, Generic[ConfigType]):
|
|
|
112
116
|
tool_system_prompt=self.tool_description_for_system_prompt(),
|
|
113
117
|
tool_format_information_for_system_prompt=self.tool_format_information_for_system_prompt(),
|
|
114
118
|
input_model=self.tool_description_as_json(),
|
|
119
|
+
tool_user_prompt=self.tool_description_for_user_prompt(),
|
|
120
|
+
tool_format_information_for_user_prompt=self.tool_format_information_for_user_prompt(),
|
|
115
121
|
)
|
|
116
122
|
|
|
117
123
|
# Properties that we should soon deprecate
|
|
@@ -103,7 +103,7 @@ class ToolManager:
|
|
|
103
103
|
]
|
|
104
104
|
|
|
105
105
|
# Get MCP tools (these are already properly instantiated)
|
|
106
|
-
mcp_tools = self._mcp_manager.get_all_mcp_tools(
|
|
106
|
+
mcp_tools = self._mcp_manager.get_all_mcp_tools()
|
|
107
107
|
# Combine both types of tools
|
|
108
108
|
self.available_tools = internal_tools + mcp_tools
|
|
109
109
|
|
|
@@ -277,8 +277,6 @@ class ToolManager:
|
|
|
277
277
|
)
|
|
278
278
|
return unique_tool_calls
|
|
279
279
|
|
|
280
|
-
from typing import Any
|
|
281
|
-
|
|
282
280
|
def _convert_to_forced_tool(self, tool_name: str) -> dict[str, Any]:
|
|
283
281
|
return {
|
|
284
282
|
"type": "function",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: unique_toolkit
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.26
|
|
4
4
|
Summary:
|
|
5
5
|
License: Proprietary
|
|
6
6
|
Author: Martin Fadler
|
|
@@ -12,6 +12,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
13
13
|
Requires-Dist: numpy (>=1.26.4,<2.0.0)
|
|
14
14
|
Requires-Dist: openai (>=1.99.9,<2.0.0)
|
|
15
|
+
Requires-Dist: platformdirs (>=4.0.0,<5.0.0)
|
|
15
16
|
Requires-Dist: pydantic (>=2.8.2,<3.0.0)
|
|
16
17
|
Requires-Dist: pydantic-settings (>=2.10.1,<3.0.0)
|
|
17
18
|
Requires-Dist: pyhumps (>=3.8.0,<4.0.0)
|
|
@@ -114,6 +115,15 @@ All notable changes to this project will be documented in this file.
|
|
|
114
115
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
115
116
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
116
117
|
|
|
118
|
+
## [0.8.26] - 2025-08-27
|
|
119
|
+
- Optimized MCP manager
|
|
120
|
+
|
|
121
|
+
## [0.8.25] - 2025-08-27
|
|
122
|
+
- Load environment variables automatically from plattform dirs or environment
|
|
123
|
+
- General Endpoint definition utility
|
|
124
|
+
- Expose `LanguageModelToolDescription` and `LanguageModelName` directly
|
|
125
|
+
- Get initial debug information from chat payload
|
|
126
|
+
|
|
117
127
|
## [0.8.24] - 2025-08-25
|
|
118
128
|
- Optimized hallucination manager
|
|
119
129
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
unique_toolkit/__init__.py,sha256=
|
|
1
|
+
unique_toolkit/__init__.py,sha256=nbOYPIKERt-ITsgifrnJhatn1YNR38Ntumw-dCn_tsA,714
|
|
2
2
|
unique_toolkit/_common/_base_service.py,sha256=S8H0rAebx7GsOldA7xInLp3aQJt9yEPDQdsGSFRJsGg,276
|
|
3
3
|
unique_toolkit/_common/_time_utils.py,sha256=ztmTovTvr-3w71Ns2VwXC65OKUUh-sQlzbHdKTQWm-w,135
|
|
4
4
|
unique_toolkit/_common/default_language_model.py,sha256=tmHSqg6e8G7RmKqmdE_tmLxkSN0x-aGoyUdy6Pl2oAE,334
|
|
5
|
+
unique_toolkit/_common/endpoint_builder.py,sha256=oM6uDmxUqTAJut6MuJQj3bIX4yOccyErWD5bJ1d1lcY,4526
|
|
5
6
|
unique_toolkit/_common/exception.py,sha256=caQIE1btsQnpKCHqL2cgWUSbHup06enQu_Pt7uGUTTE,727
|
|
6
7
|
unique_toolkit/_common/token/image_token_counting.py,sha256=VpFfZyY0GIH27q_Wy4YNjk2algqvbCtJyzuuROoFQPw,2189
|
|
7
8
|
unique_toolkit/_common/token/token_counting.py,sha256=Jo5B11GLlnaZDi4u7xCzIboLl8zn5cY_dmrogHixVdk,6132
|
|
@@ -13,14 +14,14 @@ unique_toolkit/app/init_logging.py,sha256=Sh26SRxOj8i8dzobKhYha2lLrkrMTHfB1V4jR3
|
|
|
13
14
|
unique_toolkit/app/init_sdk.py,sha256=5_oDoETr6akwYyBCb0ivTdMNu3SVgPSkrXcDS6ELyY8,2269
|
|
14
15
|
unique_toolkit/app/performance/async_tasks.py,sha256=H0l3OAcosLwNHZ8d2pd-Di4wHIXfclEvagi5kfqLFPA,1941
|
|
15
16
|
unique_toolkit/app/performance/async_wrapper.py,sha256=yVVcRDkcdyfjsxro-N29SBvi-7773wnfDplef6-y8xw,1077
|
|
16
|
-
unique_toolkit/app/schemas.py,sha256=
|
|
17
|
-
unique_toolkit/app/unique_settings.py,sha256=
|
|
17
|
+
unique_toolkit/app/schemas.py,sha256=uh7PR-YYXfdTnuO5V_eGrW96eJmP5tJ4jeePyU8tsNI,7888
|
|
18
|
+
unique_toolkit/app/unique_settings.py,sha256=hsQ4ViOs-E5K_bq96PwDvuVpfMJ-vNPq01B2gCK5R5g,9802
|
|
18
19
|
unique_toolkit/app/verification.py,sha256=GxFFwcJMy25fCA_Xe89wKW7bgqOu8PAs5y8QpHF0GSc,3861
|
|
19
20
|
unique_toolkit/chat/__init__.py,sha256=LRs2G-JTVuci4lbtHTkVUiNcZcSR6uqqfnAyo7af6nY,619
|
|
20
21
|
unique_toolkit/chat/constants.py,sha256=05kq6zjqUVB2d6_P7s-90nbljpB3ryxwCI-CAz0r2O4,83
|
|
21
22
|
unique_toolkit/chat/functions.py,sha256=I6r81PpWNu-j9DvHxNFKN3V6T5E5q42FhW9kF8pJJSY,29950
|
|
22
23
|
unique_toolkit/chat/schemas.py,sha256=ekmDzxZRnO4pF-8DAnP4JqKmYuJnSC5RpYcO7j5T3aI,4927
|
|
23
|
-
unique_toolkit/chat/service.py,sha256
|
|
24
|
+
unique_toolkit/chat/service.py,sha256=-agXxkB_cl7Mcr3aFVHbBXhO-RioiouAfoHAHmlhQJs,37981
|
|
24
25
|
unique_toolkit/chat/state.py,sha256=Cjgwv_2vhDFbV69xxsn7SefhaoIAEqLx3ferdVFCnOg,1445
|
|
25
26
|
unique_toolkit/chat/utils.py,sha256=ihm-wQykBWhB4liR3LnwPVPt_qGW6ETq21Mw4HY0THE,854
|
|
26
27
|
unique_toolkit/content/__init__.py,sha256=EdJg_A_7loEtCQf4cah3QARQreJx6pdz89Rm96YbMVg,940
|
|
@@ -65,13 +66,15 @@ unique_toolkit/evaluators/hallucination/service.py,sha256=k8qro5Lw4Ak58m4HYp3G4H
|
|
|
65
66
|
unique_toolkit/evaluators/hallucination/utils.py,sha256=gO2AOzDQwVTev2_5vDKgJ9A6A9e0himJyAta_wglVG8,8326
|
|
66
67
|
unique_toolkit/evaluators/output_parser.py,sha256=eI72qkzK1dZyUvnfP2SOAQCGBj_-PwX5wy_aLPMsJMY,883
|
|
67
68
|
unique_toolkit/evaluators/schemas.py,sha256=Jaue6Uhx75X1CyHKWj8sT3RE1JZXTqoLtfLt2xQNCX8,2507
|
|
68
|
-
unique_toolkit/framework_utilities/
|
|
69
|
+
unique_toolkit/framework_utilities/__init__.py,sha256=fvAn9y4MRL1JgoO14ufQtLVRPRHn4jP07XRqt-TItCA,68
|
|
70
|
+
unique_toolkit/framework_utilities/langchain/client.py,sha256=9LDRS2l9XGxL0HoFLh0ZrFUXrlt8o_J-o-1rU8j-uMQ,1432
|
|
69
71
|
unique_toolkit/framework_utilities/langchain/history.py,sha256=R9RuCeSFNaUO3OZ0G_LmIC4gmOCIANcl91MfyWLnZ1c,650
|
|
70
|
-
unique_toolkit/framework_utilities/openai/
|
|
72
|
+
unique_toolkit/framework_utilities/openai/__init__.py,sha256=UDigDBIXPAfGgnaopg8Yvcr1S8RDbXyv3RsJDclAIGw,180
|
|
73
|
+
unique_toolkit/framework_utilities/openai/client.py,sha256=CiQjTq2erh8mPJ5gOSKUW4DUQqykB7Qhb9azjtOk1u8,1309
|
|
71
74
|
unique_toolkit/framework_utilities/openai/message_builder.py,sha256=VU6mJm_upLcarJQKFft_t1RlLRncWDxDuLC5LIJ5lQQ,4339
|
|
72
75
|
unique_toolkit/framework_utilities/utils.py,sha256=JK7g2yMfEx3eMprug26769xqNpS5WJcizf8n2zWMBng,789
|
|
73
76
|
unique_toolkit/history_manager/history_construction_with_contents.py,sha256=c8Zy3erSbHGT8AdICRRlSK91T_FN6tNpTznvUzpLbWk,9023
|
|
74
|
-
unique_toolkit/history_manager/history_manager.py,sha256=
|
|
77
|
+
unique_toolkit/history_manager/history_manager.py,sha256=e18ldkof6ZRSzD8FefodazNFNHvmQIxP9cG0j6Yrl18,8350
|
|
75
78
|
unique_toolkit/history_manager/loop_token_reducer.py,sha256=9kqJioUehfoYs5-XMoCv-b_5JpNRrhOz62QwhC3LF3E,17899
|
|
76
79
|
unique_toolkit/history_manager/utils.py,sha256=iu4LsYOElx8HlZjcx3ZC75I-TmEYBiEP9q2J93Q63Mg,5606
|
|
77
80
|
unique_toolkit/language_model/__init__.py,sha256=lRQyLlbwHbNFf4-0foBU13UGb09lwEeodbVsfsSgaCk,1971
|
|
@@ -98,21 +101,21 @@ unique_toolkit/smart_rules/compile.py,sha256=cxWjb2dxEI2HGsakKdVCkSNi7VK9mr08w5s
|
|
|
98
101
|
unique_toolkit/thinking_manager/thinking_manager.py,sha256=AJfmrTXTr-DxBnJ2_zYYpYo57kr5deqT0LiZb8UdaDQ,4175
|
|
99
102
|
unique_toolkit/tools/config.py,sha256=E4lFQA4gCi3_j4hcdnd5YUHhbaWTyIGWrgD18QardQw,3796
|
|
100
103
|
unique_toolkit/tools/factory.py,sha256=w3uNHuYBIJ330Xi8PTdAkr8G3OMbQH2cBgvk5UT16oE,1253
|
|
101
|
-
unique_toolkit/tools/mcp/__init__.py,sha256=
|
|
102
|
-
unique_toolkit/tools/mcp/manager.py,sha256=
|
|
103
|
-
unique_toolkit/tools/mcp/models.py,sha256=
|
|
104
|
-
unique_toolkit/tools/mcp/tool_wrapper.py,sha256=
|
|
105
|
-
unique_toolkit/tools/schemas.py,sha256=
|
|
104
|
+
unique_toolkit/tools/mcp/__init__.py,sha256=RLF_p-LDRC7GhiB3fdCi4u3bh6V9PY_w26fg61BLyco,122
|
|
105
|
+
unique_toolkit/tools/mcp/manager.py,sha256=KyPiCLgSfpGkkOhvJtwQCm_dQ1M7LkQlctkBzt0bMAM,2683
|
|
106
|
+
unique_toolkit/tools/mcp/models.py,sha256=6-qFq3ip2OhqRC5H6YzhsC27w5CYrWln75dWmeIovRk,713
|
|
107
|
+
unique_toolkit/tools/mcp/tool_wrapper.py,sha256=w7Fbo4FSMYvtgSq7Sqt1dmAPvqHjoBQS-TwnSp0p6j0,9927
|
|
108
|
+
unique_toolkit/tools/schemas.py,sha256=rArQccbfIv7CWcozClAZ-BVlOwAsjpgL8KUab_WeO3k,4817
|
|
106
109
|
unique_toolkit/tools/test/test_mcp_manager.py,sha256=jnVKxqJyeJBDb6LvI8bM9dd8XPxrbXV0VCOHMhCdlug,14398
|
|
107
110
|
unique_toolkit/tools/test/test_tool_progress_reporter.py,sha256=GTtmBqOUo0-4fh_q0lRgxDhwKeankc3FHFD5ULZAm4Y,6299
|
|
108
|
-
unique_toolkit/tools/tool.py,sha256=
|
|
109
|
-
unique_toolkit/tools/tool_manager.py,sha256
|
|
111
|
+
unique_toolkit/tools/tool.py,sha256=E0qORpQBrdksAylf_RuPaEbXKi_AlswVGv-67t3DNZI,5879
|
|
112
|
+
unique_toolkit/tools/tool_manager.py,sha256=_Qt5fUGvA82YHn5LvqNJurLYW41jH9endNwsiSeyXyM,9948
|
|
110
113
|
unique_toolkit/tools/tool_progress_reporter.py,sha256=ixud9VoHey1vlU1t86cW0-WTvyTwMxNSWBon8I11SUk,7955
|
|
111
114
|
unique_toolkit/tools/utils/execution/execution.py,sha256=vjG2Y6awsGNtlvyQAGCTthQ5thWHYnn-vzZXaYLb3QE,7922
|
|
112
115
|
unique_toolkit/tools/utils/source_handling/schema.py,sha256=vzAyf6ZWNexjMO0OrnB8y2glGkvAilmGGQXd6zcDaKw,870
|
|
113
116
|
unique_toolkit/tools/utils/source_handling/source_formatting.py,sha256=C7uayNbdkNVJdEARA5CENnHtNY1SU6etlaqbgHNyxaQ,9152
|
|
114
117
|
unique_toolkit/tools/utils/source_handling/tests/test_source_formatting.py,sha256=oM5ZxEgzROrnX1229KViCAFjRxl9wCTzWZoinYSHleM,6979
|
|
115
|
-
unique_toolkit-0.8.
|
|
116
|
-
unique_toolkit-0.8.
|
|
117
|
-
unique_toolkit-0.8.
|
|
118
|
-
unique_toolkit-0.8.
|
|
118
|
+
unique_toolkit-0.8.26.dist-info/LICENSE,sha256=GlN8wHNdh53xwOPg44URnwag6TEolCjoq3YD_KrWgss,193
|
|
119
|
+
unique_toolkit-0.8.26.dist-info/METADATA,sha256=sTwRsRjcDR2QqTqbe8Oz5j5MHObkvMkOfND9EUyh7XE,28950
|
|
120
|
+
unique_toolkit-0.8.26.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
121
|
+
unique_toolkit-0.8.26.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|