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.
Files changed (51) hide show
  1. jl_ecms_client-0.2.0.dist-info/METADATA +295 -0
  2. jl_ecms_client-0.2.0.dist-info/RECORD +51 -0
  3. jl_ecms_client-0.2.0.dist-info/WHEEL +5 -0
  4. jl_ecms_client-0.2.0.dist-info/licenses/LICENSE +190 -0
  5. jl_ecms_client-0.2.0.dist-info/top_level.txt +1 -0
  6. mirix/client/__init__.py +72 -0
  7. mirix/client/client.py +2594 -0
  8. mirix/client/remote_client.py +1136 -0
  9. mirix/helpers/__init__.py +1 -0
  10. mirix/helpers/converters.py +429 -0
  11. mirix/helpers/datetime_helpers.py +90 -0
  12. mirix/helpers/json_helpers.py +47 -0
  13. mirix/helpers/message_helpers.py +74 -0
  14. mirix/helpers/tool_rule_solver.py +166 -0
  15. mirix/schemas/__init__.py +1 -0
  16. mirix/schemas/agent.py +400 -0
  17. mirix/schemas/block.py +188 -0
  18. mirix/schemas/cloud_file_mapping.py +29 -0
  19. mirix/schemas/embedding_config.py +114 -0
  20. mirix/schemas/enums.py +69 -0
  21. mirix/schemas/environment_variables.py +82 -0
  22. mirix/schemas/episodic_memory.py +170 -0
  23. mirix/schemas/file.py +57 -0
  24. mirix/schemas/health.py +10 -0
  25. mirix/schemas/knowledge_vault.py +181 -0
  26. mirix/schemas/llm_config.py +187 -0
  27. mirix/schemas/memory.py +318 -0
  28. mirix/schemas/message.py +1315 -0
  29. mirix/schemas/mirix_base.py +107 -0
  30. mirix/schemas/mirix_message.py +411 -0
  31. mirix/schemas/mirix_message_content.py +230 -0
  32. mirix/schemas/mirix_request.py +39 -0
  33. mirix/schemas/mirix_response.py +183 -0
  34. mirix/schemas/openai/__init__.py +1 -0
  35. mirix/schemas/openai/chat_completion_request.py +122 -0
  36. mirix/schemas/openai/chat_completion_response.py +144 -0
  37. mirix/schemas/openai/chat_completions.py +127 -0
  38. mirix/schemas/openai/embedding_response.py +11 -0
  39. mirix/schemas/openai/openai.py +229 -0
  40. mirix/schemas/organization.py +38 -0
  41. mirix/schemas/procedural_memory.py +151 -0
  42. mirix/schemas/providers.py +816 -0
  43. mirix/schemas/resource_memory.py +134 -0
  44. mirix/schemas/sandbox_config.py +132 -0
  45. mirix/schemas/semantic_memory.py +162 -0
  46. mirix/schemas/source.py +96 -0
  47. mirix/schemas/step.py +53 -0
  48. mirix/schemas/tool.py +241 -0
  49. mirix/schemas/tool_rule.py +209 -0
  50. mirix/schemas/usage.py +31 -0
  51. mirix/schemas/user.py +67 -0
