letta-nightly 0.4.1.dev20241008104105__py3-none-any.whl → 0.4.1.dev20241009104130__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/agent.py +18 -2
- letta/client/client.py +8 -1
- letta/credentials.py +2 -2
- letta/functions/schema_generator.py +1 -1
- letta/llm_api/anthropic.py +3 -24
- letta/llm_api/azure_openai.py +47 -98
- letta/llm_api/azure_openai_constants.py +10 -0
- letta/llm_api/google_ai.py +39 -64
- letta/llm_api/helpers.py +57 -2
- letta/llm_api/llm_api_tools.py +4 -3
- letta/llm_api/openai.py +5 -49
- letta/main.py +1 -1
- letta/metadata.py +2 -0
- letta/providers.py +139 -30
- letta/schemas/agent.py +14 -0
- letta/schemas/llm_config.py +0 -3
- letta/schemas/openai/chat_completion_response.py +3 -0
- letta/schemas/tool.py +3 -3
- letta/server/rest_api/routers/openai/assistants/threads.py +5 -5
- letta/server/rest_api/routers/openai/chat_completions/chat_completions.py +2 -2
- letta/server/rest_api/routers/v1/agents.py +11 -11
- letta/server/rest_api/routers/v1/blocks.py +2 -2
- letta/server/rest_api/routers/v1/jobs.py +2 -2
- letta/server/rest_api/routers/v1/sources.py +12 -12
- letta/server/rest_api/routers/v1/tools.py +6 -6
- letta/server/server.py +18 -5
- letta/settings.py +3 -112
- {letta_nightly-0.4.1.dev20241008104105.dist-info → letta_nightly-0.4.1.dev20241009104130.dist-info}/METADATA +1 -1
- {letta_nightly-0.4.1.dev20241008104105.dist-info → letta_nightly-0.4.1.dev20241009104130.dist-info}/RECORD +32 -31
- {letta_nightly-0.4.1.dev20241008104105.dist-info → letta_nightly-0.4.1.dev20241009104130.dist-info}/LICENSE +0 -0
- {letta_nightly-0.4.1.dev20241008104105.dist-info → letta_nightly-0.4.1.dev20241009104130.dist-info}/WHEEL +0 -0
- {letta_nightly-0.4.1.dev20241008104105.dist-info → letta_nightly-0.4.1.dev20241009104130.dist-info}/entry_points.txt +0 -0
letta/llm_api/llm_api_tools.py
CHANGED
|
@@ -28,7 +28,6 @@ from letta.local_llm.constants import (
|
|
|
28
28
|
INNER_THOUGHTS_KWARG,
|
|
29
29
|
INNER_THOUGHTS_KWARG_DESCRIPTION,
|
|
30
30
|
)
|
|
31
|
-
from letta.providers import GoogleAIProvider
|
|
32
31
|
from letta.schemas.enums import OptionState
|
|
33
32
|
from letta.schemas.llm_config import LLMConfig
|
|
34
33
|
from letta.schemas.message import Message
|
|
@@ -189,6 +188,9 @@ def create(
|
|
|
189
188
|
if model_settings.azure_base_url is None:
|
|
190
189
|
raise ValueError(f"Azure base url is missing. Did you set AZURE_BASE_URL in your env?")
|
|
191
190
|
|
|
191
|
+
if model_settings.azure_api_version is None:
|
|
192
|
+
raise ValueError(f"Azure API version is missing. Did you set AZURE_API_VERSION in your env?")
|
|
193
|
+
|
|
192
194
|
# Set the llm config model_endpoint from model_settings
|
|
193
195
|
# For Azure, this model_endpoint is required to be configured via env variable, so users don't need to provide it in the LLM config
|
|
194
196
|
llm_config.model_endpoint = model_settings.azure_base_url
|
|
@@ -228,7 +230,7 @@ def create(
|
|
|
228
230
|
|
|
229
231
|
return google_ai_chat_completions_request(
|
|
230
232
|
inner_thoughts_in_kwargs=google_ai_inner_thoughts_in_kwarg,
|
|
231
|
-
|
|
233
|
+
base_url=llm_config.model_endpoint,
|
|
232
234
|
model=llm_config.model,
|
|
233
235
|
api_key=model_settings.gemini_api_key,
|
|
234
236
|
# see structure of payload here: https://ai.google.dev/docs/function_calling
|
|
@@ -296,7 +298,6 @@ def create(
|
|
|
296
298
|
raise NotImplementedError(f"Streaming not yet implemented for Groq.")
|
|
297
299
|
|
|
298
300
|
if model_settings.groq_api_key is None and llm_config.model_endpoint == "https://api.groq.com/openai/v1/chat/completions":
|
|
299
|
-
# only is a problem if we are *not* using an openai proxy
|
|
300
301
|
raise ValueError(f"Groq key is missing from letta config file")
|
|
301
302
|
|
|
302
303
|
# force to true for groq, since they don't support 'content' is non-null
|
letta/llm_api/openai.py
CHANGED
|
@@ -9,7 +9,7 @@ from httpx_sse._exceptions import SSEError
|
|
|
9
9
|
|
|
10
10
|
from letta.constants import OPENAI_CONTEXT_WINDOW_ERROR_SUBSTRING
|
|
11
11
|
from letta.errors import LLMError
|
|
12
|
-
from letta.llm_api.helpers import add_inner_thoughts_to_functions
|
|
12
|
+
from letta.llm_api.helpers import add_inner_thoughts_to_functions, make_post_request
|
|
13
13
|
from letta.local_llm.constants import (
|
|
14
14
|
INNER_THOUGHTS_KWARG,
|
|
15
15
|
INNER_THOUGHTS_KWARG_DESCRIPTION,
|
|
@@ -483,58 +483,14 @@ def openai_chat_completions_request(
|
|
|
483
483
|
data.pop("tools")
|
|
484
484
|
data.pop("tool_choice", None) # extra safe, should exist always (default="auto")
|
|
485
485
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
response = requests.post(url, headers=headers, json=data)
|
|
489
|
-
printd(f"response = {response}, response.text = {response.text}")
|
|
490
|
-
# print(json.dumps(data, indent=4))
|
|
491
|
-
# raise requests.exceptions.HTTPError
|
|
492
|
-
response.raise_for_status() # Raises HTTPError for 4XX/5XX status
|
|
493
|
-
|
|
494
|
-
response = response.json() # convert to dict from string
|
|
495
|
-
printd(f"response.json = {response}")
|
|
496
|
-
|
|
497
|
-
response = ChatCompletionResponse(**response) # convert to 'dot-dict' style which is the openai python client default
|
|
498
|
-
return response
|
|
499
|
-
except requests.exceptions.HTTPError as http_err:
|
|
500
|
-
# Handle HTTP errors (e.g., response 4XX, 5XX)
|
|
501
|
-
printd(f"Got HTTPError, exception={http_err}, payload={data}")
|
|
502
|
-
raise http_err
|
|
503
|
-
except requests.exceptions.RequestException as req_err:
|
|
504
|
-
# Handle other requests-related errors (e.g., connection error)
|
|
505
|
-
printd(f"Got RequestException, exception={req_err}")
|
|
506
|
-
raise req_err
|
|
507
|
-
except Exception as e:
|
|
508
|
-
# Handle other potential errors
|
|
509
|
-
printd(f"Got unknown Exception, exception={e}")
|
|
510
|
-
raise e
|
|
486
|
+
response_json = make_post_request(url, headers, data)
|
|
487
|
+
return ChatCompletionResponse(**response_json)
|
|
511
488
|
|
|
512
489
|
|
|
513
490
|
def openai_embeddings_request(url: str, api_key: str, data: dict) -> EmbeddingResponse:
|
|
514
491
|
"""https://platform.openai.com/docs/api-reference/embeddings/create"""
|
|
515
|
-
from letta.utils import printd
|
|
516
492
|
|
|
517
493
|
url = smart_urljoin(url, "embeddings")
|
|
518
494
|
headers = {"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
try:
|
|
522
|
-
response = requests.post(url, headers=headers, json=data)
|
|
523
|
-
printd(f"response = {response}")
|
|
524
|
-
response.raise_for_status() # Raises HTTPError for 4XX/5XX status
|
|
525
|
-
response = response.json() # convert to dict from string
|
|
526
|
-
printd(f"response.json = {response}")
|
|
527
|
-
response = EmbeddingResponse(**response) # convert to 'dot-dict' style which is the openai python client default
|
|
528
|
-
return response
|
|
529
|
-
except requests.exceptions.HTTPError as http_err:
|
|
530
|
-
# Handle HTTP errors (e.g., response 4XX, 5XX)
|
|
531
|
-
printd(f"Got HTTPError, exception={http_err}, payload={data}")
|
|
532
|
-
raise http_err
|
|
533
|
-
except requests.exceptions.RequestException as req_err:
|
|
534
|
-
# Handle other requests-related errors (e.g., connection error)
|
|
535
|
-
printd(f"Got RequestException, exception={req_err}")
|
|
536
|
-
raise req_err
|
|
537
|
-
except Exception as e:
|
|
538
|
-
# Handle other potential errors
|
|
539
|
-
printd(f"Got unknown Exception, exception={e}")
|
|
540
|
-
raise e
|
|
495
|
+
response_json = make_post_request(url, headers, data)
|
|
496
|
+
return EmbeddingResponse(**response_json)
|
letta/main.py
CHANGED
|
@@ -366,7 +366,7 @@ def run_agent_loop(
|
|
|
366
366
|
first_message=False,
|
|
367
367
|
skip_verify=no_verify,
|
|
368
368
|
stream=stream,
|
|
369
|
-
|
|
369
|
+
inner_thoughts_in_kwargs_option=inner_thoughts_in_kwargs,
|
|
370
370
|
ms=ms,
|
|
371
371
|
)
|
|
372
372
|
new_messages = step_response.messages
|
letta/metadata.py
CHANGED
|
@@ -218,6 +218,7 @@ class AgentModel(Base):
|
|
|
218
218
|
tools = Column(JSON)
|
|
219
219
|
|
|
220
220
|
# configs
|
|
221
|
+
agent_type = Column(String)
|
|
221
222
|
llm_config = Column(LLMConfigColumn)
|
|
222
223
|
embedding_config = Column(EmbeddingConfigColumn)
|
|
223
224
|
|
|
@@ -243,6 +244,7 @@ class AgentModel(Base):
|
|
|
243
244
|
memory=Memory.load(self.memory), # load dictionary
|
|
244
245
|
system=self.system,
|
|
245
246
|
tools=self.tools,
|
|
247
|
+
agent_type=self.agent_type,
|
|
246
248
|
llm_config=self.llm_config,
|
|
247
249
|
embedding_config=self.embedding_config,
|
|
248
250
|
metadata_=self.metadata_,
|
letta/providers.py
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
from typing import List, Optional
|
|
2
2
|
|
|
3
|
-
from pydantic import BaseModel, Field
|
|
3
|
+
from pydantic import BaseModel, Field, model_validator
|
|
4
4
|
|
|
5
5
|
from letta.constants import LLM_MAX_TOKENS
|
|
6
|
+
from letta.llm_api.azure_openai import (
|
|
7
|
+
get_azure_chat_completions_endpoint,
|
|
8
|
+
get_azure_embeddings_endpoint,
|
|
9
|
+
)
|
|
10
|
+
from letta.llm_api.azure_openai_constants import AZURE_MODEL_TO_CONTEXT_LENGTH
|
|
6
11
|
from letta.schemas.embedding_config import EmbeddingConfig
|
|
7
12
|
from letta.schemas.llm_config import LLMConfig
|
|
8
13
|
|
|
@@ -122,34 +127,64 @@ class OllamaProvider(OpenAIProvider):
|
|
|
122
127
|
response = requests.post(f"{self.base_url}/api/show", json={"name": model_name, "verbose": True})
|
|
123
128
|
response_json = response.json()
|
|
124
129
|
|
|
125
|
-
|
|
126
|
-
possible_keys = [
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
]
|
|
142
|
-
|
|
130
|
+
## thank you vLLM: https://github.com/vllm-project/vllm/blob/main/vllm/config.py#L1675
|
|
131
|
+
# possible_keys = [
|
|
132
|
+
# # OPT
|
|
133
|
+
# "max_position_embeddings",
|
|
134
|
+
# # GPT-2
|
|
135
|
+
# "n_positions",
|
|
136
|
+
# # MPT
|
|
137
|
+
# "max_seq_len",
|
|
138
|
+
# # ChatGLM2
|
|
139
|
+
# "seq_length",
|
|
140
|
+
# # Command-R
|
|
141
|
+
# "model_max_length",
|
|
142
|
+
# # Others
|
|
143
|
+
# "max_sequence_length",
|
|
144
|
+
# "max_seq_length",
|
|
145
|
+
# "seq_len",
|
|
146
|
+
# ]
|
|
143
147
|
# max_position_embeddings
|
|
144
148
|
# parse model cards: nous, dolphon, llama
|
|
145
149
|
for key, value in response_json["model_info"].items():
|
|
146
|
-
if "
|
|
150
|
+
if "context_length" in key:
|
|
151
|
+
return value
|
|
152
|
+
return None
|
|
153
|
+
|
|
154
|
+
def get_model_embedding_dim(self, model_name: str):
|
|
155
|
+
import requests
|
|
156
|
+
|
|
157
|
+
response = requests.post(f"{self.base_url}/api/show", json={"name": model_name, "verbose": True})
|
|
158
|
+
response_json = response.json()
|
|
159
|
+
for key, value in response_json["model_info"].items():
|
|
160
|
+
if "embedding_length" in key:
|
|
147
161
|
return value
|
|
148
162
|
return None
|
|
149
163
|
|
|
150
164
|
def list_embedding_models(self) -> List[EmbeddingConfig]:
|
|
151
|
-
#
|
|
152
|
-
|
|
165
|
+
# https://github.com/ollama/ollama/blob/main/docs/api.md#list-local-models
|
|
166
|
+
import requests
|
|
167
|
+
|
|
168
|
+
response = requests.get(f"{self.base_url}/api/tags")
|
|
169
|
+
if response.status_code != 200:
|
|
170
|
+
raise Exception(f"Failed to list Ollama models: {response.text}")
|
|
171
|
+
response_json = response.json()
|
|
172
|
+
|
|
173
|
+
configs = []
|
|
174
|
+
for model in response_json["models"]:
|
|
175
|
+
embedding_dim = self.get_model_embedding_dim(model["name"])
|
|
176
|
+
if not embedding_dim:
|
|
177
|
+
continue
|
|
178
|
+
configs.append(
|
|
179
|
+
EmbeddingConfig(
|
|
180
|
+
embedding_model=model["name"],
|
|
181
|
+
embedding_endpoint_type="ollama",
|
|
182
|
+
embedding_endpoint=self.base_url,
|
|
183
|
+
embedding_dim=embedding_dim,
|
|
184
|
+
embedding_chunk_size=300,
|
|
185
|
+
)
|
|
186
|
+
)
|
|
187
|
+
return configs
|
|
153
188
|
|
|
154
189
|
|
|
155
190
|
class GroqProvider(OpenAIProvider):
|
|
@@ -182,20 +217,21 @@ class GroqProvider(OpenAIProvider):
|
|
|
182
217
|
class GoogleAIProvider(Provider):
|
|
183
218
|
# gemini
|
|
184
219
|
api_key: str = Field(..., description="API key for the Google AI API.")
|
|
185
|
-
service_endpoint: str = "generativelanguage"
|
|
186
220
|
base_url: str = "https://generativelanguage.googleapis.com"
|
|
187
221
|
|
|
188
222
|
def list_llm_models(self):
|
|
189
223
|
from letta.llm_api.google_ai import google_ai_get_model_list
|
|
190
224
|
|
|
191
|
-
|
|
192
|
-
|
|
225
|
+
model_options = google_ai_get_model_list(base_url=self.base_url, api_key=self.api_key)
|
|
226
|
+
# filter by 'generateContent' models
|
|
227
|
+
model_options = [mo for mo in model_options if "generateContent" in mo["supportedGenerationMethods"]]
|
|
193
228
|
model_options = [str(m["name"]) for m in model_options]
|
|
229
|
+
|
|
230
|
+
# filter by model names
|
|
194
231
|
model_options = [mo[len("models/") :] if mo.startswith("models/") else mo for mo in model_options]
|
|
232
|
+
|
|
195
233
|
# TODO remove manual filtering for gemini-pro
|
|
196
234
|
model_options = [mo for mo in model_options if str(mo).startswith("gemini") and "-pro" in str(mo)]
|
|
197
|
-
# TODO: add context windows
|
|
198
|
-
# model_options = ["gemini-pro"]
|
|
199
235
|
|
|
200
236
|
configs = []
|
|
201
237
|
for model in model_options:
|
|
@@ -210,21 +246,94 @@ class GoogleAIProvider(Provider):
|
|
|
210
246
|
return configs
|
|
211
247
|
|
|
212
248
|
def list_embedding_models(self):
|
|
213
|
-
|
|
249
|
+
from letta.llm_api.google_ai import google_ai_get_model_list
|
|
250
|
+
|
|
251
|
+
# TODO: use base_url instead
|
|
252
|
+
model_options = google_ai_get_model_list(base_url=self.base_url, api_key=self.api_key)
|
|
253
|
+
# filter by 'generateContent' models
|
|
254
|
+
model_options = [mo for mo in model_options if "embedContent" in mo["supportedGenerationMethods"]]
|
|
255
|
+
model_options = [str(m["name"]) for m in model_options]
|
|
256
|
+
model_options = [mo[len("models/") :] if mo.startswith("models/") else mo for mo in model_options]
|
|
257
|
+
|
|
258
|
+
configs = []
|
|
259
|
+
for model in model_options:
|
|
260
|
+
configs.append(
|
|
261
|
+
EmbeddingConfig(
|
|
262
|
+
embedding_model=model,
|
|
263
|
+
embedding_endpoint_type="google_ai",
|
|
264
|
+
embedding_endpoint=self.base_url,
|
|
265
|
+
embedding_dim=768,
|
|
266
|
+
embedding_chunk_size=300, # NOTE: max is 2048
|
|
267
|
+
)
|
|
268
|
+
)
|
|
269
|
+
return configs
|
|
214
270
|
|
|
215
271
|
def get_model_context_window(self, model_name: str):
|
|
216
272
|
from letta.llm_api.google_ai import google_ai_get_model_context_window
|
|
217
273
|
|
|
218
|
-
|
|
219
|
-
return google_ai_get_model_context_window(self.service_endpoint, self.api_key, model_name)
|
|
274
|
+
return google_ai_get_model_context_window(self.base_url, self.api_key, model_name)
|
|
220
275
|
|
|
221
276
|
|
|
222
277
|
class AzureProvider(Provider):
|
|
223
278
|
name: str = "azure"
|
|
279
|
+
latest_api_version: str = "2024-09-01-preview" # https://learn.microsoft.com/en-us/azure/ai-services/openai/api-version-deprecation
|
|
224
280
|
base_url: str = Field(
|
|
225
281
|
..., description="Base URL for the Azure API endpoint. This should be specific to your org, e.g. `https://letta.openai.azure.com`."
|
|
226
282
|
)
|
|
227
283
|
api_key: str = Field(..., description="API key for the Azure API.")
|
|
284
|
+
api_version: str = Field(latest_api_version, description="API version for the Azure API")
|
|
285
|
+
|
|
286
|
+
@model_validator(mode="before")
|
|
287
|
+
def set_default_api_version(cls, values):
|
|
288
|
+
"""
|
|
289
|
+
This ensures that api_version is always set to the default if None is passed in.
|
|
290
|
+
"""
|
|
291
|
+
if values.get("api_version") is None:
|
|
292
|
+
values["api_version"] = cls.model_fields["latest_api_version"].default
|
|
293
|
+
return values
|
|
294
|
+
|
|
295
|
+
def list_llm_models(self) -> List[LLMConfig]:
|
|
296
|
+
from letta.llm_api.azure_openai import (
|
|
297
|
+
azure_openai_get_chat_completion_model_list,
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
model_options = azure_openai_get_chat_completion_model_list(self.base_url, api_key=self.api_key, api_version=self.api_version)
|
|
301
|
+
configs = []
|
|
302
|
+
for model_option in model_options:
|
|
303
|
+
model_name = model_option["id"]
|
|
304
|
+
context_window_size = self.get_model_context_window(model_name)
|
|
305
|
+
model_endpoint = get_azure_chat_completions_endpoint(self.base_url, model_name, self.api_version)
|
|
306
|
+
configs.append(
|
|
307
|
+
LLMConfig(model=model_name, model_endpoint_type="azure", model_endpoint=model_endpoint, context_window=context_window_size)
|
|
308
|
+
)
|
|
309
|
+
return configs
|
|
310
|
+
|
|
311
|
+
def list_embedding_models(self) -> List[EmbeddingConfig]:
|
|
312
|
+
from letta.llm_api.azure_openai import azure_openai_get_embeddings_model_list
|
|
313
|
+
|
|
314
|
+
model_options = azure_openai_get_embeddings_model_list(
|
|
315
|
+
self.base_url, api_key=self.api_key, api_version=self.api_version, require_embedding_in_name=True
|
|
316
|
+
)
|
|
317
|
+
configs = []
|
|
318
|
+
for model_option in model_options:
|
|
319
|
+
model_name = model_option["id"]
|
|
320
|
+
model_endpoint = get_azure_embeddings_endpoint(self.base_url, model_name, self.api_version)
|
|
321
|
+
configs.append(
|
|
322
|
+
EmbeddingConfig(
|
|
323
|
+
embedding_model=model_name,
|
|
324
|
+
embedding_endpoint_type="azure",
|
|
325
|
+
embedding_endpoint=model_endpoint,
|
|
326
|
+
embedding_dim=768,
|
|
327
|
+
embedding_chunk_size=300, # NOTE: max is 2048
|
|
328
|
+
)
|
|
329
|
+
)
|
|
330
|
+
return configs
|
|
331
|
+
|
|
332
|
+
def get_model_context_window(self, model_name: str):
|
|
333
|
+
"""
|
|
334
|
+
This is hardcoded for now, since there is no API endpoints to retrieve metadata for a model.
|
|
335
|
+
"""
|
|
336
|
+
return AZURE_MODEL_TO_CONTEXT_LENGTH.get(model_name, 4096)
|
|
228
337
|
|
|
229
338
|
|
|
230
339
|
class VLLMProvider(OpenAIProvider):
|
letta/schemas/agent.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import uuid
|
|
2
2
|
from datetime import datetime
|
|
3
|
+
from enum import Enum
|
|
3
4
|
from typing import Dict, List, Optional, Union
|
|
4
5
|
|
|
5
6
|
from pydantic import BaseModel, Field, field_validator
|
|
@@ -21,6 +22,15 @@ class BaseAgent(LettaBase, validate_assignment=True):
|
|
|
21
22
|
user_id: Optional[str] = Field(None, description="The user id of the agent.")
|
|
22
23
|
|
|
23
24
|
|
|
25
|
+
class AgentType(str, Enum):
|
|
26
|
+
"""
|
|
27
|
+
Enum to represent the type of agent.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
memgpt_agent = "memgpt_agent"
|
|
31
|
+
split_thread_agent = "split_thread_agent"
|
|
32
|
+
|
|
33
|
+
|
|
24
34
|
class AgentState(BaseAgent):
|
|
25
35
|
"""
|
|
26
36
|
Representation of an agent's state. This is the state of the agent at a given time, and is persisted in the DB backend. The state has all the information needed to recreate a persisted agent.
|
|
@@ -52,6 +62,9 @@ class AgentState(BaseAgent):
|
|
|
52
62
|
# system prompt
|
|
53
63
|
system: str = Field(..., description="The system prompt used by the agent.")
|
|
54
64
|
|
|
65
|
+
# agent configuration
|
|
66
|
+
agent_type: AgentType = Field(..., description="The type of agent.")
|
|
67
|
+
|
|
55
68
|
# llm information
|
|
56
69
|
llm_config: LLMConfig = Field(..., description="The LLM configuration used by the agent.")
|
|
57
70
|
embedding_config: EmbeddingConfig = Field(..., description="The embedding configuration used by the agent.")
|
|
@@ -64,6 +77,7 @@ class CreateAgent(BaseAgent):
|
|
|
64
77
|
memory: Optional[Memory] = Field(None, description="The in-context memory of the agent.")
|
|
65
78
|
tools: Optional[List[str]] = Field(None, description="The tools used by the agent.")
|
|
66
79
|
system: Optional[str] = Field(None, description="The system prompt used by the agent.")
|
|
80
|
+
agent_type: Optional[AgentType] = Field(None, description="The type of agent.")
|
|
67
81
|
llm_config: Optional[LLMConfig] = Field(None, description="The LLM configuration used by the agent.")
|
|
68
82
|
embedding_config: Optional[EmbeddingConfig] = Field(None, description="The embedding configuration used by the agent.")
|
|
69
83
|
|
letta/schemas/llm_config.py
CHANGED
|
@@ -35,9 +35,6 @@ class LLMConfig(BaseModel):
|
|
|
35
35
|
"hugging-face",
|
|
36
36
|
] = Field(..., description="The endpoint type for the model.")
|
|
37
37
|
model_endpoint: Optional[str] = Field(None, description="The endpoint for the model.")
|
|
38
|
-
api_version: Optional[str] = Field(
|
|
39
|
-
None, description="The version for the model API. Used by the Azure provider backend, e.g. 2023-03-15-preview."
|
|
40
|
-
)
|
|
41
38
|
model_wrapper: Optional[str] = Field(None, description="The wrapper for the model.")
|
|
42
39
|
context_window: int = Field(..., description="The context window size for the model.")
|
|
43
40
|
|
|
@@ -74,6 +74,9 @@ class ChatCompletionResponse(BaseModel):
|
|
|
74
74
|
object: Literal["chat.completion"] = "chat.completion"
|
|
75
75
|
usage: UsageStatistics
|
|
76
76
|
|
|
77
|
+
def __str__(self):
|
|
78
|
+
return self.model_dump_json(indent=4)
|
|
79
|
+
|
|
77
80
|
|
|
78
81
|
class FunctionCallDelta(BaseModel):
|
|
79
82
|
# arguments: Optional[str] = None
|
letta/schemas/tool.py
CHANGED
|
@@ -93,7 +93,7 @@ class Tool(BaseTool):
|
|
|
93
93
|
# append heartbeat (necessary for triggering another reasoning step after this tool call)
|
|
94
94
|
json_schema["parameters"]["properties"]["request_heartbeat"] = {
|
|
95
95
|
"type": "boolean",
|
|
96
|
-
"description": "Request an immediate heartbeat after function execution. Set to
|
|
96
|
+
"description": "Request an immediate heartbeat after function execution. Set to `True` if you want to send a follow-up message or run a follow-up function.",
|
|
97
97
|
}
|
|
98
98
|
json_schema["parameters"]["required"].append("request_heartbeat")
|
|
99
99
|
|
|
@@ -128,7 +128,7 @@ class Tool(BaseTool):
|
|
|
128
128
|
# append heartbeat (necessary for triggering another reasoning step after this tool call)
|
|
129
129
|
json_schema["parameters"]["properties"]["request_heartbeat"] = {
|
|
130
130
|
"type": "boolean",
|
|
131
|
-
"description": "Request an immediate heartbeat after function execution. Set to
|
|
131
|
+
"description": "Request an immediate heartbeat after function execution. Set to `True` if you want to send a follow-up message or run a follow-up function.",
|
|
132
132
|
}
|
|
133
133
|
json_schema["parameters"]["required"].append("request_heartbeat")
|
|
134
134
|
|
|
@@ -161,7 +161,7 @@ class Tool(BaseTool):
|
|
|
161
161
|
# append heartbeat (necessary for triggering another reasoning step after this tool call)
|
|
162
162
|
json_schema["parameters"]["properties"]["request_heartbeat"] = {
|
|
163
163
|
"type": "boolean",
|
|
164
|
-
"description": "Request an immediate heartbeat after function execution. Set to
|
|
164
|
+
"description": "Request an immediate heartbeat after function execution. Set to `True` if you want to send a follow-up message or run a follow-up function.",
|
|
165
165
|
}
|
|
166
166
|
json_schema["parameters"]["required"].append("request_heartbeat")
|
|
167
167
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import uuid
|
|
2
|
-
from typing import TYPE_CHECKING, List
|
|
2
|
+
from typing import TYPE_CHECKING, List, Optional
|
|
3
3
|
|
|
4
4
|
from fastapi import APIRouter, Body, Depends, Header, HTTPException, Path, Query
|
|
5
5
|
|
|
@@ -43,7 +43,7 @@ router = APIRouter(prefix="/v1/threads", tags=["threads"])
|
|
|
43
43
|
def create_thread(
|
|
44
44
|
request: CreateThreadRequest = Body(...),
|
|
45
45
|
server: SyncServer = Depends(get_letta_server),
|
|
46
|
-
user_id: str = Header(None), # Extract user_id from header, default to None if not present
|
|
46
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
47
47
|
):
|
|
48
48
|
# TODO: use requests.description and requests.metadata fields
|
|
49
49
|
# TODO: handle requests.file_ids and requests.tools
|
|
@@ -68,7 +68,7 @@ def create_thread(
|
|
|
68
68
|
def retrieve_thread(
|
|
69
69
|
thread_id: str = Path(..., description="The unique identifier of the thread."),
|
|
70
70
|
server: SyncServer = Depends(get_letta_server),
|
|
71
|
-
user_id: str = Header(None), # Extract user_id from header, default to None if not present
|
|
71
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
72
72
|
):
|
|
73
73
|
actor = server.get_user_or_default(user_id=user_id)
|
|
74
74
|
agent = server.get_agent(user_id=actor.id, agent_id=thread_id)
|
|
@@ -102,7 +102,7 @@ def create_message(
|
|
|
102
102
|
thread_id: str = Path(..., description="The unique identifier of the thread."),
|
|
103
103
|
request: CreateMessageRequest = Body(...),
|
|
104
104
|
server: SyncServer = Depends(get_letta_server),
|
|
105
|
-
user_id: str = Header(None), # Extract user_id from header, default to None if not present
|
|
105
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
106
106
|
):
|
|
107
107
|
actor = server.get_user_or_default(user_id=user_id)
|
|
108
108
|
agent_id = thread_id
|
|
@@ -146,7 +146,7 @@ def list_messages(
|
|
|
146
146
|
after: str = Query(None, description="A cursor for use in pagination. `after` is an object ID that defines your place in the list."),
|
|
147
147
|
before: str = Query(None, description="A cursor for use in pagination. `after` is an object ID that defines your place in the list."),
|
|
148
148
|
server: SyncServer = Depends(get_letta_server),
|
|
149
|
-
user_id: str = Header(None), # Extract user_id from header, default to None if not present
|
|
149
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
150
150
|
):
|
|
151
151
|
actor = server.get_user_or_default(user_id)
|
|
152
152
|
after_uuid = after if before else None
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import json
|
|
2
|
-
from typing import TYPE_CHECKING
|
|
2
|
+
from typing import TYPE_CHECKING, Optional
|
|
3
3
|
|
|
4
4
|
from fastapi import APIRouter, Body, Depends, Header, HTTPException
|
|
5
5
|
|
|
@@ -30,7 +30,7 @@ router = APIRouter(prefix="/v1/chat/completions", tags=["chat_completions"])
|
|
|
30
30
|
async def create_chat_completion(
|
|
31
31
|
completion_request: ChatCompletionRequest = Body(...),
|
|
32
32
|
server: "SyncServer" = Depends(get_letta_server),
|
|
33
|
-
user_id: str = Header(None), # Extract user_id from header, default to None if not present
|
|
33
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
34
34
|
):
|
|
35
35
|
"""Send a message to a Letta agent via a /chat/completions completion_request
|
|
36
36
|
The bearer token will be used to identify the user.
|
|
@@ -40,7 +40,7 @@ router = APIRouter(prefix="/agents", tags=["agents"])
|
|
|
40
40
|
@router.get("/", response_model=List[AgentState], operation_id="list_agents")
|
|
41
41
|
def list_agents(
|
|
42
42
|
server: "SyncServer" = Depends(get_letta_server),
|
|
43
|
-
user_id: str = Header(None), # Extract user_id from header, default to None if not present
|
|
43
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
44
44
|
):
|
|
45
45
|
"""
|
|
46
46
|
List all agents associated with a given user.
|
|
@@ -55,7 +55,7 @@ def list_agents(
|
|
|
55
55
|
def create_agent(
|
|
56
56
|
agent: CreateAgent = Body(...),
|
|
57
57
|
server: "SyncServer" = Depends(get_letta_server),
|
|
58
|
-
user_id: str = Header(None), # Extract user_id from header, default to None if not present
|
|
58
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
59
59
|
):
|
|
60
60
|
"""
|
|
61
61
|
Create a new agent with the specified configuration.
|
|
@@ -76,7 +76,7 @@ def update_agent(
|
|
|
76
76
|
agent_id: str,
|
|
77
77
|
update_agent: UpdateAgentState = Body(...),
|
|
78
78
|
server: "SyncServer" = Depends(get_letta_server),
|
|
79
|
-
user_id: str = Header(None), # Extract user_id from header, default to None if not present
|
|
79
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
80
80
|
):
|
|
81
81
|
"""Update an exsiting agent"""
|
|
82
82
|
actor = server.get_user_or_default(user_id=user_id)
|
|
@@ -89,7 +89,7 @@ def update_agent(
|
|
|
89
89
|
def get_agent_state(
|
|
90
90
|
agent_id: str,
|
|
91
91
|
server: "SyncServer" = Depends(get_letta_server),
|
|
92
|
-
user_id: str = Header(None), # Extract user_id from header, default to None if not present
|
|
92
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
93
93
|
):
|
|
94
94
|
"""
|
|
95
95
|
Get the state of the agent.
|
|
@@ -107,7 +107,7 @@ def get_agent_state(
|
|
|
107
107
|
def delete_agent(
|
|
108
108
|
agent_id: str,
|
|
109
109
|
server: "SyncServer" = Depends(get_letta_server),
|
|
110
|
-
user_id: str = Header(None), # Extract user_id from header, default to None if not present
|
|
110
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
111
111
|
):
|
|
112
112
|
"""
|
|
113
113
|
Delete an agent.
|
|
@@ -159,7 +159,7 @@ def update_agent_memory(
|
|
|
159
159
|
agent_id: str,
|
|
160
160
|
request: Dict = Body(...),
|
|
161
161
|
server: "SyncServer" = Depends(get_letta_server),
|
|
162
|
-
user_id: str = Header(None), # Extract user_id from header, default to None if not present
|
|
162
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
163
163
|
):
|
|
164
164
|
"""
|
|
165
165
|
Update the core memory of a specific agent.
|
|
@@ -202,7 +202,7 @@ def get_agent_archival_memory(
|
|
|
202
202
|
after: Optional[int] = Query(None, description="Unique ID of the memory to start the query range at."),
|
|
203
203
|
before: Optional[int] = Query(None, description="Unique ID of the memory to end the query range at."),
|
|
204
204
|
limit: Optional[int] = Query(None, description="How many results to include in the response."),
|
|
205
|
-
user_id: str = Header(None), # Extract user_id from header, default to None if not present
|
|
205
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
206
206
|
):
|
|
207
207
|
"""
|
|
208
208
|
Retrieve the memories in an agent's archival memory store (paginated query).
|
|
@@ -227,7 +227,7 @@ def insert_agent_archival_memory(
|
|
|
227
227
|
agent_id: str,
|
|
228
228
|
request: CreateArchivalMemory = Body(...),
|
|
229
229
|
server: "SyncServer" = Depends(get_letta_server),
|
|
230
|
-
user_id: str = Header(None), # Extract user_id from header, default to None if not present
|
|
230
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
231
231
|
):
|
|
232
232
|
"""
|
|
233
233
|
Insert a memory into an agent's archival memory store.
|
|
@@ -245,7 +245,7 @@ def delete_agent_archival_memory(
|
|
|
245
245
|
memory_id: str,
|
|
246
246
|
# memory_id: str = Query(..., description="Unique ID of the memory to be deleted."),
|
|
247
247
|
server: "SyncServer" = Depends(get_letta_server),
|
|
248
|
-
user_id: str = Header(None), # Extract user_id from header, default to None if not present
|
|
248
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
249
249
|
):
|
|
250
250
|
"""
|
|
251
251
|
Delete a memory from an agent's archival memory store.
|
|
@@ -276,7 +276,7 @@ def get_agent_messages(
|
|
|
276
276
|
DEFAULT_MESSAGE_TOOL_KWARG,
|
|
277
277
|
description="[Only applicable if use_assistant_message is True] The name of the message argument in the designated message tool.",
|
|
278
278
|
),
|
|
279
|
-
user_id: str = Header(None), # Extract user_id from header, default to None if not present
|
|
279
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
280
280
|
):
|
|
281
281
|
"""
|
|
282
282
|
Retrieve message history for an agent.
|
|
@@ -315,7 +315,7 @@ async def send_message(
|
|
|
315
315
|
agent_id: str,
|
|
316
316
|
server: SyncServer = Depends(get_letta_server),
|
|
317
317
|
request: LettaRequest = Body(...),
|
|
318
|
-
user_id: str = Header(None), # Extract user_id from header, default to None if not present
|
|
318
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
319
319
|
):
|
|
320
320
|
"""
|
|
321
321
|
Process a user message and return the agent's response.
|
|
@@ -19,7 +19,7 @@ def list_blocks(
|
|
|
19
19
|
templates_only: bool = Query(True, description="Whether to include only templates"),
|
|
20
20
|
name: Optional[str] = Query(None, description="Name of the block"),
|
|
21
21
|
server: SyncServer = Depends(get_letta_server),
|
|
22
|
-
user_id: str = Header(None), # Extract user_id from header, default to None if not present
|
|
22
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
23
23
|
):
|
|
24
24
|
actor = server.get_user_or_default(user_id=user_id)
|
|
25
25
|
|
|
@@ -33,7 +33,7 @@ def list_blocks(
|
|
|
33
33
|
def create_block(
|
|
34
34
|
create_block: CreateBlock = Body(...),
|
|
35
35
|
server: SyncServer = Depends(get_letta_server),
|
|
36
|
-
user_id: str = Header(None), # Extract user_id from header, default to None if not present
|
|
36
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
37
37
|
):
|
|
38
38
|
actor = server.get_user_or_default(user_id=user_id)
|
|
39
39
|
|
|
@@ -13,7 +13,7 @@ router = APIRouter(prefix="/jobs", tags=["jobs"])
|
|
|
13
13
|
def list_jobs(
|
|
14
14
|
server: "SyncServer" = Depends(get_letta_server),
|
|
15
15
|
source_id: Optional[str] = Query(None, description="Only list jobs associated with the source."),
|
|
16
|
-
user_id: str = Header(None), # Extract user_id from header, default to None if not present
|
|
16
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
17
17
|
):
|
|
18
18
|
"""
|
|
19
19
|
List all jobs.
|
|
@@ -34,7 +34,7 @@ def list_jobs(
|
|
|
34
34
|
@router.get("/active", response_model=List[Job], operation_id="list_active_jobs")
|
|
35
35
|
def list_active_jobs(
|
|
36
36
|
server: "SyncServer" = Depends(get_letta_server),
|
|
37
|
-
user_id: str = Header(None), # Extract user_id from header, default to None if not present
|
|
37
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
38
38
|
):
|
|
39
39
|
"""
|
|
40
40
|
List all active jobs.
|