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,318 @@
1
+ from typing import TYPE_CHECKING, List, Optional
2
+
3
+ from jinja2 import Environment, Template, TemplateSyntaxError
4
+ from pydantic import BaseModel, Field
5
+
6
+ # Forward referencing to avoid circular import with Agent -> Memory -> Agent
7
+ if TYPE_CHECKING:
8
+ from mirix.schemas.agent import AgentState
9
+
10
+ from mirix.constants import CORE_MEMORY_BLOCK_CHAR_LIMIT
11
+ from mirix.schemas.block import Block
12
+ from mirix.schemas.message import Message
13
+ from mirix.schemas.openai.chat_completion_request import Tool
14
+ from mirix.schemas.user import User as PydanticUser
15
+
16
+
17
+ class ContextWindowOverview(BaseModel):
18
+ """
19
+ Overview of the context window, including the number of messages and tokens.
20
+ """
21
+
22
+ # top-level information
23
+ context_window_size_max: int = Field(
24
+ ..., description="The maximum amount of tokens the context window can hold."
25
+ )
26
+ context_window_size_current: int = Field(
27
+ ..., description="The current number of tokens in the context window."
28
+ )
29
+
30
+ # context window breakdown (in messages)
31
+ # (technically not in the context window, but useful to know)
32
+ num_messages: int = Field(
33
+ ..., description="The number of messages in the context window."
34
+ )
35
+ num_archival_memory: int = Field(
36
+ ..., description="The number of messages in the archival memory."
37
+ )
38
+ num_recall_memory: int = Field(
39
+ ..., description="The number of messages in the recall memory."
40
+ )
41
+ num_tokens_external_memory_summary: int = Field(
42
+ ...,
43
+ description="The number of tokens in the external memory summary (archival + recall metadata).",
44
+ )
45
+ external_memory_summary: str = Field(
46
+ ...,
47
+ description="The metadata summary of the external memory sources (archival + recall metadata).",
48
+ )
49
+
50
+ # context window breakdown (in tokens)
51
+ # this should all add up to context_window_size_current
52
+
53
+ num_tokens_system: int = Field(
54
+ ..., description="The number of tokens in the system prompt."
55
+ )
56
+ system_prompt: str = Field(..., description="The content of the system prompt.")
57
+
58
+ num_tokens_core_memory: int = Field(
59
+ ..., description="The number of tokens in the core memory."
60
+ )
61
+ core_memory: str = Field(..., description="The content of the core memory.")
62
+
63
+ num_tokens_summary_memory: int = Field(
64
+ ..., description="The number of tokens in the summary memory."
65
+ )
66
+ summary_memory: Optional[str] = Field(
67
+ None, description="The content of the summary memory."
68
+ )
69
+
70
+ num_tokens_functions_definitions: int = Field(
71
+ ..., description="The number of tokens in the functions definitions."
72
+ )
73
+ functions_definitions: Optional[List[Tool]] = Field(
74
+ ..., description="The content of the functions definitions."
75
+ )
76
+
77
+ num_tokens_messages: int = Field(
78
+ ..., description="The number of tokens in the messages list."
79
+ )
80
+ # TODO make list of messages?
81
+ # messages: List[dict] = Field(..., description="The messages in the context window.")
82
+ messages: List[Message] = Field(
83
+ ..., description="The messages in the context window."
84
+ )
85
+
86
+
87
+ def line_numbers(value: str, prefix: str = "Line ") -> str:
88
+ """
89
+ Turn
90
+ "a\nb"
91
+ into
92
+ "Line 1:\ta\nLine 2:\tb"
93
+ """
94
+ return "\n".join(
95
+ f"{prefix}{idx + 1}:\t{line}" for idx, line in enumerate(value.splitlines())
96
+ )
97
+
98
+
99
+ # Build an environment and add a custom filter
100
+ env = Environment()
101
+ env.filters["line_numbers"] = line_numbers
102
+
103
+
104
+ class Memory(BaseModel, validate_assignment=True):
105
+ """
106
+
107
+ Represents the in-context memory (i.e. Core memory) of the agent. This includes both the `Block` objects (labelled by sections), as well as tools to edit the blocks.
108
+
109
+ """
110
+
111
+ # Memory.block contains the list of memory blocks in the core memory
112
+ blocks: List[Block] = Field(
113
+ ..., description="Memory blocks contained in the agent's in-context memory"
114
+ )
115
+
116
+ # Memory.template is a Jinja2 template for compiling memory module into a prompt string.
117
+ prompt_template: str = Field(
118
+ default="{% for block in blocks %}"
119
+ '<{{ block.label }} characters="{{ block.value|length }}/{{ block.limit }}">\n'
120
+ "{{ block.value|line_numbers }}\n"
121
+ "</{{ block.label }}>"
122
+ "{% if not loop.last %}\n{% endif %}"
123
+ "{% endfor %}",
124
+ description="Jinja2 template for compiling memory blocks into a prompt string",
125
+ )
126
+
127
+ def get_prompt_template(self) -> str:
128
+ """Return the current Jinja2 template string."""
129
+ return str(self.prompt_template)
130
+
131
+ def set_prompt_template(self, prompt_template: str):
132
+ """
133
+ Set a new Jinja2 template string.
134
+ Validates the template syntax and compatibility with current memory structure.
135
+ """
136
+ try:
137
+ # Validate Jinja2 syntax
138
+ Template(prompt_template)
139
+
140
+ # Validate compatibility with current memory structure
141
+ # Defensive: Handle None blocks
142
+ blocks = self.blocks if self.blocks is not None else []
143
+ Template(prompt_template).render(blocks=blocks)
144
+
145
+ # If we get here, the template is valid and compatible
146
+ self.prompt_template = prompt_template
147
+ except TemplateSyntaxError as e:
148
+ raise ValueError(f"Invalid Jinja2 template syntax: {str(e)}")
149
+ except Exception as e:
150
+ raise ValueError(
151
+ f"Prompt template is not compatible with current memory structure: {str(e)}"
152
+ )
153
+
154
+ def compile(self) -> str:
155
+ """Generate a string representation of the memory in-context using the Jinja2 template"""
156
+ template = env.from_string(self.prompt_template)
157
+ # Defensive: Handle None blocks (backward compatibility with old cached agents)
158
+ blocks = self.blocks if self.blocks is not None else []
159
+ return template.render(blocks=blocks)
160
+
161
+ def list_block_labels(self) -> List[str]:
162
+ """Return a list of the block names held inside the memory object"""
163
+ # return list(self.memory.keys())
164
+ # Defensive: Handle None blocks
165
+ if self.blocks is None:
166
+ return []
167
+ return [block.label for block in self.blocks]
168
+
169
+ # TODO: these should actually be label, not name
170
+ def get_block(self, label: str) -> Block:
171
+ """Correct way to index into the memory.memory field, returns a Block"""
172
+ keys = []
173
+ # Defensive: Handle None blocks
174
+ blocks = self.blocks if self.blocks is not None else []
175
+ for block in blocks:
176
+ if block.label == label:
177
+ return block
178
+ keys.append(block.label)
179
+ raise KeyError(
180
+ f"Block field {label} does not exist (available sections = {', '.join(keys)})"
181
+ )
182
+
183
+ def get_blocks(self) -> List[Block]:
184
+ """Return a list of the blocks held inside the memory object"""
185
+ # return list(self.memory.values())
186
+ # Defensive: Handle None blocks
187
+ return self.blocks if self.blocks is not None else []
188
+
189
+ def set_block(self, block: Block):
190
+ """Set a block in the memory object"""
191
+ # Defensive: Handle None blocks - initialize if needed
192
+ if self.blocks is None:
193
+ self.blocks = []
194
+ for i, b in enumerate(self.blocks):
195
+ if b.label == block.label:
196
+ self.blocks[i] = block
197
+ return
198
+ self.blocks.append(block)
199
+
200
+ def update_block_value(self, label: str, value: str):
201
+ """Update the value of a block"""
202
+ if not isinstance(value, str):
203
+ raise ValueError("Provided value must be a string")
204
+
205
+ # Defensive: Handle None blocks - initialize if needed
206
+ if self.blocks is None:
207
+ self.blocks = []
208
+
209
+ for block in self.blocks:
210
+ if block.label == label:
211
+ block.value = value
212
+ return
213
+ raise ValueError(f"Block with label {label} does not exist")
214
+
215
+
216
+ # TODO: ideally this is refactored into ChatMemory and the subclasses are given more specific names.
217
+ class BasicBlockMemory(Memory):
218
+ """
219
+ BasicBlockMemory is a basic implemention of the Memory class, which takes in a list of blocks and links them to the memory object. These are editable by the agent via the core memory functions.
220
+
221
+ Attributes:
222
+ memory (Dict[str, Block]): Mapping from memory block section to memory block.
223
+
224
+ Methods:
225
+ core_memory_append: Append to the contents of core memory.
226
+ core_memory_rewrite: Rewrite the contents of core memory.
227
+ """
228
+
229
+ def __init__(self, blocks: List[Block] = []):
230
+ """
231
+ Initialize the BasicBlockMemory object with a list of pre-defined blocks.
232
+
233
+ Args:
234
+ blocks (List[Block]): List of blocks to be linked to the memory object.
235
+ """
236
+ super().__init__(blocks=blocks)
237
+
238
+ def core_memory_append(
239
+ agent_state: "AgentState", label: str, content: str
240
+ ) -> Optional[str]: # type: ignore
241
+ """
242
+ Append to the contents of core memory.
243
+
244
+ Args:
245
+ label (str): Section of the memory to be edited (persona or human).
246
+ content (str): Content to write to the memory. All unicode (including emojis) are supported.
247
+
248
+ Returns:
249
+ Optional[str]: None is always returned as this function does not produce a response.
250
+ """
251
+ current_value = str(agent_state.memory.get_block(label).value)
252
+ new_value = current_value + "\n" + str(content)
253
+ agent_state.memory.update_block_value(label=label, value=new_value)
254
+ return None
255
+
256
+ # def core_memory_replace(agent_state: "AgentState", label: str, old_content: str, new_content: str) -> Optional[str]: # type: ignore
257
+ # """
258
+ # Replace the contents of core memory. To delete memories, use an empty string for new_content.
259
+
260
+ # Args:
261
+ # label (str): Section of the memory to be edited (persona or human).
262
+ # old_content (str): String to replace. Must be an exact match.
263
+ # new_content (str): Content to write to the memory. All unicode (including emojis) are supported.
264
+
265
+ # Returns:
266
+ # Optional[str]: None is always returned as this function does not produce a response.
267
+ # """
268
+ # current_value = str(agent_state.memory.get_block(label).value)
269
+ # if old_content not in current_value:
270
+ # raise ValueError(f"Old content '{old_content}' not found in memory block '{label}'")
271
+ # new_value = current_value.replace(str(old_content), str(new_content))
272
+ # agent_state.memory.update_block_value(label=label, value=new_value)
273
+ # return None
274
+
275
+
276
+ class ChatMemory(BasicBlockMemory):
277
+ """
278
+ ChatMemory initializes a BaseChatMemory with two default blocks, `human` and `persona`.
279
+ """
280
+
281
+ def __init__(
282
+ self,
283
+ persona: str,
284
+ human: str,
285
+ actor: PydanticUser,
286
+ limit: int = CORE_MEMORY_BLOCK_CHAR_LIMIT,
287
+ ):
288
+ """
289
+ Initialize the ChatMemory object with a persona and human string.
290
+
291
+ Args:
292
+ persona (str): The starter value for the persona block.
293
+ human (str): The starter value for the human block.
294
+ limit (int): The character limit for each block.
295
+ """
296
+ # TODO: Should these be CreateBlocks?
297
+ super().__init__(
298
+ blocks=[
299
+ Block(value=persona, limit=limit, label="persona", user_id=actor.id),
300
+ Block(value=human, limit=limit, label="human", user_id=actor.id),
301
+ ]
302
+ )
303
+
304
+
305
+ class UpdateMemory(BaseModel):
306
+ """Update the memory of the agent"""
307
+
308
+
309
+ class ArchivalMemorySummary(BaseModel):
310
+ size: int = Field(..., description="Number of rows in archival memory")
311
+
312
+
313
+ class RecallMemorySummary(BaseModel):
314
+ size: int = Field(..., description="Number of rows in recall memory")
315
+
316
+
317
+ class CreateArchivalMemory(BaseModel):
318
+ text: str = Field(..., description="Text to write to archival memory.")