letta-nightly 0.13.0.dev20251030104218__py3-none-any.whl → 0.13.1.dev20251031234110__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.
Potentially problematic release.
This version of letta-nightly might be problematic. Click here for more details.
- letta/__init__.py +1 -1
- letta/adapters/simple_llm_stream_adapter.py +1 -0
- letta/agents/letta_agent_v2.py +8 -0
- letta/agents/letta_agent_v3.py +120 -27
- letta/agents/temporal/activities/__init__.py +25 -0
- letta/agents/temporal/activities/create_messages.py +26 -0
- letta/agents/temporal/activities/create_step.py +57 -0
- letta/agents/temporal/activities/example_activity.py +9 -0
- letta/agents/temporal/activities/execute_tool.py +130 -0
- letta/agents/temporal/activities/llm_request.py +114 -0
- letta/agents/temporal/activities/prepare_messages.py +27 -0
- letta/agents/temporal/activities/refresh_context.py +160 -0
- letta/agents/temporal/activities/summarize_conversation_history.py +77 -0
- letta/agents/temporal/activities/update_message_ids.py +25 -0
- letta/agents/temporal/activities/update_run.py +43 -0
- letta/agents/temporal/constants.py +59 -0
- letta/agents/temporal/temporal_agent_workflow.py +704 -0
- letta/agents/temporal/types.py +275 -0
- letta/constants.py +8 -0
- letta/errors.py +4 -0
- letta/functions/function_sets/base.py +0 -11
- letta/groups/helpers.py +7 -1
- letta/groups/sleeptime_multi_agent_v4.py +4 -3
- letta/interfaces/anthropic_streaming_interface.py +0 -1
- letta/interfaces/openai_streaming_interface.py +103 -100
- letta/llm_api/anthropic_client.py +57 -12
- letta/llm_api/bedrock_client.py +1 -0
- letta/llm_api/deepseek_client.py +3 -2
- letta/llm_api/google_vertex_client.py +1 -0
- letta/llm_api/groq_client.py +1 -0
- letta/llm_api/llm_client_base.py +15 -1
- letta/llm_api/openai.py +2 -2
- letta/llm_api/openai_client.py +17 -3
- letta/llm_api/xai_client.py +1 -0
- letta/orm/organization.py +4 -0
- letta/orm/sqlalchemy_base.py +7 -0
- letta/otel/tracing.py +131 -4
- letta/schemas/agent_file.py +10 -10
- letta/schemas/block.py +22 -3
- letta/schemas/enums.py +21 -0
- letta/schemas/environment_variables.py +3 -2
- letta/schemas/group.py +3 -3
- letta/schemas/letta_response.py +36 -4
- letta/schemas/llm_batch_job.py +3 -3
- letta/schemas/llm_config.py +27 -3
- letta/schemas/mcp.py +3 -2
- letta/schemas/mcp_server.py +3 -2
- letta/schemas/message.py +167 -49
- letta/schemas/organization.py +2 -1
- letta/schemas/passage.py +2 -1
- letta/schemas/provider_trace.py +2 -1
- letta/schemas/providers/openrouter.py +1 -2
- letta/schemas/run_metrics.py +2 -1
- letta/schemas/sandbox_config.py +3 -1
- letta/schemas/step_metrics.py +2 -1
- letta/schemas/tool_rule.py +2 -2
- letta/schemas/user.py +2 -1
- letta/server/rest_api/app.py +5 -1
- letta/server/rest_api/routers/v1/__init__.py +4 -0
- letta/server/rest_api/routers/v1/agents.py +71 -9
- letta/server/rest_api/routers/v1/blocks.py +7 -7
- letta/server/rest_api/routers/v1/groups.py +40 -0
- letta/server/rest_api/routers/v1/identities.py +2 -2
- letta/server/rest_api/routers/v1/internal_agents.py +31 -0
- letta/server/rest_api/routers/v1/internal_blocks.py +177 -0
- letta/server/rest_api/routers/v1/internal_runs.py +25 -1
- letta/server/rest_api/routers/v1/runs.py +2 -22
- letta/server/rest_api/routers/v1/tools.py +10 -0
- letta/server/server.py +5 -2
- letta/services/agent_manager.py +4 -4
- letta/services/archive_manager.py +16 -0
- letta/services/group_manager.py +44 -0
- letta/services/helpers/run_manager_helper.py +2 -2
- letta/services/lettuce/lettuce_client.py +148 -0
- letta/services/mcp/base_client.py +9 -3
- letta/services/run_manager.py +148 -37
- letta/services/source_manager.py +91 -3
- letta/services/step_manager.py +2 -3
- letta/services/streaming_service.py +52 -13
- letta/services/summarizer/summarizer.py +28 -2
- letta/services/tool_executor/builtin_tool_executor.py +1 -1
- letta/services/tool_executor/core_tool_executor.py +2 -117
- letta/services/tool_schema_generator.py +2 -2
- letta/validators.py +21 -0
- {letta_nightly-0.13.0.dev20251030104218.dist-info → letta_nightly-0.13.1.dev20251031234110.dist-info}/METADATA +1 -1
- {letta_nightly-0.13.0.dev20251030104218.dist-info → letta_nightly-0.13.1.dev20251031234110.dist-info}/RECORD +89 -84
- letta/agent.py +0 -1758
- letta/cli/cli_load.py +0 -16
- letta/client/__init__.py +0 -0
- letta/client/streaming.py +0 -95
- letta/client/utils.py +0 -78
- letta/functions/async_composio_toolset.py +0 -109
- letta/functions/composio_helpers.py +0 -96
- letta/helpers/composio_helpers.py +0 -38
- letta/orm/job_messages.py +0 -33
- letta/schemas/providers.py +0 -1617
- letta/server/rest_api/routers/openai/chat_completions/chat_completions.py +0 -132
- letta/services/tool_executor/composio_tool_executor.py +0 -57
- {letta_nightly-0.13.0.dev20251030104218.dist-info → letta_nightly-0.13.1.dev20251031234110.dist-info}/WHEEL +0 -0
- {letta_nightly-0.13.0.dev20251030104218.dist-info → letta_nightly-0.13.1.dev20251031234110.dist-info}/entry_points.txt +0 -0
- {letta_nightly-0.13.0.dev20251030104218.dist-info → letta_nightly-0.13.1.dev20251031234110.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Dict, List, Optional
|
|
3
|
+
|
|
4
|
+
from letta.helpers import ToolRulesSolver
|
|
5
|
+
from letta.schemas.agent import AgentState
|
|
6
|
+
from letta.schemas.enums import RunStatus
|
|
7
|
+
from letta.schemas.letta_message import LettaMessageUnion, MessageType
|
|
8
|
+
from letta.schemas.letta_message_content import (
|
|
9
|
+
OmittedReasoningContent,
|
|
10
|
+
ReasoningContent,
|
|
11
|
+
RedactedReasoningContent,
|
|
12
|
+
TextContent,
|
|
13
|
+
)
|
|
14
|
+
from letta.schemas.letta_stop_reason import LettaStopReason, StopReasonType
|
|
15
|
+
from letta.schemas.message import Message, MessageCreate
|
|
16
|
+
from letta.schemas.openai.chat_completion_response import ToolCall, UsageStatistics
|
|
17
|
+
from letta.schemas.step import Step
|
|
18
|
+
from letta.schemas.tool_execution_result import ToolExecutionResult
|
|
19
|
+
from letta.schemas.usage import LettaUsageStatistics
|
|
20
|
+
from letta.schemas.user import User
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class WorkflowInputParams:
|
|
25
|
+
agent_state: AgentState
|
|
26
|
+
messages: list[MessageCreate]
|
|
27
|
+
actor: User
|
|
28
|
+
max_steps: int
|
|
29
|
+
run_id: str
|
|
30
|
+
use_assistant_message: bool = True
|
|
31
|
+
include_return_message_types: list[MessageType] | None = None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class PreparedMessages:
|
|
36
|
+
in_context_messages: List[Message]
|
|
37
|
+
input_messages_to_persist: List[Message]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class FinalResult:
|
|
42
|
+
messages: List[LettaMessageUnion]
|
|
43
|
+
stop_reason: str
|
|
44
|
+
usage: LettaUsageStatistics
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class InnerStepResult:
|
|
49
|
+
"""Result from a single inner_step execution."""
|
|
50
|
+
|
|
51
|
+
stop_reason: StopReasonType
|
|
52
|
+
usage: LettaUsageStatistics
|
|
53
|
+
should_continue: bool
|
|
54
|
+
response_messages: List[Message]
|
|
55
|
+
agent_state: AgentState
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# ===== Additional types for activities up to _handle_ai_response =====
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass
|
|
62
|
+
class RefreshContextParams:
|
|
63
|
+
"""Input to refresh_context_and_system_message activity.
|
|
64
|
+
|
|
65
|
+
- agent_state: Current agent state (memory, sources, tools, etc.)
|
|
66
|
+
- in_context_messages: Current message history to potentially rebuild/scrub
|
|
67
|
+
- actor: Requesting user (for DB access control)
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
agent_state: AgentState
|
|
71
|
+
in_context_messages: List[Message]
|
|
72
|
+
tool_rules_solver: ToolRulesSolver
|
|
73
|
+
actor: User
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@dataclass
|
|
77
|
+
class RefreshContextResult:
|
|
78
|
+
"""Output from refresh_context_and_system_message activity.
|
|
79
|
+
|
|
80
|
+
- messages: Updated in-context messages with refreshed system message
|
|
81
|
+
- agent_state: Updated agent state with refreshed memory
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
messages: List[Message]
|
|
85
|
+
agent_state: AgentState
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@dataclass
|
|
89
|
+
class LLMRequestParams:
|
|
90
|
+
"""Input to llm_request activity.
|
|
91
|
+
|
|
92
|
+
- agent_state: Needed primarily for llm_config (model, endpoint, etc.)
|
|
93
|
+
- messages: Full prompt messages (context + input + responses if any)
|
|
94
|
+
- allowed_tools: Tools JSON schema list after rules + strict mode + runtime overrides
|
|
95
|
+
- force_tool_call: Optional tool name to force call when only one valid tool exists
|
|
96
|
+
- requires_approval_tools: Optional list of tool names that require approval
|
|
97
|
+
- actor: Requesting user (for audit/tenant context)
|
|
98
|
+
- step_id: Current step id for tracing/telemetry correlation
|
|
99
|
+
- use_assistant_message: Whether to use assistant message format for responses
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
agent_state: AgentState
|
|
103
|
+
messages: List[Message]
|
|
104
|
+
allowed_tools: List[dict]
|
|
105
|
+
force_tool_call: Optional[str] = None
|
|
106
|
+
requires_approval_tools: Optional[List[str]] = None
|
|
107
|
+
actor: Optional[User] = None
|
|
108
|
+
step_id: Optional[str] = None
|
|
109
|
+
use_assistant_message: bool = True
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@dataclass
|
|
113
|
+
class LLMCallResult:
|
|
114
|
+
"""Output from llm_request activity.
|
|
115
|
+
|
|
116
|
+
- tool_call: Parsed tool call from LLM (None for stub/approval paths)
|
|
117
|
+
- reasoning_content: Optional reasoning/assistant content stream collected
|
|
118
|
+
- assistant_message_id: Optional precomputed assistant message id (if adapter sets)
|
|
119
|
+
- usage: Provider usage statistics for this call
|
|
120
|
+
- request_finish_ns: Provider request finish time (ns) for metrics, if available
|
|
121
|
+
- llm_request_start_ns: Start time of LLM request in nanoseconds
|
|
122
|
+
- llm_request_ns: Duration of LLM request in nanoseconds
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
tool_call: Optional[ToolCall]
|
|
126
|
+
reasoning_content: Optional[List[TextContent | ReasoningContent | RedactedReasoningContent | OmittedReasoningContent]]
|
|
127
|
+
assistant_message_id: Optional[str]
|
|
128
|
+
usage: UsageStatistics
|
|
129
|
+
request_finish_ns: Optional[int]
|
|
130
|
+
llm_request_start_ns: Optional[int] = None
|
|
131
|
+
llm_request_ns: Optional[int] = None
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@dataclass
|
|
135
|
+
class SummarizeParams:
|
|
136
|
+
"""Input to summarize_conversation_history activity.
|
|
137
|
+
|
|
138
|
+
- agent_state: Current agent state (summarizer config, ids)
|
|
139
|
+
- in_context_messages: Current context window
|
|
140
|
+
- new_letta_messages: Newly generated/persisted messages to consider
|
|
141
|
+
- actor: Requesting user
|
|
142
|
+
- force: Whether to force summarization + clear
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
agent_state: AgentState
|
|
146
|
+
in_context_messages: List[Message]
|
|
147
|
+
new_letta_messages: List[Message]
|
|
148
|
+
actor: User
|
|
149
|
+
force: bool = True
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
# ===== Tool execution and message handling types =====
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@dataclass
|
|
156
|
+
class ExecuteToolParams:
|
|
157
|
+
"""Input to execute_tool_activity.
|
|
158
|
+
|
|
159
|
+
- tool_name: Name of the tool to execute
|
|
160
|
+
- tool_args: Arguments to pass to the tool
|
|
161
|
+
- agent_state: Current agent state containing tools and configuration
|
|
162
|
+
- actor: Requesting user for access control
|
|
163
|
+
- step_id: Current step ID for tracing
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
tool_name: str
|
|
167
|
+
tool_args: Dict
|
|
168
|
+
agent_state: AgentState
|
|
169
|
+
actor: User
|
|
170
|
+
step_id: Optional[str]
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@dataclass
|
|
174
|
+
class ExecuteToolResult:
|
|
175
|
+
"""Output from execute_tool_activity."""
|
|
176
|
+
|
|
177
|
+
tool_execution_result: ToolExecutionResult
|
|
178
|
+
execution_time_ns: int
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@dataclass
|
|
182
|
+
class CreateStepParams:
|
|
183
|
+
"""Input to create_step_activity."""
|
|
184
|
+
|
|
185
|
+
agent_state: AgentState
|
|
186
|
+
messages: List[Message]
|
|
187
|
+
actor: User
|
|
188
|
+
run_id: str
|
|
189
|
+
step_id: Optional[str]
|
|
190
|
+
usage: UsageStatistics
|
|
191
|
+
step_ns: Optional[int] = None
|
|
192
|
+
llm_request_ns: Optional[int] = None
|
|
193
|
+
tool_execution_ns: Optional[int] = None
|
|
194
|
+
stop_reason: Optional[str] = None
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@dataclass
|
|
198
|
+
class CreateStepResult:
|
|
199
|
+
"""Output from create_step_activity."""
|
|
200
|
+
|
|
201
|
+
step: Step
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
@dataclass
|
|
205
|
+
class CreateMessagesParams:
|
|
206
|
+
"""Input to create_messages_activity.
|
|
207
|
+
|
|
208
|
+
Persists messages to the database.
|
|
209
|
+
"""
|
|
210
|
+
|
|
211
|
+
messages: List[Message]
|
|
212
|
+
actor: User
|
|
213
|
+
project_id: Optional[str]
|
|
214
|
+
template_id: Optional[str]
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@dataclass
|
|
218
|
+
class CreateMessagesResult:
|
|
219
|
+
"""Output from create_messages_activity."""
|
|
220
|
+
|
|
221
|
+
messages: List[Message]
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
@dataclass
|
|
225
|
+
class PersistMessagesParams:
|
|
226
|
+
"""Input to persist_messages_activity.
|
|
227
|
+
|
|
228
|
+
Persists messages to database and optionally updates job messages.
|
|
229
|
+
"""
|
|
230
|
+
|
|
231
|
+
messages: List[Message]
|
|
232
|
+
actor: User
|
|
233
|
+
project_id: Optional[str]
|
|
234
|
+
template_id: Optional[str]
|
|
235
|
+
run_id: Optional[str]
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
@dataclass
|
|
239
|
+
class PersistMessagesResult:
|
|
240
|
+
"""Output from persist_messages_activity."""
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
@dataclass
|
|
244
|
+
class UpdateRunParams:
|
|
245
|
+
"""Input to update_run_activity."""
|
|
246
|
+
|
|
247
|
+
run_id: str
|
|
248
|
+
actor: User
|
|
249
|
+
run_status: RunStatus
|
|
250
|
+
stop_reason: LettaStopReason | None
|
|
251
|
+
persisted_messages: List[Message]
|
|
252
|
+
usage: LettaUsageStatistics
|
|
253
|
+
total_duration_ns: int | None
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
@dataclass
|
|
257
|
+
class UpdateMessageIdsParams:
|
|
258
|
+
"""Input to update_message_ids_activity.
|
|
259
|
+
|
|
260
|
+
Updates the agent's message IDs in the database.
|
|
261
|
+
Used for immediate approval persistence to prevent bad state.
|
|
262
|
+
"""
|
|
263
|
+
|
|
264
|
+
agent_id: str
|
|
265
|
+
message_ids: List[str]
|
|
266
|
+
actor: User
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
@dataclass
|
|
270
|
+
class UpdateMessageIdsResult:
|
|
271
|
+
"""Output from update_message_ids_activity."""
|
|
272
|
+
|
|
273
|
+
success: bool
|
|
274
|
+
agent_state: AgentState
|
|
275
|
+
persisted_messages: List[Message]
|
letta/constants.py
CHANGED
|
@@ -51,6 +51,11 @@ DEFAULT_MAX_STEPS = 50
|
|
|
51
51
|
MIN_CONTEXT_WINDOW = 4096
|
|
52
52
|
DEFAULT_CONTEXT_WINDOW = 32000
|
|
53
53
|
|
|
54
|
+
# Summarization trigger threshold (multiplier of context_window limit)
|
|
55
|
+
# Summarization triggers when step usage > context_window * SUMMARIZATION_TRIGGER_MULTIPLIER
|
|
56
|
+
# Set to 0.9 (90%) to provide buffer before hitting hard limit
|
|
57
|
+
SUMMARIZATION_TRIGGER_MULTIPLIER = 0.9
|
|
58
|
+
|
|
54
59
|
# number of concurrent embedding requests to sent
|
|
55
60
|
EMBEDDING_BATCH_SIZE = 200
|
|
56
61
|
|
|
@@ -373,6 +378,9 @@ FUNCTION_RETURN_CHAR_LIMIT = 50000 # ~300 words
|
|
|
373
378
|
BASE_FUNCTION_RETURN_CHAR_LIMIT = 50000 # same as regular function limit
|
|
374
379
|
FILE_IS_TRUNCATED_WARNING = "# NOTE: This block is truncated, use functions to view the full content."
|
|
375
380
|
|
|
381
|
+
# Tool return truncation limit for LLM context window management
|
|
382
|
+
TOOL_RETURN_TRUNCATION_CHARS = 5000
|
|
383
|
+
|
|
376
384
|
MAX_PAUSE_HEARTBEATS = 360 # in min
|
|
377
385
|
|
|
378
386
|
MESSAGE_CHATGPT_FUNCTION_MODEL = "gpt-3.5-turbo"
|
letta/errors.py
CHANGED
|
@@ -202,6 +202,10 @@ class LLMTimeoutError(LLMError):
|
|
|
202
202
|
"""Error when LLM request times out"""
|
|
203
203
|
|
|
204
204
|
|
|
205
|
+
class LLMProviderOverloaded(LLMError):
|
|
206
|
+
"""Error when LLM provider is overloaded"""
|
|
207
|
+
|
|
208
|
+
|
|
205
209
|
class BedrockPermissionError(LettaError):
|
|
206
210
|
"""Exception raised for errors in the Bedrock permission process."""
|
|
207
211
|
|
|
@@ -24,7 +24,6 @@ def memory(
|
|
|
24
24
|
|
|
25
25
|
Args:
|
|
26
26
|
command (str): The sub-command to execute. Supported commands:
|
|
27
|
-
- "view": List memory blocks or view specific block content
|
|
28
27
|
- "create": Create a new memory block
|
|
29
28
|
- "str_replace": Replace text in a memory block
|
|
30
29
|
- "insert": Insert text at a specific line in a memory block
|
|
@@ -39,21 +38,11 @@ def memory(
|
|
|
39
38
|
insert_text (Optional[str]): Text to insert (for insert)
|
|
40
39
|
old_path (Optional[str]): Old path for rename operation
|
|
41
40
|
new_path (Optional[str]): New path for rename operation
|
|
42
|
-
view_range (Optional[int]): Range of lines to view (for view)
|
|
43
41
|
|
|
44
42
|
Returns:
|
|
45
43
|
Optional[str]: Success message or error description
|
|
46
44
|
|
|
47
45
|
Examples:
|
|
48
|
-
# List all memory blocks
|
|
49
|
-
memory(agent_state, "view", path="/memories")
|
|
50
|
-
|
|
51
|
-
# View specific memory block content
|
|
52
|
-
memory(agent_state, "view", path="/memories/user_preferences")
|
|
53
|
-
|
|
54
|
-
# View first 10 lines of a memory block
|
|
55
|
-
memory(agent_state, "view", path="/memories/user_preferences", view_range=10)
|
|
56
|
-
|
|
57
46
|
# Replace text in a memory block
|
|
58
47
|
memory(agent_state, "str_replace", path="/memories/user_preferences", old_str="theme: dark", new_str="theme: light")
|
|
59
48
|
|
letta/groups/helpers.py
CHANGED
|
@@ -6,7 +6,7 @@ from letta.orm.group import Group
|
|
|
6
6
|
from letta.orm.user import User
|
|
7
7
|
from letta.schemas.agent import AgentState
|
|
8
8
|
from letta.schemas.group import ManagerType
|
|
9
|
-
from letta.schemas.letta_message_content import ImageContent, TextContent
|
|
9
|
+
from letta.schemas.letta_message_content import ImageContent, ReasoningContent, TextContent
|
|
10
10
|
from letta.schemas.message import Message
|
|
11
11
|
from letta.services.mcp.base_client import AsyncBaseMCPClient
|
|
12
12
|
|
|
@@ -100,6 +100,12 @@ def stringify_message(message: Message, use_assistant_name: bool = False) -> str
|
|
|
100
100
|
return f"{message.name or 'user'}: {message.content[0].text}"
|
|
101
101
|
elif message.role == "assistant":
|
|
102
102
|
messages = []
|
|
103
|
+
if message.content:
|
|
104
|
+
for content in message.content:
|
|
105
|
+
if isinstance(content, TextContent):
|
|
106
|
+
messages.append(f"{assistant_name}: {content.text}")
|
|
107
|
+
elif isinstance(content, ReasoningContent):
|
|
108
|
+
messages.append(f"{assistant_name}: *thinking* {content.reasoning}")
|
|
103
109
|
if message.tool_calls:
|
|
104
110
|
if message.tool_calls[0].function.name == "send_message":
|
|
105
111
|
messages.append(f"{assistant_name}: {json.loads(message.tool_calls[0].function.arguments)['message']}")
|
|
@@ -59,8 +59,8 @@ class SleeptimeMultiAgentV4(LettaAgentV3):
|
|
|
59
59
|
request_start_timestamp_ns=request_start_timestamp_ns,
|
|
60
60
|
)
|
|
61
61
|
|
|
62
|
-
await self.run_sleeptime_agents()
|
|
63
|
-
response.usage.run_ids =
|
|
62
|
+
run_ids = await self.run_sleeptime_agents()
|
|
63
|
+
response.usage.run_ids = run_ids
|
|
64
64
|
return response
|
|
65
65
|
|
|
66
66
|
@trace_method
|
|
@@ -94,7 +94,7 @@ class SleeptimeMultiAgentV4(LettaAgentV3):
|
|
|
94
94
|
await self.run_sleeptime_agents()
|
|
95
95
|
|
|
96
96
|
@trace_method
|
|
97
|
-
async def run_sleeptime_agents(self):
|
|
97
|
+
async def run_sleeptime_agents(self) -> list[str]:
|
|
98
98
|
# Get response messages
|
|
99
99
|
last_response_messages = self.response_messages
|
|
100
100
|
|
|
@@ -122,6 +122,7 @@ class SleeptimeMultiAgentV4(LettaAgentV3):
|
|
|
122
122
|
# Individual task failures
|
|
123
123
|
print(f"Sleeptime agent processing failed: {e!s}")
|
|
124
124
|
raise e
|
|
125
|
+
return self.run_ids
|
|
125
126
|
|
|
126
127
|
@trace_method
|
|
127
128
|
async def _issue_background_task(
|
|
@@ -23,7 +23,6 @@ from anthropic.types.beta import (
|
|
|
23
23
|
BetaThinkingDelta,
|
|
24
24
|
BetaToolUseBlock,
|
|
25
25
|
)
|
|
26
|
-
from letta_client.types import assistant_message
|
|
27
26
|
|
|
28
27
|
from letta.constants import DEFAULT_MESSAGE_TOOL, DEFAULT_MESSAGE_TOOL_KWARG
|
|
29
28
|
from letta.local_llm.constants import INNER_THOUGHTS_KWARG
|
|
@@ -6,6 +6,7 @@ from typing import Optional
|
|
|
6
6
|
from openai import AsyncStream
|
|
7
7
|
from openai.types.chat.chat_completion_chunk import ChatCompletionChunk
|
|
8
8
|
from openai.types.responses import (
|
|
9
|
+
ParsedResponse,
|
|
9
10
|
ResponseCompletedEvent,
|
|
10
11
|
ResponseContentPartAddedEvent,
|
|
11
12
|
ResponseContentPartDoneEvent,
|
|
@@ -524,10 +525,9 @@ class SimpleOpenAIStreamingInterface:
|
|
|
524
525
|
self.messages = messages or []
|
|
525
526
|
self.tools = tools or []
|
|
526
527
|
|
|
527
|
-
#
|
|
528
|
-
self.
|
|
529
|
-
self.
|
|
530
|
-
self.tool_call_id = ""
|
|
528
|
+
# Accumulate per-index tool call fragments and preserve order
|
|
529
|
+
self._tool_calls_acc: dict[int, dict[str, str]] = {}
|
|
530
|
+
self._tool_call_start_order: list[int] = []
|
|
531
531
|
|
|
532
532
|
self.content_messages = []
|
|
533
533
|
self.emitted_hidden_reasoning = False # Track if we've emitted hidden reasoning message
|
|
@@ -561,19 +561,27 @@ class SimpleOpenAIStreamingInterface:
|
|
|
561
561
|
|
|
562
562
|
return merged_messages
|
|
563
563
|
|
|
564
|
-
def
|
|
565
|
-
"""
|
|
566
|
-
if not self.
|
|
567
|
-
|
|
568
|
-
if
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
564
|
+
def get_tool_call_objects(self) -> list[ToolCall]:
|
|
565
|
+
"""Return finalized tool calls (parallel supported)."""
|
|
566
|
+
if not self._tool_calls_acc:
|
|
567
|
+
return []
|
|
568
|
+
ordered_indices = [i for i in self._tool_call_start_order if i in self._tool_calls_acc]
|
|
569
|
+
result: list[ToolCall] = []
|
|
570
|
+
for idx in ordered_indices:
|
|
571
|
+
ctx = self._tool_calls_acc[idx]
|
|
572
|
+
name = ctx.get("name", "")
|
|
573
|
+
args = ctx.get("arguments", "")
|
|
574
|
+
call_id = ctx.get("id", "")
|
|
575
|
+
if call_id and name:
|
|
576
|
+
result.append(ToolCall(id=call_id, function=FunctionCall(arguments=args or "", name=name)))
|
|
577
|
+
return result
|
|
572
578
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
579
|
+
def get_tool_call_object(self) -> ToolCall:
|
|
580
|
+
"""Backwards-compatible single tool call accessor (first tool if multiple)."""
|
|
581
|
+
calls = self.get_tool_call_objects()
|
|
582
|
+
if not calls:
|
|
583
|
+
raise ValueError("No tool calls available")
|
|
584
|
+
return calls[0]
|
|
577
585
|
|
|
578
586
|
async def process(
|
|
579
587
|
self,
|
|
@@ -718,70 +726,61 @@ class SimpleOpenAIStreamingInterface:
|
|
|
718
726
|
yield reasoning_msg
|
|
719
727
|
|
|
720
728
|
if message_delta.tool_calls is not None and len(message_delta.tool_calls) > 0:
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
# id=self.letta_message_id,
|
|
730
|
-
# date=datetime.now(timezone.utc),
|
|
731
|
-
# state="omitted",
|
|
732
|
-
# hidden_reasoning=None,
|
|
733
|
-
# otid=Message.generate_otid_from_id(self.letta_message_id, message_index),
|
|
734
|
-
# )
|
|
735
|
-
# self.content_messages.append(hidden_message)
|
|
736
|
-
# prev_message_type = hidden_message.message_type
|
|
737
|
-
# message_index += 1 # Increment for the next message
|
|
738
|
-
# yield hidden_message
|
|
739
|
-
|
|
740
|
-
if not tool_call.function.name and not tool_call.function.arguments and not tool_call.id:
|
|
741
|
-
# No chunks to process, exit
|
|
742
|
-
return
|
|
743
|
-
|
|
744
|
-
if tool_call.function.name:
|
|
745
|
-
self.tool_call_name += tool_call.function.name
|
|
746
|
-
if tool_call.function.arguments:
|
|
747
|
-
self.tool_call_args += tool_call.function.arguments
|
|
748
|
-
if tool_call.id:
|
|
749
|
-
self.tool_call_id += tool_call.id
|
|
729
|
+
# Accumulate per-index tool call fragments and emit deltas
|
|
730
|
+
for tool_call in message_delta.tool_calls:
|
|
731
|
+
if (
|
|
732
|
+
not (tool_call.function and (tool_call.function.name or tool_call.function.arguments))
|
|
733
|
+
and not tool_call.id
|
|
734
|
+
and getattr(tool_call, "index", None) is None
|
|
735
|
+
):
|
|
736
|
+
continue
|
|
750
737
|
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
name=tool_call.function.name,
|
|
770
|
-
arguments=tool_call.function.arguments,
|
|
771
|
-
tool_call_id=tool_call.id,
|
|
772
|
-
)
|
|
773
|
-
tool_call_msg = ToolCallMessage(
|
|
774
|
-
id=self.letta_message_id,
|
|
775
|
-
date=datetime.now(timezone.utc),
|
|
776
|
-
tool_call=tool_call_delta,
|
|
777
|
-
tool_calls=tool_call_delta,
|
|
778
|
-
# name=name,
|
|
779
|
-
otid=Message.generate_otid_from_id(self.letta_message_id, message_index),
|
|
780
|
-
run_id=self.run_id,
|
|
781
|
-
step_id=self.step_id,
|
|
738
|
+
idx = getattr(tool_call, "index", None)
|
|
739
|
+
if idx is None:
|
|
740
|
+
idx = 0
|
|
741
|
+
|
|
742
|
+
if idx not in self._tool_call_start_order:
|
|
743
|
+
self._tool_call_start_order.append(idx)
|
|
744
|
+
if idx not in self._tool_calls_acc:
|
|
745
|
+
self._tool_calls_acc[idx] = {"name": "", "arguments": "", "id": ""}
|
|
746
|
+
acc = self._tool_calls_acc[idx]
|
|
747
|
+
|
|
748
|
+
if tool_call.function and tool_call.function.name:
|
|
749
|
+
acc["name"] += tool_call.function.name
|
|
750
|
+
if tool_call.function and tool_call.function.arguments:
|
|
751
|
+
acc["arguments"] += tool_call.function.arguments
|
|
752
|
+
if tool_call.id:
|
|
753
|
+
acc["id"] += tool_call.id
|
|
754
|
+
|
|
755
|
+
delta = ToolCallDelta(
|
|
756
|
+
name=tool_call.function.name if (tool_call.function and tool_call.function.name) else None,
|
|
757
|
+
arguments=tool_call.function.arguments if (tool_call.function and tool_call.function.arguments) else None,
|
|
758
|
+
tool_call_id=tool_call.id if tool_call.id else None,
|
|
782
759
|
)
|
|
783
|
-
|
|
784
|
-
|
|
760
|
+
|
|
761
|
+
if acc.get("name") and acc["name"] in self.requires_approval_tools:
|
|
762
|
+
tool_call_msg = ApprovalRequestMessage(
|
|
763
|
+
id=decrement_message_uuid(self.letta_message_id),
|
|
764
|
+
date=datetime.now(timezone.utc),
|
|
765
|
+
tool_call=delta,
|
|
766
|
+
otid=Message.generate_otid_from_id(decrement_message_uuid(self.letta_message_id), -1),
|
|
767
|
+
run_id=self.run_id,
|
|
768
|
+
step_id=self.step_id,
|
|
769
|
+
)
|
|
770
|
+
else:
|
|
771
|
+
if prev_message_type and prev_message_type != "tool_call_message":
|
|
772
|
+
message_index += 1
|
|
773
|
+
tool_call_msg = ToolCallMessage(
|
|
774
|
+
id=self.letta_message_id,
|
|
775
|
+
date=datetime.now(timezone.utc),
|
|
776
|
+
tool_call=delta,
|
|
777
|
+
tool_calls=delta,
|
|
778
|
+
otid=Message.generate_otid_from_id(self.letta_message_id, message_index),
|
|
779
|
+
run_id=self.run_id,
|
|
780
|
+
step_id=self.step_id,
|
|
781
|
+
)
|
|
782
|
+
prev_message_type = tool_call_msg.message_type
|
|
783
|
+
yield tool_call_msg
|
|
785
784
|
|
|
786
785
|
|
|
787
786
|
class SimpleOpenAIResponsesStreamingInterface:
|
|
@@ -813,7 +812,7 @@ class SimpleOpenAIResponsesStreamingInterface:
|
|
|
813
812
|
# Premake IDs for database writes
|
|
814
813
|
self.letta_message_id = Message.generate_id()
|
|
815
814
|
self.model = model
|
|
816
|
-
self.final_response = None
|
|
815
|
+
self.final_response: Optional[ParsedResponse] = None
|
|
817
816
|
|
|
818
817
|
def get_content(self) -> list[TextContent | SummarizedReasoningContent]:
|
|
819
818
|
"""This includes both SummarizedReasoningContent and TextContent"""
|
|
@@ -844,31 +843,35 @@ class SimpleOpenAIResponsesStreamingInterface:
|
|
|
844
843
|
|
|
845
844
|
return content
|
|
846
845
|
|
|
847
|
-
def
|
|
848
|
-
"""
|
|
846
|
+
def get_tool_call_objects(self) -> list[ToolCall]:
|
|
847
|
+
"""Return finalized tool calls (parallel supported) from final response."""
|
|
849
848
|
if self.final_response is None:
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
tool_calls = []
|
|
853
|
-
for
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
849
|
+
return []
|
|
850
|
+
|
|
851
|
+
tool_calls: list[ToolCall] = []
|
|
852
|
+
for item in self.final_response.output:
|
|
853
|
+
if isinstance(item, ResponseFunctionToolCall):
|
|
854
|
+
call_id = item.call_id
|
|
855
|
+
name = item.name
|
|
856
|
+
arguments = item.arguments
|
|
857
|
+
if call_id and name is not None:
|
|
858
|
+
tool_calls.append(
|
|
859
|
+
ToolCall(
|
|
860
|
+
id=call_id,
|
|
861
|
+
function=FunctionCall(
|
|
862
|
+
name=name,
|
|
863
|
+
arguments=arguments,
|
|
864
|
+
),
|
|
865
|
+
)
|
|
863
866
|
)
|
|
864
|
-
)
|
|
865
867
|
|
|
866
|
-
|
|
867
|
-
raise ValueError("No tool calls available")
|
|
868
|
-
if len(tool_calls) > 1:
|
|
869
|
-
raise ValueError(f"Got {len(tool_calls)} tool calls, expected 1")
|
|
868
|
+
return tool_calls
|
|
870
869
|
|
|
871
|
-
|
|
870
|
+
def get_tool_call_object(self) -> ToolCall:
|
|
871
|
+
calls = self.get_tool_call_objects()
|
|
872
|
+
if not calls:
|
|
873
|
+
raise ValueError("No tool calls available")
|
|
874
|
+
return calls[0]
|
|
872
875
|
|
|
873
876
|
async def process(
|
|
874
877
|
self,
|