@@ -0,0 +1,230 @@
1
+ from enum import Enum
2
+ from typing import Annotated, Literal, Optional, Union
3
+
4
+ from pydantic import BaseModel, Field
5
+
6
+
7
+ class MessageContentType(str, Enum):
8
+ text = "text"
9
+ image_url = "image_url"
10
+ file_uri = "file_uri"
11
+ google_cloud_file_uri = "google_cloud_file_uri"
12
+ tool_call = "tool_call"
13
+ tool_return = "tool_return"
14
+ reasoning = "reasoning"
15
+ redacted_reasoning = "redacted_reasoning"
16
+ omitted_reasoning = "omitted_reasoning"
17
+
18
+
19
+ class MessageContent(BaseModel):
20
+ type: MessageContentType = Field(..., description="The type of the message.")
21
+
22
+
23
+ # -------------------------------
24
+ # User Content Types
25
+ # -------------------------------
26
+
27
+
28
+ class TextContent(MessageContent):
29
+ type: Literal[MessageContentType.text] = Field(
30
+ MessageContentType.text, description="The type of the message."
31
+ )
32
+ text: str = Field(..., description="The text content of the message.")
33
+
34
+
35
+ class ImageContent(MessageContent):
36
+ type: Literal[MessageContentType.image_url] = Field(
37
+ MessageContentType.image_url, description="The type of the message."
38
+ )
39
+ image_id: str = Field(..., description="The id of the image in the database")
40
+ detail: Optional[str] = Field(None, description="The detail of the image.")
41
+
42
+
43
+ class FileContent(MessageContent):
44
+ type: Literal[MessageContentType.file_uri] = Field(
45
+ MessageContentType.file_uri, description="The type of the message."
46
+ )
47
+ file_id: str = Field(..., description="The id of the file in the database")
48
+
49
+
50
+ class CloudFileContent(MessageContent):
51
+ type: Literal[MessageContentType.google_cloud_file_uri] = Field(
52
+ MessageContentType.google_cloud_file_uri, description="The type of the message."
53
+ )
54
+ cloud_file_uri: str = Field(..., description="The URI of the file in the database")
55
+
56
+
57
+ MirixUserMessageContentUnion = Annotated[
58
+ Union[TextContent, ImageContent, FileContent, CloudFileContent],
59
+ Field(discriminator="type"),
60
+ ]
61
+
62
+
63
+ def create_mirix_user_message_content_union_schema():
64
+ return {
65
+ "oneOf": [
66
+ {"$ref": "#/components/schemas/TextContent"},
67
+ ],
68
+ "discriminator": {
69
+ "propertyName": "type",
70
+ "mapping": {
71
+ "text": "#/components/schemas/TextContent",
72
+ "image": "#/components/schemas/ImageContent", # TODO: (yu) Not sure about this
73
+ },
74
+ },
75
+ }
76
+
77
+
78
+ def get_mirix_user_message_content_union_str_json_schema():
79
+ return {
80
+ "anyOf": [
81
+ {
82
+ "type": "array",
83
+ "items": {
84
+ "$ref": "#/components/schemas/MirixUserMessageContentUnion",
85
+ },
86
+ },
87
+ {"type": "string"},
88
+ ],
89
+ }
90
+
91
+
92
+ # -------------------------------
93
+ # Assistant Content Types
94
+ # -------------------------------
95
+
96
+
97
+ MirixAssistantMessageContentUnion = Annotated[
98
+ Union[TextContent],
99
+ Field(discriminator="type"),
100
+ ]
101
+
102
+
103
+ def create_mirix_assistant_message_content_union_schema():
104
+ return {
105
+ "oneOf": [
106
+ {"$ref": "#/components/schemas/TextContent"},
107
+ ],
108
+ "discriminator": {
109
+ "propertyName": "type",
110
+ "mapping": {
111
+ "text": "#/components/schemas/TextContent",
112
+ },
113
+ },
114
+ }
115
+
116
+
117
+ def get_mirix_assistant_message_content_union_str_json_schema():
118
+ return {
119
+ "anyOf": [
120
+ {
121
+ "type": "array",
122
+ "items": {
123
+ "$ref": "#/components/schemas/MirixAssistantMessageContentUnion",
124
+ },
125
+ },
126
+ {"type": "string"},
127
+ ],
128
+ }
129
+
130
+
131
+ # -------------------------------
132
+ # Intermediate Step Content Types
133
+ # -------------------------------
134
+
135
+
136
+ class ToolCallContent(MessageContent):
137
+ type: Literal[MessageContentType.tool_call] = Field(
138
+ MessageContentType.tool_call,
139
+ description="Indicates this content represents a tool call event.",
140
+ )
141
+ id: str = Field(
142
+ ..., description="A unique identifier for this specific tool call instance."
143
+ )
144
+ name: str = Field(..., description="The name of the tool being called.")
145
+ input: dict = Field(
146
+ ...,
147
+ description="The parameters being passed to the tool, structured as a dictionary of parameter names to values.",
148
+ )
149
+
150
+
151
+ class ToolReturnContent(MessageContent):
152
+ type: Literal[MessageContentType.tool_return] = Field(
153
+ MessageContentType.tool_return,
154
+ description="Indicates this content represents a tool return event.",
155
+ )
156
+ tool_call_id: str = Field(
157
+ ...,
158
+ description="References the ID of the ToolCallContent that initiated this tool call.",
159
+ )
160
+ content: str = Field(..., description="The content returned by the tool execution.")
161
+ is_error: bool = Field(
162
+ ..., description="Indicates whether the tool execution resulted in an error."
163
+ )
164
+
165
+
166
+ class ReasoningContent(MessageContent):
167
+ type: Literal[MessageContentType.reasoning] = Field(
168
+ MessageContentType.reasoning,
169
+ description="Indicates this is a reasoning/intermediate step.",
170
+ )
171
+ is_native: bool = Field(
172
+ ...,
173
+ description="Whether the reasoning content was generated by a reasoner model that processed this step.",
174
+ )
175
+ reasoning: str = Field(
176
+ ..., description="The intermediate reasoning or thought process content."
177
+ )
178
+ signature: Optional[str] = Field(
179
+ None, description="A unique identifier for this reasoning step."
180
+ )
181
+
182
+
183
+ class RedactedReasoningContent(MessageContent):
184
+ type: Literal[MessageContentType.redacted_reasoning] = Field(
185
+ MessageContentType.redacted_reasoning,
186
+ description="Indicates this is a redacted thinking step.",
187
+ )
188
+ data: str = Field(
189
+ ..., description="The redacted or filtered intermediate reasoning content."
190
+ )
191
+
192
+
193
+ class OmittedReasoningContent(MessageContent):
194
+ type: Literal[MessageContentType.omitted_reasoning] = Field(
195
+ MessageContentType.omitted_reasoning,
196
+ description="Indicates this is an omitted reasoning step.",
197
+ )
198
+ tokens: int = Field(
199
+ ..., description="The reasoning token count for intermediate reasoning content."
200
+ )
201
+
202
+
203
+ MirixMessageContentUnion = Annotated[
204
+ Union[
205
+ TextContent,
206
+ ImageContent,
207
+ FileContent,
208
+ CloudFileContent,
209
+ ToolCallContent,
210
+ ToolReturnContent,
211
+ ReasoningContent,
212
+ RedactedReasoningContent,
213
+ OmittedReasoningContent,
214
+ ],
215
+ Field(discriminator="type"),
216
+ ]
217
+
218
+
219
+ def get_mirix_message_content_union_str_json_schema():
220
+ return {
221
+ "anyOf": [
222
+ {
223
+ "type": "array",
224
+ "items": {
225
+ "$ref": "#/components/schemas/MiriMessageContentUnion",
226
+ },
227
+ },
228
+ {"type": "string"},
229
+ ],
230
+ }
@@ -0,0 +1,39 @@
1
+ from typing import List
2
+
3
+ from pydantic import BaseModel, Field
4
+
5
+ from mirix.constants import DEFAULT_MESSAGE_TOOL, DEFAULT_MESSAGE_TOOL_KWARG
6
+ from mirix.schemas.message import MessageCreate
7
+
8
+
9
+ class MirixRequestConfig(BaseModel):
10
+ # Flags to support the use of AssistantMessage message types
11
+ use_assistant_message: bool = Field(
12
+ default=True,
13
+ description="Whether the server should parse specific tool call arguments (default `send_message`) as `AssistantMessage` objects.",
14
+ )
15
+ assistant_message_tool_name: str = Field(
16
+ default=DEFAULT_MESSAGE_TOOL,
17
+ description="The name of the designated message tool.",
18
+ )
19
+ assistant_message_tool_kwarg: str = Field(
20
+ default=DEFAULT_MESSAGE_TOOL_KWARG,
21
+ description="The name of the message argument in the designated message tool.",
22
+ )
23
+
24
+
25
+ class MirixRequest(BaseModel):
26
+ messages: List[MessageCreate] = Field(
27
+ ..., description="The messages to be sent to the agent."
28
+ )
29
+ config: MirixRequestConfig = Field(
30
+ default=MirixRequestConfig(),
31
+ description="Configuration options for the MirixRequest.",
32
+ )
33
+
34
+
35
+ class MirixStreamingRequest(MirixRequest):
36
+ stream_tokens: bool = Field(
37
+ default=False,
38
+ description="Flag to determine if individual tokens should be streamed. Set to True for token streaming (requires stream_steps = True).",
39
+ )
@@ -0,0 +1,183 @@
1
+ import html
2
+ import json
3
+ import re
4
+ from typing import List, Union
5
+
6
+ from pydantic import BaseModel, Field
7
+
8
+ from mirix.schemas.enums import MessageStreamStatus
9
+ from mirix.schemas.mirix_message import MirixMessage, MirixMessageUnion
10
+ from mirix.schemas.usage import MirixUsageStatistics
11
+ from mirix.utils import json_dumps
12
+
13
+ # TODO: consider moving into own file
14
+
15
+
16
+ class MirixResponse(BaseModel):
17
+ """
18
+ Response object from an agent interaction, consisting of the new messages generated by the agent and usage statistics.
19
+ The type of the returned messages can be either `Message` or `MirixMessage`, depending on what was specified in the request.
20
+
21
+ Attributes:
22
+ messages (List[Union[Message, MirixMessage]]): The messages returned by the agent.
23
+ usage (MirixUsageStatistics): The usage statistics
24
+ """
25
+
26
+ messages: List[MirixMessageUnion] = Field(
27
+ ...,
28
+ description="The messages returned by the agent.",
29
+ json_schema_extra={
30
+ "items": {
31
+ "$ref": "#/components/schemas/MirixMessageUnion",
32
+ }
33
+ },
34
+ )
35
+ usage: MirixUsageStatistics = Field(
36
+ ...,
37
+ description="The usage statistics of the agent.",
38
+ )
39
+
40
+ def __str__(self):
41
+ return json_dumps(
42
+ {
43
+ "messages": [message.model_dump() for message in self.messages],
44
+ # Assume `Message` and `MirixMessage` have a `dict()` method
45
+ "usage": self.usage.model_dump(), # Assume `MirixUsageStatistics` has a `dict()` method
46
+ },
47
+ indent=4,
48
+ )
49
+
50
+ def _repr_html_(self):
51
+ def get_formatted_content(msg):
52
+ if msg.message_type == "internal_monologue":
53
+ return f'<div class="content"><span class="internal-monologue">{html.escape(msg.internal_monologue)}</span></div>'
54
+ if msg.message_type == "reasoning_message":
55
+ return f'<div class="content"><span class="internal-monologue">{html.escape(msg.reasoning)}</span></div>'
56
+ elif msg.message_type == "function_call":
57
+ args = format_json(msg.function_call.arguments)
58
+ return f'<div class="content"><span class="function-name">{html.escape(msg.function_call.name)}</span>({args})</div>'
59
+ elif msg.message_type == "tool_call_message":
60
+ args = format_json(msg.tool_call.arguments)
61
+ return f'<div class="content"><span class="function-name">{html.escape(msg.tool_call.name)}</span>({args})</div>'
62
+ elif msg.message_type == "function_return":
63
+ return_value = format_json(msg.function_return)
64
+ # return f'<div class="status-line">Status: {html.escape(msg.status)}</div><div class="content">{return_value}</div>'
65
+ return f'<div class="content">{return_value}</div>'
66
+ elif msg.message_type == "tool_return_message":
67
+ return_value = format_json(msg.tool_return)
68
+ # return f'<div class="status-line">Status: {html.escape(msg.status)}</div><div class="content">{return_value}</div>'
69
+ return f'<div class="content">{return_value}</div>'
70
+ elif msg.message_type == "user_message":
71
+ if is_json(msg.message):
72
+ return f'<div class="content">{format_json(msg.message)}</div>'
73
+ else:
74
+ return f'<div class="content">{html.escape(msg.message)}</div>'
75
+ elif msg.message_type in ["assistant_message", "system_message"]:
76
+ return f'<div class="content">{html.escape(msg.message)}</div>'
77
+ else:
78
+ return f'<div class="content">{html.escape(str(msg))}</div>'
79
+
80
+ def is_json(string):
81
+ try:
82
+ json.loads(string)
83
+ return True
84
+ except ValueError:
85
+ return False
86
+
87
+ def format_json(json_str):
88
+ try:
89
+ parsed = json.loads(json_str)
90
+ formatted = json.dumps(parsed, indent=2, ensure_ascii=False)
91
+ formatted = (
92
+ formatted.replace("&", "&amp;")
93
+ .replace("<", "&lt;")
94
+ .replace(">", "&gt;")
95
+ )
96
+ formatted = formatted.replace("\n", "<br>").replace(
97
+ " ", "&nbsp;&nbsp;"
98
+ )
99
+ formatted = re.sub(
100
+ r'(".*?"):', r'<span class="json-key">\1</span>:', formatted
101
+ )
102
+ formatted = re.sub(
103
+ r': (".*?")', r': <span class="json-string">\1</span>', formatted
104
+ )
105
+ formatted = re.sub(
106
+ r": (\d+)", r': <span class="json-number">\1</span>', formatted
107
+ )
108
+ formatted = re.sub(
109
+ r": (true|false)",
110
+ r': <span class="json-boolean">\1</span>',
111
+ formatted,
112
+ )
113
+ return formatted
114
+ except json.JSONDecodeError:
115
+ return html.escape(json_str)
116
+
117
+ html_output = """
118
+ <style>
119
+ .message-container, .usage-container {
120
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
121
+ max-width: 800px;
122
+ margin: 20px auto;
123
+ background-color: #1e1e1e;
124
+ border-radius: 8px;
125
+ overflow: hidden;
126
+ color: #d4d4d4;
127
+ }
128
+ .message, .usage-stats {
129
+ padding: 10px 15px;
130
+ border-bottom: 1px solid #3a3a3a;
131
+ }
132
+ .message:last-child, .usage-stats:last-child {
133
+ border-bottom: none;
134
+ }
135
+ .title {
136
+ font-weight: bold;
137
+ margin-bottom: 5px;
138
+ color: #ffffff;
139
+ text-transform: uppercase;
140
+ font-size: 0.9em;
141
+ }
142
+ .content {
143
+ background-color: #2d2d2d;
144
+ border-radius: 4px;
145
+ padding: 5px 10px;
146
+ font-family: 'Consolas', 'Courier New', monospace;
147
+ white-space: pre-wrap;
148
+ }
149
+ .json-key, .function-name, .json-boolean { color: #9cdcfe; }
150
+ .json-string { color: #ce9178; }
151
+ .json-number { color: #b5cea8; }
152
+ .internal-monologue { font-style: italic; }
153
+ </style>
154
+ <div class="message-container">
155
+ """
156
+
157
+ for msg in self.messages:
158
+ content = get_formatted_content(msg)
159
+ title = msg.message_type.replace("_", " ").upper()
160
+ html_output += f"""
161
+ <div class="message">
162
+ <div class="title">{title}</div>
163
+ {content}
164
+ </div>
165
+ """
166
+ html_output += "</div>"
167
+
168
+ # Formatting the usage statistics
169
+ usage_html = json.dumps(self.usage.model_dump(), indent=2)
170
+ html_output += f"""
171
+ <div class="usage-container">
172
+ <div class="usage-stats">
173
+ <div class="title">USAGE STATISTICS</div>
174
+ <div class="content">{format_json(usage_html)}</div>
175
+ </div>
176
+ </div>
177
+ """
178
+
179
+ return html_output
180
+
181
+
182
+ # The streaming response is either [DONE], [DONE_STEP], [DONE], an error, or a MirixMessage
183
+ MirixStreamingResponse = Union[MirixMessage, MessageStreamStatus, MirixUsageStatistics]
@@ -0,0 +1 @@
1
+ # OpenAI schema package
@@ -0,0 +1,122 @@
1
+ from typing import Any, Dict, List, Literal, Optional, Union
2
+
3
+ from pydantic import BaseModel, Field
4
+
5
+
6
+ class SystemMessage(BaseModel):
7
+ content: str
8
+ role: str = "system"
9
+ name: Optional[str] = None
10
+
11
+
12
+ class UserMessage(BaseModel):
13
+ content: Union[str, List[dict]]
14
+ role: str = "user"
15
+ name: Optional[str] = None
16
+
17
+
18
+ class ToolCallFunction(BaseModel):
19
+ name: str
20
+ arguments: str
21
+
22
+
23
+ class ToolCall(BaseModel):
24
+ id: str
25
+ type: Literal["function"] = "function"
26
+ function: ToolCallFunction
27
+
28
+
29
+ class AssistantMessage(BaseModel):
30
+ content: Optional[str] = None
31
+ role: str = "assistant"
32
+ name: Optional[str] = None
33
+ tool_calls: Optional[List[ToolCall]] = None
34
+
35
+
36
+ class ToolMessage(BaseModel):
37
+ content: str
38
+ role: str = "tool"
39
+ tool_call_id: str
40
+
41
+
42
+ ChatMessage = Union[SystemMessage, UserMessage, AssistantMessage, ToolMessage]
43
+
44
+
45
+ # TODO: this might not be necessary with the validator
46
+ def cast_message_to_subtype(m_dict: dict) -> ChatMessage:
47
+ """Cast a dictionary to one of the individual message types"""
48
+ role = m_dict.get("role")
49
+ if role == "system" or role == "developer":
50
+ return SystemMessage(**m_dict)
51
+ elif role == "user":
52
+ return UserMessage(**m_dict)
53
+ elif role == "assistant":
54
+ return AssistantMessage(**m_dict)
55
+ elif role == "tool":
56
+ return ToolMessage(**m_dict)
57
+ else:
58
+ raise ValueError(f"Unknown message role: {role}")
59
+
60
+
61
+ class ResponseFormat(BaseModel):
62
+ type: str = Field(default="text", pattern="^(text|json_object)$")
63
+
64
+
65
+ ## tool_choice ##
66
+ class FunctionCall(BaseModel):
67
+ name: str
68
+
69
+
70
+ class ToolFunctionChoice(BaseModel):
71
+ # The type of the tool. Currently, only function is supported
72
+ type: Literal["function"] = "function"
73
+ # type: str = Field(default="function", const=True)
74
+ function: FunctionCall
75
+
76
+
77
+ ToolChoice = Union[Literal["none", "auto", "required"], ToolFunctionChoice]
78
+
79
+
80
+ ## tools ##
81
+ class FunctionSchema(BaseModel):
82
+ name: str
83
+ description: Optional[str] = None
84
+ parameters: Optional[Dict[str, Any]] = None # JSON Schema for the parameters
85
+
86
+
87
+ class Tool(BaseModel):
88
+ # The type of the tool. Currently, only function is supported
89
+ type: Literal["function"] = "function"
90
+ # type: str = Field(default="function", const=True)
91
+ function: FunctionSchema
92
+
93
+
94
+ ## function_call ##
95
+ FunctionCallChoice = Union[Literal["none", "auto"], FunctionCall]
96
+
97
+
98
+ class ChatCompletionRequest(BaseModel):
99
+ """https://platform.openai.com/docs/api-reference/chat/create"""
100
+
101
+ model: str
102
+ messages: List[ChatMessage]
103
+ frequency_penalty: Optional[float] = 0
104
+ logit_bias: Optional[Dict[str, int]] = None
105
+ logprobs: Optional[bool] = False
106
+ top_logprobs: Optional[int] = None
107
+ max_tokens: Optional[int] = None
108
+ n: Optional[int] = 1
109
+ presence_penalty: Optional[float] = 0
110
+ response_format: Optional[ResponseFormat] = None
111
+ seed: Optional[int] = None
112
+ stop: Optional[Union[str, List[str]]] = None
113
+ stream: Optional[bool] = False
114
+ temperature: Optional[float] = 1 # TODO: might need to add logics to control this
115
+ top_p: Optional[float] = 1
116
+
117
+ # function-calling related
118
+ tools: Optional[List[Tool]] = None
119
+ tool_choice: Optional[ToolChoice] = None # "none" means don't call a tool
120
+ # deprecated scheme
121
+ functions: Optional[List[FunctionSchema]] = None
122
+ function_call: Optional[FunctionCallChoice] = None