letta-nightly 0.6.25.dev20250213104102__py3-none-any.whl → 0.6.26.dev20250214104050__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.

Files changed (50) hide show
  1. letta/__init__.py +1 -1
  2. letta/agent.py +24 -6
  3. letta/cli/cli_config.py +3 -3
  4. letta/constants.py +1 -0
  5. letta/functions/function_sets/base.py +1 -1
  6. letta/functions/function_sets/extras.py +1 -1
  7. letta/functions/helpers.py +41 -12
  8. letta/helpers/composio_helpers.py +21 -0
  9. letta/helpers/datetime_helpers.py +90 -0
  10. letta/helpers/json_helpers.py +15 -0
  11. letta/interface.py +2 -1
  12. letta/llm_api/anthropic.py +1 -1
  13. letta/llm_api/cohere.py +3 -1
  14. letta/llm_api/google_ai.py +3 -1
  15. letta/llm_api/google_constants.py +12 -0
  16. letta/llm_api/google_vertex.py +3 -1
  17. letta/llm_api/helpers.py +2 -1
  18. letta/local_llm/chat_completion_proxy.py +3 -1
  19. letta/local_llm/function_parser.py +1 -1
  20. letta/local_llm/grammars/gbnf_grammar_generator.py +1 -1
  21. letta/local_llm/json_parser.py +1 -1
  22. letta/local_llm/llm_chat_completion_wrappers/airoboros.py +1 -2
  23. letta/local_llm/llm_chat_completion_wrappers/chatml.py +1 -1
  24. letta/local_llm/llm_chat_completion_wrappers/configurable_wrapper.py +1 -2
  25. letta/local_llm/llm_chat_completion_wrappers/dolphin.py +1 -2
  26. letta/local_llm/llm_chat_completion_wrappers/llama3.py +1 -1
  27. letta/local_llm/llm_chat_completion_wrappers/simple_summary_wrapper.py +1 -2
  28. letta/local_llm/llm_chat_completion_wrappers/zephyr.py +1 -2
  29. letta/local_llm/utils.py +1 -4
  30. letta/openai_backcompat/openai_object.py +1 -1
  31. letta/schemas/letta_response.py +1 -1
  32. letta/schemas/message.py +2 -1
  33. letta/schemas/organization.py +2 -1
  34. letta/schemas/passage.py +1 -1
  35. letta/server/rest_api/interface.py +1 -1
  36. letta/server/rest_api/routers/v1/tools.py +38 -39
  37. letta/server/server.py +7 -3
  38. letta/server/ws_api/protocol.py +1 -1
  39. letta/services/agent_manager.py +2 -1
  40. letta/services/helpers/agent_manager_helper.py +1 -1
  41. letta/services/job_manager.py +2 -1
  42. letta/services/sandbox_config_manager.py +1 -1
  43. letta/services/tool_execution_sandbox.py +1 -1
  44. letta/system.py +2 -1
  45. letta/utils.py +2 -103
  46. {letta_nightly-0.6.25.dev20250213104102.dist-info → letta_nightly-0.6.26.dev20250214104050.dist-info}/METADATA +3 -3
  47. {letta_nightly-0.6.25.dev20250213104102.dist-info → letta_nightly-0.6.26.dev20250214104050.dist-info}/RECORD +50 -46
  48. {letta_nightly-0.6.25.dev20250213104102.dist-info → letta_nightly-0.6.26.dev20250214104050.dist-info}/LICENSE +0 -0
  49. {letta_nightly-0.6.25.dev20250213104102.dist-info → letta_nightly-0.6.26.dev20250214104050.dist-info}/WHEEL +0 -0
  50. {letta_nightly-0.6.25.dev20250213104102.dist-info → letta_nightly-0.6.26.dev20250214104050.dist-info}/entry_points.txt +0 -0
letta/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "0.6.25"
1
+ __version__ = "0.6.26"
2
2
 
3
3
  # import clients
4
4
  from letta.client.client import LocalClient, RESTClient, create_client
letta/agent.py CHANGED
@@ -3,12 +3,13 @@ import time
3
3
  import traceback
4
4
  import warnings
5
5
  from abc import ABC, abstractmethod
6
- from typing import List, Optional, Tuple, Union
6
+ from typing import Any, List, Optional, Tuple, Union
7
7
 
8
8
  from openai.types.beta.function_tool import FunctionTool as OpenAITool
9
9
 
