letta-nightly 0.4.1.dev20241010104112__py3-none-any.whl → 0.4.1.dev20241012104008__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 CHANGED
@@ -481,7 +481,7 @@ class Agent(BaseAgent):
481
481
  first_message=first_message,
482
482
  # streaming
483
483
  stream=stream,
484
- stream_inferface=self.interface,
484
+ stream_interface=self.interface,
485
485
  # putting inner thoughts in func args or not
486
486
  inner_thoughts_in_kwargs_option=inner_thoughts_in_kwargs_option,
487
487
  )
letta/agent_store/db.py CHANGED
@@ -18,13 +18,14 @@ from sqlalchemy import (
18
18
  select,
19
19
  text,
20
20
  )
21
- from sqlalchemy.orm import declarative_base, mapped_column
21
+ from sqlalchemy.orm import mapped_column
22
22
  from sqlalchemy.orm.session import close_all_sessions
23
23
  from sqlalchemy.sql import func
24
24
  from sqlalchemy_json import MutableJson
25
25
  from tqdm import tqdm
26
26
 
27
27
  from letta.agent_store.storage import StorageConnector, TableType
28
+ from letta.base import Base
28
29
  from letta.config import LettaConfig
29
30
  from letta.constants import MAX_EMBEDDING_DIM
30
31
  from letta.metadata import EmbeddingConfigColumn, ToolCallColumn
@@ -35,7 +36,6 @@ from letta.schemas.openai.chat_completions import ToolCall
35
36
  from letta.schemas.passage import Passage
36
37
  from letta.settings import settings
37
38
 
38
- Base = declarative_base()
39
39
  config = LettaConfig()
40
40
 
41
41
 
@@ -560,3 +560,9 @@ class SQLLiteStorageConnector(SQLStorageConnector):
560
560
 
561
561
  # Commit the changes to the database
562
562
  session.commit()
