letta-nightly 0.13.0.dev20251031104146__py3-none-any.whl → 0.13.1.dev20251101010313__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 +127 -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 +11 -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 +5 -4
- 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/agent.py +3 -0
- letta/orm/organization.py +4 -0
- letta/orm/sqlalchemy_base.py +7 -0
- letta/otel/tracing.py +131 -4
- letta/schemas/agent.py +108 -40
- 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 +123 -4
- letta/schemas/mcp.py +3 -2
- letta/schemas/mcp_server.py +3 -2
- letta/schemas/message.py +167 -49
- letta/schemas/model.py +265 -0
- 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 +12 -1
- letta/server/server.py +20 -4
- 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_sandbox/e2b_sandbox.py +4 -1
- letta/services/tool_schema_generator.py +2 -2
- letta/validators.py +21 -0
- {letta_nightly-0.13.0.dev20251031104146.dist-info → letta_nightly-0.13.1.dev20251101010313.dist-info}/METADATA +1 -1
- {letta_nightly-0.13.0.dev20251031104146.dist-info → letta_nightly-0.13.1.dev20251101010313.dist-info}/RECORD +93 -87
- 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.dev20251031104146.dist-info → letta_nightly-0.13.1.dev20251101010313.dist-info}/WHEEL +0 -0
- {letta_nightly-0.13.0.dev20251031104146.dist-info → letta_nightly-0.13.1.dev20251101010313.dist-info}/entry_points.txt +0 -0
- {letta_nightly-0.13.0.dev20251031104146.dist-info → letta_nightly-0.13.1.dev20251101010313.dist-info}/licenses/LICENSE +0 -0
letta/schemas/model.py
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
from typing import Annotated, Literal, Optional, Union
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
from letta.schemas.embedding_config import EmbeddingConfig
|
|
6
|
+
from letta.schemas.enums import ProviderType
|
|
7
|
+
from letta.schemas.llm_config import LLMConfig
|
|
8
|
+
from letta.schemas.response_format import ResponseFormatUnion
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ModelBase(BaseModel):
|
|
12
|
+
handle: str = Field(..., description="Unique handle for API reference (format: provider_display_name/model_display_name)")
|
|
13
|
+
name: str = Field(..., description="The actual model name used by the provider")
|
|
14
|
+
display_name: str = Field(..., description="Display name for the model shown in UI")
|
|
15
|
+
provider_type: ProviderType = Field(..., description="The type of the provider")
|
|
16
|
+
provider_name: str = Field(..., description="The name of the provider")
|
|
17
|
+
model_type: Literal["llm", "embedding"] = Field(..., description="Type of model (llm or embedding)")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Model(ModelBase):
|
|
21
|
+
model_type: Literal["llm"] = Field("llm", description="Type of model (llm or embedding)")
|
|
22
|
+
max_context_window: int = Field(..., description="The maximum context window for the model")
|
|
23
|
+
# supports_token_streaming: Optional[bool] = Field(None, description="Whether token streaming is supported")
|
|
24
|
+
# supports_tool_calling: Optional[bool] = Field(None, description="Whether tool calling is supported")
|
|
25
|
+
|
|
26
|
+
def _from_llm_config(self, llm_config: LLMConfig) -> "Model":
|
|
27
|
+
return self(
|
|
28
|
+
handle=llm_config.handle,
|
|
29
|
+
name=llm_config.model,
|
|
30
|
+
display_name=llm_config.display_name,
|
|
31
|
+
provider_type=llm_config.model_endpoint_type,
|
|
32
|
+
provider_name=llm_config.provider_name,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class EmbeddingModel(ModelBase):
|
|
37
|
+
model_type: Literal["embedding"] = Field("embedding", description="Type of model (llm or embedding)")
|
|
38
|
+
embedding_dim: int = Field(..., description="The dimension of the embedding")
|
|
39
|
+
|
|
40
|
+
def _from_embedding_config(self, embedding_config: EmbeddingConfig) -> "Model":
|
|
41
|
+
return self(
|
|
42
|
+
handle=embedding_config.handle,
|
|
43
|
+
name=embedding_config.embedding_model,
|
|
44
|
+
display_name=embedding_config.embedding_model,
|
|
45
|
+
provider_type=embedding_config.embedding_endpoint_type,
|
|
46
|
+
provider_name=embedding_config.embedding_endpoint_type,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class ModelSettings(BaseModel):
|
|
51
|
+
"""Schema for defining settings for a model"""
|
|
52
|
+
|
|
53
|
+
model: str = Field(..., description="The name of the model.")
|
|
54
|
+
max_output_tokens: int = Field(4096, description="The maximum number of tokens the model can generate.")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class OpenAIReasoning(BaseModel):
|
|
58
|
+
reasoning_effort: Literal["minimal", "low", "medium", "high"] = Field(
|
|
59
|
+
"minimal", description="The reasoning effort to use when generating text reasoning models"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# TODO: implement support for this
|
|
63
|
+
# summary: Optional[Literal["auto", "detailed"]] = Field(
|
|
64
|
+
# None, description="The reasoning summary level to use when generating text reasoning models"
|
|
65
|
+
# )
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class OpenAIModelSettings(ModelSettings):
|
|
69
|
+
provider: Literal["openai"] = Field("openai", description="The provider of the model.")
|
|
70
|
+
temperature: float = Field(0.7, description="The temperature of the model.")
|
|
71
|
+
reasoning: OpenAIReasoning = Field(OpenAIReasoning(reasoning_effort="high"), description="The reasoning configuration for the model.")
|
|
72
|
+
response_format: Optional[ResponseFormatUnion] = Field(None, description="The response format for the model.")
|
|
73
|
+
|
|
74
|
+
# TODO: implement support for these
|
|
75
|
+
# reasoning_summary: Optional[Literal["none", "short", "detailed"]] = Field(
|
|
76
|
+
# None, description="The reasoning summary level to use when generating text reasoning models"
|
|
77
|
+
# )
|
|
78
|
+
# max_tool_calls: int = Field(10, description="The maximum number of tool calls the model can make.")
|
|
79
|
+
# parallel_tool_calls: bool = Field(False, description="Whether the model supports parallel tool calls.")
|
|
80
|
+
# top_logprobs: int = Field(10, description="The number of top logprobs to return.")
|
|
81
|
+
# top_p: float = Field(1.0, description="The top-p value to use when generating text.")
|
|
82
|
+
|
|
83
|
+
def _to_legacy_config_params(self) -> dict:
|
|
84
|
+
return {
|
|
85
|
+
"temperature": self.temperature,
|
|
86
|
+
"max_tokens": self.max_output_tokens,
|
|
87
|
+
"reasoning_effort": self.reasoning.reasoning_effort,
|
|
88
|
+
"response_format": self.response_format,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# "thinking": {
|
|
93
|
+
# "type": "enabled",
|
|
94
|
+
# "budget_tokens": 10000
|
|
95
|
+
# }
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class AnthropicThinking(BaseModel):
|
|
99
|
+
type: Literal["enabled", "disabled"] = Field("enabled", description="The type of thinking to use.")
|
|
100
|
+
budget_tokens: int = Field(1024, description="The maximum number of tokens the model can use for extended thinking.")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class AnthropicModelSettings(ModelSettings):
|
|
104
|
+
provider: Literal["anthropic"] = Field("anthropic", description="The provider of the model.")
|
|
105
|
+
temperature: float = Field(1.0, description="The temperature of the model.")
|
|
106
|
+
thinking: AnthropicThinking = Field(
|
|
107
|
+
AnthropicThinking(type="enabled", budget_tokens=1024), description="The thinking configuration for the model."
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# gpt-5 models only
|
|
111
|
+
verbosity: Optional[Literal["low", "medium", "high"]] = Field(
|
|
112
|
+
None,
|
|
113
|
+
description="Soft control for how verbose model output should be, used for GPT-5 models.",
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# TODO: implement support for these
|
|
117
|
+
# top_k: Optional[int] = Field(None, description="The number of top tokens to return.")
|
|
118
|
+
# top_p: Optional[float] = Field(None, description="The top-p value to use when generating text.")
|
|
119
|
+
|
|
120
|
+
def _to_legacy_config_params(self) -> dict:
|
|
121
|
+
return {
|
|
122
|
+
"temperature": self.temperature,
|
|
123
|
+
"max_tokens": self.max_output_tokens,
|
|
124
|
+
"extended_thinking": self.thinking.type == "enabled",
|
|
125
|
+
"thinking_budget_tokens": self.thinking.budget_tokens,
|
|
126
|
+
"verbosity": self.verbosity,
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class GeminiThinkingConfig(BaseModel):
|
|
131
|
+
include_thoughts: bool = Field(True, description="Whether to include thoughts in the model's response.")
|
|
132
|
+
thinking_budget: int = Field(1024, description="The thinking budget for the model.")
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class GoogleAIModelSettings(ModelSettings):
|
|
136
|
+
provider: Literal["google_ai"] = Field("google_ai", description="The provider of the model.")
|
|
137
|
+
temperature: float = Field(0.7, description="The temperature of the model.")
|
|
138
|
+
thinking_config: GeminiThinkingConfig = Field(
|
|
139
|
+
GeminiThinkingConfig(include_thoughts=True, thinking_budget=1024), description="The thinking configuration for the model."
|
|
140
|
+
)
|
|
141
|
+
response_schema: Optional[ResponseFormatUnion] = Field(None, description="The response schema for the model.")
|
|
142
|
+
max_output_tokens: int = Field(65536, description="The maximum number of tokens the model can generate.")
|
|
143
|
+
|
|
144
|
+
def _to_legacy_config_params(self) -> dict:
|
|
145
|
+
return {
|
|
146
|
+
"temperature": self.temperature,
|
|
147
|
+
"max_tokens": self.max_output_tokens,
|
|
148
|
+
"max_reasoning_tokens": self.thinking_config.thinking_budget if self.thinking_config.include_thoughts else 0,
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class GoogleVertexModelSettings(GoogleAIModelSettings):
|
|
153
|
+
provider: Literal["google_vertex"] = Field("google_vertex", description="The provider of the model.")
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class AzureModelSettings(ModelSettings):
|
|
157
|
+
"""Azure OpenAI model configuration (OpenAI-compatible)."""
|
|
158
|
+
|
|
159
|
+
provider: Literal["azure"] = Field("azure", description="The provider of the model.")
|
|
160
|
+
temperature: float = Field(0.7, description="The temperature of the model.")
|
|
161
|
+
response_format: Optional[ResponseFormatUnion] = Field(None, description="The response format for the model.")
|
|
162
|
+
|
|
163
|
+
def _to_legacy_config_params(self) -> dict:
|
|
164
|
+
return {
|
|
165
|
+
"temperature": self.temperature,
|
|
166
|
+
"max_tokens": self.max_output_tokens,
|
|
167
|
+
"response_format": self.response_format,
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class XAIModelSettings(ModelSettings):
|
|
172
|
+
"""xAI model configuration (OpenAI-compatible)."""
|
|
173
|
+
|
|
174
|
+
provider: Literal["xai"] = Field("xai", description="The provider of the model.")
|
|
175
|
+
temperature: float = Field(0.7, description="The temperature of the model.")
|
|
176
|
+
response_format: Optional[ResponseFormatUnion] = Field(None, description="The response format for the model.")
|
|
177
|
+
|
|
178
|
+
def _to_legacy_config_params(self) -> dict:
|
|
179
|
+
return {
|
|
180
|
+
"temperature": self.temperature,
|
|
181
|
+
"max_tokens": self.max_output_tokens,
|
|
182
|
+
"response_format": self.response_format,
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
class GroqModelSettings(ModelSettings):
|
|
187
|
+
"""Groq model configuration (OpenAI-compatible)."""
|
|
188
|
+
|
|
189
|
+
provider: Literal["groq"] = Field("groq", description="The provider of the model.")
|
|
190
|
+
temperature: float = Field(0.7, description="The temperature of the model.")
|
|
191
|
+
response_format: Optional[ResponseFormatUnion] = Field(None, description="The response format for the model.")
|
|
192
|
+
|
|
193
|
+
def _to_legacy_config_params(self) -> dict:
|
|
194
|
+
return {
|
|
195
|
+
"temperature": self.temperature,
|
|
196
|
+
"max_tokens": self.max_output_tokens,
|
|
197
|
+
"response_format": self.response_format,
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
class DeepseekModelSettings(ModelSettings):
|
|
202
|
+
"""Deepseek model configuration (OpenAI-compatible)."""
|
|
203
|
+
|
|
204
|
+
provider: Literal["deepseek"] = Field("deepseek", description="The provider of the model.")
|
|
205
|
+
temperature: float = Field(0.7, description="The temperature of the model.")
|
|
206
|
+
response_format: Optional[ResponseFormatUnion] = Field(None, description="The response format for the model.")
|
|
207
|
+
|
|
208
|
+
def _to_legacy_config_params(self) -> dict:
|
|
209
|
+
return {
|
|
210
|
+
"temperature": self.temperature,
|
|
211
|
+
"max_tokens": self.max_output_tokens,
|
|
212
|
+
"response_format": self.response_format,
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class TogetherModelSettings(ModelSettings):
|
|
217
|
+
"""Together AI model configuration (OpenAI-compatible)."""
|
|
218
|
+
|
|
219
|
+
provider: Literal["together"] = Field("together", description="The provider of the model.")
|
|
220
|
+
temperature: float = Field(0.7, description="The temperature of the model.")
|
|
221
|
+
response_format: Optional[ResponseFormatUnion] = Field(None, description="The response format for the model.")
|
|
222
|
+
|
|
223
|
+
def _to_legacy_config_params(self) -> dict:
|
|
224
|
+
return {
|
|
225
|
+
"temperature": self.temperature,
|
|
226
|
+
"max_tokens": self.max_output_tokens,
|
|
227
|
+
"response_format": self.response_format,
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class BedrockModelSettings(ModelSettings):
|
|
232
|
+
"""AWS Bedrock model configuration."""
|
|
233
|
+
|
|
234
|
+
provider: Literal["bedrock"] = Field("bedrock", description="The provider of the model.")
|
|
235
|
+
temperature: float = Field(0.7, description="The temperature of the model.")
|
|
236
|
+
response_format: Optional[ResponseFormatUnion] = Field(None, description="The response format for the model.")
|
|
237
|
+
|
|
238
|
+
def _to_legacy_config_params(self) -> dict:
|
|
239
|
+
return {
|
|
240
|
+
"temperature": self.temperature,
|
|
241
|
+
"max_tokens": self.max_output_tokens,
|
|
242
|
+
"response_format": self.response_format,
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
ModelSettingsUnion = Annotated[
|
|
247
|
+
Union[
|
|
248
|
+
OpenAIModelSettings,
|
|
249
|
+
AnthropicModelSettings,
|
|
250
|
+
GoogleAIModelSettings,
|
|
251
|
+
GoogleVertexModelSettings,
|
|
252
|
+
AzureModelSettings,
|
|
253
|
+
XAIModelSettings,
|
|
254
|
+
GroqModelSettings,
|
|
255
|
+
DeepseekModelSettings,
|
|
256
|
+
TogetherModelSettings,
|
|
257
|
+
BedrockModelSettings,
|
|
258
|
+
],
|
|
259
|
+
Field(discriminator="provider"),
|
|
260
|
+
]
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
class EmbeddingModelSettings(BaseModel):
|
|
264
|
+
model: str = Field(..., description="The name of the model.")
|
|
265
|
+
provider: Literal["openai", "ollama"] = Field(..., description="The provider of the model.")
|
letta/schemas/organization.py
CHANGED
|
@@ -4,12 +4,13 @@ from typing import Optional
|
|
|
4
4
|
from pydantic import Field
|
|
5
5
|
|
|
6
6
|
from letta.helpers.datetime_helpers import get_utc_time
|
|
7
|
+
from letta.schemas.enums import PrimitiveType
|
|
7
8
|
from letta.schemas.letta_base import LettaBase
|
|
8
9
|
from letta.utils import create_random_username
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class OrganizationBase(LettaBase):
|
|
12
|
-
__id_prefix__ =
|
|
13
|
+
__id_prefix__ = PrimitiveType.ORGANIZATION.value
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class Organization(OrganizationBase):
|
letta/schemas/passage.py
CHANGED
|
@@ -6,11 +6,12 @@ from pydantic import Field, field_validator
|
|
|
6
6
|
from letta.constants import MAX_EMBEDDING_DIM
|
|
7
7
|
from letta.helpers.datetime_helpers import get_utc_time
|
|
8
8
|
from letta.schemas.embedding_config import EmbeddingConfig
|
|
9
|
+
from letta.schemas.enums import PrimitiveType
|
|
9
10
|
from letta.schemas.letta_base import OrmMetadataBase
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class PassageBase(OrmMetadataBase):
|
|
13
|
-
__id_prefix__ =
|
|
14
|
+
__id_prefix__ = PrimitiveType.PASSAGE.value
|
|
14
15
|
|
|
15
16
|
is_deleted: bool = Field(False, description="Whether this passage is deleted or not.")
|
|
16
17
|
|
letta/schemas/provider_trace.py
CHANGED
|
@@ -6,11 +6,12 @@ from typing import Any, Dict, Optional
|
|
|
6
6
|
from pydantic import BaseModel, Field
|
|
7
7
|
|
|
8
8
|
from letta.helpers.datetime_helpers import get_utc_time
|
|
9
|
+
from letta.schemas.enums import PrimitiveType
|
|
9
10
|
from letta.schemas.letta_base import OrmMetadataBase
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class BaseProviderTrace(OrmMetadataBase):
|
|
13
|
-
__id_prefix__ =
|
|
14
|
+
__id_prefix__ = PrimitiveType.PROVIDER_TRACE.value
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class ProviderTraceCreate(BaseModel):
|
|
@@ -21,7 +21,6 @@ class OpenRouterProvider(OpenAIProvider):
|
|
|
21
21
|
provider_category: ProviderCategory = Field(ProviderCategory.base, description="The category of the provider (base or byok)")
|
|
22
22
|
api_key: str = Field(..., description="API key for the OpenRouter API.")
|
|
23
23
|
base_url: str = Field("https://openrouter.ai/api/v1", description="Base URL for the OpenRouter API.")
|
|
24
|
-
handle_base: str | None = Field(None, description="Custom handle base name for model handles (e.g., 'custom' instead of 'openrouter').")
|
|
25
24
|
|
|
26
25
|
def _list_llm_models(self, data: list[dict]) -> list[LLMConfig]:
|
|
27
26
|
"""
|
|
@@ -34,7 +33,7 @@ class OpenRouterProvider(OpenAIProvider):
|
|
|
34
33
|
continue
|
|
35
34
|
model_name, context_window_size = check
|
|
36
35
|
|
|
37
|
-
handle = self.get_handle(model_name
|
|
36
|
+
handle = self.get_handle(model_name)
|
|
38
37
|
|
|
39
38
|
config = LLMConfig(
|
|
40
39
|
model=model_name,
|
letta/schemas/run_metrics.py
CHANGED
|
@@ -2,11 +2,12 @@ from typing import List, Optional
|
|
|
2
2
|
|
|
3
3
|
from pydantic import Field
|
|
4
4
|
|
|
5
|
+
from letta.schemas.enums import PrimitiveType
|
|
5
6
|
from letta.schemas.letta_base import LettaBase
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class RunMetricsBase(LettaBase):
|
|
9
|
-
__id_prefix__ =
|
|
10
|
+
__id_prefix__ = PrimitiveType.RUN.value
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class RunMetrics(RunMetricsBase):
|
letta/schemas/sandbox_config.py
CHANGED
|
@@ -102,7 +102,9 @@ class SandboxConfig(SandboxConfigBase):
|
|
|
102
102
|
config: Dict = Field(default_factory=lambda: {}, description="The JSON sandbox settings data.")
|
|
103
103
|
|
|
104
104
|
def get_e2b_config(self) -> E2BSandboxConfig:
|
|
105
|
-
|
|
105
|
+
config_dict = self.config.copy()
|
|
106
|
+
config_dict["template"] = tool_settings.e2b_sandbox_template_id
|
|
107
|
+
return E2BSandboxConfig(**config_dict)
|
|
106
108
|
|
|
107
109
|
def get_local_config(self) -> LocalSandboxConfig:
|
|
108
110
|
return LocalSandboxConfig(**self.config)
|
letta/schemas/step_metrics.py
CHANGED
|
@@ -2,11 +2,12 @@ from typing import Optional
|
|
|
2
2
|
|
|
3
3
|
from pydantic import Field
|
|
4
4
|
|
|
5
|
+
from letta.schemas.enums import PrimitiveType
|
|
5
6
|
from letta.schemas.letta_base import LettaBase
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class StepMetricsBase(LettaBase):
|
|
9
|
-
__id_prefix__ =
|
|
10
|
+
__id_prefix__ = PrimitiveType.STEP.value
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class StepMetrics(StepMetricsBase):
|
letta/schemas/tool_rule.py
CHANGED
|
@@ -4,14 +4,14 @@ from typing import Annotated, Any, Dict, List, Literal, Optional, Set, Union
|
|
|
4
4
|
|
|
5
5
|
from pydantic import BaseModel, Field, field_validator, model_validator
|
|
6
6
|
|
|
7
|
-
from letta.schemas.enums import ToolRuleType
|
|
7
|
+
from letta.schemas.enums import PrimitiveType, ToolRuleType
|
|
8
8
|
from letta.schemas.letta_base import LettaBase
|
|
9
9
|
|
|
10
10
|
logger = logging.getLogger(__name__)
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class BaseToolRule(LettaBase):
|
|
14
|
-
__id_prefix__ =
|
|
14
|
+
__id_prefix__ = PrimitiveType.TOOL_RULE.value
|
|
15
15
|
tool_name: str = Field(..., description="The name of the tool. Must exist in the database for the user's organization.")
|
|
16
16
|
type: ToolRuleType = Field(..., description="The type of the message.")
|
|
17
17
|
prompt_template: Optional[str] = Field(
|
letta/schemas/user.py
CHANGED
|
@@ -4,11 +4,12 @@ from typing import Optional
|
|
|
4
4
|
from pydantic import Field
|
|
5
5
|
|
|
6
6
|
from letta.constants import DEFAULT_ORG_ID
|
|
7
|
+
from letta.schemas.enums import PrimitiveType
|
|
7
8
|
from letta.schemas.letta_base import LettaBase
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
class UserBase(LettaBase):
|
|
11
|
-
__id_prefix__ =
|
|
12
|
+
__id_prefix__ = PrimitiveType.USER.value
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
class User(UserBase):
|
letta/server/rest_api/app.py
CHANGED
|
@@ -26,6 +26,7 @@ from letta.errors import (
|
|
|
26
26
|
AgentFileImportError,
|
|
27
27
|
AgentNotFoundForExportError,
|
|
28
28
|
BedrockPermissionError,
|
|
29
|
+
HandleNotFoundError,
|
|
29
30
|
LettaAgentNotFoundError,
|
|
30
31
|
LettaExpiredError,
|
|
31
32
|
LettaInvalidArgumentError,
|
|
@@ -39,6 +40,7 @@ from letta.errors import (
|
|
|
39
40
|
LettaUserNotFoundError,
|
|
40
41
|
LLMAuthenticationError,
|
|
41
42
|
LLMError,
|
|
43
|
+
LLMProviderOverloaded,
|
|
42
44
|
LLMRateLimitError,
|
|
43
45
|
LLMTimeoutError,
|
|
44
46
|
PendingApprovalError,
|
|
@@ -270,7 +272,7 @@ def create_application() -> "FastAPI":
|
|
|
270
272
|
return JSONResponse(
|
|
271
273
|
status_code=500,
|
|
272
274
|
content={
|
|
273
|
-
"detail": "An
|
|
275
|
+
"detail": "An unknown error occurred",
|
|
274
276
|
# Only include error details in debug/development mode
|
|
275
277
|
# "debug_info": str(exc) if settings.debug else None
|
|
276
278
|
},
|
|
@@ -369,6 +371,7 @@ def create_application() -> "FastAPI":
|
|
|
369
371
|
app.add_exception_handler(LettaAgentNotFoundError, _error_handler_404_agent)
|
|
370
372
|
app.add_exception_handler(LettaUserNotFoundError, _error_handler_404_user)
|
|
371
373
|
app.add_exception_handler(AgentNotFoundForExportError, _error_handler_404)
|
|
374
|
+
app.add_exception_handler(HandleNotFoundError, _error_handler_404)
|
|
372
375
|
|
|
373
376
|
# 410 Expired errors
|
|
374
377
|
app.add_exception_handler(LettaExpiredError, _error_handler_410)
|
|
@@ -396,6 +399,7 @@ def create_application() -> "FastAPI":
|
|
|
396
399
|
# 503 Service Unavailable errors
|
|
397
400
|
app.add_exception_handler(OperationalError, _error_handler_503)
|
|
398
401
|
app.add_exception_handler(LettaServiceUnavailableError, _error_handler_503)
|
|
402
|
+
app.add_exception_handler(LLMProviderOverloaded, _error_handler_503)
|
|
399
403
|
|
|
400
404
|
@app.exception_handler(IncompatibleAgentType)
|
|
401
405
|
async def handle_incompatible_agent_type(request: Request, exc: IncompatibleAgentType):
|
|
@@ -7,6 +7,8 @@ from letta.server.rest_api.routers.v1.folders import router as folders_router
|
|
|
7
7
|
from letta.server.rest_api.routers.v1.groups import router as groups_router
|
|
8
8
|
from letta.server.rest_api.routers.v1.health import router as health_router
|
|
9
9
|
from letta.server.rest_api.routers.v1.identities import router as identities_router
|
|
10
|
+
from letta.server.rest_api.routers.v1.internal_agents import router as internal_agents_router
|
|
11
|
+
from letta.server.rest_api.routers.v1.internal_blocks import router as internal_blocks_router
|
|
10
12
|
from letta.server.rest_api.routers.v1.internal_runs import router as internal_runs_router
|
|
11
13
|
from letta.server.rest_api.routers.v1.internal_templates import router as internal_templates_router
|
|
12
14
|
from letta.server.rest_api.routers.v1.jobs import router as jobs_router
|
|
@@ -32,6 +34,8 @@ ROUTERS = [
|
|
|
32
34
|
chat_completions_router,
|
|
33
35
|
groups_router,
|
|
34
36
|
identities_router,
|
|
37
|
+
internal_agents_router,
|
|
38
|
+
internal_blocks_router,
|
|
35
39
|
internal_runs_router,
|
|
36
40
|
internal_templates_router,
|
|
37
41
|
llm_router,
|
|
@@ -25,21 +25,23 @@ from letta.errors import (
|
|
|
25
25
|
AgentNotFoundForExportError,
|
|
26
26
|
PendingApprovalError,
|
|
27
27
|
)
|
|
28
|
-
from letta.
|
|
28
|
+
from letta.groups.sleeptime_multi_agent_v4 import SleeptimeMultiAgentV4
|
|
29
|
+
from letta.helpers.datetime_helpers import get_utc_time, get_utc_timestamp_ns
|
|
29
30
|
from letta.log import get_logger
|
|
30
31
|
from letta.orm.errors import NoResultFound
|
|
31
32
|
from letta.otel.context import get_ctx_attributes
|
|
32
33
|
from letta.otel.metric_registry import MetricRegistry
|
|
33
34
|
from letta.schemas.agent import AgentRelationships, AgentState, CreateAgent, UpdateAgent
|
|
34
35
|
from letta.schemas.agent_file import AgentFileSchema
|
|
35
|
-
from letta.schemas.block import BaseBlock, Block, BlockUpdate
|
|
36
|
-
from letta.schemas.enums import AgentType, RunStatus
|
|
36
|
+
from letta.schemas.block import BaseBlock, Block, BlockResponse, BlockUpdate
|
|
37
|
+
from letta.schemas.enums import AgentType, MessageRole, RunStatus
|
|
37
38
|
from letta.schemas.file import AgentFileAttachment, FileMetadataBase, PaginatedAgentFiles
|
|
38
39
|
from letta.schemas.group import Group
|
|
39
40
|
from letta.schemas.job import LettaRequestConfig
|
|
40
41
|
from letta.schemas.letta_message import LettaMessageUnion, LettaMessageUpdateUnion, MessageType
|
|
42
|
+
from letta.schemas.letta_message_content import TextContent
|
|
41
43
|
from letta.schemas.letta_request import LettaAsyncRequest, LettaRequest, LettaStreamingRequest
|
|
42
|
-
from letta.schemas.letta_response import LettaResponse
|
|
44
|
+
from letta.schemas.letta_response import LettaResponse, LettaStreamingResponse
|
|
43
45
|
from letta.schemas.letta_stop_reason import StopReasonType
|
|
44
46
|
from letta.schemas.memory import (
|
|
45
47
|
ArchivalMemorySearchResponse,
|
|
@@ -48,7 +50,7 @@ from letta.schemas.memory import (
|
|
|
48
50
|
CreateArchivalMemory,
|
|
49
51
|
Memory,
|
|
50
52
|
)
|
|
51
|
-
from letta.schemas.message import
|
|
53
|
+
from letta.schemas.message import Message, MessageCreate, MessageCreateType, MessageSearchRequest, MessageSearchResult
|
|
52
54
|
from letta.schemas.passage import Passage
|
|
53
55
|
from letta.schemas.run import Run as PydanticRun, RunUpdate
|
|
54
56
|
from letta.schemas.source import BaseSource, Source
|
|
@@ -915,7 +917,7 @@ async def retrieve_agent_memory(
|
|
|
915
917
|
return await server.get_agent_memory_async(agent_id=agent_id, actor=actor)
|
|
916
918
|
|
|
917
919
|
|
|
918
|
-
@router.get("/{agent_id}/core-memory/blocks/{block_label}", response_model=
|
|
920
|
+
@router.get("/{agent_id}/core-memory/blocks/{block_label}", response_model=BlockResponse, operation_id="retrieve_core_memory_block")
|
|
919
921
|
async def retrieve_block_for_agent(
|
|
920
922
|
block_label: str,
|
|
921
923
|
agent_id: AgentId,
|
|
@@ -930,7 +932,7 @@ async def retrieve_block_for_agent(
|
|
|
930
932
|
return await server.agent_manager.get_block_with_label_async(agent_id=agent_id, block_label=block_label, actor=actor)
|
|
931
933
|
|
|
932
934
|
|
|
933
|
-
@router.get("/{agent_id}/core-memory/blocks", response_model=list[
|
|
935
|
+
@router.get("/{agent_id}/core-memory/blocks", response_model=list[BlockResponse], operation_id="list_core_memory_blocks")
|
|
934
936
|
async def list_blocks_for_agent(
|
|
935
937
|
agent_id: AgentId,
|
|
936
938
|
server: "SyncServer" = Depends(get_letta_server),
|
|
@@ -962,7 +964,7 @@ async def list_blocks_for_agent(
|
|
|
962
964
|
)
|
|
963
965
|
|
|
964
966
|
|
|
965
|
-
@router.patch("/{agent_id}/core-memory/blocks/{block_label}", response_model=
|
|
967
|
+
@router.patch("/{agent_id}/core-memory/blocks/{block_label}", response_model=BlockResponse, operation_id="modify_core_memory_block")
|
|
966
968
|
async def modify_block_for_agent(
|
|
967
969
|
block_label: str,
|
|
968
970
|
agent_id: AgentId,
|
|
@@ -1394,7 +1396,7 @@ async def send_message(
|
|
|
1394
1396
|
# noinspection PyInconsistentReturns
|
|
1395
1397
|
@router.post(
|
|
1396
1398
|
"/{agent_id}/messages/stream",
|
|
1397
|
-
response_model=
|
|
1399
|
+
response_model=LettaStreamingResponse,
|
|
1398
1400
|
operation_id="create_agent_message_stream",
|
|
1399
1401
|
responses={
|
|
1400
1402
|
200: {
|
|
@@ -1902,3 +1904,63 @@ async def summarize_messages(
|
|
|
1902
1904
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
1903
1905
|
detail="Summarization is not currently supported for this agent configuration. Please contact Letta support.",
|
|
1904
1906
|
)
|
|
1907
|
+
|
|
1908
|
+
|
|
1909
|
+
class CaptureMessagesRequest(BaseModel):
|
|
1910
|
+
provider: str
|
|
1911
|
+
model: str
|
|
1912
|
+
request_messages: list[dict[str, Any]]
|
|
1913
|
+
response_dict: dict[str, Any]
|
|
1914
|
+
|
|
1915
|
+
|
|
1916
|
+
@router.post("/{agent_id}/messages/capture", response_model=str, operation_id="capture_messages", include_in_schema=False)
|
|
1917
|
+
async def capture_messages(
|
|
1918
|
+
agent_id: AgentId,
|
|
1919
|
+
request: CaptureMessagesRequest = Body(...),
|
|
1920
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
1921
|
+
headers: HeaderParams = Depends(get_headers),
|
|
1922
|
+
):
|
|
1923
|
+
"""
|
|
1924
|
+
Capture a list of messages for an agent.
|
|
1925
|
+
"""
|
|
1926
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
|
1927
|
+
agent = await server.agent_manager.get_agent_by_id_async(agent_id, actor, include_relationships=["multi_agent_group"])
|
|
1928
|
+
|
|
1929
|
+
messages_to_persist = []
|
|
1930
|
+
|
|
1931
|
+
# Input user messages
|
|
1932
|
+
for message in request.request_messages:
|
|
1933
|
+
if message["role"] == "user":
|
|
1934
|
+
messages_to_persist.append(
|
|
1935
|
+
Message(
|
|
1936
|
+
role=MessageRole.user,
|
|
1937
|
+
content=[(TextContent(text=message["content"]))],
|
|
1938
|
+
agent_id=agent_id,
|
|
1939
|
+
tool_calls=None,
|
|
1940
|
+
tool_call_id=None,
|
|
1941
|
+
created_at=get_utc_time(),
|
|
1942
|
+
)
|
|
1943
|
+
)
|
|
1944
|
+
|
|
1945
|
+
# Assistant response
|
|
1946
|
+
messages_to_persist.append(
|
|
1947
|
+
Message(
|
|
1948
|
+
role=MessageRole.assistant,
|
|
1949
|
+
content=[(TextContent(text=request.response_dict["content"]))],
|
|
1950
|
+
agent_id=agent_id,
|
|
1951
|
+
model=request.model,
|
|
1952
|
+
tool_calls=None,
|
|
1953
|
+
tool_call_id=None,
|
|
1954
|
+
created_at=get_utc_time(),
|
|
1955
|
+
)
|
|
1956
|
+
)
|
|
1957
|
+
|
|
1958
|
+
response_messages = await server.message_manager.create_many_messages_async(messages_to_persist, actor=actor)
|
|
1959
|
+
|
|
1960
|
+
sleeptime_group = agent.multi_agent_group if agent.multi_agent_group and agent.multi_agent_group.manager_type == "sleeptime" else None
|
|
1961
|
+
if sleeptime_group:
|
|
1962
|
+
sleeptime_agent_loop = SleeptimeMultiAgentV4(agent_state=agent, actor=actor, group=sleeptime_group)
|
|
1963
|
+
sleeptime_agent_loop.response_messages = response_messages
|
|
1964
|
+
run_ids = await sleeptime_agent_loop.run_sleeptime_agents()
|
|
1965
|
+
|
|
1966
|
+
return JSONResponse({"success": True, "messages_created": len(response_messages), "run_ids": run_ids})
|
|
@@ -4,7 +4,7 @@ from fastapi import APIRouter, Body, Depends, HTTPException, Query
|
|
|
4
4
|
|
|
5
5
|
from letta.orm.errors import NoResultFound
|
|
6
6
|
from letta.schemas.agent import AgentRelationships, AgentState
|
|
7
|
-
from letta.schemas.block import BaseBlock, Block, BlockUpdate, CreateBlock
|
|
7
|
+
from letta.schemas.block import BaseBlock, Block, BlockResponse, BlockUpdate, CreateBlock
|
|
8
8
|
from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server
|
|
9
9
|
from letta.server.server import SyncServer
|
|
10
10
|
from letta.utils import is_1_0_sdk_version
|
|
@@ -16,7 +16,7 @@ if TYPE_CHECKING:
|
|
|
16
16
|
router = APIRouter(prefix="/blocks", tags=["blocks"])
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
@router.get("/", response_model=List[
|
|
19
|
+
@router.get("/", response_model=List[BlockResponse], operation_id="list_blocks")
|
|
20
20
|
async def list_blocks(
|
|
21
21
|
# query parameters
|
|
22
22
|
label: Optional[str] = Query(None, description="Labels to include (e.g. human, persona)"),
|
|
@@ -117,7 +117,7 @@ async def count_blocks(
|
|
|
117
117
|
return await server.block_manager.size_async(actor=actor)
|
|
118
118
|
|
|
119
119
|
|
|
120
|
-
@router.post("/", response_model=
|
|
120
|
+
@router.post("/", response_model=BlockResponse, operation_id="create_block")
|
|
121
121
|
async def create_block(
|
|
122
122
|
create_block: CreateBlock = Body(...),
|
|
123
123
|
server: SyncServer = Depends(get_letta_server),
|
|
@@ -128,7 +128,7 @@ async def create_block(
|
|
|
128
128
|
return await server.block_manager.create_or_update_block_async(actor=actor, block=block)
|
|
129
129
|
|
|
130
130
|
|
|
131
|
-
@router.patch("/{block_id}", response_model=
|
|
131
|
+
@router.patch("/{block_id}", response_model=BlockResponse, operation_id="modify_block")
|
|
132
132
|
async def modify_block(
|
|
133
133
|
block_id: BlockId,
|
|
134
134
|
block_update: BlockUpdate = Body(...),
|
|
@@ -149,7 +149,7 @@ async def delete_block(
|
|
|
149
149
|
await server.block_manager.delete_block_async(block_id=block_id, actor=actor)
|
|
150
150
|
|
|
151
151
|
|
|
152
|
-
@router.get("/{block_id}", response_model=
|
|
152
|
+
@router.get("/{block_id}", response_model=BlockResponse, operation_id="retrieve_block")
|
|
153
153
|
async def retrieve_block(
|
|
154
154
|
block_id: BlockId,
|
|
155
155
|
server: SyncServer = Depends(get_letta_server),
|
|
@@ -214,7 +214,7 @@ async def list_agents_for_block(
|
|
|
214
214
|
return agents
|
|
215
215
|
|
|
216
216
|
|
|
217
|
-
@router.patch("/{block_id}/identities/attach/{identity_id}", response_model=
|
|
217
|
+
@router.patch("/{block_id}/identities/attach/{identity_id}", response_model=BlockResponse, operation_id="attach_identity_to_block")
|
|
218
218
|
async def attach_identity_to_block(
|
|
219
219
|
identity_id: str,
|
|
220
220
|
block_id: BlockId,
|
|
@@ -233,7 +233,7 @@ async def attach_identity_to_block(
|
|
|
233
233
|
return await server.block_manager.get_block_by_id_async(block_id=block_id, actor=actor)
|
|
234
234
|
|
|
235
235
|
|
|
236
|
-
@router.patch("/{block_id}/identities/detach/{identity_id}", response_model=
|
|
236
|
+
@router.patch("/{block_id}/identities/detach/{identity_id}", response_model=BlockResponse, operation_id="detach_identity_from_block")
|
|
237
237
|
async def detach_identity_from_block(
|
|
238
238
|
identity_id: str,
|
|
239
239
|
block_id: BlockId,
|