unique_toolkit 0.7.41__tar.gz → 0.8.0__tar.gz
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-0.7.41 → unique_toolkit-0.8.0}/CHANGELOG.md +6 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/PKG-INFO +8 -2
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/pyproject.toml +3 -3
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/app/__init__.py +6 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/app/schemas.py +51 -0
- unique_toolkit-0.8.0/unique_toolkit/tools/tool_definitions.py +145 -0
- unique_toolkit-0.8.0/unique_toolkit/tools/tool_definitionsV2.py +137 -0
- unique_toolkit-0.8.0/unique_toolkit/tools/tool_factory.py +31 -0
- unique_toolkit-0.8.0/unique_toolkit/tools/tool_progress_reporter.py +225 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/LICENSE +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/README.md +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/__init__.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/_common/_base_service.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/_common/_time_utils.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/_common/exception.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/_common/validate_required_values.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/_common/validators.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/app/event_util.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/app/init_logging.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/app/init_sdk.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/app/performance/async_tasks.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/app/performance/async_wrapper.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/app/sse_client.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/app/unique_settings.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/app/verification.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/chat/__init__.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/chat/constants.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/chat/functions.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/chat/schemas.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/chat/service.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/chat/state.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/chat/utils.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/content/__init__.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/content/constants.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/content/functions.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/content/schemas.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/content/service.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/content/utils.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/embedding/__init__.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/embedding/constants.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/embedding/functions.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/embedding/schemas.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/embedding/service.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/embedding/utils.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/evaluators/__init__.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/evaluators/config.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/evaluators/constants.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/evaluators/context_relevancy/constants.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/evaluators/context_relevancy/prompts.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/evaluators/context_relevancy/service.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/evaluators/context_relevancy/utils.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/evaluators/exception.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/evaluators/hallucination/constants.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/evaluators/hallucination/prompts.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/evaluators/hallucination/service.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/evaluators/hallucination/utils.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/evaluators/output_parser.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/evaluators/schemas.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/framework_utilities/langchain/client.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/framework_utilities/langchain/history.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/framework_utilities/openai/client.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/framework_utilities/openai/message_builder.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/framework_utilities/utils.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/language_model/__init__.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/language_model/builder.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/language_model/constants.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/language_model/functions.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/language_model/infos.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/language_model/prompt.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/language_model/reference.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/language_model/schemas.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/language_model/service.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/language_model/utils.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/protocols/support.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/short_term_memory/__init__.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/short_term_memory/constants.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/short_term_memory/functions.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/short_term_memory/schemas.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/short_term_memory/service.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/smart_rules/__init__.py +0 -0
- {unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/smart_rules/compile.py +0 -0
|
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.8.0] - 2025-08-04
|
|
9
|
+
- Add MCP support
|
|
10
|
+
|
|
11
|
+
## [0.7.42] - 2025-08-01
|
|
12
|
+
- Added tool definitions
|
|
13
|
+
|
|
8
14
|
## [0.7.41] - 2025-07-31
|
|
9
15
|
- Add new chat event attribute indicating tools disabled on a company level
|
|
10
16
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: unique_toolkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary:
|
|
5
5
|
License: Proprietary
|
|
6
6
|
Author: Martin Fadler
|
|
@@ -19,7 +19,7 @@ Requires-Dist: regex (>=2024.5.15,<2025.0.0)
|
|
|
19
19
|
Requires-Dist: sseclient (>=0.0.27,<0.0.28)
|
|
20
20
|
Requires-Dist: tiktoken (>=0.7.0,<0.8.0)
|
|
21
21
|
Requires-Dist: typing-extensions (>=4.9.0,<5.0.0)
|
|
22
|
-
Requires-Dist: unique-sdk (>=0.
|
|
22
|
+
Requires-Dist: unique-sdk (>=0.10.0,<0.11.0)
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
|
|
25
25
|
# Unique Toolkit
|
|
@@ -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.8.0] - 2025-08-04
|
|
117
|
+
- Add MCP support
|
|
118
|
+
|
|
119
|
+
## [0.7.42] - 2025-08-01
|
|
120
|
+
- Added tool definitions
|
|
121
|
+
|
|
116
122
|
## [0.7.41] - 2025-07-31
|
|
117
123
|
- Add new chat event attribute indicating tools disabled on a company level
|
|
118
124
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "unique_toolkit"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.8.0"
|
|
4
4
|
description = ""
|
|
5
5
|
authors = [
|
|
6
6
|
"Martin Fadler <martin.fadler@unique.ch>",
|
|
@@ -20,7 +20,7 @@ numpy = "^1.26.4"
|
|
|
20
20
|
python-dotenv = "^1.0.1"
|
|
21
21
|
regex = "^2024.5.15"
|
|
22
22
|
tiktoken = "^0.7.0"
|
|
23
|
-
unique-sdk = "^0.
|
|
23
|
+
unique-sdk = "^0.10.0"
|
|
24
24
|
pydantic-settings = "^2.10.1"
|
|
25
25
|
sseclient = "^0.0.27"
|
|
26
26
|
|
|
@@ -47,7 +47,7 @@ pytest-cov = "^4.1.0"
|
|
|
47
47
|
pre-commit = "^3.7.1"
|
|
48
48
|
pytest-asyncio = "^0.23.8"
|
|
49
49
|
pytest-mock = "^3.14.0"
|
|
50
|
-
unique-sdk = {path = "../unique_sdk"}
|
|
50
|
+
unique-sdk = { path = "../unique_sdk" }
|
|
51
51
|
ipykernel = "^6.29.5"
|
|
52
52
|
|
|
53
53
|
[build-system]
|
|
@@ -41,5 +41,11 @@ from .schemas import (
|
|
|
41
41
|
from .verification import (
|
|
42
42
|
verify_signature_and_construct_event as verify_signature_and_construct_event,
|
|
43
43
|
)
|
|
44
|
+
from .schemas import (
|
|
45
|
+
McpServer as McpServer,
|
|
46
|
+
)
|
|
47
|
+
from .schemas import (
|
|
48
|
+
McpTool as McpTool,
|
|
49
|
+
)
|
|
44
50
|
|
|
45
51
|
DOMAIN_NAME = "app"
|
|
@@ -38,6 +38,53 @@ class BaseEvent(BaseModel):
|
|
|
38
38
|
return cls.model_validate(data)
|
|
39
39
|
|
|
40
40
|
|
|
41
|
+
###
|
|
42
|
+
# MCP schemas
|
|
43
|
+
###
|
|
44
|
+
|
|
45
|
+
class McpTool(BaseModel):
|
|
46
|
+
model_config = model_config
|
|
47
|
+
|
|
48
|
+
name: str
|
|
49
|
+
description: Optional[str] = None
|
|
50
|
+
input_schema: dict[str, Any]
|
|
51
|
+
output_schema: Optional[dict[str, Any]] = None
|
|
52
|
+
annotations: Optional[dict[str, Any]] = None
|
|
53
|
+
title: Optional[str] = Field(
|
|
54
|
+
default=None,
|
|
55
|
+
description="The display title for a tool. This is a Unique specific field.",
|
|
56
|
+
)
|
|
57
|
+
icon: Optional[str] = Field(
|
|
58
|
+
default=None,
|
|
59
|
+
description="An icon name from the Lucide icon set for the tool. This is a Unique specific field.",
|
|
60
|
+
)
|
|
61
|
+
system_prompt: Optional[str] = Field(
|
|
62
|
+
default=None,
|
|
63
|
+
description="An optional system prompt for the tool. This is a Unique specific field.",
|
|
64
|
+
)
|
|
65
|
+
user_prompt: Optional[str] = Field(
|
|
66
|
+
default=None,
|
|
67
|
+
description="An optional user prompt for the tool. This is a Unique specific field.",
|
|
68
|
+
)
|
|
69
|
+
is_connected: bool = Field(
|
|
70
|
+
description="Whether the tool is connected to the MCP server. This is a Unique specific field.",
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
class McpServer(BaseModel):
|
|
74
|
+
model_config = model_config
|
|
75
|
+
|
|
76
|
+
id: str
|
|
77
|
+
name: str
|
|
78
|
+
system_prompt: Optional[str] = Field(
|
|
79
|
+
default=None,
|
|
80
|
+
description="An optional system prompt for the MCP server.",
|
|
81
|
+
)
|
|
82
|
+
user_prompt: Optional[str] = Field(
|
|
83
|
+
default=None,
|
|
84
|
+
description="An optional user prompt for the MCP server.",
|
|
85
|
+
)
|
|
86
|
+
tools: list[McpTool] = []
|
|
87
|
+
|
|
41
88
|
###
|
|
42
89
|
# ChatEvent schemas
|
|
43
90
|
###
|
|
@@ -133,6 +180,10 @@ class ChatEventPayload(BaseModel):
|
|
|
133
180
|
default=None,
|
|
134
181
|
description="Raw UniqueQL rule that can be compiled to a metadata filter.",
|
|
135
182
|
)
|
|
183
|
+
mcp_servers: list[McpServer] = Field(
|
|
184
|
+
default_factory=list,
|
|
185
|
+
description="A list of MCP servers with tools available for the chat session.",
|
|
186
|
+
)
|
|
136
187
|
|
|
137
188
|
@field_validator("raw_scope_rules", mode="before")
|
|
138
189
|
def validate_scope_rules(cls, value: dict[str, Any] | None) -> UniqueQL | None:
|
|
@@ -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", [])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/_common/validate_required_values.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/app/performance/async_tasks.py
RENAMED
|
File without changes
|
{unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/app/performance/async_wrapper.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/evaluators/context_relevancy/utils.py
RENAMED
|
File without changes
|
|
File without changes
|
{unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/evaluators/hallucination/constants.py
RENAMED
|
File without changes
|
{unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/evaluators/hallucination/prompts.py
RENAMED
|
File without changes
|
{unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/evaluators/hallucination/service.py
RENAMED
|
File without changes
|
{unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/evaluators/hallucination/utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/framework_utilities/openai/client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/short_term_memory/constants.py
RENAMED
|
File without changes
|
{unique_toolkit-0.7.41 → unique_toolkit-0.8.0}/unique_toolkit/short_term_memory/functions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|