10
10
  from letta.constants import (
11
11
  CLI_WARNING_PREFIX,
12
+ COMPOSIO_ENTITY_ENV_VAR_KEY,
12
13
  ERROR_MESSAGE_PREFIX,
13
14
  FIRST_MESSAGE_ATTEMPTS,
14
15
  FUNC_FAILED_HEARTBEAT_MESSAGE,
@@ -20,7 +21,11 @@ from letta.constants import (
20
21
  from letta.errors import ContextWindowExceededError
21
22
  from letta.functions.ast_parsers import coerce_dict_args_by_annotations, get_function_annotations_from_source
22
23
  from letta.functions.functions import get_function_from_module
24
+ from letta.functions.helpers import execute_composio_action, generate_composio_action_from_func_name
23
25
  from letta.helpers import ToolRulesSolver
26
+ from letta.helpers.composio_helpers import get_composio_api_key
27
+ from letta.helpers.datetime_helpers import get_utc_time
28
+ from letta.helpers.json_helpers import json_dumps, json_loads
24
29
  from letta.interface import AgentInterface
25
30
  from letta.llm_api.helpers import calculate_summarizer_cutoff, get_token_counts_for_messages, is_context_overflow_error
26
31
  from letta.llm_api.llm_api_tools import create
@@ -51,6 +56,7 @@ from letta.services.passage_manager import PassageManager
51
56
  from letta.services.provider_manager import ProviderManager
52
57
  from letta.services.step_manager import StepManager
53
58
  from letta.services.tool_execution_sandbox import ToolExecutionSandbox
59
+ from letta.services.tool_manager import ToolManager
54
60
  from letta.settings import summarizer_settings
55
61
  from letta.streaming_interface import StreamingRefreshCLIInterface
56
62
  from letta.system import get_heartbeat, get_token_limit_warning, package_function_response, package_summarize_message, package_user_message
@@ -58,9 +64,6 @@ from letta.utils import (
58
64
  count_tokens,
59
65
  get_friendly_error_msg,
60
66
  get_tool_call_id,
61
- get_utc_time,
62
- json_dumps,
63
- json_loads,
64
67
  log_telemetry,
65
68
  parse_json,
66
69
  printd,
@@ -202,7 +205,7 @@ class Agent(BaseAgent):
202
205
 
203
206
  def execute_tool_and_persist_state(
204
207
  self, function_name: str, function_args: dict, target_letta_tool: Tool
205
- ) -> tuple[str, Optional[SandboxRunResult]]:
208
+ ) -> tuple[Any, Optional[SandboxRunResult]]:
206
209
  """
207
210
  Execute tool modifications and persist the state of the agent.
208
211
  Note: only some agent state modifications will be persisted, such as data in the AgentState ORM and block data
@@ -228,6 +231,18 @@ class Agent(BaseAgent):
228
231
  function_args["agent_state"] = agent_state_copy # need to attach self to arg since it's dynamically linked
229
232
  function_response = callable_func(**function_args)
230
233
  self.update_memory_if_changed(agent_state_copy.memory)
234
+ elif target_letta_tool.tool_type == ToolType.EXTERNAL_COMPOSIO:
235
+ action_name = generate_composio_action_from_func_name(target_letta_tool.name)
236
+ # Get entity ID from the agent_state
237
+ entity_id = None
238
+ for env_var in self.agent_state.tool_exec_environment_variables:
239
+ if env_var.key == COMPOSIO_ENTITY_ENV_VAR_KEY:
240
+ entity_id = env_var.value
241
+ # Get composio_api_key
242
+ composio_api_key = get_composio_api_key(actor=self.user, logger=self.logger)
243
+ function_response = execute_composio_action(
244
+ action_name=action_name, args=function_args, api_key=composio_api_key, entity_id=entity_id
245
+ )
231
246
  else:
232
247
  # Parse the source code to extract function annotations
233
248
  annotations = get_function_annotations_from_source(target_letta_tool.source_code, function_name)
@@ -460,7 +475,10 @@ class Agent(BaseAgent):
460
475
  target_letta_tool = None
461
476
  for t in self.agent_state.tools:
462
477
  if t.name == function_name:
463
- target_letta_tool = t
478
+ # This force refreshes the target_letta_tool from the database
479
+ # We only do this on name match to confirm that the agent state contains a specific tool with the right name
480
+ target_letta_tool = ToolManager().get_tool_by_name(tool_name=function_name, actor=self.user)
481
+ break
464
482
 
465
483
  if not target_letta_tool:
466
484
  error_msg = f"No function named {function_name}"
letta/cli/cli_config.py CHANGED
@@ -8,7 +8,7 @@ import typer
8
8
  from prettytable.colortable import ColorTable, Themes
9
9
  from tqdm import tqdm
10
10
 
11
- from letta import utils
11
+ import letta.helpers.datetime_helpers
12
12
 
13
13
  app = typer.Typer()
14
14
 
@@ -51,7 +51,7 @@ def list(arg: Annotated[ListChoice, typer.Argument]):
51
51
  agent.memory.get_block("persona").value[:100] + "...",
52
52
  agent.memory.get_block("human").value[:100] + "...",
53
53
  ",".join(source_names),
54
- utils.format_datetime(agent.created_at),
54
+ letta.helpers.datetime_helpers.format_datetime(agent.created_at),
55
55
  ]
56
56
  )
57
57
  print(table)
@@ -84,7 +84,7 @@ def list(arg: Annotated[ListChoice, typer.Argument]):
84
84
  source.description,
85
85
  source.embedding_config.embedding_model,
86
86
  source.embedding_config.embedding_dim,
87
- utils.format_datetime(source.created_at),
87
+ letta.helpers.datetime_helpers.format_datetime(source.created_at),
88
88
  ]
89
89
  )
90
90
 
letta/constants.py CHANGED
@@ -27,6 +27,7 @@ TOOL_CALL_ID_MAX_LEN = 29
27
27
 
28
28
  # minimum context window size
29
29
  MIN_CONTEXT_WINDOW = 4096
30
+ DEFAULT_CONTEXT_WINDOW_SIZE = 32000
30
31
 
31
32
  # embeddings
32
33
  MAX_EMBEDDING_DIM = 4096 # maximum supported embeding size - do NOT change or else DBs will need to be reset
@@ -33,7 +33,7 @@ def conversation_search(self: "Agent", query: str, page: Optional[int] = 0) -> O
33
33
  import math
34
34
 
35
35
  from letta.constants import RETRIEVAL_QUERY_DEFAULT_PAGE_SIZE
36
- from letta.utils import json_dumps
36
+ from letta.helpers.json_helpers import json_dumps
37
37
 
38
38
  if page is None or (isinstance(page, str) and page.lower().strip() == "none"):
39
39
  page = 0
@@ -5,9 +5,9 @@ from typing import Optional
5
5
  import requests
6
6
 
7
7
  from letta.constants import MESSAGE_CHATGPT_FUNCTION_MODEL, MESSAGE_CHATGPT_FUNCTION_SYSTEM_MESSAGE
8
+ from letta.helpers.json_helpers import json_dumps, json_loads
8
9
  from letta.llm_api.llm_api_tools import create
9
10
  from letta.schemas.message import Message, TextContent
10
- from letta.utils import json_dumps, json_loads
11
11
 
12
12
 
13
13
  def message_chatgpt(self, message: str):
@@ -52,24 +52,53 @@ def generate_composio_tool_wrapper(action_name: str) -> tuple[str, str]:
52
52
  # Generate func name
53
53
  func_name = generate_func_name_from_composio_action(action_name)
54
54
 
55
- wrapper_function_str = f"""
55
+ wrapper_function_str = f"""\
56
56
  def {func_name}(**kwargs):
57
- from composio_langchain import ComposioToolSet
57
+ raise RuntimeError("Something went wrong - we should never be using the persisted source code for Composio. Please reach out to Letta team")
58
+ """
59
+
60
+ # Compile safety check
61
+ assert_code_gen_compilable(wrapper_function_str.strip())
62
+
63
+ return func_name, wrapper_function_str.strip()
64
+
65
+
66
+ def execute_composio_action(
67
+ action_name: str, args: dict, api_key: Optional[str] = None, entity_id: Optional[str] = None
68
+ ) -> tuple[str, str]:
58
69
  import os
59
70
 
60
- entity_id = os.getenv('{COMPOSIO_ENTITY_ENV_VAR_KEY}', '{DEFAULT_ENTITY_ID}')
61
- composio_toolset = ComposioToolSet(entity_id=entity_id)
62
- response = composio_toolset.execute_action(action='{action_name}', params=kwargs)
71
+ from composio.exceptions import (
72
+ ApiKeyNotProvidedError,
73
+ ComposioSDKError,
74
+ ConnectedAccountNotFoundError,
75
+ EnumMetadataNotFound,
76
+ EnumStringNotFound,
77
+ )
78
+ from composio_langchain import ComposioToolSet
63
79
 
64
- if response["error"]:
65
- raise RuntimeError(response["error"])
66
- return response["data"]
67
- """
80
+ entity_id = entity_id or os.getenv(COMPOSIO_ENTITY_ENV_VAR_KEY, DEFAULT_ENTITY_ID)
81
+ try:
82
+ composio_toolset = ComposioToolSet(api_key=api_key, entity_id=entity_id)
83
+ response = composio_toolset.execute_action(action=action_name, params=args)
84
+ except ApiKeyNotProvidedError:
85
+ raise RuntimeError(
86
+ f"Composio API key is missing for action '{action_name}'. "
87
+ "Please set the sandbox environment variables either through the ADE or the API."
88
+ )
89
+ except ConnectedAccountNotFoundError:
90
+ raise RuntimeError(f"No connected account was found for action '{action_name}'. " "Please link an account and try again.")
91
+ except EnumStringNotFound as e:
92
+ raise RuntimeError(f"Invalid value provided for action '{action_name}': " + str(e) + ". Please check the action parameters.")
93
+ except EnumMetadataNotFound as e:
94
+ raise RuntimeError(f"Invalid value provided for action '{action_name}': " + str(e) + ". Please check the action parameters.")
95
+ except ComposioSDKError as e:
96
+ raise RuntimeError(f"An unexpected error occurred in Composio SDK while executing action '{action_name}': " + str(e))
68
97
 
69
- # Compile safety check
70
- assert_code_gen_compilable(wrapper_function_str)
98
+ if response["error"]:
99
+ raise RuntimeError(f"Error while executing action '{action_name}': " + str(response["error"]))
71
100
 
72
- return func_name, wrapper_function_str
101
+ return response["data"]
73
102
 
74
103
 
75
104
  def generate_langchain_tool_wrapper(
@@ -0,0 +1,21 @@
1
+ from logging import Logger
2
+ from typing import Optional
3
+
4
+ from letta.schemas.user import User
5
+ from letta.services.sandbox_config_manager import SandboxConfigManager
6
+ from letta.settings import tool_settings
7
+
8
+
9
+ def get_composio_api_key(actor: User, logger: Logger) -> Optional[str]:
10
+ api_keys = SandboxConfigManager().list_sandbox_env_vars_by_key(key="COMPOSIO_API_KEY", actor=actor)
11
+ if not api_keys:
12
+ logger.warning(f"No API keys found for Composio. Defaulting to the environment variable...")
13
+ if tool_settings.composio_api_key:
14
+ return tool_settings.composio_api_key
15
+ else:
16
+ return None
17
+ else:
18
+ # TODO: Add more protections around this
19
+ # Ideally, not tied to a specific sandbox, but for now we just get the first one
20
+ # Theoretically possible for someone to have different composio api keys per sandbox
21
+ return api_keys[0].value
@@ -0,0 +1,90 @@
1
+ import re
2
+ from datetime import datetime, timedelta, timezone
3
+
4
+ import pytz
5
+
6
+
7
+ def parse_formatted_time(formatted_time):
8
+ # parse times returned by letta.utils.get_formatted_time()
9
+ return datetime.strptime(formatted_time, "%Y-%m-%d %I:%M:%S %p %Z%z")
10
+
11
+
12
+ def datetime_to_timestamp(dt):
13
+ # convert datetime object to integer timestamp
14
+ return int(dt.timestamp())
15
+
16
+
17
+ def timestamp_to_datetime(ts):
18
+ # convert integer timestamp to datetime object
19
+ return datetime.fromtimestamp(ts)
20
+
21
+
22
+ def get_local_time_military():
23
+ # Get the current time in UTC
24
+ current_time_utc = datetime.now(pytz.utc)
25
+
26
+ # Convert to San Francisco's time zone (PST/PDT)
27
+ sf_time_zone = pytz.timezone("America/Los_Angeles")
28
+ local_time = current_time_utc.astimezone(sf_time_zone)
29
+
30
+ # You may format it as you desire
31
+ formatted_time = local_time.strftime("%Y-%m-%d %H:%M:%S %Z%z")
32
+
33
+ return formatted_time
34
+
35
+
36
+ def get_local_time_timezone(timezone="America/Los_Angeles"):
37
+ # Get the current time in UTC
38
+ current_time_utc = datetime.now(pytz.utc)
39
+
40
+ # Convert to San Francisco's time zone (PST/PDT)
41
+ sf_time_zone = pytz.timezone(timezone)
42
+ local_time = current_time_utc.astimezone(sf_time_zone)
43
+
44
+ # You may format it as you desire, including AM/PM
45
+ formatted_time = local_time.strftime("%Y-%m-%d %I:%M:%S %p %Z%z")
46
+
47
+ return formatted_time
48
+
49
+
50
+ def get_local_time(timezone=None):
51
+ if timezone is not None:
52
+ time_str = get_local_time_timezone(timezone)
53
+ else:
54
+ # Get the current time, which will be in the local timezone of the computer
55
+ local_time = datetime.now().astimezone()
56
+
57
+ # You may format it as you desire, including AM/PM
58
+ time_str = local_time.strftime("%Y-%m-%d %I:%M:%S %p %Z%z")
59
+
60
+ return time_str.strip()
61
+
62
+
63
+ def get_utc_time() -> datetime:
64
+ """Get the current UTC time"""
65
+ # return datetime.now(pytz.utc)
66
+ return datetime.now(timezone.utc)
67
+
68
+
69
+ def format_datetime(dt):
70
+ return dt.strftime("%Y-%m-%d %I:%M:%S %p %Z%z")
71
+
72
+
73
+ def validate_date_format(date_str):
74
+ """Validate the given date string in the format 'YYYY-MM-DD'."""
75
+ try:
76
+ datetime.strptime(date_str, "%Y-%m-%d")
77
+ return True
78
+ except (ValueError, TypeError):
79
+ return False
80
+
81
+
82
+ def extract_date_from_timestamp(timestamp):
83
+ """Extracts and returns the date from the given timestamp."""
84
+ # Extracts the date (ignoring the time and timezone)
85
+ match = re.match(r"(\d{4}-\d{2}-\d{2})", timestamp)
86
+ return match.group(1) if match else None
87
+
88
+
89
+ def is_utc_datetime(dt: datetime) -> bool:
90
+ return dt.tzinfo is not None and dt.tzinfo.utcoffset(dt) == timedelta(0)
@@ -0,0 +1,15 @@
1
+ import json
2
+ from datetime import datetime
3
+
4
+
5
+ def json_loads(data):
6
+ return json.loads(data, strict=False)
7
+
8
+
9
+ def json_dumps(data, indent=2):
10
+ def safe_serializer(obj):
11
+ if isinstance(obj, datetime):
12
+ return obj.isoformat()
13
+ raise TypeError(f"Type {type(obj)} not serializable")
14
+
15
+ return json.dumps(data, indent=indent, default=safe_serializer, ensure_ascii=False)
letta/interface.py CHANGED
@@ -5,9 +5,10 @@ from typing import List, Optional
5
5
  from colorama import Fore, Style, init
6
6
 
7
7
  from letta.constants import CLI_WARNING_PREFIX
8
+ from letta.helpers.json_helpers import json_loads
8
9
  from letta.local_llm.constants import ASSISTANT_MESSAGE_CLI_SYMBOL, INNER_THOUGHTS_CLI_SYMBOL
9
10
  from letta.schemas.message import Message
10
- from letta.utils import json_loads, printd
11
+ from letta.utils import printd
11
12
 
12
13
  init(autoreset=True)
13
14
 
@@ -18,6 +18,7 @@ from anthropic.types.beta import (
18
18
  )
19
19
 
20
20
  from letta.errors import BedrockError, BedrockPermissionError
21
+ from letta.helpers.datetime_helpers import get_utc_time
21
22
  from letta.llm_api.aws_bedrock import get_bedrock_client
22
23
  from letta.llm_api.helpers import add_inner_thoughts_to_functions
23
24
  from letta.local_llm.constants import INNER_THOUGHTS_KWARG, INNER_THOUGHTS_KWARG_DESCRIPTION
@@ -39,7 +40,6 @@ from letta.schemas.openai.chat_completion_response import MessageDelta, ToolCall
39
40
  from letta.services.provider_manager import ProviderManager
40
41
  from letta.settings import model_settings
41
42
  from letta.streaming_interface import AgentChunkStreamingInterface, AgentRefreshStreamingInterface
42
- from letta.utils import get_utc_time
43
43
 
44
44
  BASE_URL = "https://api.anthropic.com/v1"
45
45
 
letta/llm_api/cohere.py CHANGED
@@ -4,6 +4,8 @@ from typing import List, Optional, Union
4
4
 
5
5
  import requests
6
6
 
7
+ from letta.helpers.datetime_helpers import get_utc_time
8
+ from letta.helpers.json_helpers import json_dumps
7
9
  from letta.local_llm.utils import count_tokens
8
10
  from letta.schemas.message import Message
9
11
  from letta.schemas.openai.chat_completion_request import ChatCompletionRequest, Tool
@@ -12,7 +14,7 @@ from letta.schemas.openai.chat_completion_response import (
12
14
  Message as ChoiceMessage, # NOTE: avoid conflict with our own Letta Message datatype
13
15
  )
14
16
  from letta.schemas.openai.chat_completion_response import ToolCall, UsageStatistics
15
- from letta.utils import get_tool_call_id, get_utc_time, json_dumps, smart_urljoin
17
+ from letta.utils import get_tool_call_id, smart_urljoin
16
18
 
17
19
  BASE_URL = "https://api.cohere.ai/v1"
18
20
 
@@ -4,12 +4,14 @@ from typing import List, Optional, Tuple
4
4
  import requests
5
5
 
6
6
  from letta.constants import NON_USER_MSG_PREFIX
7
+ from letta.helpers.datetime_helpers import get_utc_time
8
+ from letta.helpers.json_helpers import json_dumps
7
9
  from letta.llm_api.helpers import make_post_request
8
10
  from letta.local_llm.json_parser import clean_json_string_extra_backslash
9
11
  from letta.local_llm.utils import count_tokens
10
12
  from letta.schemas.openai.chat_completion_request import Tool
11
13
  from letta.schemas.openai.chat_completion_response import ChatCompletionResponse, Choice, FunctionCall, Message, ToolCall, UsageStatistics
12
- from letta.utils import get_tool_call_id, get_utc_time, json_dumps
14
+ from letta.utils import get_tool_call_id
13
15
 
14
16
 
15
17
  def get_gemini_endpoint_and_headers(
@@ -0,0 +1,12 @@
1
+ GOOGLE_MODEL_TO_CONTEXT_LENGTH = {
2
+ "gemini-2.0-flash-001": 1048576,
3
+ "gemini-2.0-pro-exp-02-05": 2097152,
4
+ "gemini-2.0-flash-lite-preview-02-05": 1048576,
5
+ "gemini-2.0-flash-thinking-exp-01-21": 1048576,
6
+ "gemini-1.5-flash": 1048576,
7
+ "gemini-1.5-pro": 2097152,
8
+ "gemini-1.0-pro": 32760,
9
+ "gemini-1.0-pro-vision": 16384,
10
+ }
11
+
12
+ GOOGLE_EMBEDING_MODEL_TO_DIM = {"text-embedding-005": 768, "text-multilingual-embedding-002": 768}
@@ -2,11 +2,13 @@ import uuid
2
2
  from typing import List, Optional
3
3
 
4
4
  from letta.constants import NON_USER_MSG_PREFIX
5
+ from letta.helpers.datetime_helpers import get_utc_time
6
+ from letta.helpers.json_helpers import json_dumps
5
7
  from letta.local_llm.json_parser import clean_json_string_extra_backslash
6
8
  from letta.local_llm.utils import count_tokens
7
9
  from letta.schemas.openai.chat_completion_request import Tool
8
10
  from letta.schemas.openai.chat_completion_response import ChatCompletionResponse, Choice, FunctionCall, Message, ToolCall, UsageStatistics
9
- from letta.utils import get_tool_call_id, get_utc_time, json_dumps
11
+ from letta.utils import get_tool_call_id
10
12
 
11
13
 
12
14
  def add_dummy_model_messages(messages: List[dict]) -> List[dict]:
letta/llm_api/helpers.py CHANGED
@@ -7,10 +7,11 @@ from typing import Any, List, Union
7
7
  import requests
8
8
 
9
9
  from letta.constants import OPENAI_CONTEXT_WINDOW_ERROR_SUBSTRING
10
+ from letta.helpers.json_helpers import json_dumps
10
11
  from letta.schemas.message import Message
11
12
  from letta.schemas.openai.chat_completion_response import ChatCompletionResponse, Choice
12
13
  from letta.settings import summarizer_settings
13
- from letta.utils import count_tokens, json_dumps, printd
14
+ from letta.utils import count_tokens, printd
14
15
 
15
16
 
16
17
  def _convert_to_structured_output_helper(property: dict) -> dict:
@@ -6,6 +6,8 @@ import requests
6
6
 
7
7
  from letta.constants import CLI_WARNING_PREFIX
8
8
  from letta.errors import LocalLLMConnectionError, LocalLLMError
9
+ from letta.helpers.datetime_helpers import get_utc_time
10
+ from letta.helpers.json_helpers import json_dumps
9
11
  from letta.local_llm.constants import DEFAULT_WRAPPER
10
12
  from letta.local_llm.function_parser import patch_function
11
13
  from letta.local_llm.grammars.gbnf_grammar_generator import create_dynamic_model_from_function, generate_gbnf_grammar_and_documentation
@@ -20,7 +22,7 @@ from letta.local_llm.webui.api import get_webui_completion
20
22
  from letta.local_llm.webui.legacy_api import get_webui_completion as get_webui_completion_legacy
21
23
  from letta.prompts.gpt_summarize import SYSTEM as SUMMARIZE_SYSTEM_MESSAGE
22
24
  from letta.schemas.openai.chat_completion_response import ChatCompletionResponse, Choice, Message, ToolCall, UsageStatistics
23
- from letta.utils import get_tool_call_id, get_utc_time, json_dumps
25
+ from letta.utils import get_tool_call_id
24
26
 
25
27
  has_shown_warning = False
26
28
  grammar_supported_backends = ["koboldcpp", "llamacpp", "webui", "webui-legacy"]
@@ -1,7 +1,7 @@
1
1
  import copy
2
2
  import json
3
3
 
4
- from letta.utils import json_dumps, json_loads
4
+ from letta.helpers.json_helpers import json_dumps, json_loads
5
5
 
6
6
  NO_HEARTBEAT_FUNCS = ["send_message"]
7
7
 
@@ -10,7 +10,7 @@ from typing import Any, Callable, List, Optional, Tuple, Type, Union, _GenericAl
10
10
  from docstring_parser import parse
11
11
  from pydantic import BaseModel, create_model
12
12
 
13
- from letta.utils import json_dumps
13
+ from letta.helpers.json_helpers import json_dumps
14
14
 
15
15
 
16
16
  class PydanticDataType(Enum):
@@ -2,7 +2,7 @@ import json
2
2
  import re
3
3
 
4
4
  from letta.errors import LLMJSONParsingError
5
- from letta.utils import json_loads
5
+ from letta.helpers.json_helpers import json_loads
6
6
 
7
7
 
8
8
  def clean_json_string_extra_backslash(s):
@@ -1,6 +1,5 @@
1
- from letta.utils import json_dumps, json_loads
2
-
3
1
  from ...errors import LLMJSONParsingError
2
+ from ...helpers.json_helpers import json_dumps, json_loads
4
3
  from ..json_parser import clean_json
5
4
  from .wrapper_base import LLMChatCompletionWrapper
6
5
 
@@ -1,8 +1,8 @@
1
1
  from letta.errors import LLMJSONParsingError
2
+ from letta.helpers.json_helpers import json_dumps, json_loads
2
3
  from letta.local_llm.json_parser import clean_json
3
4
  from letta.local_llm.llm_chat_completion_wrappers.wrapper_base import LLMChatCompletionWrapper
4
5
  from letta.schemas.enums import MessageRole
5
- from letta.utils import json_dumps, json_loads
6
6
 
7
7
  PREFIX_HINT = """# Reminders:
8
8
  # Important information about yourself and the user is stored in (limited) core memory
@@ -1,8 +1,7 @@
1
1
  import yaml
2
2
 
3
- from letta.utils import json_dumps, json_loads
4
-
5
3
  from ...errors import LLMJSONParsingError
4
+ from ...helpers.json_helpers import json_dumps, json_loads
6
5
  from ..json_parser import clean_json
7
6
  from .wrapper_base import LLMChatCompletionWrapper
8
7
 
@@ -1,6 +1,5 @@
1
- from letta.utils import json_dumps, json_loads
2
-
3
1
  from ...errors import LLMJSONParsingError
2
+ from ...helpers.json_helpers import json_dumps, json_loads
4
3
  from ..json_parser import clean_json
5
4
  from .wrapper_base import LLMChatCompletionWrapper
6
5
 
@@ -1,7 +1,7 @@
1
1
  from letta.errors import LLMJSONParsingError
2
+ from letta.helpers.json_helpers import json_dumps, json_loads
2
3
  from letta.local_llm.json_parser import clean_json
3
4
  from letta.local_llm.llm_chat_completion_wrappers.wrapper_base import LLMChatCompletionWrapper
4
- from letta.utils import json_dumps, json_loads
5
5
 
6
6
  PREFIX_HINT = """# Reminders:
7
7
  # Important information about yourself and the user is stored in (limited) core memory
@@ -1,5 +1,4 @@
1
- from letta.utils import json_dumps, json_loads
2
-
1
+ from ...helpers.json_helpers import json_dumps, json_loads
3
2
  from .wrapper_base import LLMChatCompletionWrapper
4
3
 
5
4
 
@@ -1,6 +1,5 @@
1
- from letta.utils import json_dumps, json_loads
2
-
3
1
  from ...errors import LLMJSONParsingError
2
+ from ...helpers.json_helpers import json_dumps, json_loads
4
3
  from ..json_parser import clean_json
5
4
  from .wrapper_base import LLMChatCompletionWrapper
6
5
 
letta/local_llm/utils.py CHANGED
@@ -249,10 +249,7 @@ def num_tokens_from_messages(messages: List[dict], model: str = "gpt-4") -> int:
249
249
  # num_tokens += len(encoding.encode(value["arguments"]))
250
250
 
251
251
  else:
252
- if value is None:
253
- # raise ValueError(f"Message has null value: {key} with value: {value} - message={message}")
254
- warnings.warn(f"Message has null value: {key} with value: {value} - message={message}")
255
- else:
252
+ if value is not None:
256
253
  if not isinstance(value, str):
257
254
  raise ValueError(f"Message has non-string value: {key} with value: {value} - message={message}")
258
255
  num_tokens += len(encoding.encode(value))
@@ -4,7 +4,7 @@ from copy import deepcopy
4
4
  from enum import Enum
5
5
  from typing import Optional, Tuple, Union
6
6
 
7
- from letta.utils import json_dumps
7
+ from letta.helpers.json_helpers import json_dumps
8
8
 
9
9
  api_requestor = None
10
10
  api_resources = None
@@ -5,10 +5,10 @@ from typing import List, Union
5
5
 
6
6
  from pydantic import BaseModel, Field
7
7
 
8
+ from letta.helpers.json_helpers import json_dumps
8
9
  from letta.schemas.enums import MessageStreamStatus
9
10
  from letta.schemas.letta_message import LettaMessage, LettaMessageUnion
10
11
  from letta.schemas.usage import LettaUsageStatistics
11
- from letta.utils import json_dumps
12
12
 
13
13
  # TODO: consider moving into own file
14
14
 
letta/schemas/message.py CHANGED
@@ -12,6 +12,8 @@ from openai.types.chat.chat_completion_message_tool_call import Function as Open
12
12
  from pydantic import BaseModel, Field, field_validator, model_validator
13
13
 
14
14
  from letta.constants import DEFAULT_MESSAGE_TOOL, DEFAULT_MESSAGE_TOOL_KWARG, TOOL_CALL_ID_MAX_LEN
15
+ from letta.helpers.datetime_helpers import get_utc_time, is_utc_datetime
16
+ from letta.helpers.json_helpers import json_dumps
15
17
  from letta.local_llm.constants import INNER_THOUGHTS_KWARG
16
18
  from letta.schemas.enums import MessageContentType, MessageRole
17
19
  from letta.schemas.letta_base import OrmMetadataBase
@@ -28,7 +30,6 @@ from letta.schemas.letta_message import (
28
30
  UserMessage,
29
31
  )
30
32
  from letta.system import unpack_message
31
- from letta.utils import get_utc_time, is_utc_datetime, json_dumps
32
33
 
33
34
 
34
35
  def add_inner_thoughts_to_tool_call(
@@ -3,8 +3,9 @@ from typing import Optional
3
3
 
4
4
  from pydantic import Field
5
5
 
6
+ from letta.helpers.datetime_helpers import get_utc_time
6
7
  from letta.schemas.letta_base import LettaBase
7
- from letta.utils import create_random_username, get_utc_time
8
+ from letta.utils import create_random_username
8
9
 
9
10
 
10
11
  class OrganizationBase(LettaBase):
letta/schemas/passage.py CHANGED
@@ -4,9 +4,9 @@ from typing import Dict, List, Optional
4
4
  from pydantic import Field, field_validator
5
5
 
6
6
  from letta.constants import MAX_EMBEDDING_DIM
7
+ from letta.helpers.datetime_helpers import get_utc_time
7
8
  from letta.schemas.embedding_config import EmbeddingConfig
8
9
  from letta.schemas.letta_base import OrmMetadataBase
9
- from letta.utils import get_utc_time
10
10
 
11
11
 
12
12
  class PassageBase(OrmMetadataBase):