letta-nightly 0.11.7.dev20250916104104__py3-none-any.whl → 0.11.7.dev20250918104055__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.
- letta/__init__.py +10 -2
- letta/adapters/letta_llm_request_adapter.py +0 -1
- letta/adapters/letta_llm_stream_adapter.py +0 -1
- letta/agent.py +4 -4
- letta/agents/agent_loop.py +2 -1
- letta/agents/base_agent.py +1 -1
- letta/agents/letta_agent.py +1 -4
- letta/agents/letta_agent_v2.py +5 -4
- letta/agents/temporal/activities/__init__.py +4 -0
- letta/agents/temporal/activities/example_activity.py +7 -0
- letta/agents/temporal/activities/prepare_messages.py +10 -0
- letta/agents/temporal/temporal_agent_workflow.py +56 -0
- letta/agents/temporal/types.py +25 -0
- letta/agents/voice_agent.py +3 -3
- letta/helpers/converters.py +8 -2
- letta/helpers/crypto_utils.py +144 -0
- letta/llm_api/llm_api_tools.py +0 -1
- letta/llm_api/llm_client_base.py +0 -2
- letta/orm/__init__.py +1 -0
- letta/orm/agent.py +9 -4
- letta/orm/job.py +3 -1
- letta/orm/mcp_oauth.py +6 -0
- letta/orm/mcp_server.py +7 -1
- letta/orm/sqlalchemy_base.py +2 -1
- letta/prompts/prompt_generator.py +4 -4
- letta/schemas/agent.py +14 -200
- letta/schemas/enums.py +15 -0
- letta/schemas/job.py +10 -0
- letta/schemas/mcp.py +146 -6
- letta/schemas/memory.py +216 -103
- letta/schemas/provider_trace.py +0 -2
- letta/schemas/run.py +2 -0
- letta/schemas/secret.py +378 -0
- letta/schemas/step.py +5 -1
- letta/schemas/tool_rule.py +34 -44
- letta/serialize_schemas/marshmallow_agent.py +4 -0
- letta/server/rest_api/routers/v1/__init__.py +2 -0
- letta/server/rest_api/routers/v1/agents.py +9 -4
- letta/server/rest_api/routers/v1/archives.py +113 -0
- letta/server/rest_api/routers/v1/jobs.py +7 -2
- letta/server/rest_api/routers/v1/runs.py +9 -1
- letta/server/rest_api/routers/v1/steps.py +29 -0
- letta/server/rest_api/routers/v1/tools.py +7 -26
- letta/server/server.py +2 -2
- letta/services/agent_manager.py +21 -15
- letta/services/agent_serialization_manager.py +11 -3
- letta/services/archive_manager.py +73 -0
- letta/services/helpers/agent_manager_helper.py +10 -5
- letta/services/job_manager.py +18 -2
- letta/services/mcp_manager.py +198 -82
- letta/services/step_manager.py +26 -0
- letta/services/summarizer/summarizer.py +25 -3
- letta/services/telemetry_manager.py +2 -0
- letta/services/tool_executor/composio_tool_executor.py +1 -1
- letta/services/tool_executor/sandbox_tool_executor.py +2 -2
- letta/services/tool_sandbox/base.py +135 -9
- letta/settings.py +2 -2
- {letta_nightly-0.11.7.dev20250916104104.dist-info → letta_nightly-0.11.7.dev20250918104055.dist-info}/METADATA +6 -3
- {letta_nightly-0.11.7.dev20250916104104.dist-info → letta_nightly-0.11.7.dev20250918104055.dist-info}/RECORD +62 -55
- letta/templates/template_helper.py +0 -53
- {letta_nightly-0.11.7.dev20250916104104.dist-info → letta_nightly-0.11.7.dev20250918104055.dist-info}/WHEEL +0 -0
- {letta_nightly-0.11.7.dev20250916104104.dist-info → letta_nightly-0.11.7.dev20250918104055.dist-info}/entry_points.txt +0 -0
- {letta_nightly-0.11.7.dev20250916104104.dist-info → letta_nightly-0.11.7.dev20250918104055.dist-info}/licenses/LICENSE +0 -0
letta/schemas/memory.py
CHANGED
@@ -1,20 +1,17 @@
|
|
1
1
|
import asyncio
|
2
2
|
import logging
|
3
3
|
from datetime import datetime
|
4
|
-
from
|
5
|
-
|
6
|
-
from jinja2 import Template, TemplateSyntaxError
|
7
|
-
from pydantic import BaseModel, Field, field_validator
|
8
|
-
|
9
|
-
# Forward referencing to avoid circular import with Agent -> Memory -> Agent
|
10
|
-
if TYPE_CHECKING:
|
11
|
-
pass
|
4
|
+
from io import StringIO
|
5
|
+
from typing import TYPE_CHECKING, List, Optional, Union
|
12
6
|
|
13
7
|
from openai.types.beta.function_tool import FunctionTool as OpenAITool
|
8
|
+
from pydantic import BaseModel, Field, field_validator
|
14
9
|
|
15
|
-
from letta.constants import CORE_MEMORY_BLOCK_CHAR_LIMIT
|
10
|
+
from letta.constants import CORE_MEMORY_BLOCK_CHAR_LIMIT, CORE_MEMORY_LINE_NUMBER_WARNING
|
16
11
|
from letta.otel.tracing import trace_method
|
17
12
|
from letta.schemas.block import Block, FileBlock
|
13
|
+
from letta.schemas.enums import AgentType
|
14
|
+
from letta.schemas.file import FileStatus
|
18
15
|
from letta.schemas.message import Message
|
19
16
|
|
20
17
|
|
@@ -23,12 +20,9 @@ class ContextWindowOverview(BaseModel):
|
|
23
20
|
Overview of the context window, including the number of messages and tokens.
|
24
21
|
"""
|
25
22
|
|
26
|
-
# top-level information
|
27
23
|
context_window_size_max: int = Field(..., description="The maximum amount of tokens the context window can hold.")
|
28
24
|
context_window_size_current: int = Field(..., description="The current number of tokens in the context window.")
|
29
25
|
|
30
|
-
# context window breakdown (in messages)
|
31
|
-
# (technically not in the context window, but useful to know)
|
32
26
|
num_messages: int = Field(..., description="The number of messages in the context window.")
|
33
27
|
num_archival_memory: int = Field(..., description="The number of messages in the archival memory.")
|
34
28
|
num_recall_memory: int = Field(..., description="The number of messages in the recall memory.")
|
@@ -39,9 +33,6 @@ class ContextWindowOverview(BaseModel):
|
|
39
33
|
..., description="The metadata summary of the external memory sources (archival + recall metadata)."
|
40
34
|
)
|
41
35
|
|
42
|
-
# context window breakdown (in tokens)
|
43
|
-
# this should all add up to context_window_size_current
|
44
|
-
|
45
36
|
num_tokens_system: int = Field(..., description="The number of tokens in the system prompt.")
|
46
37
|
system_prompt: str = Field(..., description="The content of the system prompt.")
|
47
38
|
|
@@ -55,8 +46,6 @@ class ContextWindowOverview(BaseModel):
|
|
55
46
|
functions_definitions: Optional[List[OpenAITool]] = Field(..., description="The content of the functions definitions.")
|
56
47
|
|
57
48
|
num_tokens_messages: int = Field(..., description="The number of tokens in the messages list.")
|
58
|
-
# TODO make list of messages?
|
59
|
-
# messages: List[dict] = Field(..., description="The messages in the context window.")
|
60
49
|
messages: List[Message] = Field(..., description="The messages in the context window.")
|
61
50
|
|
62
51
|
|
@@ -67,7 +56,7 @@ class Memory(BaseModel, validate_assignment=True):
|
|
67
56
|
|
68
57
|
"""
|
69
58
|
|
70
|
-
|
59
|
+
agent_type: Optional[Union["AgentType", str]] = Field(None, description="Agent type controlling prompt rendering.")
|
71
60
|
blocks: List[Block] = Field(..., description="Memory blocks contained in the agent's in-context memory")
|
72
61
|
file_blocks: List[FileBlock] = Field(
|
73
62
|
default_factory=list, description="Special blocks representing the agent's in-context memory of an attached file"
|
@@ -97,111 +86,238 @@ class Memory(BaseModel, validate_assignment=True):
|
|
97
86
|
|
98
87
|
return unique_blocks
|
99
88
|
|
100
|
-
|
101
|
-
prompt_template: str = Field(
|
102
|
-
default="{% for block in blocks %}"
|
103
|
-
"<{{ block.label }}>\n"
|
104
|
-
"<metadata>"
|
105
|
-
'read_only="{{ block.read_only}}" chars_current="{{ block.value|length }}" chars_limit="{{ block.limit }}"'
|
106
|
-
"</metadata>"
|
107
|
-
"<value>"
|
108
|
-
"{{ block.value }}\n"
|
109
|
-
"</value>"
|
110
|
-
"</{{ block.label }}>\n"
|
111
|
-
"{% if not loop.last %}\n{% endif %}"
|
112
|
-
"{% endfor %}",
|
113
|
-
description="Jinja2 template for compiling memory blocks into a prompt string",
|
114
|
-
)
|
89
|
+
prompt_template: str = Field(default="", description="Deprecated. Ignored for performance.")
|
115
90
|
|
116
91
|
def get_prompt_template(self) -> str:
|
117
|
-
"""Return the
|
92
|
+
"""Return the stored (deprecated) prompt template string."""
|
118
93
|
return str(self.prompt_template)
|
119
94
|
|
120
95
|
@trace_method
|
121
96
|
def set_prompt_template(self, prompt_template: str):
|
122
|
-
"""
|
123
|
-
|
124
|
-
Validates the template syntax and compatibility with current memory structure.
|
125
|
-
"""
|
126
|
-
try:
|
127
|
-
# Validate Jinja2 syntax
|
128
|
-
Template(prompt_template)
|
129
|
-
|
130
|
-
# Validate compatibility with current memory structure
|
131
|
-
Template(prompt_template).render(blocks=self.blocks, file_blocks=self.file_blocks, sources=[], max_files_open=None)
|
132
|
-
|
133
|
-
# If we get here, the template is valid and compatible
|
134
|
-
self.prompt_template = prompt_template
|
135
|
-
except TemplateSyntaxError as e:
|
136
|
-
raise ValueError(f"Invalid Jinja2 template syntax: {str(e)}")
|
137
|
-
except Exception as e:
|
138
|
-
raise ValueError(f"Prompt template is not compatible with current memory structure: {str(e)}")
|
97
|
+
"""Deprecated. Stores the provided string but is not used for rendering."""
|
98
|
+
self.prompt_template = prompt_template
|
139
99
|
|
140
100
|
@trace_method
|
141
101
|
async def set_prompt_template_async(self, prompt_template: str):
|
142
|
-
"""
|
143
|
-
|
144
|
-
"""
|
145
|
-
try:
|
146
|
-
# Validate Jinja2 syntax with async enabled
|
147
|
-
Template(prompt_template)
|
148
|
-
|
149
|
-
# Validate compatibility with current memory structure - use async rendering
|
150
|
-
template = Template(prompt_template)
|
151
|
-
await asyncio.to_thread(template.render, blocks=self.blocks, file_blocks=self.file_blocks, sources=[], max_files_open=None)
|
152
|
-
|
153
|
-
# If we get here, the template is valid and compatible
|
154
|
-
self.prompt_template = prompt_template
|
155
|
-
except TemplateSyntaxError as e:
|
156
|
-
raise ValueError(f"Invalid Jinja2 template syntax: {str(e)}")
|
157
|
-
except Exception as e:
|
158
|
-
raise ValueError(f"Prompt template is not compatible with current memory structure: {str(e)}")
|
102
|
+
"""Deprecated. Async setter that stores the string but does not validate or use it."""
|
103
|
+
self.prompt_template = prompt_template
|
159
104
|
|
160
105
|
@trace_method
|
106
|
+
def _render_memory_blocks_standard(self, s: StringIO):
|
107
|
+
if len(self.blocks) == 0:
|
108
|
+
# s.write("<memory_blocks></memory_blocks>") # TODO: consider empty tags
|
109
|
+
s.write("")
|
110
|
+
return
|
111
|
+
|
112
|
+
s.write("<memory_blocks>\nThe following memory blocks are currently engaged in your core memory unit:\n\n")
|
113
|
+
for idx, block in enumerate(self.blocks):
|
114
|
+
label = block.label or "block"
|
115
|
+
value = block.value or ""
|
116
|
+
desc = block.description or ""
|
117
|
+
chars_current = len(value)
|
118
|
+
limit = block.limit if block.limit is not None else 0
|
119
|
+
|
120
|
+
s.write(f"<{label}>\n")
|
121
|
+
s.write("<description>\n")
|
122
|
+
s.write(f"{desc}\n")
|
123
|
+
s.write("</description>\n")
|
124
|
+
s.write("<metadata>")
|
125
|
+
if getattr(block, "read_only", False):
|
126
|
+
s.write("\n- read_only=true")
|
127
|
+
s.write(f"\n- chars_current={chars_current}")
|
128
|
+
s.write(f"\n- chars_limit={limit}\n")
|
129
|
+
s.write("</metadata>\n")
|
130
|
+
s.write("<value>\n")
|
131
|
+
s.write(f"{value}\n")
|
132
|
+
s.write("</value>\n")
|
133
|
+
s.write(f"</{label}>\n")
|
134
|
+
if idx != len(self.blocks) - 1:
|
135
|
+
s.write("\n")
|
136
|
+
s.write("\n</memory_blocks>")
|
137
|
+
|
138
|
+
def _render_memory_blocks_line_numbered(self, s: StringIO):
|
139
|
+
s.write("<memory_blocks>\nThe following memory blocks are currently engaged in your core memory unit:\n\n")
|
140
|
+
for idx, block in enumerate(self.blocks):
|
141
|
+
label = block.label or "block"
|
142
|
+
value = block.value or ""
|
143
|
+
desc = block.description or ""
|
144
|
+
limit = block.limit if block.limit is not None else 0
|
145
|
+
|
146
|
+
s.write(f"<{label}>\n")
|
147
|
+
s.write("<description>\n")
|
148
|
+
s.write(f"{desc}\n")
|
149
|
+
s.write("</description>\n")
|
150
|
+
s.write("<metadata>")
|
151
|
+
if getattr(block, "read_only", False):
|
152
|
+
s.write("\n- read_only=true")
|
153
|
+
s.write(f"\n- chars_current={len(value)}")
|
154
|
+
s.write(f"\n- chars_limit={limit}\n")
|
155
|
+
s.write("</metadata>\n")
|
156
|
+
s.write("<value>\n")
|
157
|
+
s.write(f"{CORE_MEMORY_LINE_NUMBER_WARNING}\n")
|
158
|
+
if value:
|
159
|
+
for i, line in enumerate(value.split("\n"), start=1):
|
160
|
+
s.write(f"Line {i}: {line}\n")
|
161
|
+
s.write("</value>\n")
|
162
|
+
s.write(f"</{label}>\n")
|
163
|
+
if idx != len(self.blocks) - 1:
|
164
|
+
s.write("\n")
|
165
|
+
s.write("\n</memory_blocks>")
|
166
|
+
|
167
|
+
def _render_directories_common(self, s: StringIO, sources, max_files_open):
|
168
|
+
s.write("\n\n<directories>\n")
|
169
|
+
if max_files_open is not None:
|
170
|
+
current_open = sum(1 for b in self.file_blocks if getattr(b, "value", None))
|
171
|
+
s.write("<file_limits>\n")
|
172
|
+
s.write(f"- current_files_open={current_open}\n")
|
173
|
+
s.write(f"- max_files_open={max_files_open}\n")
|
174
|
+
s.write("</file_limits>\n")
|
175
|
+
|
176
|
+
for source in sources:
|
177
|
+
source_name = getattr(source, "name", "")
|
178
|
+
source_desc = getattr(source, "description", None)
|
179
|
+
source_instr = getattr(source, "instructions", None)
|
180
|
+
source_id = getattr(source, "id", None)
|
181
|
+
|
182
|
+
s.write(f'<directory name="{source_name}">\n')
|
183
|
+
if source_desc:
|
184
|
+
s.write(f"<description>{source_desc}</description>\n")
|
185
|
+
if source_instr:
|
186
|
+
s.write(f"<instructions>{source_instr}</instructions>\n")
|
187
|
+
|
188
|
+
if self.file_blocks:
|
189
|
+
for fb in self.file_blocks:
|
190
|
+
if source_id is not None and getattr(fb, "source_id", None) == source_id:
|
191
|
+
status = FileStatus.open.value if getattr(fb, "value", None) else FileStatus.closed.value
|
192
|
+
label = fb.label or "file"
|
193
|
+
desc = fb.description or ""
|
194
|
+
chars_current = len(fb.value or "")
|
195
|
+
limit = fb.limit if fb.limit is not None else 0
|
196
|
+
|
197
|
+
s.write(f'<file status="{status}" name="{label}">\n')
|
198
|
+
if desc:
|
199
|
+
s.write("<description>\n")
|
200
|
+
s.write(f"{desc}\n")
|
201
|
+
s.write("</description>\n")
|
202
|
+
s.write("<metadata>")
|
203
|
+
if getattr(fb, "read_only", False):
|
204
|
+
s.write("\n- read_only=true")
|
205
|
+
s.write(f"\n- chars_current={chars_current}\n")
|
206
|
+
s.write(f"- chars_limit={limit}\n")
|
207
|
+
s.write("</metadata>\n")
|
208
|
+
if getattr(fb, "value", None):
|
209
|
+
s.write("<value>\n")
|
210
|
+
s.write(f"{fb.value}\n")
|
211
|
+
s.write("</value>\n")
|
212
|
+
s.write("</file>\n")
|
213
|
+
|
214
|
+
s.write("</directory>\n")
|
215
|
+
s.write("</directories>")
|
216
|
+
|
217
|
+
def _render_directories_react(self, s: StringIO, sources, max_files_open):
|
218
|
+
s.write("\n\n<directories>\n")
|
219
|
+
if max_files_open is not None:
|
220
|
+
current_open = sum(1 for b in self.file_blocks if getattr(b, "value", None))
|
221
|
+
s.write("<file_limits>\n")
|
222
|
+
s.write(f"- current_files_open={current_open}\n")
|
223
|
+
s.write(f"- max_files_open={max_files_open}\n")
|
224
|
+
s.write("</file_limits>\n")
|
225
|
+
|
226
|
+
for source in sources:
|
227
|
+
source_name = getattr(source, "name", "")
|
228
|
+
source_desc = getattr(source, "description", None)
|
229
|
+
source_instr = getattr(source, "instructions", None)
|
230
|
+
source_id = getattr(source, "id", None)
|
231
|
+
|
232
|
+
s.write(f'<directory name="{source_name}">\n')
|
233
|
+
if source_desc:
|
234
|
+
s.write(f"<description>{source_desc}</description>\n")
|
235
|
+
if source_instr:
|
236
|
+
s.write(f"<instructions>{source_instr}</instructions>\n")
|
237
|
+
|
238
|
+
if self.file_blocks:
|
239
|
+
for fb in self.file_blocks:
|
240
|
+
if source_id is not None and getattr(fb, "source_id", None) == source_id:
|
241
|
+
status = FileStatus.open.value if getattr(fb, "value", None) else FileStatus.closed.value
|
242
|
+
label = fb.label or "file"
|
243
|
+
desc = fb.description or ""
|
244
|
+
chars_current = len(fb.value or "")
|
245
|
+
limit = fb.limit if fb.limit is not None else 0
|
246
|
+
|
247
|
+
s.write(f'<file status="{status}">\n')
|
248
|
+
s.write(f"<{label}>\n")
|
249
|
+
s.write("<description>\n")
|
250
|
+
s.write(f"{desc}\n")
|
251
|
+
s.write("</description>\n")
|
252
|
+
s.write("<metadata>")
|
253
|
+
if getattr(fb, "read_only", False):
|
254
|
+
s.write("\n- read_only=true")
|
255
|
+
s.write(f"\n- chars_current={chars_current}\n")
|
256
|
+
s.write(f"- chars_limit={limit}\n")
|
257
|
+
s.write("</metadata>\n")
|
258
|
+
s.write("<value>\n")
|
259
|
+
s.write(f"{fb.value or ''}\n")
|
260
|
+
s.write("</value>\n")
|
261
|
+
s.write(f"</{label}>\n")
|
262
|
+
s.write("</file>\n")
|
263
|
+
|
264
|
+
s.write("</directory>\n")
|
265
|
+
s.write("</directories>")
|
266
|
+
|
161
267
|
def compile(self, tool_usage_rules=None, sources=None, max_files_open=None) -> str:
|
162
|
-
"""
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
268
|
+
"""Efficiently render memory, tool rules, and sources into a prompt string."""
|
269
|
+
s = StringIO()
|
270
|
+
|
271
|
+
raw_type = self.agent_type.value if hasattr(self.agent_type, "value") else (self.agent_type or "")
|
272
|
+
norm_type = raw_type.lower()
|
273
|
+
is_react = norm_type in ("react_agent", "workflow_agent")
|
274
|
+
is_line_numbered = norm_type in ("sleeptime_agent", "memgpt_v2_agent")
|
275
|
+
|
276
|
+
# Memory blocks (not for react/workflow). Always include wrapper for preview/tests.
|
277
|
+
if not is_react:
|
278
|
+
if is_line_numbered:
|
279
|
+
self._render_memory_blocks_line_numbered(s)
|
280
|
+
else:
|
281
|
+
self._render_memory_blocks_standard(s)
|
282
|
+
|
283
|
+
if tool_usage_rules is not None:
|
284
|
+
desc = getattr(tool_usage_rules, "description", None) or ""
|
285
|
+
val = getattr(tool_usage_rules, "value", None) or ""
|
286
|
+
s.write("\n\n<tool_usage_rules>\n")
|
287
|
+
s.write(f"{desc}\n\n")
|
288
|
+
s.write(f"{val}\n")
|
289
|
+
s.write("</tool_usage_rules>")
|
290
|
+
|
291
|
+
if sources:
|
292
|
+
if is_react:
|
293
|
+
self._render_directories_react(s, sources, max_files_open)
|
294
|
+
else:
|
295
|
+
self._render_directories_common(s, sources, max_files_open)
|
296
|
+
|
297
|
+
return s.getvalue()
|
176
298
|
|
177
299
|
@trace_method
|
178
300
|
async def compile_async(self, tool_usage_rules=None, sources=None, max_files_open=None) -> str:
|
179
|
-
"""Async version
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
sources=sources,
|
187
|
-
max_files_open=max_files_open,
|
188
|
-
)
|
189
|
-
except TemplateSyntaxError as e:
|
190
|
-
raise ValueError(f"Invalid Jinja2 template syntax: {str(e)}")
|
191
|
-
except Exception as e:
|
192
|
-
raise ValueError(f"Prompt template is not compatible with current memory structure: {str(e)}")
|
301
|
+
"""Async version that offloads to a thread for CPU-bound string building."""
|
302
|
+
return await asyncio.to_thread(
|
303
|
+
self.compile,
|
304
|
+
tool_usage_rules=tool_usage_rules,
|
305
|
+
sources=sources,
|
306
|
+
max_files_open=max_files_open,
|
307
|
+
)
|
193
308
|
|
194
309
|
@trace_method
|
195
310
|
async def compile_in_thread_async(self, tool_usage_rules=None, sources=None, max_files_open=None) -> str:
|
196
|
-
"""
|
197
|
-
|
311
|
+
"""Deprecated: use compile() instead."""
|
312
|
+
import warnings
|
313
|
+
|
314
|
+
warnings.warn("compile_in_thread_async is deprecated; use compile()", DeprecationWarning, stacklevel=2)
|
315
|
+
return self.compile(tool_usage_rules=tool_usage_rules, sources=sources, max_files_open=max_files_open)
|
198
316
|
|
199
317
|
def list_block_labels(self) -> List[str]:
|
200
318
|
"""Return a list of the block names held inside the memory object"""
|
201
|
-
# return list(self.memory.keys())
|
202
319
|
return [block.label for block in self.blocks]
|
203
320
|
|
204
|
-
# TODO: these should actually be label, not name
|
205
321
|
def get_block(self, label: str) -> Block:
|
206
322
|
"""Correct way to index into the memory.memory field, returns a Block"""
|
207
323
|
keys = []
|
@@ -213,7 +329,6 @@ class Memory(BaseModel, validate_assignment=True):
|
|
213
329
|
|
214
330
|
def get_blocks(self) -> List[Block]:
|
215
331
|
"""Return a list of the blocks held inside the memory object"""
|
216
|
-
# return list(self.memory.values())
|
217
332
|
return self.blocks
|
218
333
|
|
219
334
|
def set_block(self, block: Block):
|
@@ -236,7 +351,6 @@ class Memory(BaseModel, validate_assignment=True):
|
|
236
351
|
raise ValueError(f"Block with label {label} does not exist")
|
237
352
|
|
238
353
|
|
239
|
-
# TODO: ideally this is refactored into ChatMemory and the subclasses are given more specific names.
|
240
354
|
class BasicBlockMemory(Memory):
|
241
355
|
"""
|
242
356
|
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.
|
@@ -308,7 +422,6 @@ class ChatMemory(BasicBlockMemory):
|
|
308
422
|
human (str): The starter value for the human block.
|
309
423
|
limit (int): The character limit for each block.
|
310
424
|
"""
|
311
|
-
# TODO: Should these be CreateBlocks?
|
312
425
|
super().__init__(blocks=[Block(value=persona, limit=limit, label="persona"), Block(value=human, limit=limit, label="human")])
|
313
426
|
|
314
427
|
|
letta/schemas/provider_trace.py
CHANGED
@@ -19,7 +19,6 @@ class ProviderTraceCreate(BaseModel):
|
|
19
19
|
request_json: dict[str, Any] = Field(..., description="JSON content of the provider request")
|
20
20
|
response_json: dict[str, Any] = Field(..., description="JSON content of the provider response")
|
21
21
|
step_id: str = Field(None, description="ID of the step that this trace is associated with")
|
22
|
-
organization_id: str = Field(..., description="The unique identifier of the organization.")
|
23
22
|
|
24
23
|
|
25
24
|
class ProviderTrace(BaseProviderTrace):
|
@@ -39,5 +38,4 @@ class ProviderTrace(BaseProviderTrace):
|
|
39
38
|
request_json: Dict[str, Any] = Field(..., description="JSON content of the provider request")
|
40
39
|
response_json: Dict[str, Any] = Field(..., description="JSON content of the provider response")
|
41
40
|
step_id: Optional[str] = Field(None, description="ID of the step that this trace is associated with")
|
42
|
-
organization_id: str = Field(..., description="The unique identifier of the organization.")
|
43
41
|
created_at: datetime = Field(default_factory=get_utc_time, description="The timestamp when the object was created.")
|
letta/schemas/run.py
CHANGED
@@ -4,6 +4,7 @@ from pydantic import Field
|
|
4
4
|
|
5
5
|
from letta.schemas.enums import JobType
|
6
6
|
from letta.schemas.job import Job, JobBase, LettaRequestConfig
|
7
|
+
from letta.schemas.letta_stop_reason import StopReasonType
|
7
8
|
|
8
9
|
|
9
10
|
class RunBase(JobBase):
|
@@ -29,6 +30,7 @@ class Run(RunBase):
|
|
29
30
|
id: str = RunBase.generate_id_field()
|
30
31
|
user_id: Optional[str] = Field(None, description="The unique identifier of the user associated with the run.")
|
31
32
|
request_config: Optional[LettaRequestConfig] = Field(None, description="The request configuration for the run.")
|
33
|
+
stop_reason: Optional[StopReasonType] = Field(None, description="The reason why the run was stopped.")
|
32
34
|
|
33
35
|
@classmethod
|
34
36
|
def from_job(cls, job: Job) -> "Run":
|