563
+
564
+
565
+ def attach_base():
566
+ # This should be invoked in server.py to make sure Base gets initialized properly
567
+ # DO NOT REMOVE
568
+ print("Initializing database...")
letta/base.py ADDED
@@ -0,0 +1,3 @@
1
+ from sqlalchemy.ext.declarative import declarative_base
2
+
3
+ Base = declarative_base()
letta/cli/cli.py CHANGED
@@ -233,25 +233,32 @@ def run(
233
233
  # choose from list of llm_configs
234
234
  llm_configs = client.list_llm_configs()
235
235
  llm_options = [llm_config.model for llm_config in llm_configs]
236
+ llm_choices = [questionary.Choice(title=llm_config.pretty_print(), value=llm_config) for llm_config in llm_configs]
237
+
236
238
  # select model
237
239
  if len(llm_options) == 0:
238
240
  raise ValueError("No LLM models found. Please enable a provider.")
239
241
  elif len(llm_options) == 1:
240
242
  llm_model_name = llm_options[0]
241
243
  else:
242
- llm_model_name = questionary.select("Select LLM model:", choices=llm_options).ask()
244
+ llm_model_name = questionary.select("Select LLM model:", choices=llm_choices).ask().model
243
245
  llm_config = [llm_config for llm_config in llm_configs if llm_config.model == llm_model_name][0]
244
246
 
245
247
  # choose form list of embedding configs
246
248
  embedding_configs = client.list_embedding_configs()
247
249
  embedding_options = [embedding_config.embedding_model for embedding_config in embedding_configs]
250
+
251
+ embedding_choices = [
252
+ questionary.Choice(title=embedding_config.pretty_print(), value=embedding_config) for embedding_config in embedding_configs
253
+ ]
254
+
248
255
  # select model
249
256
  if len(embedding_options) == 0:
250
257
  raise ValueError("No embedding models found. Please enable a provider.")
251
258
  elif len(embedding_options) == 1:
252
259
  embedding_model_name = embedding_options[0]
253
260
  else:
254
- embedding_model_name = questionary.select("Select embedding model:", choices=embedding_options).ask()
261
+ embedding_model_name = questionary.select("Select embedding model:", choices=embedding_choices).ask().embedding_model
255
262
  embedding_config = [
256
263
  embedding_config for embedding_config in embedding_configs if embedding_config.embedding_model == embedding_model_name
257
264
  ][0]
letta/constants.py CHANGED
@@ -75,6 +75,27 @@ NON_USER_MSG_PREFIX = "[This is an automated system message hidden from the user
75
75
  LLM_MAX_TOKENS = {
76
76
  "DEFAULT": 8192,
77
77
  ## OpenAI models: https://platform.openai.com/docs/models/overview
78
+ # "o1-preview
79
+ "chatgpt-4o-latest": 128000,
80
+ # "o1-preview-2024-09-12
81
+ "gpt-4o-2024-08-06": 128000,
82
+ "gpt-4-turbo-preview": 128000,
83
+ "gpt-4o": 128000,
84
+ "gpt-3.5-turbo-instruct": 16385,
85
+ "gpt-4-0125-preview": 128000,
86
+ "gpt-3.5-turbo-0125": 16385,
87
+ # "babbage-002": 128000,
88
+ # "davinci-002": 128000,
89
+ "gpt-4-turbo-2024-04-09": 128000,
90
+ # "gpt-4o-realtime-preview-2024-10-01
91
+ "gpt-4-turbo": 8192,
92
+ "gpt-4o-2024-05-13": 128000,
93
+ # "o1-mini
94
+ # "o1-mini-2024-09-12
95
+ # "gpt-3.5-turbo-instruct-0914
96
+ "gpt-4o-mini": 128000,
97
+ # "gpt-4o-realtime-preview
98
+ "gpt-4o-mini-2024-07-18": 128000,
78
99
  # gpt-4
79
100
  "gpt-4-1106-preview": 128000,
80
101
  "gpt-4": 8192,
@@ -79,7 +79,7 @@ def azure_openai_chat_completions_request(
79
79
  data.pop("tools")
80
80
  data.pop("tool_choice", None) # extra safe, should exist always (default="auto")
81
81
 
82
- url = get_azure_chat_completions_endpoint(model_settings.azure_base_url, llm_config.model, model_settings.api_version)
82
+ url = get_azure_chat_completions_endpoint(model_settings.azure_base_url, llm_config.model, model_settings.azure_api_version)
83
83
  response_json = make_post_request(url, headers, data)
84
84
  # NOTE: azure openai does not include "content" in the response when it is None, so we need to add it
85
85
  if "content" not in response_json["choices"][0].get("message"):
letta/llm_api/helpers.py CHANGED
@@ -153,6 +153,7 @@ def unpack_inner_thoughts_from_kwargs(choice: Choice, inner_thoughts_key: str) -
153
153
  return new_choice
154
154
  else:
155
155
  warnings.warn(f"Did not find inner thoughts in tool call: {str(tool_call)}")
156
+ return choice
156
157
 
157
158
  except json.JSONDecodeError as e:
158
159
  warnings.warn(f"Failed to strip inner thoughts from kwargs: {e}")
@@ -70,6 +70,10 @@ def retry_with_exponential_backoff(
70
70
  return func(*args, **kwargs)
71
71
 
72
72
  except requests.exceptions.HTTPError as http_err:
73
+
74
+ if not hasattr(http_err, "response") or not http_err.response:
75
+ raise
76
+
73
77
  # Retry on specified errors
74
78
  if http_err.response.status_code in error_codes:
75
79
  # Increment retries
@@ -115,7 +119,7 @@ def create(
115
119
  use_tool_naming: bool = True,
116
120
  # streaming?
117
121
  stream: bool = False,
118
- stream_inferface: Optional[Union[AgentRefreshStreamingInterface, AgentChunkStreamingInterface]] = None,
122
+ stream_interface: Optional[Union[AgentRefreshStreamingInterface, AgentChunkStreamingInterface]] = None,
119
123
  # TODO move to llm_config?
120
124
  # if unspecified (None), default to something we've tested
121
125
  inner_thoughts_in_kwargs_option: OptionState = OptionState.DEFAULT,
@@ -149,19 +153,19 @@ def create(
149
153
 
150
154
  if stream: # Client requested token streaming
151
155
  data.stream = True
152
- assert isinstance(stream_inferface, AgentChunkStreamingInterface) or isinstance(
153
- stream_inferface, AgentRefreshStreamingInterface
154
- ), type(stream_inferface)
156
+ assert isinstance(stream_interface, AgentChunkStreamingInterface) or isinstance(
157
+ stream_interface, AgentRefreshStreamingInterface
158
+ ), type(stream_interface)
155
159
  response = openai_chat_completions_process_stream(
156
160
  url=llm_config.model_endpoint, # https://api.openai.com/v1 -> https://api.openai.com/v1/chat/completions
157
161
  api_key=model_settings.openai_api_key,
158
162
  chat_completion_request=data,
159
- stream_inferface=stream_inferface,
163
+ stream_interface=stream_interface,
160
164
  )
161
165
  else: # Client did not request token streaming (expect a blocking backend response)
162
166
  data.stream = False
163
- if isinstance(stream_inferface, AgentChunkStreamingInterface):
164
- stream_inferface.stream_start()
167
+ if isinstance(stream_interface, AgentChunkStreamingInterface):
168
+ stream_interface.stream_start()
165
169
  try:
166
170
  response = openai_chat_completions_request(
167
171
  url=llm_config.model_endpoint, # https://api.openai.com/v1 -> https://api.openai.com/v1/chat/completions
@@ -169,8 +173,8 @@ def create(
169
173
  chat_completion_request=data,
170
174
  )
171
175
  finally:
172
- if isinstance(stream_inferface, AgentChunkStreamingInterface):
173
- stream_inferface.stream_end()
176
+ if isinstance(stream_interface, AgentChunkStreamingInterface):
177
+ stream_interface.stream_end()
174
178
 
175
179
  if inner_thoughts_in_kwargs:
176
180
  response = unpack_all_inner_thoughts_from_kwargs(response=response, inner_thoughts_key=INNER_THOUGHTS_KWARG)
@@ -317,8 +321,8 @@ def create(
317
321
  # They mention that none of the messages can have names, but it seems to not error out (for now)
318
322
 
319
323
  data.stream = False
320
- if isinstance(stream_inferface, AgentChunkStreamingInterface):
321
- stream_inferface.stream_start()
324
+ if isinstance(stream_interface, AgentChunkStreamingInterface):
325
+ stream_interface.stream_start()
322
326
  try:
323
327
  # groq uses the openai chat completions API, so this component should be reusable
324
328
  assert model_settings.groq_api_key is not None, "Groq key is missing"
@@ -328,8 +332,8 @@ def create(
328
332
  chat_completion_request=data,
329
333
  )
330
334
  finally:
331
- if isinstance(stream_inferface, AgentChunkStreamingInterface):
332
- stream_inferface.stream_end()
335
+ if isinstance(stream_interface, AgentChunkStreamingInterface):
336
+ stream_interface.stream_end()
333
337
 
334
338
  if inner_thoughts_in_kwargs:
335
339
  response = unpack_all_inner_thoughts_from_kwargs(response=response, inner_thoughts_key=INNER_THOUGHTS_KWARG)
letta/llm_api/openai.py CHANGED
@@ -41,7 +41,9 @@ from letta.utils import smart_urljoin
41
41
  OPENAI_SSE_DONE = "[DONE]"
42
42
 
43
43
 
44
- def openai_get_model_list(url: str, api_key: Union[str, None], fix_url: Optional[bool] = False) -> dict:
44
+ def openai_get_model_list(
45
+ url: str, api_key: Union[str, None], fix_url: Optional[bool] = False, extra_params: Optional[dict] = None
46
+ ) -> dict:
45
47
  """https://platform.openai.com/docs/api-reference/models/list"""
46
48
  from letta.utils import printd
47
49
 
@@ -59,8 +61,10 @@ def openai_get_model_list(url: str, api_key: Union[str, None], fix_url: Optional
59
61
  headers["Authorization"] = f"Bearer {api_key}"
60
62
 
61
63
  printd(f"Sending request to {url}")
64
+ response = None
62
65
  try:
63
- response = requests.get(url, headers=headers)
66
+ # TODO add query param "tool" to be true
67
+ response = requests.get(url, headers=headers, params=extra_params)
64
68
  response.raise_for_status() # Raises HTTPError for 4XX/5XX status
65
69
  response = response.json() # convert to dict from string
66
70
  printd(f"response = {response}")
@@ -68,7 +72,8 @@ def openai_get_model_list(url: str, api_key: Union[str, None], fix_url: Optional
68
72
  except requests.exceptions.HTTPError as http_err:
69
73
  # Handle HTTP errors (e.g., response 4XX, 5XX)
70
74
  try:
71
- response = response.json()
75
+ if response:
76
+ response = response.json()
72
77
  except:
73
78
  pass
74
79
  printd(f"Got HTTPError, exception={http_err}, response={response}")
@@ -76,7 +81,8 @@ def openai_get_model_list(url: str, api_key: Union[str, None], fix_url: Optional
76
81
  except requests.exceptions.RequestException as req_err:
77
82
  # Handle other requests-related errors (e.g., connection error)
78
83
  try:
79
- response = response.json()
84
+ if response:
85
+ response = response.json()
80
86
  except:
81
87
  pass
82
88
  printd(f"Got RequestException, exception={req_err}, response={response}")
@@ -84,7 +90,8 @@ def openai_get_model_list(url: str, api_key: Union[str, None], fix_url: Optional
84
90
  except Exception as e:
85
91
  # Handle other potential errors
86
92
  try:
87
- response = response.json()
93
+ if response:
94
+ response = response.json()
88
95
  except:
89
96
  pass
90
97
  printd(f"Got unknown Exception, exception={e}, response={response}")
@@ -154,7 +161,7 @@ def openai_chat_completions_process_stream(
154
161
  url: str,
155
162
  api_key: str,
156
163
  chat_completion_request: ChatCompletionRequest,
157
- stream_inferface: Optional[Union[AgentChunkStreamingInterface, AgentRefreshStreamingInterface]] = None,
164
+ stream_interface: Optional[Union[AgentChunkStreamingInterface, AgentRefreshStreamingInterface]] = None,
158
165
  create_message_id: bool = True,
159
166
  create_message_datetime: bool = True,
160
167
  ) -> ChatCompletionResponse:
@@ -164,7 +171,7 @@ def openai_chat_completions_process_stream(
164
171
  on the chunks received from the OpenAI-compatible server POST SSE response.
165
172
  """
166
173
  assert chat_completion_request.stream == True
167
- assert stream_inferface is not None, "Required"
174
+ assert stream_interface is not None, "Required"
168
175
 
169
176
  # Count the prompt tokens
170
177
  # TODO move to post-request?
@@ -217,8 +224,8 @@ def openai_chat_completions_process_stream(
217
224
  ),
218
225
  )
219
226
 
220
- if stream_inferface:
221
- stream_inferface.stream_start()
227
+ if stream_interface:
228
+ stream_interface.stream_start()
222
229
 
223
230
  n_chunks = 0 # approx == n_tokens
224
231
  try:
@@ -227,17 +234,17 @@ def openai_chat_completions_process_stream(
227
234
  ):
228
235
  assert isinstance(chat_completion_chunk, ChatCompletionChunkResponse), type(chat_completion_chunk)
229
236
 
230
- if stream_inferface:
231
- if isinstance(stream_inferface, AgentChunkStreamingInterface):
232
- stream_inferface.process_chunk(
237
+ if stream_interface:
238
+ if isinstance(stream_interface, AgentChunkStreamingInterface):
239
+ stream_interface.process_chunk(
233
240
  chat_completion_chunk,
234
241
  message_id=chat_completion_response.id if create_message_id else chat_completion_chunk.id,
235
242
  message_date=chat_completion_response.created if create_message_datetime else chat_completion_chunk.created,
236
243
  )
237
- elif isinstance(stream_inferface, AgentRefreshStreamingInterface):
238
- stream_inferface.process_refresh(chat_completion_response)
244
+ elif isinstance(stream_interface, AgentRefreshStreamingInterface):
245
+ stream_interface.process_refresh(chat_completion_response)
239
246
  else:
240
- raise TypeError(stream_inferface)
247
+ raise TypeError(stream_interface)
241
248
 
242
249
  if chunk_idx == 0:
243
250
  # initialize the choice objects which we will increment with the deltas
@@ -311,13 +318,13 @@ def openai_chat_completions_process_stream(
311
318
  n_chunks += 1
312
319
 
313
320
  except Exception as e:
314
- if stream_inferface:
315
- stream_inferface.stream_end()
321
+ if stream_interface:
322
+ stream_interface.stream_end()
316
323
  print(f"Parsing ChatCompletion stream failed with error:\n{str(e)}")
317
324
  raise e
318
325
  finally:
319
- if stream_inferface:
320
- stream_inferface.stream_end()
326
+ if stream_interface:
327
+ stream_interface.stream_end()
321
328
 
322
329
  # make sure we didn't leave temp stuff in
323
330
  assert all([c.finish_reason != TEMP_STREAM_FINISH_REASON for c in chat_completion_response.choices])
@@ -3,7 +3,7 @@ from urllib.parse import urljoin
3
3
  from letta.local_llm.settings.settings import get_completions_settings
4
4
  from letta.local_llm.utils import count_tokens, post_json_auth_request
5
5
 
6
- WEBUI_API_SUFFIX = "/v1/completions"
6
+ WEBUI_API_SUFFIX = "/completions"
7
7
 
8
8
 
9
9
  def get_vllm_completion(endpoint, auth_type, auth_key, model, prompt, context_window, user, grammar=None):
letta/metadata.py CHANGED
@@ -14,11 +14,10 @@ from sqlalchemy import (
14
14
  String,
15
15
  TypeDecorator,
16
16
  desc,
17
- func,
18
17
  )
19
- from sqlalchemy.orm import declarative_base
20
18
  from sqlalchemy.sql import func
21
19
 
20
+ from letta.base import Base
22
21
  from letta.config import LettaConfig
23
22
  from letta.schemas.agent import AgentState
24
23
  from letta.schemas.api_key import APIKey
@@ -28,6 +27,8 @@ from letta.schemas.enums import JobStatus
28
27
  from letta.schemas.job import Job
29
28
  from letta.schemas.llm_config import LLMConfig
30
29
  from letta.schemas.memory import Memory
30
+
31
+ # from letta.schemas.message import Message, Passage, Record, RecordType, ToolCall
31
32
  from letta.schemas.openai.chat_completions import ToolCall, ToolCallFunction
32
33
  from letta.schemas.organization import Organization
33
34
  from letta.schemas.source import Source
@@ -36,8 +37,6 @@ from letta.schemas.user import User
36
37
  from letta.settings import settings
37
38
  from letta.utils import enforce_types, get_utc_time, printd
38
39
 
39
- Base = declarative_base()
40
-
41
40
 
42
41
  class LLMConfigColumn(TypeDecorator):
43
42
  """Custom type for storing LLMConfig as JSON"""
letta/providers.py CHANGED
@@ -14,14 +14,18 @@ from letta.schemas.llm_config import LLMConfig
14
14
 
15
15
  class Provider(BaseModel):
16
16
 
17
- def list_llm_models(self):
17
+ def list_llm_models(self) -> List[LLMConfig]:
18
18
  return []
19
19
 
20
- def list_embedding_models(self):
20
+ def list_embedding_models(self) -> List[EmbeddingConfig]:
21
21
  return []
22
22
 
23
- def get_model_context_window(self, model_name: str):
24
- pass
23
+ def get_model_context_window(self, model_name: str) -> Optional[int]:
24
+ raise NotImplementedError
25
+
26
+ def provider_tag(self) -> str:
27
+ """String representation of the provider for display purposes"""
28
+ raise NotImplementedError
25
29
 
26
30
 
27
31
  class LettaProvider(Provider):
@@ -53,23 +57,40 @@ class LettaProvider(Provider):
53
57
  class OpenAIProvider(Provider):
54
58
  name: str = "openai"
55
59
  api_key: str = Field(..., description="API key for the OpenAI API.")
56
- base_url: str = "https://api.openai.com/v1"
60
+ base_url: str = Field(..., description="Base URL for the OpenAI API.")
57
61
 
58
62
  def list_llm_models(self) -> List[LLMConfig]:
59
63
  from letta.llm_api.openai import openai_get_model_list
60
64
 
61
- response = openai_get_model_list(self.base_url, api_key=self.api_key)
62
- model_options = [obj["id"] for obj in response["data"]]
65
+ # Some hardcoded support for OpenRouter (so that we only get models with tool calling support)...
66
+ # See: https://openrouter.ai/docs/requests
67
+ extra_params = {"supported_parameters": "tools"} if "openrouter.ai" in self.base_url else None
68
+ response = openai_get_model_list(self.base_url, api_key=self.api_key, extra_params=extra_params)
69
+
70
+ assert "data" in response, f"OpenAI model query response missing 'data' field: {response}"
63
71
 
64
72
  configs = []
65
- for model_name in model_options:
66
- context_window_size = self.get_model_context_window_size(model_name)
73
+ for model in response["data"]:
74
+ assert "id" in model, f"OpenAI model missing 'id' field: {model}"
75
+ model_name = model["id"]
76
+
77
+ if "context_length" in model:
78
+ # Context length is returned in OpenRouter as "context_length"
79
+ context_window_size = model["context_length"]
80
+ else:
81
+ context_window_size = self.get_model_context_window_size(model_name)
67
82
 
68
83
  if not context_window_size:
69
84
  continue
70
85
  configs.append(
71
86
  LLMConfig(model=model_name, model_endpoint_type="openai", model_endpoint=self.base_url, context_window=context_window_size)
72
87
  )
88
+
89
+ # for OpenAI, sort in reverse order
90
+ if self.base_url == "https://api.openai.com/v1":
91
+ # alphnumeric sort
92
+ configs.sort(key=lambda x: x.model, reverse=True)
93
+
73
94
  return configs
74
95
 
75
96
  def list_embedding_models(self) -> List[EmbeddingConfig]:
@@ -145,7 +166,7 @@ class OllamaProvider(OpenAIProvider):
145
166
  )
146
167
  return configs
147
168
 
148
- def get_model_context_window(self, model_name: str):
169
+ def get_model_context_window(self, model_name: str) -> Optional[int]:
149
170
 
150
171
  import requests
151
172
 
@@ -293,7 +314,7 @@ class GoogleAIProvider(Provider):
293
314
  )
294
315
  return configs
295
316
 
296
- def get_model_context_window(self, model_name: str):
317
+ def get_model_context_window(self, model_name: str) -> Optional[int]:
297
318
  from letta.llm_api.google_ai import google_ai_get_model_context_window
298
319
 
299
320
  return google_ai_get_model_context_window(self.base_url, self.api_key, model_name)
@@ -354,16 +375,75 @@ class AzureProvider(Provider):
354
375
  )
355
376
  return configs
356
377
 
357
- def get_model_context_window(self, model_name: str):
378
+ def get_model_context_window(self, model_name: str) -> Optional[int]:
358
379
  """
359
380
  This is hardcoded for now, since there is no API endpoints to retrieve metadata for a model.
360
381
  """
361
382
  return AZURE_MODEL_TO_CONTEXT_LENGTH.get(model_name, 4096)
362
383
 
363
384
 
364
- class VLLMProvider(OpenAIProvider):
385
+ class VLLMChatCompletionsProvider(Provider):
386
+ """vLLM provider that treats vLLM as an OpenAI /chat/completions proxy"""
387
+
365
388
  # NOTE: vLLM only serves one model at a time (so could configure that through env variables)
366
- pass
389
+ name: str = "vllm"
390
+ base_url: str = Field(..., description="Base URL for the vLLM API.")
391
+
392
+ def list_llm_models(self) -> List[LLMConfig]:
393
+ # not supported with vLLM
394
+ from letta.llm_api.openai import openai_get_model_list
395
+
396
+ assert self.base_url, "base_url is required for vLLM provider"
397
+ response = openai_get_model_list(self.base_url, api_key=None)
398
+
399
+ configs = []
400
+ print(response)
401
+ for model in response["data"]:
402
+ configs.append(
403
+ LLMConfig(
404
+ model=model["id"],
405
+ model_endpoint_type="openai",
406
+ model_endpoint=self.base_url,
407
+ context_window=model["max_model_len"],
408
+ )
409
+ )
410
+ return configs
411
+
412
+ def list_embedding_models(self) -> List[EmbeddingConfig]:
413
+ # not supported with vLLM
414
+ return []
415
+
416
+
417
+ class VLLMCompletionsProvider(Provider):
418
+ """This uses /completions API as the backend, not /chat/completions, so we need to specify a model wrapper"""
419
+
420
+ # NOTE: vLLM only serves one model at a time (so could configure that through env variables)
421
+ name: str = "vllm"
422
+ base_url: str = Field(..., description="Base URL for the vLLM API.")
423
+ default_prompt_formatter: str = Field(..., description="Default prompt formatter (aka model wrapper)to use on vLLM /completions API.")
424
+
425
+ def list_llm_models(self) -> List[LLMConfig]:
426
+ # not supported with vLLM
427
+ from letta.llm_api.openai import openai_get_model_list
428
+
429
+ response = openai_get_model_list(self.base_url, api_key=None)
430
+
431
+ configs = []
432
+ for model in response["data"]:
433
+ configs.append(
434
+ LLMConfig(
435
+ model=model["id"],
436
+ model_endpoint_type="vllm",
437
+ model_endpoint=self.base_url,
438
+ model_wrapper=self.default_prompt_formatter,
439
+ context_window=model["max_model_len"],
440
+ )
441
+ )
442
+ return configs
443
+
444
+ def list_embedding_models(self) -> List[EmbeddingConfig]:
445
+ # not supported with vLLM
446
+ return []
367
447
 
368
448
 
369
449
  class CohereProvider(OpenAIProvider):
@@ -52,3 +52,10 @@ class EmbeddingConfig(BaseModel):
52
52
  )
53
53
  else:
54
54
  raise ValueError(f"Model {model_name} not supported.")
55
+
56
+ def pretty_print(self) -> str:
57
+ return (
58
+ f"{self.embedding_model}"
59
+ + (f" [type={self.embedding_endpoint_type}]" if self.embedding_endpoint_type else "")
60
+ + (f" [ip={self.embedding_endpoint}]" if self.embedding_endpoint else "")
61
+ )
@@ -68,3 +68,10 @@ class LLMConfig(BaseModel):
68
68
  )
69
69
  else:
70
70
  raise ValueError(f"Model {model_name} not supported.")
71
+
72
+ def pretty_print(self) -> str:
73
+ return (
74
+ f"{self.model}"
75
+ + (f" [type={self.model_endpoint_type}]" if self.model_endpoint_type else "")
76
+ + (f" [ip={self.model_endpoint}]" if self.model_endpoint else "")
77
+ )
letta/server/server.py CHANGED
@@ -14,8 +14,8 @@ import letta.constants as constants
14
14
  import letta.server.utils as server_utils
15
15
  import letta.system as system
16
16
  from letta.agent import Agent, save_agent
17
+ from letta.agent_store.db import attach_base
17
18
  from letta.agent_store.storage import StorageConnector, TableType
18
- from letta.config import LettaConfig
19
19
  from letta.credentials import LettaCredentials
20
20
  from letta.data_sources.connectors import DataConnector, load_data
21
21
 
@@ -41,7 +41,7 @@ from letta.interface import AgentInterface # abstract
41
41
  from letta.interface import CLIInterface # for printing to terminal
42
42
  from letta.log import get_logger
43
43
  from letta.memory import get_memory_functions
44
- from letta.metadata import MetadataStore
44
+ from letta.metadata import Base, MetadataStore
45
45
  from letta.prompts import gpt_system
46
46
  from letta.providers import (
47
47
  AnthropicProvider,
@@ -50,7 +50,9 @@ from letta.providers import (
50
50
  LettaProvider,
51
51
  OllamaProvider,
52
52
  OpenAIProvider,
53
- VLLMProvider,
53
+ Provider,
54
+ VLLMChatCompletionsProvider,
55
+ VLLMCompletionsProvider,
54
56
  )
55
57
  from letta.schemas.agent import AgentState, AgentType, CreateAgent, UpdateAgentState
56
58
  from letta.schemas.api_key import APIKey, APIKeyCreate
@@ -149,23 +151,11 @@ class Server(object):
149
151
 
150
152
 
151
153
  from sqlalchemy import create_engine
152
- from sqlalchemy.orm import declarative_base, sessionmaker
154
+ from sqlalchemy.orm import sessionmaker
153
155
 
154
- from letta.agent_store.db import MessageModel, PassageModel
155
156
  from letta.config import LettaConfig
156
157
 
157
158
  # NOTE: hack to see if single session management works
158
- from letta.metadata import (
159
- AgentModel,
160
- AgentSourceMappingModel,
161
- APIKeyModel,
162
- BlockModel,
163
- JobModel,
164
- OrganizationModel,
165
- SourceModel,
166
- ToolModel,
167
- UserModel,
168
- )
169
159
  from letta.settings import model_settings, settings
170
160
 
171
161
  config = LettaConfig.load()
@@ -182,24 +172,12 @@ else:
182
172
  # TODO: don't rely on config storage
183
173
  engine = create_engine("sqlite:///" + os.path.join(config.recall_storage_path, "sqlite.db"))
184
174
 
185
- Base = declarative_base()
175
+
186
176
  SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
187
- Base.metadata.create_all(
188
- engine,
189
- tables=[
190
- UserModel.__table__,
191
- AgentModel.__table__,
192
- SourceModel.__table__,
193
- AgentSourceMappingModel.__table__,
194
- APIKeyModel.__table__,
195
- BlockModel.__table__,
196
- ToolModel.__table__,
197
- JobModel.__table__,
198
- PassageModel.__table__,
199
- MessageModel.__table__,
200
- OrganizationModel.__table__,
201
- ],
202
- )
177
+
178
+ attach_base()
179
+
180
+ Base.metadata.create_all(bind=engine)
203
181
 
204
182
 
205
183
  # Dependency
@@ -261,18 +239,17 @@ class SyncServer(Server):
261
239
  self.add_default_tools(module_name="base")
262
240
 
263
241
  # collect providers (always has Letta as a default)
264
- self._enabled_providers = [LettaProvider()]
242
+ self._enabled_providers: List[Provider] = [LettaProvider()]
265
243
  if model_settings.openai_api_key:
266
- self._enabled_providers.append(OpenAIProvider(api_key=model_settings.openai_api_key))
244
+ self._enabled_providers.append(OpenAIProvider(api_key=model_settings.openai_api_key, base_url=model_settings.openai_api_base))
267
245
  if model_settings.anthropic_api_key:
268
246
  self._enabled_providers.append(AnthropicProvider(api_key=model_settings.anthropic_api_key))
269
247
  if model_settings.ollama_base_url:
270
- self._enabled_providers.append(OllamaProvider(base_url=model_settings.ollama_base_url))
271
- if model_settings.vllm_base_url:
272
- self._enabled_providers.append(VLLMProvider(base_url=model_settings.vllm_base_url))
248
+ self._enabled_providers.append(OllamaProvider(base_url=model_settings.ollama_base_url, api_key=None))
273
249
  if model_settings.gemini_api_key:
274
250
  self._enabled_providers.append(GoogleAIProvider(api_key=model_settings.gemini_api_key))
275
251
  if model_settings.azure_api_key and model_settings.azure_base_url:
252
+ assert model_settings.azure_api_version, "AZURE_API_VERSION is required"
276
253
  self._enabled_providers.append(
277
254
  AzureProvider(
278
255
  api_key=model_settings.azure_api_key,
@@ -280,6 +257,18 @@ class SyncServer(Server):
280
257
  api_version=model_settings.azure_api_version,
281
258
  )
282
259
  )
260
+ if model_settings.vllm_api_base:
261
+ # vLLM exposes both a /chat/completions and a /completions endpoint
262
+ self._enabled_providers.append(
263
+ VLLMCompletionsProvider(
264
+ base_url=model_settings.vllm_api_base,
265
+ default_prompt_formatter=model_settings.default_prompt_formatter,
266
+ )
267
+ )
268
+ # NOTE: to use the /chat/completions endpoint, you need to specify extra flags on vLLM startup
269
+ # see: https://docs.vllm.ai/en/latest/getting_started/examples/openai_chat_completion_client_with_tools.html
270
+ # e.g. "... --enable-auto-tool-choice --tool-call-parser hermes"
271
+ self._enabled_providers.append(VLLMChatCompletionsProvider(base_url=model_settings.vllm_api_base))
283
272
 
284
273
  def save_agents(self):
285
274
  """Saves all the agents that are in the in-memory object store"""
letta/settings.py CHANGED
@@ -4,14 +4,20 @@ from typing import Optional
4
4
  from pydantic import Field
5
5
  from pydantic_settings import BaseSettings, SettingsConfigDict
6
6
 
7
+ from letta.local_llm.constants import DEFAULT_WRAPPER_NAME
8
+
7
9
 
8
10
  class ModelSettings(BaseSettings):
9
11
 
10
12
  # env_prefix='my_prefix_'
11
13
 
14
+ # when we use /completions APIs (instead of /chat/completions), we need to specify a model wrapper
15
+ # the "model wrapper" is responsible for prompt formatting and function calling parsing
16
+ default_prompt_formatter: str = DEFAULT_WRAPPER_NAME
17
+
12
18
  # openai
13
19
  openai_api_key: Optional[str] = None
14
- # TODO: provide overriding BASE_URL?
20
+ openai_api_base: str = "https://api.openai.com/v1"
15
21
 
16
22
  # groq
17
23
  groq_api_key: Optional[str] = None
@@ -25,13 +31,16 @@ class ModelSettings(BaseSettings):
25
31
  # azure
26
32
  azure_api_key: Optional[str] = None
27
33
  azure_base_url: Optional[str] = None
28
- azure_api_version: Optional[str] = None
34
+ # We provide a default here, since usually people will want to be on the latest API version.
35
+ azure_api_version: Optional[str] = (
36
+ "2024-09-01-preview" # https://learn.microsoft.com/en-us/azure/ai-services/openai/api-version-deprecation
37
+ )
29
38
 
30
39
  # google ai
31
40
  gemini_api_key: Optional[str] = None
32
41
 
33
42
  # vLLM
34
- vllm_base_url: Optional[str] = None
43
+ vllm_api_base: Optional[str] = None
35
44
 
36
45
  # openllm
37
46
  openllm_auth_type: Optional[str] = None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: letta-nightly
3
- Version: 0.4.1.dev20241010104112
3
+ Version: 0.4.1.dev20241012104008
4
4
  Summary: Create LLM agents with long-term memory and custom tools
5
5
  License: Apache License
6
6
  Author: Letta Team
@@ -20,6 +20,7 @@ Provides-Extra: postgres
20
20
  Provides-Extra: qdrant
21
21
  Provides-Extra: server
22
22
  Provides-Extra: tests
23
+ Requires-Dist: alembic (>=1.13.3,<2.0.0)
23
24
  Requires-Dist: autoflake (>=2.3.0,<3.0.0) ; extra == "dev"
24
25
  Requires-Dist: black[jupyter] (>=24.2.0,<25.0.0) ; extra == "dev"
25
26
  Requires-Dist: chromadb (>=0.4.24,<0.5.0)
@@ -1,15 +1,16 @@
1
1
  letta/__init__.py,sha256=btKRPdyhkpIyHlCPRLwwz-SCSVcEGNBeheYA-XNesiI,996
2
2
  letta/__main__.py,sha256=6Hs2PV7EYc5Tid4g4OtcLXhqVHiNYTGzSBdoOnW2HXA,29
3
- letta/agent.py,sha256=2yYk8H76EYF8-EF5eFqv8dp8avaBcV_hfXOQcwYuAwU,66559
3
+ letta/agent.py,sha256=pdsd1M_AF3uF-AgJrMmxj8uB0Rkt5AXKBxHNw9mMaP4,66559
4
4
  letta/agent_store/chroma.py,sha256=upR5zGnGs6I6btulEYbiZdGG87BgKjxUJOQZ4Y-RQ_M,12492
5
- letta/agent_store/db.py,sha256=Hbw4HsxPZSQL9h_3I-SiJRisaYdzk1XSdYJFarjrUtM,22575
5
+ letta/agent_store/db.py,sha256=TAQ1rcqS8zuC1hT_BaiXn6tSz5aKHAb-YQxr4QLKgz4,22724
6
6
  letta/agent_store/lancedb.py,sha256=8RWmqVjowm5g0cc6DNRcb6f1FHGEqFnccnuekhWY39U,5101
7
7
  letta/agent_store/milvus.py,sha256=VxEKz9XR7_3QTY59K_38NtJCCQvi41rhHoFibfzW7yw,8469
8
8
  letta/agent_store/qdrant.py,sha256=qIEJhXJb6GzcT4wp8iV5Ox5W1CFMvcPViTI4HLSh59E,7879
9
9
  letta/agent_store/storage.py,sha256=QWrPdIEJCnsPg1xnPrG1xbOXmbjpz37ZNhvuH52M7A8,6642
10
+ letta/base.py,sha256=Ba-wt8p59bLmeUONkYSo5MhrkH-_HdT4zE1Y9MVGrSQ,83
10
11
  letta/benchmark/benchmark.py,sha256=ebvnwfp3yezaXOQyGXkYCDYpsmre-b9hvNtnyx4xkG0,3701
11
12
  letta/benchmark/constants.py,sha256=aXc5gdpMGJT327VuxsT5FngbCK2J41PQYeICBO7g_RE,536
12
- letta/cli/cli.py,sha256=-gJtASac1OJyboFzXXZtY2MH5DPylYYk3ZO8zV519Tc,15870
13
+ letta/cli/cli.py,sha256=XZDF7EfBYqdYEyDbENVp-tTyGcdvd9Wa1PNk70D-MGs,16195
13
14
  letta/cli/cli_config.py,sha256=G7QqPNTtlQ4TdrXZrrFFGblZEhnkyrqN1Cl5z415C-g,8689
14
15
  letta/cli/cli_load.py,sha256=aVlGWiNEUs_eG793HLl7cES-dEIuA1CJfZpT1Cm8Uo4,4591
15
16
  letta/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -18,7 +19,7 @@ letta/client/client.py,sha256=bPvSQrbym4xXZu9EfEbX02fpkNVxFBpKoyzK9PFwykE,84515
18
19
  letta/client/streaming.py,sha256=bfWlUu7z7EoPfKxBqIarYxGKyrL7Pj79BlliToqcCgI,4592
19
20
  letta/client/utils.py,sha256=AQWl2q11AzSjd1Y7slIENoZ6fO1YW1JSv1qw0fPt57k,2419
20
21
  letta/config.py,sha256=j2I90fOh9d9__kOYObwTDLbvVwYR50rIql5nzrvREKg,19161
21
- letta/constants.py,sha256=VV6T8O4w4ju8q5CrPCbvPwHlUTltkeFji-r7hz8LTIw,5930
22
+ letta/constants.py,sha256=PAvnF4l9hDuWSrPSKVoXhwjWb6V282dI43EOOpKZaFE,6572
22
23
  letta/credentials.py,sha256=D9mlcPsdDWlIIXQQD8wSPE9M_QvsRrb0p3LB5i9OF5Q,5806
23
24
  letta/data_sources/connectors.py,sha256=E2rJNqVT4WEvxBqOQl0YgNKa_JQXkG0h1luw_XLcTis,10232
24
25
  letta/embeddings.py,sha256=ayAMxW6RUK1RUpLsDiJCG1oY2H6fgricaoqMa4GBjRE,8170
@@ -35,13 +36,13 @@ letta/humans/examples/cs_phd.txt,sha256=9C9ZAV_VuG7GB31ksy3-_NAyk8rjE6YtVOkhp08k
35
36
  letta/interface.py,sha256=QI4hFP0WrNsgM5qX6TbnhH1ZZxsLYr5DaccuxpEQ8S4,12768
36
37
  letta/llm_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
38
  letta/llm_api/anthropic.py,sha256=bAb9PVrpYjo2QN51_SJbW7Vry2_Sf55B05UoruHXb7A,12932
38
- letta/llm_api/azure_openai.py,sha256=8uBQIYa3WpRcxLXgJTYZrl-GhDGflqxXraRLZlpkdh4,4566
39
+ letta/llm_api/azure_openai.py,sha256=C-fuuholudcLJDWjqnXJwpXsfmGWfNugEVWyj6YCrpg,4572
39
40
  letta/llm_api/azure_openai_constants.py,sha256=oXtKrgBFHf744gyt5l1thILXgyi8NDNUrKEa2GGGpjw,278
40
41
  letta/llm_api/cohere.py,sha256=vDRd-SUGp1t_JUIdwC3RkIhwMl0OY7n-tAU9uPORYkY,14826
41
42
  letta/llm_api/google_ai.py,sha256=3xZ074nSOCC22c15yerA5ngWzh0ex4wxeI-6faNbHPE,17708
42
- letta/llm_api/helpers.py,sha256=Qe1YC36QjjOKE-Xh1Ss3dhMNcWergOK_MpG9xdtN9CM,9519
43
- letta/llm_api/llm_api_tools.py,sha256=8HndYHAH6ENL5vFEYn2px6CjZdx3ttCGxbEtRfK2RAY,15237
44
- letta/llm_api/openai.py,sha256=3C_1PjyBcLDraQur6jBzEONqANRYmL78Ym1SaE6LgqE,21469
43
+ letta/llm_api/helpers.py,sha256=LjUtCjvPzSP3-3Ak0J--2RqUXOO6Of8287mm1L1LAMU,9549
44
+ letta/llm_api/llm_api_tools.py,sha256=Z3eiYUtvZKBVBcmKI2l4qWkKM4hgvLN9Y1aSxXc7y-k,15344
45
+ letta/llm_api/openai.py,sha256=EXpktSI_TYjsCDEXBxdNXsY5uE9Rzb7BPF1F6cz8bkg,21689
45
46
  letta/local_llm/README.md,sha256=hFJyw5B0TU2jrh9nb0zGZMgdH-Ei1dSRfhvPQG_NSoU,168
46
47
  letta/local_llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
48
  letta/local_llm/chat_completion_proxy.py,sha256=PXgNveahts5DbZ7GVcPShxmrDKropL81PY2JHc31yAA,13091
@@ -74,7 +75,7 @@ letta/local_llm/settings/deterministic_mirostat.py,sha256=kgRikcxYHfIbPFydHW6W7I
74
75
  letta/local_llm/settings/settings.py,sha256=ZAbzDpu2WsBXjVGXJ-TKUpS99VTI__3EoZml9KqYef0,2971
75
76
  letta/local_llm/settings/simple.py,sha256=HAO2jBJ_hJCEsXWIJcD0sckR0tI0zs3x2CPdf6ORQLs,719
76
77
  letta/local_llm/utils.py,sha256=DTv8S0lIpFBZlY6fjprtAU3n06eOPDKT6EXwnOgexLo,11363
77
- letta/local_llm/vllm/api.py,sha256=KhIB-cSYGKKsq5ZVarxPDAL9fTL8HT2-WmWS5gnY6QQ,2593
78
+ letta/local_llm/vllm/api.py,sha256=2kAGZjc_GH9ILJnVRq-45yfsfKELVfbC9VEl_cIC6vg,2590
78
79
  letta/local_llm/webui/api.py,sha256=kkxncdCFq1vjgvaHOoQ__j7rcDPgC1F64KcEm94Y6Rs,2639
79
80
  letta/local_llm/webui/legacy_api.py,sha256=k3H3y4qp2Fs-XmP24iSIEyvq6wjWFWBzklY3-wRAJNI,2335
80
81
  letta/local_llm/webui/legacy_settings.py,sha256=BLmd3TSx5StnY3ibjwaxYATPt_Lvq-o1rlcc_-Q1JcU,538
@@ -82,7 +83,7 @@ letta/local_llm/webui/settings.py,sha256=gmLHfiOl1u4JmlAZU2d2O8YKF9lafdakyjwR_ft
82
83
  letta/log.py,sha256=QHquDnL7oUAvdKlAwUlCK9zXKDMUjrU9WA0bxnMsP0Y,2101
83
84
  letta/main.py,sha256=7uuiFv8TeOd3K92WwfYkls7l7KXJ5P0l2zgK9AfmD08,18502
84
85
  letta/memory.py,sha256=6q1x3-PY-PeXzAt6hvP-UF1ajvroPZ7XW-5nLy-JhMo,17657
85
- letta/metadata.py,sha256=-LUv7w5yaCn10wh7vnFCrhH4s5M5F0K3Ui7pClXN3tE,33225
86
+ letta/metadata.py,sha256=50pEJ5BuNKT8dPv34-H_Id9an3ql45Ze7BKqn7DGQ6s,33256
86
87
  letta/openai_backcompat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
87
88
  letta/openai_backcompat/openai_object.py,sha256=Y1ZS1sATP60qxJiOsjOP3NbwSzuzvkNAvb3DeuhM5Uk,13490
88
89
  letta/persistence_manager.py,sha256=LlLgEDpSafCPAiyKmuq0NvVAnfBkZo6TWbGIKYQjQBs,5200
@@ -106,13 +107,13 @@ letta/prompts/system/memgpt_doc.txt,sha256=AsT55NOORoH-K-p0fxklrDRZ3qHs4MIKMuR-M
106
107
  letta/prompts/system/memgpt_gpt35_extralong.txt,sha256=FheNhYoIzNz6qnJKhVquZVSMj3HduC48reFaX7Pf7ig,5046
107
108
  letta/prompts/system/memgpt_intuitive_knowledge.txt,sha256=sA7c3urYqREVnSBI81nTGImXAekqC0Fxc7RojFqud1g,2966
108
109
  letta/prompts/system/memgpt_modified_chat.txt,sha256=HOaPVurEftD8KsuwsclDgE2afIfklMjxhuSO96q1-6I,4656
109
- letta/providers.py,sha256=nC0xGWEDIpnZz_tiFMswrko9HFSUQQJO8o9jRxGStb0,13579
110
+ letta/providers.py,sha256=1vxUxMpLHyZ_XDVTUcpB98HOqszRwnt2sX9xU8zts1U,16949
110
111
  letta/pytest.ini,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
111
112
  letta/schemas/agent.py,sha256=ztnUqdhY9V3g0jsbTjF1ypKPC1tZx4QVFaRuLAOXNSA,6230
112
113
  letta/schemas/api_key.py,sha256=u07yzzMn-hBAHZIIKbWY16KsgiFjSNR8lAghpMUo3_4,682
113
114
  letta/schemas/block.py,sha256=1_GwFtfykloYU4Mp2DV3-DqkvsKo79Mu3LAzVYOgMzk,3998
114
115
  letta/schemas/document.py,sha256=JpvU0YkvOVLvHaDNcg-ihFzpeHC2zqsWBgyJ6zHnfNw,745
115
- letta/schemas/embedding_config.py,sha256=KhEOStOD8VbHFyLI9pkQVbTG1Av2g-Ql0yf9M868LME,2570
116
+ letta/schemas/embedding_config.py,sha256=1kD6NpiXeH4roVumxqDAKk7xt8SpXGWNhZs_XXUSlEU,2855
116
117
  letta/schemas/enums.py,sha256=WfRYpLh_pD-VyhEnp3Y6pPfx063zq2o4jky6PulqO8w,629
117
118
  letta/schemas/health.py,sha256=zT6mYovvD17iJRuu2rcaQQzbEEYrkwvAE9TB7iU824c,139
118
119
  letta/schemas/job.py,sha256=bYDrjbJm2B4LUTSkLUdQR_HDhL2E23g0EHf7E23ezYU,1547
@@ -120,7 +121,7 @@ letta/schemas/letta_base.py,sha256=4QXFgyjCHqIagi8B6_4nmqb9eoJ52Y6aCxBxQpGX48M,2
120
121
  letta/schemas/letta_message.py,sha256=Slgxa59qZfdvqXuCVHOt03u-7JL456ZY-WLaK5UYYKU,6234
121
122
  letta/schemas/letta_request.py,sha256=Fv4hZKLMefCISwxuElXB0vsGDjqnzZuCbCbHPUPTirQ,1852
122
123
  letta/schemas/letta_response.py,sha256=_UJoO3UtC3F5DtQCHzdiGM1SHNPYPKvopIWqg8t5YZw,1564
123
- letta/schemas/llm_config.py,sha256=oC-RTimpLhNmn80AyCLnx8YUwA0sIR1UqshYxHVbamU,2671
124
+ letta/schemas/llm_config.py,sha256=xwHBO_sY-Kahtwxt_0Qz4j2CRSeJ0pGAIxpqyK_2Exk,2930
124
125
  letta/schemas/memory.py,sha256=mDJbe9mzTw0HSn6tiouC3z32r-8Fc_EaeqxDy_hXf0U,9327
125
126
  letta/schemas/message.py,sha256=1C6lz4yvMTusE8zORKbi_BKnb_X_-RuUvdpM5YeZ21o,33409
126
127
  letta/schemas/openai/chat_completion_request.py,sha256=Fa7xnSnG7WUQounJhnDu0fTSxoR6xOAh2bODuqmfypI,3345
@@ -166,7 +167,7 @@ letta/server/rest_api/routers/v1/tools.py,sha256=MEhxu-zMS2ff_wwcRpMuQyWA71w_3BJ
166
167
  letta/server/rest_api/routers/v1/users.py,sha256=Y2rDvHOG1B5FLSOjutY3R22vt48IngbZ-9h8CohG5rc,3378
167
168
  letta/server/rest_api/static_files.py,sha256=NG8sN4Z5EJ8JVQdj19tkFa9iQ1kBPTab9f_CUxd_u4Q,3143
168
169
  letta/server/rest_api/utils.py,sha256=Fc2ZGKzLaBa2sEtSTVjJ8D5M0xIwsWC0CVAOIJaD3rY,2176
169
- letta/server/server.py,sha256=Vac_o-C1l5wOybARMhmuU4Q02rl7YJPB51rjLA0cL9I,81970
170
+ letta/server/server.py,sha256=XWnXt7ViLwihQVpwykxdr-fcAOGhhyGjKSM4Z-nvxw4,82206
170
171
  letta/server/startup.sh,sha256=jeGV7B_PS0hS-tT6o6GpACrUbV9WV1NI2L9aLoUDDtc,311
171
172
  letta/server/static_files/assets/index-3ab03d5b.css,sha256=OrA9W4iKJ5h2Wlr7GwdAT4wow0CM8hVit1yOxEL49Qw,54295
172
173
  letta/server/static_files/assets/index-9a9c449b.js,sha256=qoWUq6_kuLhE9NFkNeCBptgq-oERW46r0tB3JlWe_qc,1818951
@@ -179,12 +180,12 @@ letta/server/ws_api/example_client.py,sha256=95AA5UFgTlNJ0FUQkLxli8dKNx48MNm3eWG
179
180
  letta/server/ws_api/interface.py,sha256=TWl9vkcMCnLsUtgsuENZ-ku2oMDA-OUTzLh_yNRoMa4,4120
180
181
  letta/server/ws_api/protocol.py,sha256=M_-gM5iuDBwa1cuN2IGNCG5GxMJwU2d3XW93XALv9s8,1821
181
182
  letta/server/ws_api/server.py,sha256=C2Kv48PCwl46DQFb0ZP30s86KJLQ6dZk2AhWQEZn9pY,6004
182
- letta/settings.py,sha256=a1dN-ntNXM46IuF-ITG9u881aLdESfNGWl8_uBYSH20,2677
183
+ letta/settings.py,sha256=6VWC3vtTa8vqj6dqos4p_xHTMJNJS_8LRGJmqvaU1-o,3219
183
184
  letta/streaming_interface.py,sha256=_FPUWy58j50evHcpXyd7zB1wWqeCc71NCFeWh_TBvnw,15736
184
185
  letta/system.py,sha256=buKYPqG5n2x41hVmWpu6JUpyd7vTWED9Km2_M7dLrvk,6960
185
186
  letta/utils.py,sha256=neUs7mxNfndzRL5XUxerr8Lic6w7qnyyvf8FBwMnyWw,30852
186
- letta_nightly-0.4.1.dev20241010104112.dist-info/LICENSE,sha256=mExtuZ_GYJgDEI38GWdiEYZizZS4KkVt2SF1g_GPNhI,10759
187
- letta_nightly-0.4.1.dev20241010104112.dist-info/METADATA,sha256=9yfZIXzwV7Roc1ajgsVQT6PkEN1q2TBKODtjJrMyVeo,5967
188
- letta_nightly-0.4.1.dev20241010104112.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
189
- letta_nightly-0.4.1.dev20241010104112.dist-info/entry_points.txt,sha256=2zdiyGNEZGV5oYBuS-y2nAAgjDgcC9yM_mHJBFSRt5U,40
190
- letta_nightly-0.4.1.dev20241010104112.dist-info/RECORD,,
187
+ letta_nightly-0.4.1.dev20241012104008.dist-info/LICENSE,sha256=mExtuZ_GYJgDEI38GWdiEYZizZS4KkVt2SF1g_GPNhI,10759
188
+ letta_nightly-0.4.1.dev20241012104008.dist-info/METADATA,sha256=bzoVG8Kbu7WPOswZWQ4HGRQoNqmrA2EvT8XP1sbuQ6U,6008
189
+ letta_nightly-0.4.1.dev20241012104008.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
190
+ letta_nightly-0.4.1.dev20241012104008.dist-info/entry_points.txt,sha256=2zdiyGNEZGV5oYBuS-y2nAAgjDgcC9yM_mHJBFSRt5U,40
191
+ letta_nightly-0.4.1.dev20241012104008.dist-info/RECORD,,