unique_toolkit 1.21.0__py3-none-any.whl → 1.21.2__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.
Potentially problematic release.
This version of unique_toolkit might be problematic. Click here for more details.
- unique_toolkit/agentic/history_manager/history_manager.py +7 -0
- unique_toolkit/agentic/tools/test/test_tool_progress_reporter.py +33 -17
- unique_toolkit/agentic/tools/tool_manager.py +54 -4
- unique_toolkit/agentic/tools/tool_progress_reporter.py +27 -26
- {unique_toolkit-1.21.0.dist-info → unique_toolkit-1.21.2.dist-info}/METADATA +6 -1
- {unique_toolkit-1.21.0.dist-info → unique_toolkit-1.21.2.dist-info}/RECORD +8 -8
- {unique_toolkit-1.21.0.dist-info → unique_toolkit-1.21.2.dist-info}/LICENSE +0 -0
- {unique_toolkit-1.21.0.dist-info → unique_toolkit-1.21.2.dist-info}/WHEEL +0 -0
|
@@ -120,9 +120,16 @@ class HistoryManager:
|
|
|
120
120
|
reference_manager=reference_manager,
|
|
121
121
|
)
|
|
122
122
|
self._tool_call_result_history: list[ToolCallResponse] = []
|
|
123
|
+
self._tool_calls: list[LanguageModelFunction] = []
|
|
123
124
|
self._loop_history: list[LanguageModelMessage] = []
|
|
124
125
|
self._source_enumerator = 0
|
|
125
126
|
|
|
127
|
+
def add_tool_call(self, tool_call: LanguageModelFunction) -> None:
|
|
128
|
+
self._tool_calls.append(tool_call)
|
|
129
|
+
|
|
130
|
+
def get_tool_calls(self) -> list[LanguageModelFunction]:
|
|
131
|
+
return self._tool_calls
|
|
132
|
+
|
|
126
133
|
def has_no_loop_messages(self) -> bool:
|
|
127
134
|
return len(self._loop_history) == 0
|
|
128
135
|
|
|
@@ -252,8 +252,10 @@ class TestToolProgressReporterConfig:
|
|
|
252
252
|
# Arrange
|
|
253
253
|
custom_config = ToolProgressReporterConfig(
|
|
254
254
|
state_to_display_template={
|
|
255
|
-
|
|
256
|
-
|
|
255
|
+
"started": "⚪ {tool_name}: {message}",
|
|
256
|
+
"running": "⏳ {tool_name}: {message}",
|
|
257
|
+
"finished": "✅ {tool_name}: {message}",
|
|
258
|
+
"failed": "❌ {tool_name}: {message}",
|
|
257
259
|
}
|
|
258
260
|
)
|
|
259
261
|
reporter = ToolProgressReporter(chat_service, config=custom_config)
|
|
@@ -275,19 +277,21 @@ class TestToolProgressReporterConfig:
|
|
|
275
277
|
|
|
276
278
|
@pytest.mark.ai
|
|
277
279
|
@pytest.mark.asyncio
|
|
278
|
-
async def
|
|
280
|
+
async def test_config__skips_states_with_empty_template__when_state_hidden(
|
|
279
281
|
self, chat_service, tool_call
|
|
280
282
|
) -> None:
|
|
281
283
|
"""
|
|
282
|
-
Purpose: Verify that states
|
|
284
|
+
Purpose: Verify that states with empty string templates are not displayed.
|
|
283
285
|
Why this matters: Allows selective display of only certain states (e.g., hide STARTED).
|
|
284
|
-
Setup summary: Create config
|
|
286
|
+
Setup summary: Create config with empty string for RUNNING state, verify message is not displayed.
|
|
285
287
|
"""
|
|
286
288
|
# Arrange
|
|
287
289
|
custom_config = ToolProgressReporterConfig(
|
|
288
290
|
state_to_display_template={
|
|
289
|
-
|
|
290
|
-
#
|
|
291
|
+
"started": "",
|
|
292
|
+
"running": "", # Empty string hides RUNNING state
|
|
293
|
+
"finished": "✅ {tool_name}: {message}",
|
|
294
|
+
"failed": "❌ {tool_name}: {message}",
|
|
291
295
|
}
|
|
292
296
|
)
|
|
293
297
|
reporter = ToolProgressReporter(chat_service, config=custom_config)
|
|
@@ -304,7 +308,7 @@ class TestToolProgressReporterConfig:
|
|
|
304
308
|
chat_service.modify_assistant_message_async.assert_called()
|
|
305
309
|
call_args = chat_service.modify_assistant_message_async.call_args
|
|
306
310
|
content = call_args.kwargs["content"]
|
|
307
|
-
# Content should not contain the message since RUNNING is
|
|
311
|
+
# Content should not contain the message since RUNNING template is empty
|
|
308
312
|
assert "Processing" not in content
|
|
309
313
|
assert "Test Tool" not in content
|
|
310
314
|
|
|
@@ -321,8 +325,10 @@ class TestToolProgressReporterConfig:
|
|
|
321
325
|
# Arrange
|
|
322
326
|
custom_config = ToolProgressReporterConfig(
|
|
323
327
|
state_to_display_template={
|
|
324
|
-
|
|
325
|
-
|
|
328
|
+
"started": "○ {tool_name} - {message}",
|
|
329
|
+
"running": "▶️ {tool_name} - {message}",
|
|
330
|
+
"finished": "✓ {tool_name} - {message}",
|
|
331
|
+
"failed": "✗ {tool_name} - {message}",
|
|
326
332
|
}
|
|
327
333
|
)
|
|
328
334
|
reporter = ToolProgressReporter(chat_service, config=custom_config)
|
|
@@ -355,14 +361,17 @@ class TestToolProgressReporterConfig:
|
|
|
355
361
|
self, chat_service, tool_call
|
|
356
362
|
) -> None:
|
|
357
363
|
"""
|
|
358
|
-
Purpose: Verify selective state display shows only FINISHED when
|
|
364
|
+
Purpose: Verify selective state display shows only FINISHED when other states use empty templates.
|
|
359
365
|
Why this matters: Use case where user only wants final results, not intermediate steps.
|
|
360
|
-
Setup summary: Configure only FINISHED
|
|
366
|
+
Setup summary: Configure only FINISHED with content, send STARTED and FINISHED, verify only FINISHED appears.
|
|
361
367
|
"""
|
|
362
368
|
# Arrange
|
|
363
369
|
custom_config = ToolProgressReporterConfig(
|
|
364
370
|
state_to_display_template={
|
|
365
|
-
|
|
371
|
+
"started": "", # Empty template hides STARTED
|
|
372
|
+
"running": "", # Empty template hides RUNNING
|
|
373
|
+
"finished": "Done: {tool_name} - {message}",
|
|
374
|
+
"failed": "Failed: {tool_name} - {message}",
|
|
366
375
|
}
|
|
367
376
|
)
|
|
368
377
|
reporter = ToolProgressReporter(chat_service, config=custom_config)
|
|
@@ -399,16 +408,23 @@ class TestToolProgressReporterConfig:
|
|
|
399
408
|
|
|
400
409
|
@pytest.mark.ai
|
|
401
410
|
@pytest.mark.asyncio
|
|
402
|
-
async def
|
|
411
|
+
async def test_config__handles_all_empty_templates__when_all_states_hidden(
|
|
403
412
|
self, chat_service, tool_call
|
|
404
413
|
) -> None:
|
|
405
414
|
"""
|
|
406
|
-
Purpose: Verify that
|
|
415
|
+
Purpose: Verify that all empty string templates result in no messages being displayed.
|
|
407
416
|
Why this matters: Edge case handling and allows disabling all progress display.
|
|
408
|
-
Setup summary: Create config with empty
|
|
417
|
+
Setup summary: Create config with all empty templates, verify no tool messages appear.
|
|
409
418
|
"""
|
|
410
419
|
# Arrange
|
|
411
|
-
custom_config = ToolProgressReporterConfig(
|
|
420
|
+
custom_config = ToolProgressReporterConfig(
|
|
421
|
+
state_to_display_template={
|
|
422
|
+
"started": "",
|
|
423
|
+
"running": "",
|
|
424
|
+
"finished": "",
|
|
425
|
+
"failed": "",
|
|
426
|
+
}
|
|
427
|
+
)
|
|
412
428
|
reporter = ToolProgressReporter(chat_service, config=custom_config)
|
|
413
429
|
|
|
414
430
|
# Act
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
from abc import ABC, abstractmethod
|
|
3
3
|
from logging import Logger, getLogger
|
|
4
|
-
from typing import override
|
|
4
|
+
from typing import Literal, override
|
|
5
5
|
|
|
6
6
|
from openai.types.chat import (
|
|
7
7
|
ChatCompletionNamedToolChoiceParam,
|
|
@@ -70,6 +70,24 @@ class BaseToolManager(ABC):
|
|
|
70
70
|
def get_exclusive_tools(self) -> list[str]:
|
|
71
71
|
raise NotImplementedError()
|
|
72
72
|
|
|
73
|
+
@abstractmethod
|
|
74
|
+
def filter_tool_calls(
|
|
75
|
+
self,
|
|
76
|
+
tool_calls: list[LanguageModelFunction],
|
|
77
|
+
tool_types: list[Literal["mcp", "internal", "subagent"]],
|
|
78
|
+
) -> list[LanguageModelFunction]:
|
|
79
|
+
"""
|
|
80
|
+
Filter tool calls by their types.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
tool_calls: List of tool calls to filter
|
|
84
|
+
tool_types: List of tool types to include (e.g., ["mcp", "internal", "subagent"])
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Filtered list of tool calls matching the specified types
|
|
88
|
+
"""
|
|
89
|
+
raise NotImplementedError()
|
|
90
|
+
|
|
73
91
|
def does_a_tool_take_control(self, tool_calls: list[LanguageModelFunction]) -> bool:
|
|
74
92
|
for tool_call in tool_calls:
|
|
75
93
|
tool_instance = self.get_tool_by_name(tool_call.name)
|
|
@@ -266,7 +284,7 @@ class ToolManager(BaseToolManager):
|
|
|
266
284
|
)
|
|
267
285
|
|
|
268
286
|
# Build internal tools from configurations
|
|
269
|
-
|
|
287
|
+
self._internal_tools = [
|
|
270
288
|
ToolFactory.build_tool_with_settings(
|
|
271
289
|
t.name,
|
|
272
290
|
t,
|
|
@@ -278,9 +296,9 @@ class ToolManager(BaseToolManager):
|
|
|
278
296
|
]
|
|
279
297
|
|
|
280
298
|
# Get MCP tools (these are already properly instantiated)
|
|
281
|
-
|
|
299
|
+
self._mcp_tools = self._mcp_manager.get_all_mcp_tools()
|
|
282
300
|
# Combine both types of tools
|
|
283
|
-
self.available_tools =
|
|
301
|
+
self.available_tools = self._internal_tools + self._mcp_tools + sub_agents
|
|
284
302
|
self._sub_agents = sub_agents
|
|
285
303
|
|
|
286
304
|
for t in self.available_tools:
|
|
@@ -301,6 +319,29 @@ class ToolManager(BaseToolManager):
|
|
|
301
319
|
|
|
302
320
|
self._tools.append(t)
|
|
303
321
|
|
|
322
|
+
@override
|
|
323
|
+
def filter_tool_calls(
|
|
324
|
+
self,
|
|
325
|
+
tool_calls: list[LanguageModelFunction],
|
|
326
|
+
tool_types: list[Literal["mcp", "internal", "subagent"]],
|
|
327
|
+
) -> list[LanguageModelFunction]:
|
|
328
|
+
filtered_calls = []
|
|
329
|
+
|
|
330
|
+
# Build sets for efficient lookup
|
|
331
|
+
internal_tool_names = {tool.name for tool in self._internal_tools}
|
|
332
|
+
mcp_tool_names = {tool.name for tool in self._mcp_tools}
|
|
333
|
+
sub_agent_names = {tool.name for tool in self._sub_agents}
|
|
334
|
+
|
|
335
|
+
for tool_call in tool_calls:
|
|
336
|
+
if "internal" in tool_types and tool_call.name in internal_tool_names:
|
|
337
|
+
filtered_calls.append(tool_call)
|
|
338
|
+
elif "mcp" in tool_types and tool_call.name in mcp_tool_names:
|
|
339
|
+
filtered_calls.append(tool_call)
|
|
340
|
+
elif "subagent" in tool_types and tool_call.name in sub_agent_names:
|
|
341
|
+
filtered_calls.append(tool_call)
|
|
342
|
+
|
|
343
|
+
return filtered_calls
|
|
344
|
+
|
|
304
345
|
@property
|
|
305
346
|
def sub_agents(self) -> list[SubAgentTool]:
|
|
306
347
|
return self._sub_agents
|
|
@@ -416,6 +457,15 @@ class ResponsesApiToolManager(BaseToolManager):
|
|
|
416
457
|
builtin_tools=builtin_tools,
|
|
417
458
|
)
|
|
418
459
|
|
|
460
|
+
@override
|
|
461
|
+
def filter_tool_calls(
|
|
462
|
+
self,
|
|
463
|
+
tool_calls: list[LanguageModelFunction],
|
|
464
|
+
tool_types: list[Literal["mcp", "internal", "subagent"]],
|
|
465
|
+
) -> list[LanguageModelFunction]:
|
|
466
|
+
"""Delegate filtering to the underlying tool manager."""
|
|
467
|
+
return self._tool_manager.filter_tool_calls(tool_calls, tool_types)
|
|
468
|
+
|
|
419
469
|
@override
|
|
420
470
|
def get_tool_by_name(self, name: str) -> Tool | None:
|
|
421
471
|
return self._tool_manager.get_tool_by_name(name)
|
|
@@ -2,7 +2,7 @@ import re
|
|
|
2
2
|
from datetime import datetime
|
|
3
3
|
from enum import StrEnum
|
|
4
4
|
from functools import wraps
|
|
5
|
-
from typing import Protocol
|
|
5
|
+
from typing import Protocol, TypedDict
|
|
6
6
|
|
|
7
7
|
from pydantic import BaseModel, Field
|
|
8
8
|
|
|
@@ -33,39 +33,37 @@ class ToolExecutionStatus(BaseModel):
|
|
|
33
33
|
timestamp: datetime = Field(default_factory=datetime.now)
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
),
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
),
|
|
36
|
+
class StateToDisplayTemplate(TypedDict):
|
|
37
|
+
started: str
|
|
38
|
+
running: str
|
|
39
|
+
failed: str
|
|
40
|
+
finished: str
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
_DEFAULT_STATE_TO_DISPLAY_TEMPLATE: StateToDisplayTemplate = {
|
|
44
|
+
"started": "{arrow}**{{tool_name}}** ⚪: {{message}}".format(arrow=ARROW),
|
|
45
|
+
"running": "{arrow}**{{tool_name}}** 🟡: {{message}}".format(arrow=ARROW),
|
|
46
|
+
"finished": "{arrow}**{{tool_name}}** 🟢: {{message}}".format(arrow=ARROW),
|
|
47
|
+
"failed": "{arrow}**{{tool_name}}** 🔴: {{message}}".format(arrow=ARROW),
|
|
49
48
|
}
|
|
50
49
|
|
|
51
50
|
|
|
52
51
|
state_to_display_template_description = """
|
|
53
|
-
|
|
54
|
-
The
|
|
55
|
-
|
|
52
|
+
Display templates for the different progress states.
|
|
53
|
+
The template is a string that will be used to display the progress status.
|
|
54
|
+
It can contain the following placeholders:
|
|
56
55
|
- `{tool_name}`: The name of the tool
|
|
57
56
|
- `{message}`: The message to display (sent by the tool)
|
|
58
|
-
|
|
59
|
-
If a state is not present in the mapping, then updates for that state will not be displayed.
|
|
60
57
|
""".strip()
|
|
61
58
|
|
|
62
59
|
|
|
63
60
|
class ToolProgressReporterConfig(BaseModel):
|
|
64
61
|
model_config = get_configuration_dict()
|
|
65
62
|
|
|
66
|
-
state_to_display_template:
|
|
63
|
+
state_to_display_template: StateToDisplayTemplate = Field(
|
|
67
64
|
default=_DEFAULT_STATE_TO_DISPLAY_TEMPLATE,
|
|
68
65
|
description=state_to_display_template_description,
|
|
66
|
+
title="Display Templates",
|
|
69
67
|
)
|
|
70
68
|
|
|
71
69
|
|
|
@@ -188,12 +186,15 @@ class ToolProgressReporter:
|
|
|
188
186
|
def _get_tool_status_display_message(
|
|
189
187
|
self, name: str, message: str, state: ProgressState
|
|
190
188
|
) -> str | None:
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
189
|
+
display_message = self._config.state_to_display_template[state.value].format(
|
|
190
|
+
tool_name=name,
|
|
191
|
+
message=message,
|
|
192
|
+
)
|
|
193
|
+
# Don't display empty messages
|
|
194
|
+
if display_message.strip() == "":
|
|
195
|
+
return None
|
|
196
|
+
|
|
197
|
+
return display_message
|
|
197
198
|
|
|
198
199
|
|
|
199
200
|
class ToolWithToolProgressReporter(Protocol):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: unique_toolkit
|
|
3
|
-
Version: 1.21.
|
|
3
|
+
Version: 1.21.2
|
|
4
4
|
Summary:
|
|
5
5
|
License: Proprietary
|
|
6
6
|
Author: Cedric Klinkert
|
|
@@ -118,6 +118,11 @@ All notable changes to this project will be documented in this file.
|
|
|
118
118
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
119
119
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
120
120
|
|
|
121
|
+
## [1.21.2] - 2025-10-30
|
|
122
|
+
- Fixing that system format info is only appended to system prompt if tool is called
|
|
123
|
+
|
|
124
|
+
## [1.21.1] - 2025-10-30
|
|
125
|
+
- Improve Spaces 2.0 display of tool progress reporter configuration.
|
|
121
126
|
|
|
122
127
|
## [1.21.0] - 2025-10-30
|
|
123
128
|
- Add option to customize the display of tool progress statuses.
|
|
@@ -44,7 +44,7 @@ unique_toolkit/agentic/evaluation/schemas.py,sha256=m9JMCUmeqP8KhsJOVEzsz6dRXUe1
|
|
|
44
44
|
unique_toolkit/agentic/evaluation/tests/test_context_relevancy_service.py,sha256=4tDxHTApbaTMxN1sNS8WCqj2BweRk6YqZ5_zHP45jto,7977
|
|
45
45
|
unique_toolkit/agentic/evaluation/tests/test_output_parser.py,sha256=RN_HcBbU6qy_e_PoYyUFcjWnp3ymJ6-gLj6TgEOupAI,3107
|
|
46
46
|
unique_toolkit/agentic/history_manager/history_construction_with_contents.py,sha256=c8Zy3erSbHGT8AdICRRlSK91T_FN6tNpTznvUzpLbWk,9023
|
|
47
|
-
unique_toolkit/agentic/history_manager/history_manager.py,sha256=
|
|
47
|
+
unique_toolkit/agentic/history_manager/history_manager.py,sha256=3O-AdvmfUpai8is85rKOVkRw2aTZA33UtRX2ZW0Ri9U,9543
|
|
48
48
|
unique_toolkit/agentic/history_manager/loop_token_reducer.py,sha256=4XUX2-yVBnaYthV8p0zj2scVBUdK_3IhxBgoNlrytyQ,18498
|
|
49
49
|
unique_toolkit/agentic/history_manager/utils.py,sha256=VIn_UmcR3jHtpux0qp5lQQzczgAm8XYSeQiPo87jC3A,3143
|
|
50
50
|
unique_toolkit/agentic/postprocessor/postprocessor_manager.py,sha256=s6HFhA61TE05aAay15NFTWI1JvdSlxmGpEVfpBbGFyM,7684
|
|
@@ -93,10 +93,10 @@ unique_toolkit/agentic/tools/openai_builtin/code_interpreter/service.py,sha256=R
|
|
|
93
93
|
unique_toolkit/agentic/tools/openai_builtin/manager.py,sha256=kU4wGit9AnDbkijB7LJEHcGXG8UeTBhiZh4a7lxTGA8,2246
|
|
94
94
|
unique_toolkit/agentic/tools/schemas.py,sha256=0ZR8xCdGj1sEdPE0lfTIG2uSR5zqWoprUa3Seqez4C8,4837
|
|
95
95
|
unique_toolkit/agentic/tools/test/test_mcp_manager.py,sha256=8j5fNQf3qELOcQ0m5fd8OkIFgunL5ILgdWzYD_Ccg1I,18816
|
|
96
|
-
unique_toolkit/agentic/tools/test/test_tool_progress_reporter.py,sha256=
|
|
96
|
+
unique_toolkit/agentic/tools/test/test_tool_progress_reporter.py,sha256=XHNezB8itj9KzpQgD0cwRtp2AgRUfUkQsHc3bTyPj6c,15801
|
|
97
97
|
unique_toolkit/agentic/tools/tool.py,sha256=mFuxc3h4sghClDO8OVL2-6kifiHQ-U7JMYGUyXqTYLE,6338
|
|
98
|
-
unique_toolkit/agentic/tools/tool_manager.py,sha256=
|
|
99
|
-
unique_toolkit/agentic/tools/tool_progress_reporter.py,sha256=
|
|
98
|
+
unique_toolkit/agentic/tools/tool_manager.py,sha256=pq3F_C59z47lcyvWxz-5qPBmTOuHAWDNjRrWiqbOPsk,18174
|
|
99
|
+
unique_toolkit/agentic/tools/tool_progress_reporter.py,sha256=GaR0oqDUJZvBB9WCUVYYh0Zvs6U-LMygJCCrqPlitgA,10296
|
|
100
100
|
unique_toolkit/agentic/tools/utils/__init__.py,sha256=s75sjY5nrJchjLGs3MwSIqhDW08fFXIaX7eRQjFIA4s,346
|
|
101
101
|
unique_toolkit/agentic/tools/utils/execution/__init__.py,sha256=OHiKpqBnfhBiEQagKVWJsZlHv8smPp5OI4dFIexzibw,37
|
|
102
102
|
unique_toolkit/agentic/tools/utils/execution/execution.py,sha256=ocPGGfUwa851207HNTLYiBJ1pNzJp4VhMZ49OPP33gU,8022
|
|
@@ -166,7 +166,7 @@ unique_toolkit/short_term_memory/service.py,sha256=5PeVBu1ZCAfyDb2HLVvlmqSbyzBBu
|
|
|
166
166
|
unique_toolkit/smart_rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
167
167
|
unique_toolkit/smart_rules/compile.py,sha256=Ozhh70qCn2yOzRWr9d8WmJeTo7AQurwd3tStgBMPFLA,1246
|
|
168
168
|
unique_toolkit/test_utilities/events.py,sha256=_mwV2bs5iLjxS1ynDCjaIq-gjjKhXYCK-iy3dRfvO3g,6410
|
|
169
|
-
unique_toolkit-1.21.
|
|
170
|
-
unique_toolkit-1.21.
|
|
171
|
-
unique_toolkit-1.21.
|
|
172
|
-
unique_toolkit-1.21.
|
|
169
|
+
unique_toolkit-1.21.2.dist-info/LICENSE,sha256=GlN8wHNdh53xwOPg44URnwag6TEolCjoq3YD_KrWgss,193
|
|
170
|
+
unique_toolkit-1.21.2.dist-info/METADATA,sha256=Pek5th-gw0XpiVv70BUNsH6XCiWDhmVVuyQ0fvvRHAA,39863
|
|
171
|
+
unique_toolkit-1.21.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
172
|
+
unique_toolkit-1.21.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|