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
|
@@ -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("&", "&")
|
|
93
|
+
.replace("<", "<")
|
|
94
|
+
.replace(">", ">")
|
|
95
|
+
)
|
|
96
|
+
formatted = formatted.replace("\n", "<br>").replace(
|
|
97
|
+
" ", " "
|
|
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
|