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.
- letta/__init__.py +1 -1
- letta/agent.py +24 -6
- letta/cli/cli_config.py +3 -3
- letta/constants.py +1 -0
- letta/functions/function_sets/base.py +1 -1
- letta/functions/function_sets/extras.py +1 -1
- letta/functions/helpers.py +41 -12
- letta/helpers/composio_helpers.py +21 -0
- letta/helpers/datetime_helpers.py +90 -0
- letta/helpers/json_helpers.py +15 -0
- letta/interface.py +2 -1
- letta/llm_api/anthropic.py +1 -1
- letta/llm_api/cohere.py +3 -1
- letta/llm_api/google_ai.py +3 -1
- letta/llm_api/google_constants.py +12 -0
- letta/llm_api/google_vertex.py +3 -1
- letta/llm_api/helpers.py +2 -1
- letta/local_llm/chat_completion_proxy.py +3 -1
- letta/local_llm/function_parser.py +1 -1
- letta/local_llm/grammars/gbnf_grammar_generator.py +1 -1
- letta/local_llm/json_parser.py +1 -1
- letta/local_llm/llm_chat_completion_wrappers/airoboros.py +1 -2
- letta/local_llm/llm_chat_completion_wrappers/chatml.py +1 -1
- letta/local_llm/llm_chat_completion_wrappers/configurable_wrapper.py +1 -2
- letta/local_llm/llm_chat_completion_wrappers/dolphin.py +1 -2
- letta/local_llm/llm_chat_completion_wrappers/llama3.py +1 -1
- letta/local_llm/llm_chat_completion_wrappers/simple_summary_wrapper.py +1 -2
- letta/local_llm/llm_chat_completion_wrappers/zephyr.py +1 -2
- letta/local_llm/utils.py +1 -4
- letta/openai_backcompat/openai_object.py +1 -1
- letta/schemas/letta_response.py +1 -1
- letta/schemas/message.py +2 -1
- letta/schemas/organization.py +2 -1
- letta/schemas/passage.py +1 -1
- letta/server/rest_api/interface.py +1 -1
- letta/server/rest_api/routers/v1/tools.py +38 -39
- letta/server/server.py +7 -3
- letta/server/ws_api/protocol.py +1 -1
- letta/services/agent_manager.py +2 -1
- letta/services/helpers/agent_manager_helper.py +1 -1
- letta/services/job_manager.py +2 -1
- letta/services/sandbox_config_manager.py +1 -1
- letta/services/tool_execution_sandbox.py +1 -1
- letta/system.py +2 -1
- letta/utils.py +2 -103
- {letta_nightly-0.6.25.dev20250213104102.dist-info → letta_nightly-0.6.26.dev20250214104050.dist-info}/METADATA +3 -3
- {letta_nightly-0.6.25.dev20250213104102.dist-info → letta_nightly-0.6.26.dev20250214104050.dist-info}/RECORD +50 -46
- {letta_nightly-0.6.25.dev20250213104102.dist-info → letta_nightly-0.6.26.dev20250214104050.dist-info}/LICENSE +0 -0
- {letta_nightly-0.6.25.dev20250213104102.dist-info → letta_nightly-0.6.26.dev20250214104050.dist-info}/WHEEL +0 -0
- {letta_nightly-0.6.25.dev20250213104102.dist-info → letta_nightly-0.6.26.dev20250214104050.dist-info}/entry_points.txt +0 -0
|
@@ -7,6 +7,7 @@ from datetime import datetime
|
|
|
7
7
|
from typing import AsyncGenerator, Literal, Optional, Union
|
|
8
8
|
|
|
9
9
|
from letta.constants import DEFAULT_MESSAGE_TOOL, DEFAULT_MESSAGE_TOOL_KWARG
|
|
10
|
+
from letta.helpers.datetime_helpers import is_utc_datetime
|
|
10
11
|
from letta.interface import AgentInterface
|
|
11
12
|
from letta.local_llm.constants import INNER_THOUGHTS_KWARG
|
|
12
13
|
from letta.schemas.enums import MessageStreamStatus
|
|
@@ -25,7 +26,6 @@ from letta.schemas.message import Message
|
|
|
25
26
|
from letta.schemas.openai.chat_completion_response import ChatCompletionChunkResponse
|
|
26
27
|
from letta.streaming_interface import AgentChunkStreamingInterface
|
|
27
28
|
from letta.streaming_utils import FunctionArgumentsStreamHandler, JSONInnerThoughtsExtractor
|
|
28
|
-
from letta.utils import is_utc_datetime
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
# TODO strip from code / deprecate
|
|
@@ -2,20 +2,23 @@ from typing import List, Optional
|
|
|
2
2
|
|
|
3
3
|
from composio.client import ComposioClientError, HTTPError, NoItemsFound
|
|
4
4
|
from composio.client.collections import ActionModel, AppModel
|
|
5
|
-
from composio.
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
from composio.exceptions import (
|
|
6
|
+
ApiKeyNotProvidedError,
|
|
7
|
+
ComposioSDKError,
|
|
8
|
+
ConnectedAccountNotFoundError,
|
|
9
|
+
EnumMetadataNotFound,
|
|
10
|
+
EnumStringNotFound,
|
|
11
|
+
)
|
|
8
12
|
from fastapi import APIRouter, Body, Depends, Header, HTTPException
|
|
9
13
|
|
|
10
14
|
from letta.errors import LettaToolCreateError
|
|
15
|
+
from letta.helpers.composio_helpers import get_composio_api_key
|
|
11
16
|
from letta.log import get_logger
|
|
12
17
|
from letta.orm.errors import UniqueConstraintViolationError
|
|
13
18
|
from letta.schemas.letta_message import ToolReturnMessage
|
|
14
19
|
from letta.schemas.tool import Tool, ToolCreate, ToolRunFromSource, ToolUpdate
|
|
15
|
-
from letta.schemas.user import User
|
|
16
20
|
from letta.server.rest_api.utils import get_letta_server
|
|
17
21
|
from letta.server.server import SyncServer
|
|
18
|
-
from letta.settings import tool_settings
|
|
19
22
|
|
|
20
23
|
router = APIRouter(prefix="/tools", tags=["tools"])
|
|
21
24
|
|
|
@@ -205,15 +208,18 @@ def run_tool_from_source(
|
|
|
205
208
|
|
|
206
209
|
|
|
207
210
|
# Specific routes for Composio
|
|
208
|
-
|
|
209
|
-
|
|
210
211
|
@router.get("/composio/apps", response_model=List[AppModel], operation_id="list_composio_apps")
|
|
211
212
|
def list_composio_apps(server: SyncServer = Depends(get_letta_server), user_id: Optional[str] = Header(None, alias="user_id")):
|
|
212
213
|
"""
|
|
213
214
|
Get a list of all Composio apps
|
|
214
215
|
"""
|
|
215
216
|
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
216
|
-
composio_api_key =
|
|
217
|
+
composio_api_key = get_composio_api_key(actor=actor, logger=logger)
|
|
218
|
+
if not composio_api_key:
|
|
219
|
+
raise HTTPException(
|
|
220
|
+
status_code=400, # Bad Request
|
|
221
|
+
detail=f"No API keys found for Composio. Please add your Composio API Key as an environment variable for your sandbox configuration, or set it as environment variable COMPOSIO_API_KEY.",
|
|
222
|
+
)
|
|
217
223
|
return server.get_composio_apps(api_key=composio_api_key)
|
|
218
224
|
|
|
219
225
|
|
|
@@ -227,7 +233,12 @@ def list_composio_actions_by_app(
|
|
|
227
233
|
Get a list of all Composio actions for a specific app
|
|
228
234
|
"""
|
|
229
235
|
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
230
|
-
composio_api_key =
|
|
236
|
+
composio_api_key = get_composio_api_key(actor=actor, logger=logger)
|
|
237
|
+
if not composio_api_key:
|
|
238
|
+
raise HTTPException(
|
|
239
|
+
status_code=400, # Bad Request
|
|
240
|
+
detail=f"No API keys found for Composio. Please add your Composio API Key as an environment variable for your sandbox configuration, or set it as environment variable COMPOSIO_API_KEY.",
|
|
241
|
+
)
|
|
231
242
|
return server.get_composio_actions_from_app_name(composio_app_name=composio_app_name, api_key=composio_api_key)
|
|
232
243
|
|
|
233
244
|
|
|
@@ -245,6 +256,15 @@ def add_composio_tool(
|
|
|
245
256
|
try:
|
|
246
257
|
tool_create = ToolCreate.from_composio(action_name=composio_action_name)
|
|
247
258
|
return server.tool_manager.create_or_update_composio_tool(tool_create=tool_create, actor=actor)
|
|
259
|
+
except ConnectedAccountNotFoundError as e:
|
|
260
|
+
raise HTTPException(
|
|
261
|
+
status_code=400, # Bad Request
|
|
262
|
+
detail={
|
|
263
|
+
"code": "ConnectedAccountNotFoundError",
|
|
264
|
+
"message": str(e),
|
|
265
|
+
"composio_action_name": composio_action_name,
|
|
266
|
+
},
|
|
267
|
+
)
|
|
248
268
|
except EnumStringNotFound as e:
|
|
249
269
|
raise HTTPException(
|
|
250
270
|
status_code=400, # Bad Request
|
|
@@ -254,6 +274,15 @@ def add_composio_tool(
|
|
|
254
274
|
"composio_action_name": composio_action_name,
|
|
255
275
|
},
|
|
256
276
|
)
|
|
277
|
+
except EnumMetadataNotFound as e:
|
|
278
|
+
raise HTTPException(
|
|
279
|
+
status_code=400, # Bad Request
|
|
280
|
+
detail={
|
|
281
|
+
"code": "EnumMetadataNotFound",
|
|
282
|
+
"message": str(e),
|
|
283
|
+
"composio_action_name": composio_action_name,
|
|
284
|
+
},
|
|
285
|
+
)
|
|
257
286
|
except HTTPError as e:
|
|
258
287
|
raise HTTPException(
|
|
259
288
|
status_code=400, # Bad Request
|
|
@@ -290,15 +319,6 @@ def add_composio_tool(
|
|
|
290
319
|
"composio_action_name": composio_action_name,
|
|
291
320
|
},
|
|
292
321
|
)
|
|
293
|
-
except InvalidClassDefinition as e:
|
|
294
|
-
raise HTTPException(
|
|
295
|
-
status_code=400, # Bad Request
|
|
296
|
-
detail={
|
|
297
|
-
"code": "InvalidClassDefinition",
|
|
298
|
-
"message": str(e),
|
|
299
|
-
"composio_action_name": composio_action_name,
|
|
300
|
-
},
|
|
301
|
-
)
|
|
302
322
|
except ComposioSDKError as e:
|
|
303
323
|
raise HTTPException(
|
|
304
324
|
status_code=400, # Bad Request
|
|
@@ -308,24 +328,3 @@ def add_composio_tool(
|
|
|
308
328
|
"composio_action_name": composio_action_name,
|
|
309
329
|
},
|
|
310
330
|
)
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
# TODO: Factor this out to somewhere else
|
|
314
|
-
def get_composio_key(server: SyncServer, actor: User):
|
|
315
|
-
api_keys = server.sandbox_config_manager.list_sandbox_env_vars_by_key(key="COMPOSIO_API_KEY", actor=actor)
|
|
316
|
-
if not api_keys:
|
|
317
|
-
logger.warning(f"No API keys found for Composio. Defaulting to the environment variable...")
|
|
318
|
-
|
|
319
|
-
if tool_settings.composio_api_key:
|
|
320
|
-
return tool_settings.composio_api_key
|
|
321
|
-
else:
|
|
322
|
-
# Nothing, raise fatal warning
|
|
323
|
-
raise HTTPException(
|
|
324
|
-
status_code=400, # Bad Request
|
|
325
|
-
detail=f"No API keys found for Composio. Please add your Composio API Key as an environment variable for your sandbox configuration, or set it as environment variable COMPOSIO_API_KEY.",
|
|
326
|
-
)
|
|
327
|
-
else:
|
|
328
|
-
# TODO: Add more protections around this
|
|
329
|
-
# Ideally, not tied to a specific sandbox, but for now we just get the first one
|
|
330
|
-
# Theoretically possible for someone to have different composio api keys per sandbox
|
|
331
|
-
return api_keys[0].value
|
letta/server/server.py
CHANGED
|
@@ -19,6 +19,8 @@ import letta.system as system
|
|
|
19
19
|
from letta.agent import Agent, save_agent
|
|
20
20
|
from letta.chat_only_agent import ChatOnlyAgent
|
|
21
21
|
from letta.data_sources.connectors import DataConnector, load_data
|
|
22
|
+
from letta.helpers.datetime_helpers import get_utc_time
|
|
23
|
+
from letta.helpers.json_helpers import json_dumps, json_loads
|
|
22
24
|
|
|
23
25
|
# TODO use custom interface
|
|
24
26
|
from letta.interface import AgentInterface # abstract
|
|
@@ -80,7 +82,7 @@ from letta.services.step_manager import StepManager
|
|
|
80
82
|
from letta.services.tool_execution_sandbox import ToolExecutionSandbox
|
|
81
83
|
from letta.services.tool_manager import ToolManager
|
|
82
84
|
from letta.services.user_manager import UserManager
|
|
83
|
-
from letta.utils import get_friendly_error_msg
|
|
85
|
+
from letta.utils import get_friendly_error_msg
|
|
84
86
|
|
|
85
87
|
logger = get_logger(__name__)
|
|
86
88
|
|
|
@@ -296,7 +298,7 @@ class SyncServer(Server):
|
|
|
296
298
|
self.tool_manager = ToolManager()
|
|
297
299
|
self.block_manager = BlockManager()
|
|
298
300
|
self.source_manager = SourceManager()
|
|
299
|
-
self.sandbox_config_manager = SandboxConfigManager(
|
|
301
|
+
self.sandbox_config_manager = SandboxConfigManager()
|
|
300
302
|
self.message_manager = MessageManager()
|
|
301
303
|
self.job_manager = JobManager()
|
|
302
304
|
self.agent_manager = AgentManager()
|
|
@@ -315,7 +317,7 @@ class SyncServer(Server):
|
|
|
315
317
|
|
|
316
318
|
# Add composio keys to the tool sandbox env vars of the org
|
|
317
319
|
if tool_settings.composio_api_key:
|
|
318
|
-
manager = SandboxConfigManager(
|
|
320
|
+
manager = SandboxConfigManager()
|
|
319
321
|
sandbox_config = manager.get_or_create_default_sandbox_config(sandbox_type=SandboxType.LOCAL, actor=self.default_user)
|
|
320
322
|
|
|
321
323
|
manager.create_sandbox_env_var(
|
|
@@ -1112,6 +1114,8 @@ class SyncServer(Server):
|
|
|
1112
1114
|
if context_window_limit > llm_config.context_window:
|
|
1113
1115
|
raise ValueError(f"Context window limit ({context_window_limit}) is greater than maximum of ({llm_config.context_window})")
|
|
1114
1116
|
llm_config.context_window = context_window_limit
|
|
1117
|
+
else:
|
|
1118
|
+
llm_config.context_window = min(llm_config.context_window, constants.DEFAULT_CONTEXT_WINDOW_SIZE)
|
|
1115
1119
|
|
|
1116
1120
|
return llm_config
|
|
1117
1121
|
|
letta/server/ws_api/protocol.py
CHANGED
letta/services/agent_manager.py
CHANGED
|
@@ -6,6 +6,7 @@ from sqlalchemy import Select, and_, func, literal, or_, select, union_all
|
|
|
6
6
|
|
|
7
7
|
from letta.constants import BASE_MEMORY_TOOLS, BASE_TOOLS, MAX_EMBEDDING_DIM, MULTI_AGENT_TOOLS
|
|
8
8
|
from letta.embeddings import embedding_model
|
|
9
|
+
from letta.helpers.datetime_helpers import get_utc_time
|
|
9
10
|
from letta.log import get_logger
|
|
10
11
|
from letta.orm import Agent as AgentModel
|
|
11
12
|
from letta.orm import AgentPassage, AgentsTags
|
|
@@ -42,7 +43,7 @@ from letta.services.message_manager import MessageManager
|
|
|
42
43
|
from letta.services.source_manager import SourceManager
|
|
43
44
|
from letta.services.tool_manager import ToolManager
|
|
44
45
|
from letta.settings import settings
|
|
45
|
-
from letta.utils import enforce_types,
|
|
46
|
+
from letta.utils import enforce_types, united_diff
|
|
46
47
|
|
|
47
48
|
logger = get_logger(__name__)
|
|
48
49
|
|
|
@@ -4,6 +4,7 @@ from typing import List, Literal, Optional
|
|
|
4
4
|
from letta import system
|
|
5
5
|
from letta.constants import IN_CONTEXT_MEMORY_KEYWORD, STRUCTURED_OUTPUT_MODELS
|
|
6
6
|
from letta.helpers import ToolRulesSolver
|
|
7
|
+
from letta.helpers.datetime_helpers import get_local_time
|
|
7
8
|
from letta.orm.agent import Agent as AgentModel
|
|
8
9
|
from letta.orm.agents_tags import AgentsTags
|
|
9
10
|
from letta.orm.errors import NoResultFound
|
|
@@ -15,7 +16,6 @@ from letta.schemas.message import Message, MessageCreate, TextContent
|
|
|
15
16
|
from letta.schemas.tool_rule import ToolRule
|
|
16
17
|
from letta.schemas.user import User
|
|
17
18
|
from letta.system import get_initial_boot_messages, get_login_event
|
|
18
|
-
from letta.utils import get_local_time
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
# Static methods
|
letta/services/job_manager.py
CHANGED
|
@@ -5,6 +5,7 @@ from typing import List, Literal, Optional, Union
|
|
|
5
5
|
from sqlalchemy import select
|
|
6
6
|
from sqlalchemy.orm import Session
|
|
7
7
|
|
|
8
|
+
from letta.helpers.datetime_helpers import get_utc_time
|
|
8
9
|
from letta.orm.enums import JobType
|
|
9
10
|
from letta.orm.errors import NoResultFound
|
|
10
11
|
from letta.orm.job import Job as JobModel
|
|
@@ -20,7 +21,7 @@ from letta.schemas.message import Message as PydanticMessage
|
|
|
20
21
|
from letta.schemas.run import Run as PydanticRun
|
|
21
22
|
from letta.schemas.usage import LettaUsageStatistics
|
|
22
23
|
from letta.schemas.user import User as PydanticUser
|
|
23
|
-
from letta.utils import enforce_types
|
|
24
|
+
from letta.utils import enforce_types
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
class JobManager:
|
|
@@ -19,7 +19,7 @@ logger = get_logger(__name__)
|
|
|
19
19
|
class SandboxConfigManager:
|
|
20
20
|
"""Manager class to handle business logic related to SandboxConfig and SandboxEnvironmentVariable."""
|
|
21
21
|
|
|
22
|
-
def __init__(self
|
|
22
|
+
def __init__(self):
|
|
23
23
|
from letta.server.server import db_context
|
|
24
24
|
|
|
25
25
|
self.session_maker = db_context
|
|
@@ -62,7 +62,7 @@ class ToolExecutionSandbox:
|
|
|
62
62
|
f"Agent attempted to invoke tool {self.tool_name} that does not exist for organization {self.user.organization_id}"
|
|
63
63
|
)
|
|
64
64
|
|
|
65
|
-
self.sandbox_config_manager = SandboxConfigManager(
|
|
65
|
+
self.sandbox_config_manager = SandboxConfigManager()
|
|
66
66
|
self.force_recreate = force_recreate
|
|
67
67
|
self.force_recreate_venv = force_recreate_venv
|
|
68
68
|
|
letta/system.py
CHANGED
|
@@ -9,7 +9,8 @@ from .constants import (
|
|
|
9
9
|
INITIAL_BOOT_MESSAGE_SEND_MESSAGE_THOUGHT,
|
|
10
10
|
MESSAGE_SUMMARY_WARNING_STR,
|
|
11
11
|
)
|
|
12
|
-
from .
|
|
12
|
+
from .helpers.datetime_helpers import get_local_time
|
|
13
|
+
from .helpers.json_helpers import json_dumps
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
def get_initial_boot_messages(version="startup"):
|
letta/utils.py
CHANGED
|
@@ -4,7 +4,6 @@ import difflib
|
|
|
4
4
|
import hashlib
|
|
5
5
|
import inspect
|
|
6
6
|
import io
|
|
7
|
-
import json
|
|
8
7
|
import os
|
|
9
8
|
import pickle
|
|
10
9
|
import platform
|
|
@@ -14,14 +13,13 @@ import subprocess
|
|
|
14
13
|
import sys
|
|
15
14
|
import uuid
|
|
16
15
|
from contextlib import contextmanager
|
|
17
|
-
from datetime import datetime,
|
|
16
|
+
from datetime import datetime, timezone
|
|
18
17
|
from functools import wraps
|
|
19
18
|
from logging import Logger
|
|
20
19
|
from typing import Any, Coroutine, List, Union, _GenericAlias, get_args, get_origin, get_type_hints
|
|
21
20
|
from urllib.parse import urljoin, urlparse
|
|
22
21
|
|
|
23
22
|
import demjson3 as demjson
|
|
24
|
-
import pytz
|
|
25
23
|
import tiktoken
|
|
26
24
|
from pathvalidate import sanitize_filename as pathvalidate_sanitize_filename
|
|
27
25
|
|
|
@@ -35,6 +33,7 @@ from letta.constants import (
|
|
|
35
33
|
MAX_FILENAME_LENGTH,
|
|
36
34
|
TOOL_CALL_ID_MAX_LEN,
|
|
37
35
|
)
|
|
36
|
+
from letta.helpers.json_helpers import json_dumps, json_loads
|
|
38
37
|
from letta.schemas.openai.chat_completion_response import ChatCompletionResponse
|
|
39
38
|
|
|
40
39
|
DEBUG = False
|
|
@@ -487,10 +486,6 @@ def smart_urljoin(base_url: str, relative_url: str) -> str:
|
|
|
487
486
|
return urljoin(base_url, relative_url)
|
|
488
487
|
|
|
489
488
|
|
|
490
|
-
def is_utc_datetime(dt: datetime) -> bool:
|
|
491
|
-
return dt.tzinfo is not None and dt.tzinfo.utcoffset(dt) == timedelta(0)
|
|
492
|
-
|
|
493
|
-
|
|
494
489
|
def get_tool_call_id() -> str:
|
|
495
490
|
# TODO(sarah) make this a slug-style string?
|
|
496
491
|
# e.g. OpenAI: "call_xlIfzR1HqAW7xJPa3ExJSg3C"
|
|
@@ -824,72 +819,6 @@ def united_diff(str1, str2):
|
|
|
824
819
|
return "".join(diff)
|
|
825
820
|
|
|
826
821
|
|
|
827
|
-
def parse_formatted_time(formatted_time):
|
|
828
|
-
# parse times returned by letta.utils.get_formatted_time()
|
|
829
|
-
return datetime.strptime(formatted_time, "%Y-%m-%d %I:%M:%S %p %Z%z")
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
def datetime_to_timestamp(dt):
|
|
833
|
-
# convert datetime object to integer timestamp
|
|
834
|
-
return int(dt.timestamp())
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
def timestamp_to_datetime(ts):
|
|
838
|
-
# convert integer timestamp to datetime object
|
|
839
|
-
return datetime.fromtimestamp(ts)
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
def get_local_time_military():
|
|
843
|
-
# Get the current time in UTC
|
|
844
|
-
current_time_utc = datetime.now(pytz.utc)
|
|
845
|
-
|
|
846
|
-
# Convert to San Francisco's time zone (PST/PDT)
|
|
847
|
-
sf_time_zone = pytz.timezone("America/Los_Angeles")
|
|
848
|
-
local_time = current_time_utc.astimezone(sf_time_zone)
|
|
849
|
-
|
|
850
|
-
# You may format it as you desire
|
|
851
|
-
formatted_time = local_time.strftime("%Y-%m-%d %H:%M:%S %Z%z")
|
|
852
|
-
|
|
853
|
-
return formatted_time
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
def get_local_time_timezone(timezone="America/Los_Angeles"):
|
|
857
|
-
# Get the current time in UTC
|
|
858
|
-
current_time_utc = datetime.now(pytz.utc)
|
|
859
|
-
|
|
860
|
-
# Convert to San Francisco's time zone (PST/PDT)
|
|
861
|
-
sf_time_zone = pytz.timezone(timezone)
|
|
862
|
-
local_time = current_time_utc.astimezone(sf_time_zone)
|
|
863
|
-
|
|
864
|
-
# You may format it as you desire, including AM/PM
|
|
865
|
-
formatted_time = local_time.strftime("%Y-%m-%d %I:%M:%S %p %Z%z")
|
|
866
|
-
|
|
867
|
-
return formatted_time
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
def get_local_time(timezone=None):
|
|
871
|
-
if timezone is not None:
|
|
872
|
-
time_str = get_local_time_timezone(timezone)
|
|
873
|
-
else:
|
|
874
|
-
# Get the current time, which will be in the local timezone of the computer
|
|
875
|
-
local_time = datetime.now().astimezone()
|
|
876
|
-
|
|
877
|
-
# You may format it as you desire, including AM/PM
|
|
878
|
-
time_str = local_time.strftime("%Y-%m-%d %I:%M:%S %p %Z%z")
|
|
879
|
-
|
|
880
|
-
return time_str.strip()
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
def get_utc_time() -> datetime:
|
|
884
|
-
"""Get the current UTC time"""
|
|
885
|
-
# return datetime.now(pytz.utc)
|
|
886
|
-
return datetime.now(timezone.utc)
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
def format_datetime(dt):
|
|
890
|
-
return dt.strftime("%Y-%m-%d %I:%M:%S %p %Z%z")
|
|
891
|
-
|
|
892
|
-
|
|
893
822
|
def parse_json(string) -> dict:
|
|
894
823
|
"""Parse JSON string into JSON with both json and demjson"""
|
|
895
824
|
result = None
|
|
@@ -1046,23 +975,6 @@ def get_schema_diff(schema_a, schema_b):
|
|
|
1046
975
|
return "".join(difference)
|
|
1047
976
|
|
|
1048
977
|
|
|
1049
|
-
# datetime related
|
|
1050
|
-
def validate_date_format(date_str):
|
|
1051
|
-
"""Validate the given date string in the format 'YYYY-MM-DD'."""
|
|
1052
|
-
try:
|
|
1053
|
-
datetime.strptime(date_str, "%Y-%m-%d")
|
|
1054
|
-
return True
|
|
1055
|
-
except (ValueError, TypeError):
|
|
1056
|
-
return False
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
def extract_date_from_timestamp(timestamp):
|
|
1060
|
-
"""Extracts and returns the date from the given timestamp."""
|
|
1061
|
-
# Extracts the date (ignoring the time and timezone)
|
|
1062
|
-
match = re.match(r"(\d{4}-\d{2}-\d{2})", timestamp)
|
|
1063
|
-
return match.group(1) if match else None
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
978
|
def create_uuid_from_string(val: str):
|
|
1067
979
|
"""
|
|
1068
980
|
Generate consistent UUID from a string
|
|
@@ -1072,19 +984,6 @@ def create_uuid_from_string(val: str):
|
|
|
1072
984
|
return uuid.UUID(hex=hex_string)
|
|
1073
985
|
|
|
1074
986
|
|
|
1075
|
-
def json_dumps(data, indent=2):
|
|
1076
|
-
def safe_serializer(obj):
|
|
1077
|
-
if isinstance(obj, datetime):
|
|
1078
|
-
return obj.isoformat()
|
|
1079
|
-
raise TypeError(f"Type {type(obj)} not serializable")
|
|
1080
|
-
|
|
1081
|
-
return json.dumps(data, indent=indent, default=safe_serializer, ensure_ascii=False)
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
def json_loads(data):
|
|
1085
|
-
return json.loads(data, strict=False)
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
987
|
def sanitize_filename(filename: str) -> str:
|
|
1089
988
|
"""
|
|
1090
989
|
Sanitize the given filename to prevent directory traversal, invalid characters,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: letta-nightly
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.26.dev20250214104050
|
|
4
4
|
Summary: Create LLM agents with long-term memory and custom tools
|
|
5
5
|
License: Apache License
|
|
6
6
|
Author: Letta Team
|
|
@@ -27,8 +27,8 @@ Requires-Dist: autoflake (>=2.3.0,<3.0.0) ; extra == "dev" or extra == "all"
|
|
|
27
27
|
Requires-Dist: black[jupyter] (>=24.2.0,<25.0.0) ; extra == "dev" or extra == "all"
|
|
28
28
|
Requires-Dist: brotli (>=1.1.0,<2.0.0)
|
|
29
29
|
Requires-Dist: colorama (>=0.4.6,<0.5.0)
|
|
30
|
-
Requires-Dist: composio-core (>=0.
|
|
31
|
-
Requires-Dist: composio-langchain (>=0.
|
|
30
|
+
Requires-Dist: composio-core (>=0.7.2,<0.8.0)
|
|
31
|
+
Requires-Dist: composio-langchain (>=0.7.2,<0.8.0)
|
|
32
32
|
Requires-Dist: datasets (>=2.14.6,<3.0.0) ; extra == "dev" or extra == "all"
|
|
33
33
|
Requires-Dist: demjson3 (>=3.0.6,<4.0.0)
|
|
34
34
|
Requires-Dist: docker (>=7.1.0,<8.0.0) ; extra == "external-tools" or extra == "all"
|