jl-ecms-client 0.2.0__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.
- jl_ecms_client-0.2.0.dist-info/METADATA +295 -0
- jl_ecms_client-0.2.0.dist-info/RECORD +51 -0
- jl_ecms_client-0.2.0.dist-info/WHEEL +5 -0
- jl_ecms_client-0.2.0.dist-info/licenses/LICENSE +190 -0
- jl_ecms_client-0.2.0.dist-info/top_level.txt +1 -0
- mirix/client/__init__.py +72 -0
- mirix/client/client.py +2594 -0
- mirix/client/remote_client.py +1136 -0
- mirix/helpers/__init__.py +1 -0
- mirix/helpers/converters.py +429 -0
- mirix/helpers/datetime_helpers.py +90 -0
- mirix/helpers/json_helpers.py +47 -0
- mirix/helpers/message_helpers.py +74 -0
- mirix/helpers/tool_rule_solver.py +166 -0
- mirix/schemas/__init__.py +1 -0
- mirix/schemas/agent.py +400 -0
- mirix/schemas/block.py +188 -0
- mirix/schemas/cloud_file_mapping.py +29 -0
- mirix/schemas/embedding_config.py +114 -0
- mirix/schemas/enums.py +69 -0
- mirix/schemas/environment_variables.py +82 -0
- mirix/schemas/episodic_memory.py +170 -0
- mirix/schemas/file.py +57 -0
- mirix/schemas/health.py +10 -0
- mirix/schemas/knowledge_vault.py +181 -0
- mirix/schemas/llm_config.py +187 -0
- mirix/schemas/memory.py +318 -0
- mirix/schemas/message.py +1315 -0
- mirix/schemas/mirix_base.py +107 -0
- mirix/schemas/mirix_message.py +411 -0
- mirix/schemas/mirix_message_content.py +230 -0
- mirix/schemas/mirix_request.py +39 -0
- mirix/schemas/mirix_response.py +183 -0
- mirix/schemas/openai/__init__.py +1 -0
- mirix/schemas/openai/chat_completion_request.py +122 -0
- mirix/schemas/openai/chat_completion_response.py +144 -0
- mirix/schemas/openai/chat_completions.py +127 -0
- mirix/schemas/openai/embedding_response.py +11 -0
- mirix/schemas/openai/openai.py +229 -0
- mirix/schemas/organization.py +38 -0
- mirix/schemas/procedural_memory.py +151 -0
- mirix/schemas/providers.py +816 -0
- mirix/schemas/resource_memory.py +134 -0
- mirix/schemas/sandbox_config.py +132 -0
- mirix/schemas/semantic_memory.py +162 -0
- mirix/schemas/source.py +96 -0
- mirix/schemas/step.py +53 -0
- mirix/schemas/tool.py +241 -0
- mirix/schemas/tool_rule.py +209 -0
- mirix/schemas/usage.py +31 -0
- mirix/schemas/user.py +67 -0
mirix/schemas/tool.py
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import Field, model_validator
|
|
4
|
+
|
|
5
|
+
from mirix.constants import (
|
|
6
|
+
COMPOSIO_TOOL_TAG_NAME,
|
|
7
|
+
FUNCTION_RETURN_CHAR_LIMIT,
|
|
8
|
+
MIRIX_CORE_TOOL_MODULE_NAME,
|
|
9
|
+
MIRIX_EXTRA_TOOL_MODULE_NAME,
|
|
10
|
+
MIRIX_MEMORY_TOOL_MODULE_NAME,
|
|
11
|
+
)
|
|
12
|
+
from mirix.functions.functions import (
|
|
13
|
+
derive_openai_json_schema,
|
|
14
|
+
get_json_schema_from_module,
|
|
15
|
+
)
|
|
16
|
+
from mirix.functions.helpers import generate_langchain_tool_wrapper
|
|
17
|
+
from mirix.functions.schema_generator import generate_schema_from_args_schema_v2
|
|
18
|
+
from mirix.orm.enums import ToolType
|
|
19
|
+
from mirix.schemas.mirix_base import MirixBase
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
try:
|
|
23
|
+
from langchain_core.tools import BaseTool as LangChainBaseTool
|
|
24
|
+
except ImportError:
|
|
25
|
+
LangChainBaseTool = Any # type: ignore
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class BaseTool(MirixBase):
|
|
29
|
+
__id_prefix__ = "tool"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Tool(BaseTool):
|
|
33
|
+
"""
|
|
34
|
+
Representation of a tool, which is a function that can be called by the agent.
|
|
35
|
+
|
|
36
|
+
Parameters:
|
|
37
|
+
id (str): The unique identifier of the tool.
|
|
38
|
+
name (str): The name of the function.
|
|
39
|
+
tags (List[str]): Metadata tags.
|
|
40
|
+
source_code (str): The source code of the function.
|
|
41
|
+
json_schema (Dict): The JSON schema of the function.
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
id: str = BaseTool.generate_id_field()
|
|
46
|
+
tool_type: ToolType = Field(ToolType.CUSTOM, description="The type of the tool.")
|
|
47
|
+
description: Optional[str] = Field(None, description="The description of the tool.")
|
|
48
|
+
source_type: Optional[str] = Field(None, description="The type of the source code.")
|
|
49
|
+
organization_id: Optional[str] = Field(
|
|
50
|
+
None,
|
|
51
|
+
description="The unique identifier of the organization associated with the tool.",
|
|
52
|
+
)
|
|
53
|
+
name: Optional[str] = Field(None, description="The name of the function.")
|
|
54
|
+
tags: List[str] = Field([], description="Metadata tags.")
|
|
55
|
+
|
|
56
|
+
# code
|
|
57
|
+
source_code: Optional[str] = Field(
|
|
58
|
+
None, description="The source code of the function."
|
|
59
|
+
)
|
|
60
|
+
json_schema: Optional[Dict] = Field(
|
|
61
|
+
None, description="The JSON schema of the function."
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# tool configuration
|
|
65
|
+
return_char_limit: int = Field(
|
|
66
|
+
FUNCTION_RETURN_CHAR_LIMIT,
|
|
67
|
+
description="The maximum number of characters in the response.",
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# metadata fields
|
|
71
|
+
created_by_id: Optional[str] = Field(
|
|
72
|
+
None, description="The id of the user that made this Tool."
|
|
73
|
+
)
|
|
74
|
+
last_updated_by_id: Optional[str] = Field(
|
|
75
|
+
None, description="The id of the user that made this Tool."
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
@model_validator(mode="after")
|
|
79
|
+
def populate_missing_fields(self):
|
|
80
|
+
"""
|
|
81
|
+
Populate missing fields: name, description, and json_schema.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
if self.tool_type == ToolType.CUSTOM:
|
|
85
|
+
# If it's a custom tool, we need to ensure source_code is present
|
|
86
|
+
if not self.source_code:
|
|
87
|
+
raise ValueError(
|
|
88
|
+
f"Custom tool with id={self.id} is missing source_code field."
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Always derive json_schema for freshest possible json_schema
|
|
92
|
+
# TODO: Instead of checking the tag, we should having `COMPOSIO` as a specific ToolType
|
|
93
|
+
# TODO: We skip this for Composio bc composio json schemas are derived differently
|
|
94
|
+
if COMPOSIO_TOOL_TAG_NAME not in self.tags:
|
|
95
|
+
self.json_schema = derive_openai_json_schema(
|
|
96
|
+
source_code=self.source_code
|
|
97
|
+
)
|
|
98
|
+
elif self.tool_type in {ToolType.MIRIX_CORE}:
|
|
99
|
+
# If it's mirix core tool, we generate the json_schema on the fly here
|
|
100
|
+
self.json_schema = get_json_schema_from_module(
|
|
101
|
+
module_name=MIRIX_CORE_TOOL_MODULE_NAME, function_name=self.name
|
|
102
|
+
)
|
|
103
|
+
elif self.tool_type in {ToolType.MIRIX_MEMORY_CORE}:
|
|
104
|
+
self.json_schema = get_json_schema_from_module(
|
|
105
|
+
module_name=MIRIX_MEMORY_TOOL_MODULE_NAME, function_name=self.name
|
|
106
|
+
)
|
|
107
|
+
elif self.tool_type in {ToolType.MIRIX_EXTRA}:
|
|
108
|
+
self.json_schema = get_json_schema_from_module(
|
|
109
|
+
module_name=MIRIX_EXTRA_TOOL_MODULE_NAME, function_name=self.name
|
|
110
|
+
)
|
|
111
|
+
elif self.tool_type in {ToolType.MIRIX_MCP}:
|
|
112
|
+
# MCP tools have their json_schema already provided by MCP tool registry
|
|
113
|
+
# Skip validation since these are auto-generated tools
|
|
114
|
+
if not self.json_schema:
|
|
115
|
+
raise ValueError(f"MCP tool {self.name} is missing json_schema field")
|
|
116
|
+
|
|
117
|
+
# Derive name from the JSON schema if not provided
|
|
118
|
+
if not self.name:
|
|
119
|
+
# TODO: This in theory could error, but name should always be on json_schema
|
|
120
|
+
# TODO: Make JSON schema a typed pydantic object
|
|
121
|
+
self.name = self.json_schema.get("name")
|
|
122
|
+
|
|
123
|
+
# Derive description from the JSON schema if not provided
|
|
124
|
+
if not self.description:
|
|
125
|
+
# TODO: This in theory could error, but description should always be on json_schema
|
|
126
|
+
# TODO: Make JSON schema a typed pydantic object
|
|
127
|
+
self.description = self.json_schema.get("description")
|
|
128
|
+
|
|
129
|
+
return self
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class ToolCreate(MirixBase):
|
|
133
|
+
name: Optional[str] = Field(
|
|
134
|
+
None,
|
|
135
|
+
description="The name of the function (auto-generated from source_code if not provided).",
|
|
136
|
+
)
|
|
137
|
+
description: Optional[str] = Field(None, description="The description of the tool.")
|
|
138
|
+
tags: List[str] = Field([], description="Metadata tags.")
|
|
139
|
+
source_code: str = Field(..., description="The source code of the function.")
|
|
140
|
+
source_type: str = Field("python", description="The source type of the function.")
|
|
141
|
+
json_schema: Optional[Dict] = Field(
|
|
142
|
+
None,
|
|
143
|
+
description="The JSON schema of the function (auto-generated from source_code if not provided)",
|
|
144
|
+
)
|
|
145
|
+
return_char_limit: int = Field(
|
|
146
|
+
FUNCTION_RETURN_CHAR_LIMIT,
|
|
147
|
+
description="The maximum number of characters in the response.",
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
@classmethod
|
|
151
|
+
def from_langchain(
|
|
152
|
+
cls,
|
|
153
|
+
langchain_tool: "LangChainBaseTool",
|
|
154
|
+
additional_imports_module_attr_map: dict[str, str] = None,
|
|
155
|
+
) -> "ToolCreate":
|
|
156
|
+
"""
|
|
157
|
+
Class method to create an instance of Tool from a Langchain tool (must be from langchain_community.tools).
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
langchain_tool (LangChainBaseTool): An instance of a LangChain BaseTool (BaseTool from LangChain)
|
|
161
|
+
additional_imports_module_attr_map (dict[str, str]): A mapping of module names to attribute name. This is used internally to import all the required classes for the langchain tool. For example, you would pass in `{"langchain_community.utilities": "WikipediaAPIWrapper"}` for `from langchain_community.tools import WikipediaQueryRun`. NOTE: You do NOT need to specify the tool import here, that is done automatically for you.
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
Tool: A Mirix Tool initialized with attributes derived from the provided LangChain BaseTool object.
|
|
165
|
+
"""
|
|
166
|
+
description = langchain_tool.description
|
|
167
|
+
source_type = "python"
|
|
168
|
+
tags = ["langchain"]
|
|
169
|
+
# NOTE: langchain tools may come from different packages
|
|
170
|
+
wrapper_func_name, wrapper_function_str = generate_langchain_tool_wrapper(
|
|
171
|
+
langchain_tool, additional_imports_module_attr_map
|
|
172
|
+
)
|
|
173
|
+
json_schema = generate_schema_from_args_schema_v2(
|
|
174
|
+
langchain_tool.args_schema, name=wrapper_func_name, description=description
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
return cls(
|
|
178
|
+
name=wrapper_func_name,
|
|
179
|
+
description=description,
|
|
180
|
+
source_type=source_type,
|
|
181
|
+
tags=tags,
|
|
182
|
+
source_code=wrapper_function_str,
|
|
183
|
+
json_schema=json_schema,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
@classmethod
|
|
187
|
+
def load_default_langchain_tools(cls) -> List["ToolCreate"]:
|
|
188
|
+
# For now, we only support wikipedia tool
|
|
189
|
+
from langchain_community.tools import WikipediaQueryRun
|
|
190
|
+
from langchain_community.utilities import WikipediaAPIWrapper
|
|
191
|
+
|
|
192
|
+
wikipedia_tool = ToolCreate.from_langchain(
|
|
193
|
+
WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper()),
|
|
194
|
+
{"langchain_community.utilities": "WikipediaAPIWrapper"},
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
return [wikipedia_tool]
|
|
198
|
+
|
|
199
|
+
@classmethod
|
|
200
|
+
def load_default_composio_tools(cls) -> List["ToolCreate"]:
|
|
201
|
+
pass
|
|
202
|
+
|
|
203
|
+
# TODO: Disable composio tools for now
|
|
204
|
+
# TODO: Naming is causing issues
|
|
205
|
+
# calculator = ToolCreate.from_composio(action_name=Action.MATHEMATICAL_CALCULATOR.name)
|
|
206
|
+
# serp_news = ToolCreate.from_composio(action_name=Action.SERPAPI_NEWS_SEARCH.name)
|
|
207
|
+
# serp_google_search = ToolCreate.from_composio(action_name=Action.SERPAPI_SEARCH.name)
|
|
208
|
+
# serp_google_maps = ToolCreate.from_composio(action_name=Action.SERPAPI_GOOGLE_MAPS_SEARCH.name)
|
|
209
|
+
|
|
210
|
+
return []
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class ToolUpdate(MirixBase):
|
|
214
|
+
description: Optional[str] = Field(None, description="The description of the tool.")
|
|
215
|
+
name: Optional[str] = Field(None, description="The name of the function.")
|
|
216
|
+
tags: Optional[List[str]] = Field(None, description="Metadata tags.")
|
|
217
|
+
source_code: Optional[str] = Field(
|
|
218
|
+
None, description="The source code of the function."
|
|
219
|
+
)
|
|
220
|
+
source_type: Optional[str] = Field(None, description="The type of the source code.")
|
|
221
|
+
json_schema: Optional[Dict] = Field(
|
|
222
|
+
None,
|
|
223
|
+
description="The JSON schema of the function (auto-generated from source_code if not provided)",
|
|
224
|
+
)
|
|
225
|
+
return_char_limit: Optional[int] = Field(
|
|
226
|
+
None, description="The maximum number of characters in the response."
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
class Config:
|
|
230
|
+
extra = "ignore" # Allows extra fields without validation errors
|
|
231
|
+
# TODO: Remove this, and clean usage of ToolUpdate everywhere else
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class ToolRunFromSource(MirixBase):
|
|
235
|
+
source_code: str = Field(..., description="The source code of the function.")
|
|
236
|
+
args: Dict[str, Any] = Field(..., description="The arguments to pass to the tool.")
|
|
237
|
+
env_vars: Dict[str, str] = Field(
|
|
238
|
+
None, description="The environment variables to pass to the tool."
|
|
239
|
+
)
|
|
240
|
+
name: Optional[str] = Field(None, description="The name of the tool to run.")
|
|
241
|
+
source_type: Optional[str] = Field(None, description="The type of the source code.")
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Annotated, Any, Dict, List, Literal, Optional, Set, Union
|
|
3
|
+
|
|
4
|
+
from pydantic import Field
|
|
5
|
+
|
|
6
|
+
from mirix.schemas.enums import ToolRuleType
|
|
7
|
+
from mirix.schemas.mirix_base import MirixBase
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BaseToolRule(MirixBase):
|
|
11
|
+
__id_prefix__ = "tool_rule"
|
|
12
|
+
tool_name: str = Field(
|
|
13
|
+
...,
|
|
14
|
+
description="The name of the tool. Must exist in the database for the user's organization.",
|
|
15
|
+
)
|
|
16
|
+
type: ToolRuleType = Field(..., description="The type of the message.")
|
|
17
|
+
|
|
18
|
+
def get_valid_tools(
|
|
19
|
+
self,
|
|
20
|
+
tool_call_history: List[str],
|
|
21
|
+
available_tools: Set[str],
|
|
22
|
+
last_function_response: Optional[str],
|
|
23
|
+
) -> set[str]:
|
|
24
|
+
raise NotImplementedError
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ChildToolRule(BaseToolRule):
|
|
28
|
+
"""
|
|
29
|
+
A ToolRule represents a tool that can be invoked by the agent.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
type: Literal[ToolRuleType.constrain_child_tools] = (
|
|
33
|
+
ToolRuleType.constrain_child_tools
|
|
34
|
+
)
|
|
35
|
+
children: List[str] = Field(
|
|
36
|
+
..., description="The children tools that can be invoked."
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def get_valid_tools(
|
|
40
|
+
self,
|
|
41
|
+
tool_call_history: List[str],
|
|
42
|
+
available_tools: Set[str],
|
|
43
|
+
last_function_response: Optional[str],
|
|
44
|
+
) -> Set[str]:
|
|
45
|
+
last_tool = tool_call_history[-1] if tool_call_history else None
|
|
46
|
+
return set(self.children) if last_tool == self.tool_name else available_tools
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ParentToolRule(BaseToolRule):
|
|
50
|
+
"""
|
|
51
|
+
A ToolRule that only allows a child tool to be called if the parent has been called.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
type: Literal[ToolRuleType.parent_last_tool] = ToolRuleType.parent_last_tool
|
|
55
|
+
children: List[str] = Field(
|
|
56
|
+
..., description="The children tools that can be invoked."
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
def get_valid_tools(
|
|
60
|
+
self,
|
|
61
|
+
tool_call_history: List[str],
|
|
62
|
+
available_tools: Set[str],
|
|
63
|
+
last_function_response: Optional[str],
|
|
64
|
+
) -> Set[str]:
|
|
65
|
+
last_tool = tool_call_history[-1] if tool_call_history else None
|
|
66
|
+
return (
|
|
67
|
+
set(self.children)
|
|
68
|
+
if last_tool == self.tool_name
|
|
69
|
+
else available_tools - set(self.children)
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class ConditionalToolRule(BaseToolRule):
|
|
74
|
+
"""
|
|
75
|
+
A ToolRule that conditionally maps to different child tools based on the output.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
type: Literal[ToolRuleType.conditional] = ToolRuleType.conditional
|
|
79
|
+
default_child: Optional[str] = Field(
|
|
80
|
+
None,
|
|
81
|
+
description="The default child tool to be called. If None, any tool can be called.",
|
|
82
|
+
)
|
|
83
|
+
child_output_mapping: Dict[Any, str] = Field(
|
|
84
|
+
..., description="The output case to check for mapping"
|
|
85
|
+
)
|
|
86
|
+
require_output_mapping: bool = Field(
|
|
87
|
+
default=False,
|
|
88
|
+
description="Whether to throw an error when output doesn't match any case",
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
def get_valid_tools(
|
|
92
|
+
self,
|
|
93
|
+
tool_call_history: List[str],
|
|
94
|
+
available_tools: Set[str],
|
|
95
|
+
last_function_response: Optional[str],
|
|
96
|
+
) -> Set[str]:
|
|
97
|
+
"""Determine valid tools based on function output mapping."""
|
|
98
|
+
if not tool_call_history or tool_call_history[-1] != self.tool_name:
|
|
99
|
+
return available_tools # No constraints if this rule doesn't apply
|
|
100
|
+
|
|
101
|
+
if not last_function_response:
|
|
102
|
+
raise ValueError(
|
|
103
|
+
"Conditional tool rule requires an LLM response to determine which child tool to use"
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
json_response = json.loads(last_function_response)
|
|
108
|
+
function_output = json_response.get("message", "")
|
|
109
|
+
except json.JSONDecodeError:
|
|
110
|
+
if self.require_output_mapping:
|
|
111
|
+
return set() # Strict mode: Invalid response means no allowed tools
|
|
112
|
+
return {self.default_child} if self.default_child else available_tools
|
|
113
|
+
|
|
114
|
+
# Match function output to a mapped child tool
|
|
115
|
+
for key, tool in self.child_output_mapping.items():
|
|
116
|
+
if self._matches_key(function_output, key):
|
|
117
|
+
return {tool}
|
|
118
|
+
|
|
119
|
+
# If no match found, use default or allow all tools if no default is set
|
|
120
|
+
if self.require_output_mapping:
|
|
121
|
+
return set() # Strict mode: No match means no valid tools
|
|
122
|
+
|
|
123
|
+
return {self.default_child} if self.default_child else available_tools
|
|
124
|
+
|
|
125
|
+
def _matches_key(self, function_output: str, key: Any) -> bool:
|
|
126
|
+
"""Helper function to determine if function output matches a mapping key."""
|
|
127
|
+
if isinstance(key, bool):
|
|
128
|
+
return (
|
|
129
|
+
function_output.lower() == "true"
|
|
130
|
+
if key
|
|
131
|
+
else function_output.lower() == "false"
|
|
132
|
+
)
|
|
133
|
+
elif isinstance(key, int):
|
|
134
|
+
try:
|
|
135
|
+
return int(function_output) == key
|
|
136
|
+
except ValueError:
|
|
137
|
+
return False
|
|
138
|
+
elif isinstance(key, float):
|
|
139
|
+
try:
|
|
140
|
+
return float(function_output) == key
|
|
141
|
+
except ValueError:
|
|
142
|
+
return False
|
|
143
|
+
else: # Assume string
|
|
144
|
+
return str(function_output) == str(key)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class InitToolRule(BaseToolRule):
|
|
148
|
+
"""
|
|
149
|
+
Represents the initial tool rule configuration.
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
type: Literal[ToolRuleType.run_first] = ToolRuleType.run_first
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class TerminalToolRule(BaseToolRule):
|
|
156
|
+
"""
|
|
157
|
+
Represents a terminal tool rule configuration where if this tool gets called, it must end the agent loop.
|
|
158
|
+
"""
|
|
159
|
+
|
|
160
|
+
type: Literal[ToolRuleType.exit_loop] = ToolRuleType.exit_loop
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class ContinueToolRule(BaseToolRule):
|
|
164
|
+
"""
|
|
165
|
+
Represents a tool rule configuration where if this tool gets called, it must continue the agent loop.
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
type: Literal[ToolRuleType.continue_loop] = ToolRuleType.continue_loop
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class MaxCountPerStepToolRule(BaseToolRule):
|
|
172
|
+
"""
|
|
173
|
+
Represents a tool rule configuration which constrains the total number of times this tool can be invoked in a single step.
|
|
174
|
+
"""
|
|
175
|
+
|
|
176
|
+
type: Literal[ToolRuleType.max_count_per_step] = ToolRuleType.max_count_per_step
|
|
177
|
+
max_count_limit: int = Field(
|
|
178
|
+
...,
|
|
179
|
+
description="The max limit for the total number of times this tool can be invoked in a single step.",
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
def get_valid_tools(
|
|
183
|
+
self,
|
|
184
|
+
tool_call_history: List[str],
|
|
185
|
+
available_tools: Set[str],
|
|
186
|
+
last_function_response: Optional[str],
|
|
187
|
+
) -> Set[str]:
|
|
188
|
+
"""Restricts the tool if it has been called max_count_limit times in the current step."""
|
|
189
|
+
count = tool_call_history.count(self.tool_name)
|
|
190
|
+
|
|
191
|
+
# If the tool has been used max_count_limit times, it is no longer allowed
|
|
192
|
+
if count >= self.max_count_limit:
|
|
193
|
+
return available_tools - {self.tool_name}
|
|
194
|
+
|
|
195
|
+
return available_tools
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
ToolRule = Annotated[
|
|
199
|
+
Union[
|
|
200
|
+
ChildToolRule,
|
|
201
|
+
InitToolRule,
|
|
202
|
+
TerminalToolRule,
|
|
203
|
+
ConditionalToolRule,
|
|
204
|
+
ContinueToolRule,
|
|
205
|
+
MaxCountPerStepToolRule,
|
|
206
|
+
ParentToolRule,
|
|
207
|
+
],
|
|
208
|
+
Field(discriminator="type"),
|
|
209
|
+
]
|
mirix/schemas/usage.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MirixUsageStatistics(BaseModel):
|
|
7
|
+
"""
|
|
8
|
+
Usage statistics for the agent interaction.
|
|
9
|
+
|
|
10
|
+
Attributes:
|
|
11
|
+
completion_tokens (int): The number of tokens generated by the agent.
|
|
12
|
+
prompt_tokens (int): The number of tokens in the prompt.
|
|
13
|
+
total_tokens (int): The total number of tokens processed by the agent.
|
|
14
|
+
step_count (int): The number of steps taken by the agent.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
message_type: Literal["usage_statistics"] = "usage_statistics"
|
|
18
|
+
completion_tokens: int = Field(
|
|
19
|
+
0, description="The number of tokens generated by the agent."
|
|
20
|
+
)
|
|
21
|
+
prompt_tokens: int = Field(0, description="The number of tokens in the prompt.")
|
|
22
|
+
last_prompt_tokens: int = Field(
|
|
23
|
+
0, description="The number of tokens in the last prompt."
|
|
24
|
+
)
|
|
25
|
+
last_completion_tokens: int = Field(
|
|
26
|
+
0, description="The number of tokens in the last completion."
|
|
27
|
+
)
|
|
28
|
+
total_tokens: int = Field(
|
|
29
|
+
0, description="The total number of tokens processed by the agent."
|
|
30
|
+
)
|
|
31
|
+
step_count: int = Field(0, description="The number of steps taken by the agent.")
|
mirix/schemas/user.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Optional
|
|
3
|
+
import uuid
|
|
4
|
+
|
|
5
|
+
from pydantic import Field
|
|
6
|
+
|
|
7
|
+
from mirix.helpers.datetime_helpers import get_utc_time
|
|
8
|
+
from mirix.schemas.mirix_base import MirixBase
|
|
9
|
+
from mirix.services.organization_manager import OrganizationManager
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class UserBase(MirixBase):
|
|
13
|
+
__id_prefix__ = "user"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _generate_user_id() -> str:
|
|
17
|
+
"""Generate a random user ID."""
|
|
18
|
+
return f"user-{uuid.uuid4().hex[:8]}"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class User(UserBase):
|
|
22
|
+
"""
|
|
23
|
+
Representation of a user.
|
|
24
|
+
|
|
25
|
+
Parameters:
|
|
26
|
+
id (str): The unique identifier of the user.
|
|
27
|
+
name (str): The name of the user.
|
|
28
|
+
status (str): Whether the user is active or not.
|
|
29
|
+
created_at (datetime): The creation date of the user.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
id: str = Field(
|
|
33
|
+
default_factory=_generate_user_id,
|
|
34
|
+
description="The unique identifier of the user.",
|
|
35
|
+
)
|
|
36
|
+
organization_id: Optional[str] = Field(
|
|
37
|
+
OrganizationManager.DEFAULT_ORG_ID,
|
|
38
|
+
description="The organization id of the user",
|
|
39
|
+
)
|
|
40
|
+
name: str = Field(..., description="The name of the user.")
|
|
41
|
+
status: str = Field("active", description="Whether the user is active or not.")
|
|
42
|
+
timezone: str = Field(..., description="The timezone of the user.")
|
|
43
|
+
created_at: Optional[datetime] = Field(
|
|
44
|
+
default_factory=get_utc_time, description="The creation date of the user."
|
|
45
|
+
)
|
|
46
|
+
updated_at: Optional[datetime] = Field(
|
|
47
|
+
default_factory=get_utc_time, description="The update date of the user."
|
|
48
|
+
)
|
|
49
|
+
is_deleted: bool = Field(False, description="Whether this user is deleted or not.")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class UserCreate(UserBase):
|
|
53
|
+
id: Optional[str] = Field(None, description="The unique identifier of the user.")
|
|
54
|
+
name: str = Field(..., description="The name of the user.")
|
|
55
|
+
status: str = Field("active", description="Whether the user is active or not.")
|
|
56
|
+
timezone: str = Field(..., description="The timezone of the user.")
|
|
57
|
+
organization_id: str = Field(..., description="The organization id of the user.")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class UserUpdate(UserBase):
|
|
61
|
+
id: str = Field(..., description="The id of the user to update.")
|
|
62
|
+
name: Optional[str] = Field(None, description="The new name of the user.")
|
|
63
|
+
status: Optional[str] = Field(None, description="The new status of the user.")
|
|
64
|
+
timezone: Optional[str] = Field(None, description="The new timezone of the user.")
|
|
65
|
+
organization_id: Optional[str] = Field(
|
|
66
|
+
None, description="The new organization id of the user."
|
|
67
|
+
)
|