letta-nightly 0.5.4.dev20241126104249__py3-none-any.whl → 0.5.4.dev20241128000451__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 +102 -140
- letta/agent_store/chroma.py +2 -0
- letta/cli/cli.py +3 -5
- letta/client/client.py +360 -117
- letta/config.py +2 -2
- letta/constants.py +5 -0
- letta/errors.py +12 -0
- letta/functions/function_sets/base.py +38 -1
- letta/functions/functions.py +4 -6
- letta/functions/schema_generator.py +6 -5
- letta/helpers/tool_rule_solver.py +6 -5
- letta/main.py +1 -1
- letta/metadata.py +45 -42
- letta/o1_agent.py +1 -4
- letta/orm/block.py +2 -1
- letta/orm/blocks_agents.py +4 -1
- letta/orm/sqlalchemy_base.py +13 -0
- letta/persistence_manager.py +1 -0
- letta/schemas/agent.py +57 -52
- letta/schemas/block.py +70 -26
- letta/schemas/enums.py +14 -0
- letta/schemas/letta_base.py +1 -1
- letta/schemas/letta_request.py +11 -23
- letta/schemas/letta_response.py +1 -2
- letta/schemas/memory.py +31 -100
- letta/schemas/message.py +3 -3
- letta/schemas/tool_rule.py +13 -5
- letta/server/rest_api/interface.py +12 -19
- letta/server/rest_api/routers/openai/assistants/threads.py +2 -3
- letta/server/rest_api/routers/openai/chat_completions/chat_completions.py +0 -2
- letta/server/rest_api/routers/v1/agents.py +100 -94
- letta/server/rest_api/routers/v1/blocks.py +50 -5
- letta/server/rest_api/routers/v1/tools.py +14 -3
- letta/server/server.py +246 -460
- letta/server/static_files/assets/index-9fa459a2.js +1 -1
- letta/services/block_manager.py +23 -4
- letta/services/blocks_agents_manager.py +23 -1
- letta/services/per_agent_lock_manager.py +18 -0
- letta/services/tool_execution_sandbox.py +1 -1
- letta/services/tool_manager.py +2 -1
- {letta_nightly-0.5.4.dev20241126104249.dist-info → letta_nightly-0.5.4.dev20241128000451.dist-info}/METADATA +1 -1
- {letta_nightly-0.5.4.dev20241126104249.dist-info → letta_nightly-0.5.4.dev20241128000451.dist-info}/RECORD +46 -45
- {letta_nightly-0.5.4.dev20241126104249.dist-info → letta_nightly-0.5.4.dev20241128000451.dist-info}/LICENSE +0 -0
- {letta_nightly-0.5.4.dev20241126104249.dist-info → letta_nightly-0.5.4.dev20241128000451.dist-info}/WHEEL +0 -0
- {letta_nightly-0.5.4.dev20241126104249.dist-info → letta_nightly-0.5.4.dev20241128000451.dist-info}/entry_points.txt +0 -0
letta/server/server.py
CHANGED
|
@@ -3,6 +3,7 @@ import os
|
|
|
3
3
|
import traceback
|
|
4
4
|
import warnings
|
|
5
5
|
from abc import abstractmethod
|
|
6
|
+
from asyncio import Lock
|
|
6
7
|
from datetime import datetime
|
|
7
8
|
from typing import Callable, Dict, List, Optional, Tuple, Union
|
|
8
9
|
|
|
@@ -17,24 +18,10 @@ from letta.agent_store.storage import StorageConnector, TableType
|
|
|
17
18
|
from letta.credentials import LettaCredentials
|
|
18
19
|
from letta.data_sources.connectors import DataConnector, load_data
|
|
19
20
|
|
|
20
|
-
# from letta.data_types import (
|
|
21
|
-
# AgentState,
|
|
22
|
-
# EmbeddingConfig,
|
|
23
|
-
# LLMConfig,
|
|
24
|
-
# Message,
|
|
25
|
-
# Preset,
|
|
26
|
-
# Source,
|
|
27
|
-
# Token,
|
|
28
|
-
# User,
|
|
29
|
-
# )
|
|
30
|
-
from letta.functions.functions import generate_schema, parse_source_code
|
|
31
|
-
from letta.functions.schema_generator import generate_schema
|
|
32
|
-
|
|
33
21
|
# TODO use custom interface
|
|
34
22
|
from letta.interface import AgentInterface # abstract
|
|
35
23
|
from letta.interface import CLIInterface # for printing to terminal
|
|
36
24
|
from letta.log import get_logger
|
|
37
|
-
from letta.memory import get_memory_functions
|
|
38
25
|
from letta.metadata import MetadataStore
|
|
39
26
|
from letta.o1_agent import O1Agent
|
|
40
27
|
from letta.orm import Base
|
|
@@ -53,8 +40,15 @@ from letta.providers import (
|
|
|
53
40
|
VLLMChatCompletionsProvider,
|
|
54
41
|
VLLMCompletionsProvider,
|
|
55
42
|
)
|
|
56
|
-
from letta.schemas.agent import
|
|
43
|
+
from letta.schemas.agent import (
|
|
44
|
+
AgentState,
|
|
45
|
+
AgentType,
|
|
46
|
+
CreateAgent,
|
|
47
|
+
PersistedAgentState,
|
|
48
|
+
UpdateAgentState,
|
|
49
|
+
)
|
|
57
50
|
from letta.schemas.api_key import APIKey, APIKeyCreate
|
|
51
|
+
from letta.schemas.block import Block, BlockUpdate
|
|
58
52
|
from letta.schemas.embedding_config import EmbeddingConfig
|
|
59
53
|
|
|
60
54
|
# openai schemas
|
|
@@ -79,15 +73,13 @@ from letta.services.agents_tags_manager import AgentsTagsManager
|
|
|
79
73
|
from letta.services.block_manager import BlockManager
|
|
80
74
|
from letta.services.blocks_agents_manager import BlocksAgentsManager
|
|
81
75
|
from letta.services.organization_manager import OrganizationManager
|
|
76
|
+
from letta.services.per_agent_lock_manager import PerAgentLockManager
|
|
82
77
|
from letta.services.sandbox_config_manager import SandboxConfigManager
|
|
83
78
|
from letta.services.source_manager import SourceManager
|
|
84
79
|
from letta.services.tool_manager import ToolManager
|
|
85
80
|
from letta.services.user_manager import UserManager
|
|
86
81
|
from letta.utils import create_random_username, json_dumps, json_loads
|
|
87
82
|
|
|
88
|
-
# from letta.llm_api_tools import openai_get_model_list, azure_openai_get_model_list, smart_urljoin
|
|
89
|
-
|
|
90
|
-
|
|
91
83
|
logger = get_logger(__name__)
|
|
92
84
|
|
|
93
85
|
|
|
@@ -127,10 +119,11 @@ class Server(object):
|
|
|
127
119
|
@abstractmethod
|
|
128
120
|
def create_agent(
|
|
129
121
|
self,
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
interface
|
|
133
|
-
|
|
122
|
+
request: CreateAgent,
|
|
123
|
+
actor: User,
|
|
124
|
+
# interface
|
|
125
|
+
interface: Union[AgentInterface, None] = None,
|
|
126
|
+
) -> AgentState:
|
|
134
127
|
"""Create a new agent using a config"""
|
|
135
128
|
raise NotImplementedError
|
|
136
129
|
|
|
@@ -231,6 +224,9 @@ class SyncServer(Server):
|
|
|
231
224
|
|
|
232
225
|
self.credentials = LettaCredentials.load()
|
|
233
226
|
|
|
227
|
+
# Locks
|
|
228
|
+
self.send_message_lock = Lock()
|
|
229
|
+
|
|
234
230
|
# Initialize the metadata store
|
|
235
231
|
config = LettaConfig.load()
|
|
236
232
|
if settings.letta_pg_uri_no_default:
|
|
@@ -249,8 +245,11 @@ class SyncServer(Server):
|
|
|
249
245
|
self.block_manager = BlockManager()
|
|
250
246
|
self.source_manager = SourceManager()
|
|
251
247
|
self.agents_tags_manager = AgentsTagsManager()
|
|
252
|
-
self.blocks_agents_manager = BlocksAgentsManager()
|
|
253
248
|
self.sandbox_config_manager = SandboxConfigManager(tool_settings)
|
|
249
|
+
self.blocks_agents_manager = BlocksAgentsManager()
|
|
250
|
+
|
|
251
|
+
# Managers that interface with parallelism
|
|
252
|
+
self.per_agent_lock_manager = PerAgentLockManager()
|
|
254
253
|
|
|
255
254
|
# Make default user and org
|
|
256
255
|
if init_with_default_org_and_user:
|
|
@@ -364,92 +363,23 @@ class SyncServer(Server):
|
|
|
364
363
|
}
|
|
365
364
|
)
|
|
366
365
|
|
|
367
|
-
def
|
|
368
|
-
"""
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
# If an interface isn't specified, use the default
|
|
373
|
-
if interface is None:
|
|
374
|
-
interface = self.default_interface_factory()
|
|
375
|
-
|
|
376
|
-
try:
|
|
377
|
-
logger.debug(f"Grabbing agent user_id={user_id} agent_id={agent_id} from database")
|
|
378
|
-
agent_state = self.ms.get_agent(agent_id=agent_id, user_id=user_id)
|
|
379
|
-
if not agent_state:
|
|
380
|
-
logger.exception(f"agent_id {agent_id} does not exist")
|
|
381
|
-
raise ValueError(f"agent_id {agent_id} does not exist")
|
|
382
|
-
|
|
383
|
-
# Instantiate an agent object using the state retrieved
|
|
384
|
-
logger.debug(f"Creating an agent object")
|
|
385
|
-
tool_objs = []
|
|
386
|
-
for name in agent_state.tools:
|
|
387
|
-
# TODO: This should be a hard failure, but for migration reasons, we patch it for now
|
|
388
|
-
tool_obj = self.tool_manager.get_tool_by_name(tool_name=name, actor=actor)
|
|
389
|
-
if tool_obj:
|
|
390
|
-
tool_obj = self.tool_manager.get_tool_by_name(tool_name=name, actor=actor)
|
|
391
|
-
tool_objs.append(tool_obj)
|
|
392
|
-
else:
|
|
393
|
-
warnings.warn(f"Tried to retrieve a tool with name {name} from the agent_state, but does not exist in tool db.")
|
|
394
|
-
|
|
395
|
-
# set agent_state tools to only the names of the available tools
|
|
396
|
-
agent_state.tools = [t.name for t in tool_objs]
|
|
366
|
+
def load_agent(self, agent_id: str, interface: Union[AgentInterface, None] = None) -> Agent:
|
|
367
|
+
"""Updated method to load agents from persisted storage"""
|
|
368
|
+
agent_state = self.get_agent(agent_id=agent_id)
|
|
369
|
+
actor = self.user_manager.get_user_by_id(user_id=agent_state.user_id)
|
|
397
370
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
if agent_state.agent_type == AgentType.memgpt_agent:
|
|
402
|
-
letta_agent = Agent(agent_state=agent_state, interface=interface, tools=tool_objs, user=actor)
|
|
403
|
-
elif agent_state.agent_type == AgentType.o1_agent:
|
|
404
|
-
letta_agent = O1Agent(agent_state=agent_state, interface=interface, tools=tool_objs, user=actor)
|
|
405
|
-
else:
|
|
406
|
-
raise NotImplementedError("Not a supported agent type")
|
|
407
|
-
|
|
408
|
-
# Add the agent to the in-memory store and return its reference
|
|
409
|
-
logger.debug(f"Adding agent to the agent cache: user_id={user_id}, agent_id={agent_id}")
|
|
410
|
-
self._add_agent(user_id=user_id, agent_id=agent_id, agent_obj=letta_agent)
|
|
411
|
-
return letta_agent
|
|
412
|
-
|
|
413
|
-
except Exception as e:
|
|
414
|
-
logger.exception(f"Error occurred while trying to get agent {agent_id}:\n{e}")
|
|
415
|
-
raise
|
|
416
|
-
|
|
417
|
-
def _get_or_load_agent(self, agent_id: str, caching: bool = True) -> Agent:
|
|
418
|
-
"""Check if the agent is in-memory, then load"""
|
|
419
|
-
|
|
420
|
-
# Gets the agent state
|
|
421
|
-
agent_state = self.ms.get_agent(agent_id=agent_id)
|
|
422
|
-
if not agent_state:
|
|
423
|
-
raise ValueError(f"Agent does not exist")
|
|
424
|
-
user_id = agent_state.user_id
|
|
425
|
-
actor = self.user_manager.get_user_by_id(user_id)
|
|
426
|
-
|
|
427
|
-
logger.debug(f"Checking for agent user_id={user_id} agent_id={agent_id}")
|
|
428
|
-
if caching:
|
|
429
|
-
# TODO: consider disabling loading cached agents due to potential concurrency issues
|
|
430
|
-
letta_agent = self._get_agent(user_id=user_id, agent_id=agent_id)
|
|
431
|
-
if not letta_agent:
|
|
432
|
-
logger.debug(f"Agent not loaded, loading agent user_id={user_id} agent_id={agent_id}")
|
|
433
|
-
letta_agent = self._load_agent(agent_id=agent_id, actor=actor)
|
|
371
|
+
interface = interface or self.default_interface_factory()
|
|
372
|
+
if agent_state.agent_type == AgentType.memgpt_agent:
|
|
373
|
+
return Agent(agent_state=agent_state, interface=interface, user=actor)
|
|
434
374
|
else:
|
|
435
|
-
|
|
436
|
-
letta_agent = self._load_agent(agent_id=agent_id, actor=actor)
|
|
437
|
-
|
|
438
|
-
# letta_agent = self._get_agent(user_id=user_id, agent_id=agent_id)
|
|
439
|
-
# if not letta_agent:
|
|
440
|
-
# logger.debug(f"Agent not loaded, loading agent user_id={user_id} agent_id={agent_id}")
|
|
441
|
-
|
|
442
|
-
# NOTE: no longer caching, always forcing a lot from the database
|
|
443
|
-
# Loads the agent objects
|
|
444
|
-
# letta_agent = self._load_agent(agent_id=agent_id, actor=actor)
|
|
445
|
-
|
|
446
|
-
return letta_agent
|
|
375
|
+
return O1Agent(agent_state=agent_state, interface=interface, user=actor)
|
|
447
376
|
|
|
448
377
|
def _step(
|
|
449
378
|
self,
|
|
450
379
|
user_id: str,
|
|
451
380
|
agent_id: str,
|
|
452
381
|
input_messages: Union[Message, List[Message]],
|
|
382
|
+
interface: Union[AgentInterface, None] = None, # needed to getting responses
|
|
453
383
|
# timestamp: Optional[datetime],
|
|
454
384
|
) -> LettaUsageStatistics:
|
|
455
385
|
"""Send the input message through the agent"""
|
|
@@ -465,7 +395,8 @@ class SyncServer(Server):
|
|
|
465
395
|
try:
|
|
466
396
|
|
|
467
397
|
# Get the agent object (loaded in memory)
|
|
468
|
-
letta_agent = self._get_or_load_agent(agent_id=agent_id)
|
|
398
|
+
# letta_agent = self._get_or_load_agent(agent_id=agent_id)
|
|
399
|
+
letta_agent = self.load_agent(agent_id=agent_id, interface=interface)
|
|
469
400
|
if letta_agent is None:
|
|
470
401
|
raise KeyError(f"Agent (user={user_id}, agent={agent_id}) is not loaded")
|
|
471
402
|
|
|
@@ -482,6 +413,9 @@ class SyncServer(Server):
|
|
|
482
413
|
skip_verify=True,
|
|
483
414
|
)
|
|
484
415
|
|
|
416
|
+
# save agent after step
|
|
417
|
+
save_agent(letta_agent, self.ms)
|
|
418
|
+
|
|
485
419
|
except Exception as e:
|
|
486
420
|
logger.error(f"Error in server._step: {e}")
|
|
487
421
|
print(traceback.print_exc())
|
|
@@ -499,7 +433,7 @@ class SyncServer(Server):
|
|
|
499
433
|
logger.debug(f"Got command: {command}")
|
|
500
434
|
|
|
501
435
|
# Get the agent object (loaded in memory)
|
|
502
|
-
letta_agent = self.
|
|
436
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
503
437
|
usage = None
|
|
504
438
|
|
|
505
439
|
if command.lower() == "exit":
|
|
@@ -536,7 +470,7 @@ class SyncServer(Server):
|
|
|
536
470
|
elif command.lower() == "memory":
|
|
537
471
|
ret_str = (
|
|
538
472
|
f"\nDumping memory contents:\n"
|
|
539
|
-
+ f"\n{str(letta_agent.memory)}"
|
|
473
|
+
+ f"\n{str(letta_agent.agent_state.memory)}"
|
|
540
474
|
+ f"\n{str(letta_agent.persistence_manager.archival_memory)}"
|
|
541
475
|
+ f"\n{str(letta_agent.persistence_manager.recall_memory)}"
|
|
542
476
|
)
|
|
@@ -728,6 +662,7 @@ class SyncServer(Server):
|
|
|
728
662
|
# whether or not to wrap user and system message as MemGPT-style stringified JSON
|
|
729
663
|
wrap_user_message: bool = True,
|
|
730
664
|
wrap_system_message: bool = True,
|
|
665
|
+
interface: Union[AgentInterface, None] = None, # needed to getting responses
|
|
731
666
|
) -> LettaUsageStatistics:
|
|
732
667
|
"""Send a list of messages to the agent
|
|
733
668
|
|
|
@@ -780,7 +715,7 @@ class SyncServer(Server):
|
|
|
780
715
|
raise ValueError(f"All messages must be of type Message or MessageCreate, got {[type(message) for message in messages]}")
|
|
781
716
|
|
|
782
717
|
# Run the agent state forward
|
|
783
|
-
return self._step(user_id=user_id, agent_id=agent_id, input_messages=message_objects)
|
|
718
|
+
return self._step(user_id=user_id, agent_id=agent_id, input_messages=message_objects, interface=interface)
|
|
784
719
|
|
|
785
720
|
# @LockingServer.agent_lock_decorator
|
|
786
721
|
def run_command(self, user_id: str, agent_id: str, command: str) -> LettaUsageStatistics:
|
|
@@ -828,129 +763,109 @@ class SyncServer(Server):
|
|
|
828
763
|
else:
|
|
829
764
|
raise ValueError(f"Invalid agent type: {request.agent_type}")
|
|
830
765
|
|
|
766
|
+
# create blocks (note: cannot be linked into the agent_id is created)
|
|
767
|
+
blocks = []
|
|
768
|
+
for create_block in request.memory_blocks:
|
|
769
|
+
block = self.block_manager.create_or_update_block(Block(**create_block.model_dump()), actor=actor)
|
|
770
|
+
blocks.append(block)
|
|
771
|
+
|
|
772
|
+
# get tools + only add if they exist
|
|
773
|
+
tool_objs = []
|
|
774
|
+
if request.tools:
|
|
775
|
+
for tool_name in request.tools:
|
|
776
|
+
tool_obj = self.tool_manager.get_tool_by_name(tool_name=tool_name, actor=actor)
|
|
777
|
+
if tool_obj:
|
|
778
|
+
tool_objs.append(tool_obj)
|
|
779
|
+
else:
|
|
780
|
+
warnings.warn(f"Attempted to add a nonexistent tool {tool_name} to agent {request.name}, skipping.")
|
|
781
|
+
# reset the request.tools to only valid tools
|
|
782
|
+
request.tools = [t.name for t in tool_objs]
|
|
783
|
+
|
|
784
|
+
# get the user
|
|
831
785
|
logger.debug(f"Attempting to find user: {user_id}")
|
|
832
786
|
user = self.user_manager.get_user_by_id(user_id=user_id)
|
|
833
787
|
if not user:
|
|
834
788
|
raise ValueError(f"cannot find user with associated client id: {user_id}")
|
|
835
789
|
|
|
836
|
-
|
|
837
|
-
# model configuration
|
|
838
|
-
llm_config = request.llm_config
|
|
839
|
-
embedding_config = request.embedding_config
|
|
840
|
-
|
|
841
|
-
# get tools + only add if they exist
|
|
842
|
-
tool_objs = []
|
|
843
|
-
if request.tools:
|
|
844
|
-
for tool_name in request.tools:
|
|
845
|
-
tool_obj = self.tool_manager.get_tool_by_name(tool_name=tool_name, actor=actor)
|
|
846
|
-
if tool_obj:
|
|
847
|
-
tool_objs.append(tool_obj)
|
|
848
|
-
else:
|
|
849
|
-
warnings.warn(f"Attempted to add a nonexistent tool {tool_name} to agent {request.name}, skipping.")
|
|
850
|
-
# reset the request.tools to only valid tools
|
|
851
|
-
request.tools = [t.name for t in tool_objs]
|
|
852
|
-
|
|
853
|
-
assert request.memory is not None
|
|
854
|
-
memory_functions = get_memory_functions(request.memory)
|
|
855
|
-
for func_name, func in memory_functions.items():
|
|
856
|
-
|
|
857
|
-
if request.tools and func_name in request.tools:
|
|
858
|
-
# tool already added
|
|
859
|
-
continue
|
|
860
|
-
source_code = parse_source_code(func)
|
|
861
|
-
# memory functions are not terminal
|
|
862
|
-
json_schema = generate_schema(func, name=func_name)
|
|
863
|
-
source_type = "python"
|
|
864
|
-
tags = ["memory", "memgpt-base"]
|
|
865
|
-
tool = self.tool_manager.create_or_update_tool(
|
|
866
|
-
Tool(
|
|
867
|
-
source_code=source_code,
|
|
868
|
-
source_type=source_type,
|
|
869
|
-
tags=tags,
|
|
870
|
-
json_schema=json_schema,
|
|
871
|
-
),
|
|
872
|
-
actor=actor,
|
|
873
|
-
)
|
|
874
|
-
tool_objs.append(tool)
|
|
875
|
-
if not request.tools:
|
|
876
|
-
request.tools = []
|
|
877
|
-
request.tools.append(tool.name)
|
|
878
|
-
|
|
879
|
-
# TODO: save the agent state
|
|
880
|
-
agent_state = AgentState(
|
|
881
|
-
name=request.name,
|
|
882
|
-
user_id=user_id,
|
|
883
|
-
tools=request.tools if request.tools else [],
|
|
884
|
-
tool_rules=request.tool_rules if request.tool_rules else [],
|
|
885
|
-
agent_type=request.agent_type or AgentType.memgpt_agent,
|
|
886
|
-
llm_config=llm_config,
|
|
887
|
-
embedding_config=embedding_config,
|
|
888
|
-
system=request.system,
|
|
889
|
-
memory=request.memory,
|
|
890
|
-
description=request.description,
|
|
891
|
-
metadata_=request.metadata_,
|
|
892
|
-
tags=request.tags,
|
|
893
|
-
)
|
|
894
|
-
if request.agent_type == AgentType.memgpt_agent:
|
|
895
|
-
agent = Agent(
|
|
896
|
-
interface=interface,
|
|
897
|
-
agent_state=agent_state,
|
|
898
|
-
tools=tool_objs,
|
|
899
|
-
# gpt-3.5-turbo tends to omit inner monologue, relax this requirement for now
|
|
900
|
-
first_message_verify_mono=(
|
|
901
|
-
True if (llm_config and llm_config.model is not None and "gpt-4" in llm_config.model) else False
|
|
902
|
-
),
|
|
903
|
-
user=actor,
|
|
904
|
-
initial_message_sequence=request.initial_message_sequence,
|
|
905
|
-
)
|
|
906
|
-
elif request.agent_type == AgentType.o1_agent:
|
|
907
|
-
agent = O1Agent(
|
|
908
|
-
interface=interface,
|
|
909
|
-
agent_state=agent_state,
|
|
910
|
-
tools=tool_objs,
|
|
911
|
-
# gpt-3.5-turbo tends to omit inner monologue, relax this requirement for now
|
|
912
|
-
first_message_verify_mono=(
|
|
913
|
-
True if (llm_config and llm_config.model is not None and "gpt-4" in llm_config.model) else False
|
|
914
|
-
),
|
|
915
|
-
user=actor,
|
|
916
|
-
)
|
|
917
|
-
# rebuilding agent memory on agent create in case shared memory blocks
|
|
918
|
-
# were specified in the new agent's memory config. we're doing this for two reasons:
|
|
919
|
-
# 1. if only the ID of the shared memory block was specified, we can fetch its most recent value
|
|
920
|
-
# 2. if the shared block state changed since this agent initialization started, we can be sure to have the latest value
|
|
921
|
-
agent.rebuild_memory(force=True, ms=self.ms)
|
|
922
|
-
# FIXME: this is a hacky way to get the system prompts injected into agent into the DB
|
|
923
|
-
# self.ms.update_agent(agent.agent_state)
|
|
924
|
-
except Exception as e:
|
|
925
|
-
logger.exception(e)
|
|
926
|
-
try:
|
|
927
|
-
if agent:
|
|
928
|
-
self.ms.delete_agent(agent_id=agent.agent_state.id)
|
|
929
|
-
except Exception as delete_e:
|
|
930
|
-
logger.exception(f"Failed to delete_agent:\n{delete_e}")
|
|
931
|
-
raise e
|
|
790
|
+
# TODO: create the message objects (NOTE: do this after we migrate to `CreateMessage`)
|
|
932
791
|
|
|
933
|
-
#
|
|
934
|
-
|
|
935
|
-
|
|
792
|
+
# created and persist the agent state in the DB
|
|
793
|
+
agent_state = PersistedAgentState(
|
|
794
|
+
name=request.name,
|
|
795
|
+
user_id=user_id,
|
|
796
|
+
tool_names=request.tools if request.tools else [],
|
|
797
|
+
tool_rules=request.tool_rules,
|
|
798
|
+
agent_type=request.agent_type or AgentType.memgpt_agent,
|
|
799
|
+
llm_config=request.llm_config,
|
|
800
|
+
embedding_config=request.embedding_config,
|
|
801
|
+
system=request.system,
|
|
802
|
+
# other metadata
|
|
803
|
+
description=request.description,
|
|
804
|
+
metadata_=request.metadata_,
|
|
805
|
+
)
|
|
806
|
+
# TODO: move this to agent ORM
|
|
807
|
+
# this saves the agent ID and state into the DB
|
|
808
|
+
self.ms.create_agent(agent_state)
|
|
936
809
|
|
|
937
|
-
#
|
|
810
|
+
# Note: mappings (e.g. tags, blocks) are created after the agent is persisted
|
|
811
|
+
# TODO: add source mappings here as well
|
|
812
|
+
|
|
813
|
+
# create the tags
|
|
938
814
|
if request.tags:
|
|
939
815
|
for tag in request.tags:
|
|
940
|
-
self.agents_tags_manager.add_tag_to_agent(agent_id=
|
|
816
|
+
self.agents_tags_manager.add_tag_to_agent(agent_id=agent_state.id, tag=tag, actor=actor)
|
|
817
|
+
|
|
818
|
+
# create block mappins (now that agent is persisted)
|
|
819
|
+
for block in blocks:
|
|
820
|
+
# this links the created block to the agent
|
|
821
|
+
self.blocks_agents_manager.add_block_to_agent(block_id=block.id, agent_id=agent_state.id, block_label=block.label)
|
|
822
|
+
|
|
823
|
+
in_memory_agent_state = self.get_agent(agent_state.id)
|
|
824
|
+
return in_memory_agent_state
|
|
825
|
+
|
|
826
|
+
def get_agent(self, agent_id: str) -> AgentState:
|
|
827
|
+
"""
|
|
828
|
+
Retrieve the full agent state from the DB.
|
|
829
|
+
This gathers data accross multiple tables to provide the full state of an agent, which is passed into the `Agent` object for creation.
|
|
830
|
+
"""
|
|
831
|
+
|
|
832
|
+
# get data persisted from the DB
|
|
833
|
+
agent_state = self.ms.get_agent(agent_id=agent_id)
|
|
834
|
+
if agent_state is None:
|
|
835
|
+
# agent does not exist
|
|
836
|
+
return None
|
|
837
|
+
user = self.user_manager.get_user_by_id(user_id=agent_state.user_id)
|
|
941
838
|
|
|
942
|
-
|
|
839
|
+
# construct the in-memory, full agent state - this gather data stored in different tables but that needs to be passed to `Agent`
|
|
840
|
+
# we also return this data to the user to provide all the state related to an agent
|
|
943
841
|
|
|
944
|
-
#
|
|
945
|
-
|
|
842
|
+
# get `Memory` object by getting the linked block IDs and fetching the blocks, then putting that into a `Memory` object
|
|
843
|
+
# this is the "in memory" representation of the in-context memory
|
|
844
|
+
block_ids = self.blocks_agents_manager.list_block_ids_for_agent(agent_id=agent_id)
|
|
845
|
+
blocks = []
|
|
846
|
+
for block_id in block_ids:
|
|
847
|
+
block = self.block_manager.get_block_by_id(block_id=block_id, actor=user)
|
|
848
|
+
assert block, f"Block with ID {block_id} does not exist"
|
|
849
|
+
blocks.append(block)
|
|
850
|
+
memory = Memory(blocks=blocks)
|
|
946
851
|
|
|
947
|
-
|
|
852
|
+
# get `Tool` objects
|
|
853
|
+
tools = [self.tool_manager.get_tool_by_name(tool_name=tool_name, actor=user) for tool_name in agent_state.tool_names]
|
|
854
|
+
|
|
855
|
+
# get `Source` objects
|
|
856
|
+
sources = self.list_attached_sources(agent_id=agent_id)
|
|
857
|
+
|
|
858
|
+
# get the tags
|
|
859
|
+
tags = self.agents_tags_manager.get_tags_for_agent(agent_id=agent_id, actor=user)
|
|
860
|
+
|
|
861
|
+
# return the full agent state - this contains all data needed to recreate the agent
|
|
862
|
+
return AgentState(**agent_state.model_dump(), memory=memory, tools=tools, sources=sources, tags=tags)
|
|
948
863
|
|
|
949
864
|
def update_agent(
|
|
950
865
|
self,
|
|
951
866
|
request: UpdateAgentState,
|
|
952
867
|
actor: User,
|
|
953
|
-
):
|
|
868
|
+
) -> AgentState:
|
|
954
869
|
"""Update the agents core memory block, return the new state"""
|
|
955
870
|
try:
|
|
956
871
|
self.user_manager.get_user_by_id(user_id=actor.id)
|
|
@@ -961,13 +876,7 @@ class SyncServer(Server):
|
|
|
961
876
|
raise ValueError(f"Agent agent_id={request.id} does not exist")
|
|
962
877
|
|
|
963
878
|
# Get the agent object (loaded in memory)
|
|
964
|
-
letta_agent = self.
|
|
965
|
-
|
|
966
|
-
# update the core memory of the agent
|
|
967
|
-
if request.memory:
|
|
968
|
-
assert isinstance(request.memory, Memory), type(request.memory)
|
|
969
|
-
new_memory_contents = request.memory.to_flat_dict()
|
|
970
|
-
_ = self.update_agent_core_memory(user_id=actor.id, agent_id=request.id, new_memory_contents=new_memory_contents)
|
|
879
|
+
letta_agent = self.load_agent(agent_id=request.id)
|
|
971
880
|
|
|
972
881
|
# update the system prompt
|
|
973
882
|
if request.system:
|
|
@@ -981,13 +890,13 @@ class SyncServer(Server):
|
|
|
981
890
|
letta_agent.set_message_buffer(message_ids=request.message_ids)
|
|
982
891
|
|
|
983
892
|
# tools
|
|
984
|
-
if request.
|
|
893
|
+
if request.tool_names:
|
|
985
894
|
# Replace tools and also re-link
|
|
986
895
|
|
|
987
896
|
# (1) get tools + make sure they exist
|
|
988
897
|
# Current and target tools as sets of tool names
|
|
989
|
-
current_tools = set(letta_agent.agent_state.
|
|
990
|
-
target_tools = set(request.
|
|
898
|
+
current_tools = set(letta_agent.agent_state.tool_names)
|
|
899
|
+
target_tools = set(request.tool_names)
|
|
991
900
|
|
|
992
901
|
# Calculate tools to add and remove
|
|
993
902
|
tools_to_add = target_tools - current_tools
|
|
@@ -1004,7 +913,7 @@ class SyncServer(Server):
|
|
|
1004
913
|
self.add_tool_to_agent(agent_id=request.id, tool_id=tool.id, user_id=actor.id)
|
|
1005
914
|
|
|
1006
915
|
# reload agent
|
|
1007
|
-
letta_agent = self.
|
|
916
|
+
letta_agent = self.load_agent(agent_id=request.id)
|
|
1008
917
|
|
|
1009
918
|
# configs
|
|
1010
919
|
if request.llm_config:
|
|
@@ -1032,7 +941,6 @@ class SyncServer(Server):
|
|
|
1032
941
|
self.agents_tags_manager.delete_tag_from_agent(agent_id=letta_agent.agent_state.id, tag=tag, actor=actor)
|
|
1033
942
|
|
|
1034
943
|
# save the agent
|
|
1035
|
-
assert isinstance(letta_agent.memory, Memory)
|
|
1036
944
|
save_agent(letta_agent, self.ms)
|
|
1037
945
|
# TODO: probably reload the agent somehow?
|
|
1038
946
|
return letta_agent.agent_state
|
|
@@ -1045,8 +953,8 @@ class SyncServer(Server):
|
|
|
1045
953
|
raise ValueError(f"Agent agent_id={agent_id} does not exist")
|
|
1046
954
|
|
|
1047
955
|
# Get the agent object (loaded in memory)
|
|
1048
|
-
letta_agent = self.
|
|
1049
|
-
return letta_agent.tools
|
|
956
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
957
|
+
return letta_agent.agent_state.tools
|
|
1050
958
|
|
|
1051
959
|
def add_tool_to_agent(
|
|
1052
960
|
self,
|
|
@@ -1064,7 +972,7 @@ class SyncServer(Server):
|
|
|
1064
972
|
raise ValueError(f"Agent agent_id={agent_id} does not exist")
|
|
1065
973
|
|
|
1066
974
|
# Get the agent object (loaded in memory)
|
|
1067
|
-
letta_agent = self.
|
|
975
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1068
976
|
|
|
1069
977
|
# Get all the tool objects from the request
|
|
1070
978
|
tool_objs = []
|
|
@@ -1072,7 +980,7 @@ class SyncServer(Server):
|
|
|
1072
980
|
assert tool_obj, f"Tool with id={tool_id} does not exist"
|
|
1073
981
|
tool_objs.append(tool_obj)
|
|
1074
982
|
|
|
1075
|
-
for tool in letta_agent.tools:
|
|
983
|
+
for tool in letta_agent.agent_state.tools:
|
|
1076
984
|
tool_obj = self.tool_manager.get_tool_by_id(tool_id=tool.id, actor=user)
|
|
1077
985
|
assert tool_obj, f"Tool with id={tool.id} does not exist"
|
|
1078
986
|
|
|
@@ -1081,7 +989,7 @@ class SyncServer(Server):
|
|
|
1081
989
|
tool_objs.append(tool_obj)
|
|
1082
990
|
|
|
1083
991
|
# replace the list of tool names ("ids") inside the agent state
|
|
1084
|
-
letta_agent.agent_state.
|
|
992
|
+
letta_agent.agent_state.tool_names = [tool.name for tool in tool_objs]
|
|
1085
993
|
|
|
1086
994
|
# then attempt to link the tools modules
|
|
1087
995
|
letta_agent.link_tools(tool_objs)
|
|
@@ -1106,11 +1014,11 @@ class SyncServer(Server):
|
|
|
1106
1014
|
raise ValueError(f"Agent agent_id={agent_id} does not exist")
|
|
1107
1015
|
|
|
1108
1016
|
# Get the agent object (loaded in memory)
|
|
1109
|
-
letta_agent = self.
|
|
1017
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1110
1018
|
|
|
1111
1019
|
# Get all the tool_objs
|
|
1112
1020
|
tool_objs = []
|
|
1113
|
-
for tool in letta_agent.tools:
|
|
1021
|
+
for tool in letta_agent.agent_state.tools:
|
|
1114
1022
|
tool_obj = self.tool_manager.get_tool_by_id(tool_id=tool.id, actor=user)
|
|
1115
1023
|
assert tool_obj, f"Tool with id={tool.id} does not exist"
|
|
1116
1024
|
|
|
@@ -1119,7 +1027,7 @@ class SyncServer(Server):
|
|
|
1119
1027
|
tool_objs.append(tool_obj)
|
|
1120
1028
|
|
|
1121
1029
|
# replace the list of tool names ("ids") inside the agent state
|
|
1122
|
-
letta_agent.agent_state.
|
|
1030
|
+
letta_agent.agent_state.tool_names = [tool.name for tool in tool_objs]
|
|
1123
1031
|
|
|
1124
1032
|
# then attempt to link the tools modules
|
|
1125
1033
|
letta_agent.link_tools(tool_objs)
|
|
@@ -1128,18 +1036,9 @@ class SyncServer(Server):
|
|
|
1128
1036
|
save_agent(letta_agent, self.ms)
|
|
1129
1037
|
return letta_agent.agent_state
|
|
1130
1038
|
|
|
1131
|
-
def
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
agent_config = {
|
|
1136
|
-
"id": agent_state.id,
|
|
1137
|
-
"name": agent_state.name,
|
|
1138
|
-
"human": agent_state._metadata.get("human", None),
|
|
1139
|
-
"persona": agent_state._metadata.get("persona", None),
|
|
1140
|
-
"created_at": agent_state.created_at.isoformat(),
|
|
1141
|
-
}
|
|
1142
|
-
return agent_config
|
|
1039
|
+
def get_agent_state(self, user_id: str, agent_id: str) -> AgentState:
|
|
1040
|
+
# TODO: duplicate, remove
|
|
1041
|
+
return self.get_agent(agent_id=agent_id)
|
|
1143
1042
|
|
|
1144
1043
|
def list_agents(self, user_id: str, tags: Optional[List[str]] = None) -> List[AgentState]:
|
|
1145
1044
|
"""List all available agents to a user"""
|
|
@@ -1147,13 +1046,13 @@ class SyncServer(Server):
|
|
|
1147
1046
|
|
|
1148
1047
|
if tags is None:
|
|
1149
1048
|
agents_states = self.ms.list_agents(user_id=user_id)
|
|
1150
|
-
|
|
1049
|
+
agent_ids = [agent.id for agent in agents_states]
|
|
1151
1050
|
else:
|
|
1152
1051
|
agent_ids = []
|
|
1153
1052
|
for tag in tags:
|
|
1154
1053
|
agent_ids += self.agents_tags_manager.get_agents_by_tag(tag=tag, actor=user)
|
|
1155
1054
|
|
|
1156
|
-
|
|
1055
|
+
return [self.get_agent(agent_id=agent_id) for agent_id in agent_ids]
|
|
1157
1056
|
|
|
1158
1057
|
# convert name->id
|
|
1159
1058
|
|
|
@@ -1177,34 +1076,34 @@ class SyncServer(Server):
|
|
|
1177
1076
|
|
|
1178
1077
|
def get_agent_memory(self, agent_id: str) -> Memory:
|
|
1179
1078
|
"""Return the memory of an agent (core memory)"""
|
|
1180
|
-
agent = self.
|
|
1181
|
-
return agent.memory
|
|
1079
|
+
agent = self.load_agent(agent_id=agent_id)
|
|
1080
|
+
return agent.agent_state.memory
|
|
1182
1081
|
|
|
1183
1082
|
def get_archival_memory_summary(self, agent_id: str) -> ArchivalMemorySummary:
|
|
1184
|
-
agent = self.
|
|
1083
|
+
agent = self.load_agent(agent_id=agent_id)
|
|
1185
1084
|
return ArchivalMemorySummary(size=len(agent.persistence_manager.archival_memory))
|
|
1186
1085
|
|
|
1187
1086
|
def get_recall_memory_summary(self, agent_id: str) -> RecallMemorySummary:
|
|
1188
|
-
agent = self.
|
|
1087
|
+
agent = self.load_agent(agent_id=agent_id)
|
|
1189
1088
|
return RecallMemorySummary(size=len(agent.persistence_manager.recall_memory))
|
|
1190
1089
|
|
|
1191
1090
|
def get_in_context_message_ids(self, agent_id: str) -> List[str]:
|
|
1192
1091
|
"""Get the message ids of the in-context messages in the agent's memory"""
|
|
1193
1092
|
# Get the agent object (loaded in memory)
|
|
1194
|
-
|
|
1195
|
-
return [m.id for m in
|
|
1093
|
+
agent = self.load_agent(agent_id=agent_id)
|
|
1094
|
+
return [m.id for m in agent._messages]
|
|
1196
1095
|
|
|
1197
1096
|
def get_in_context_messages(self, agent_id: str) -> List[Message]:
|
|
1198
1097
|
"""Get the in-context messages in the agent's memory"""
|
|
1199
1098
|
# Get the agent object (loaded in memory)
|
|
1200
|
-
|
|
1201
|
-
return
|
|
1099
|
+
agent = self.load_agent(agent_id=agent_id)
|
|
1100
|
+
return agent._messages
|
|
1202
1101
|
|
|
1203
1102
|
def get_agent_message(self, agent_id: str, message_id: str) -> Message:
|
|
1204
1103
|
"""Get a single message from the agent's memory"""
|
|
1205
1104
|
# Get the agent object (loaded in memory)
|
|
1206
|
-
|
|
1207
|
-
message =
|
|
1105
|
+
agent = self.load_agent(agent_id=agent_id)
|
|
1106
|
+
message = agent.persistence_manager.recall_memory.storage.get(id=message_id)
|
|
1208
1107
|
return message
|
|
1209
1108
|
|
|
1210
1109
|
def get_agent_messages(
|
|
@@ -1212,11 +1111,10 @@ class SyncServer(Server):
|
|
|
1212
1111
|
agent_id: str,
|
|
1213
1112
|
start: int,
|
|
1214
1113
|
count: int,
|
|
1215
|
-
return_message_object: bool = True,
|
|
1216
1114
|
) -> Union[List[Message], List[LettaMessage]]:
|
|
1217
1115
|
"""Paginated query of all messages in agent message queue"""
|
|
1218
1116
|
# Get the agent object (loaded in memory)
|
|
1219
|
-
letta_agent = self.
|
|
1117
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1220
1118
|
|
|
1221
1119
|
if start < 0 or count < 0:
|
|
1222
1120
|
raise ValueError("Start and count values should be non-negative")
|
|
@@ -1234,10 +1132,6 @@ class SyncServer(Server):
|
|
|
1234
1132
|
# Slice the list for pagination
|
|
1235
1133
|
messages = reversed_messages[start:end_index]
|
|
1236
1134
|
|
|
1237
|
-
## Convert to json
|
|
1238
|
-
## Add a tag indicating in-context or not
|
|
1239
|
-
# json_messages = [{**record.to_json(), "in_context": True} for record in messages]
|
|
1240
|
-
|
|
1241
1135
|
else:
|
|
1242
1136
|
# need to access persistence manager for additional messages
|
|
1243
1137
|
db_iterator = letta_agent.persistence_manager.recall_memory.storage.get_all_paginated(page_size=count, offset=start)
|
|
@@ -1257,9 +1151,6 @@ class SyncServer(Server):
|
|
|
1257
1151
|
# for d in json_messages:
|
|
1258
1152
|
# d["in_context"] = True if str(d["id"]) in in_context_message_ids else False
|
|
1259
1153
|
|
|
1260
|
-
if not return_message_object:
|
|
1261
|
-
messages = [msg for m in messages for msg in m.to_letta_message()]
|
|
1262
|
-
|
|
1263
1154
|
return messages
|
|
1264
1155
|
|
|
1265
1156
|
def get_agent_archival(self, user_id: str, agent_id: str, start: int, count: int) -> List[Passage]:
|
|
@@ -1270,7 +1161,7 @@ class SyncServer(Server):
|
|
|
1270
1161
|
raise ValueError(f"Agent agent_id={agent_id} does not exist")
|
|
1271
1162
|
|
|
1272
1163
|
# Get the agent object (loaded in memory)
|
|
1273
|
-
letta_agent = self.
|
|
1164
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1274
1165
|
|
|
1275
1166
|
# iterate over records
|
|
1276
1167
|
db_iterator = letta_agent.persistence_manager.archival_memory.storage.get_all_paginated(page_size=count, offset=start)
|
|
@@ -1295,7 +1186,7 @@ class SyncServer(Server):
|
|
|
1295
1186
|
raise ValueError(f"Agent agent_id={agent_id} does not exist")
|
|
1296
1187
|
|
|
1297
1188
|
# Get the agent object (loaded in memory)
|
|
1298
|
-
letta_agent = self.
|
|
1189
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1299
1190
|
|
|
1300
1191
|
# iterate over recorde
|
|
1301
1192
|
cursor, records = letta_agent.persistence_manager.archival_memory.storage.get_all_cursor(
|
|
@@ -1310,11 +1201,15 @@ class SyncServer(Server):
|
|
|
1310
1201
|
raise ValueError(f"Agent agent_id={agent_id} does not exist")
|
|
1311
1202
|
|
|
1312
1203
|
# Get the agent object (loaded in memory)
|
|
1313
|
-
letta_agent = self.
|
|
1204
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1314
1205
|
|
|
1315
1206
|
# Insert into archival memory
|
|
1316
1207
|
passage_ids = letta_agent.persistence_manager.archival_memory.insert(memory_string=memory_contents, return_ids=True)
|
|
1317
1208
|
|
|
1209
|
+
# Update the agent
|
|
1210
|
+
# TODO: should this update the system prompt?
|
|
1211
|
+
save_agent(letta_agent, self.ms)
|
|
1212
|
+
|
|
1318
1213
|
# TODO: this is gross, fix
|
|
1319
1214
|
return [letta_agent.persistence_manager.archival_memory.storage.get(id=passage_id) for passage_id in passage_ids]
|
|
1320
1215
|
|
|
@@ -1327,7 +1222,7 @@ class SyncServer(Server):
|
|
|
1327
1222
|
# TODO: should return a passage
|
|
1328
1223
|
|
|
1329
1224
|
# Get the agent object (loaded in memory)
|
|
1330
|
-
letta_agent = self.
|
|
1225
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1331
1226
|
|
|
1332
1227
|
# Delete by ID
|
|
1333
1228
|
# TODO check if it exists first, and throw error if not
|
|
@@ -1346,9 +1241,8 @@ class SyncServer(Server):
|
|
|
1346
1241
|
order: Optional[str] = "asc",
|
|
1347
1242
|
reverse: Optional[bool] = False,
|
|
1348
1243
|
return_message_object: bool = True,
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
assistant_message_function_kwarg: str = constants.DEFAULT_MESSAGE_TOOL_KWARG,
|
|
1244
|
+
assistant_message_tool_name: str = constants.DEFAULT_MESSAGE_TOOL,
|
|
1245
|
+
assistant_message_tool_kwarg: str = constants.DEFAULT_MESSAGE_TOOL_KWARG,
|
|
1352
1246
|
) -> Union[List[Message], List[LettaMessage]]:
|
|
1353
1247
|
if self.user_manager.get_user_by_id(user_id=user_id) is None:
|
|
1354
1248
|
raise ValueError(f"User user_id={user_id} does not exist")
|
|
@@ -1356,7 +1250,7 @@ class SyncServer(Server):
|
|
|
1356
1250
|
raise ValueError(f"Agent agent_id={agent_id} does not exist")
|
|
1357
1251
|
|
|
1358
1252
|
# Get the agent object (loaded in memory)
|
|
1359
|
-
letta_agent = self.
|
|
1253
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1360
1254
|
|
|
1361
1255
|
# iterate over records
|
|
1362
1256
|
cursor, records = letta_agent.persistence_manager.recall_memory.storage.get_all_cursor(
|
|
@@ -1367,50 +1261,19 @@ class SyncServer(Server):
|
|
|
1367
1261
|
|
|
1368
1262
|
if not return_message_object:
|
|
1369
1263
|
# If we're GETing messages in reverse, we need to reverse the inner list (generated by to_letta_message)
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
)[::-1]
|
|
1379
|
-
]
|
|
1380
|
-
else:
|
|
1381
|
-
records = [
|
|
1382
|
-
msg
|
|
1383
|
-
for m in records
|
|
1384
|
-
for msg in m.to_letta_message(
|
|
1385
|
-
assistant_message=use_assistant_message,
|
|
1386
|
-
assistant_message_function_name=assistant_message_function_name,
|
|
1387
|
-
assistant_message_function_kwarg=assistant_message_function_kwarg,
|
|
1388
|
-
)
|
|
1389
|
-
]
|
|
1390
|
-
|
|
1391
|
-
return records
|
|
1392
|
-
|
|
1393
|
-
def get_agent_state(self, user_id: str, agent_id: Optional[str], agent_name: Optional[str] = None) -> Optional[AgentState]:
|
|
1394
|
-
"""Return the config of an agent"""
|
|
1395
|
-
user = self.user_manager.get_user_by_id(user_id=user_id)
|
|
1396
|
-
if agent_id:
|
|
1397
|
-
if self.ms.get_agent(agent_id=agent_id, user_id=user_id) is None:
|
|
1398
|
-
return None
|
|
1399
|
-
else:
|
|
1400
|
-
agent_state = self.ms.get_agent(agent_name=agent_name, user_id=user_id)
|
|
1401
|
-
if agent_state is None:
|
|
1402
|
-
raise ValueError(f"Agent agent_name={agent_name} does not exist")
|
|
1403
|
-
agent_id = agent_state.id
|
|
1264
|
+
records = [
|
|
1265
|
+
msg
|
|
1266
|
+
for m in records
|
|
1267
|
+
for msg in m.to_letta_message(
|
|
1268
|
+
assistant_message_tool_name=assistant_message_tool_name,
|
|
1269
|
+
assistant_message_tool_kwarg=assistant_message_tool_kwarg,
|
|
1270
|
+
)
|
|
1271
|
+
]
|
|
1404
1272
|
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
assert isinstance(letta_agent.memory, Memory)
|
|
1273
|
+
if reverse:
|
|
1274
|
+
records = records[::-1]
|
|
1408
1275
|
|
|
1409
|
-
|
|
1410
|
-
agent_state = letta_agent.agent_state.model_copy(deep=True)
|
|
1411
|
-
# Load the tags in for the agent_state
|
|
1412
|
-
agent_state.tags = self.agents_tags_manager.get_tags_for_agent(agent_id=agent_id, actor=user)
|
|
1413
|
-
return agent_state
|
|
1276
|
+
return records
|
|
1414
1277
|
|
|
1415
1278
|
def get_server_config(self, include_defaults: bool = False) -> dict:
|
|
1416
1279
|
"""Return the base config"""
|
|
@@ -1435,39 +1298,23 @@ class SyncServer(Server):
|
|
|
1435
1298
|
|
|
1436
1299
|
return response
|
|
1437
1300
|
|
|
1438
|
-
def update_agent_core_memory(self, user_id: str, agent_id: str,
|
|
1439
|
-
"""Update the
|
|
1440
|
-
if self.user_manager.get_user_by_id(user_id=user_id) is None:
|
|
1441
|
-
raise ValueError(f"User user_id={user_id} does not exist")
|
|
1442
|
-
if self.ms.get_agent(agent_id=agent_id, user_id=user_id) is None:
|
|
1443
|
-
raise ValueError(f"Agent agent_id={agent_id} does not exist")
|
|
1301
|
+
def update_agent_core_memory(self, user_id: str, agent_id: str, label: str, value: str) -> Memory:
|
|
1302
|
+
"""Update the value of a block in the agent's memory"""
|
|
1444
1303
|
|
|
1445
|
-
#
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
# raise ValueError(f"Key {key} not found in agent memory {list(letta_agent.memory.list_block_names())}")
|
|
1454
|
-
raise ValueError(f"Key {key} not found in agent memory {str(letta_agent.memory.memory)}")
|
|
1455
|
-
if value is None:
|
|
1456
|
-
continue
|
|
1457
|
-
if letta_agent.memory.get_block(key) != value:
|
|
1458
|
-
letta_agent.memory.update_block_value(label=key, value=value) # update agent memory
|
|
1459
|
-
modified = True
|
|
1460
|
-
|
|
1461
|
-
# If we modified the memory contents, we need to rebuild the memory block inside the system message
|
|
1462
|
-
if modified:
|
|
1463
|
-
letta_agent.rebuild_memory()
|
|
1464
|
-
# letta_agent.rebuild_memory(force=True, ms=self.ms) # This breaks unit tests in test_local_client.py
|
|
1465
|
-
# save agent
|
|
1466
|
-
save_agent(letta_agent, self.ms)
|
|
1304
|
+
# get the block id
|
|
1305
|
+
block = self.get_agent_block_by_label(user_id=user_id, agent_id=agent_id, label=label)
|
|
1306
|
+
block_id = block.id
|
|
1307
|
+
|
|
1308
|
+
# update the block
|
|
1309
|
+
self.block_manager.update_block(
|
|
1310
|
+
block_id=block_id, block_update=BlockUpdate(value=value), actor=self.user_manager.get_user_by_id(user_id=user_id)
|
|
1311
|
+
)
|
|
1467
1312
|
|
|
1468
|
-
|
|
1313
|
+
# load agent
|
|
1314
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1315
|
+
return letta_agent.agent_state.memory
|
|
1469
1316
|
|
|
1470
|
-
def rename_agent(self, user_id: str, agent_id: str, new_agent_name: str) ->
|
|
1317
|
+
def rename_agent(self, user_id: str, agent_id: str, new_agent_name: str) -> PersistedAgentState:
|
|
1471
1318
|
"""Update the name of the agent in the database"""
|
|
1472
1319
|
if self.user_manager.get_user_by_id(user_id=user_id) is None:
|
|
1473
1320
|
raise ValueError(f"User user_id={user_id} does not exist")
|
|
@@ -1475,7 +1322,7 @@ class SyncServer(Server):
|
|
|
1475
1322
|
raise ValueError(f"Agent agent_id={agent_id} does not exist")
|
|
1476
1323
|
|
|
1477
1324
|
# Get the agent object (loaded in memory)
|
|
1478
|
-
letta_agent = self.
|
|
1325
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1479
1326
|
|
|
1480
1327
|
current_name = letta_agent.agent_state.name
|
|
1481
1328
|
if current_name == new_agent_name:
|
|
@@ -1497,6 +1344,7 @@ class SyncServer(Server):
|
|
|
1497
1344
|
# TODO: REMOVE THIS ONCE WE MIGRATE AGENTMODEL TO ORM MODEL
|
|
1498
1345
|
# TODO: EVENTUALLY WE GET AUTO-DELETES WHEN WE SPECIFY RELATIONSHIPS IN THE ORM
|
|
1499
1346
|
self.agents_tags_manager.delete_all_tags_from_agent(agent_id=agent_id, actor=actor)
|
|
1347
|
+
self.blocks_agents_manager.remove_all_agent_blocks(agent_id=agent_id)
|
|
1500
1348
|
|
|
1501
1349
|
if self.ms.get_agent(agent_id=agent_id, user_id=user_id) is None:
|
|
1502
1350
|
raise ValueError(f"Agent agent_id={agent_id} does not exist")
|
|
@@ -1522,7 +1370,7 @@ class SyncServer(Server):
|
|
|
1522
1370
|
|
|
1523
1371
|
# Next, attempt to delete it from the actual database
|
|
1524
1372
|
try:
|
|
1525
|
-
self.ms.delete_agent(agent_id=agent_id)
|
|
1373
|
+
self.ms.delete_agent(agent_id=agent_id, per_agent_lock_manager=self.per_agent_lock_manager)
|
|
1526
1374
|
except Exception as e:
|
|
1527
1375
|
logger.exception(f"Failed to delete agent {agent_id} via ID with:\n{str(e)}")
|
|
1528
1376
|
raise ValueError(f"Failed to delete agent {agent_id} in database")
|
|
@@ -1664,9 +1512,10 @@ class SyncServer(Server):
|
|
|
1664
1512
|
raise ValueError(f"Need to provide at least source_id or source_name to find the source.")
|
|
1665
1513
|
# get connection to data source storage
|
|
1666
1514
|
source_connector = StorageConnector.get_storage_connector(TableType.PASSAGES, self.config, user_id=user_id)
|
|
1515
|
+
assert data_source, f"Data source with id={source_id} or name={source_name} does not exist"
|
|
1667
1516
|
|
|
1668
1517
|
# load agent
|
|
1669
|
-
agent = self.
|
|
1518
|
+
agent = self.load_agent(agent_id=agent_id)
|
|
1670
1519
|
|
|
1671
1520
|
# attach source to agent
|
|
1672
1521
|
agent.attach_source(data_source.id, source_connector, self.ms)
|
|
@@ -1691,7 +1540,7 @@ class SyncServer(Server):
|
|
|
1691
1540
|
source_id = source.id
|
|
1692
1541
|
|
|
1693
1542
|
# delete all Passage objects with source_id==source_id from agent's archival memory
|
|
1694
|
-
agent = self.
|
|
1543
|
+
agent = self.load_agent(agent_id=agent_id)
|
|
1695
1544
|
archival_memory = agent.persistence_manager.archival_memory
|
|
1696
1545
|
archival_memory.storage.delete({"source_id": source_id})
|
|
1697
1546
|
|
|
@@ -1770,34 +1619,43 @@ class SyncServer(Server):
|
|
|
1770
1619
|
def get_agent_message(self, agent_id: str, message_id: str) -> Optional[Message]:
|
|
1771
1620
|
"""Get a single message from the agent's memory"""
|
|
1772
1621
|
# Get the agent object (loaded in memory)
|
|
1773
|
-
letta_agent = self.
|
|
1622
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1774
1623
|
message = letta_agent.persistence_manager.recall_memory.storage.get(id=message_id)
|
|
1624
|
+
save_agent(letta_agent, self.ms)
|
|
1775
1625
|
return message
|
|
1776
1626
|
|
|
1777
1627
|
def update_agent_message(self, agent_id: str, request: UpdateMessage) -> Message:
|
|
1778
1628
|
"""Update the details of a message associated with an agent"""
|
|
1779
1629
|
|
|
1780
1630
|
# Get the current message
|
|
1781
|
-
letta_agent = self.
|
|
1782
|
-
|
|
1631
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1632
|
+
response = letta_agent.update_message(request=request)
|
|
1633
|
+
save_agent(letta_agent, self.ms)
|
|
1634
|
+
return response
|
|
1783
1635
|
|
|
1784
1636
|
def rewrite_agent_message(self, agent_id: str, new_text: str) -> Message:
|
|
1785
1637
|
|
|
1786
1638
|
# Get the current message
|
|
1787
|
-
letta_agent = self.
|
|
1788
|
-
|
|
1639
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1640
|
+
response = letta_agent.rewrite_message(new_text=new_text)
|
|
1641
|
+
save_agent(letta_agent, self.ms)
|
|
1642
|
+
return response
|
|
1789
1643
|
|
|
1790
1644
|
def rethink_agent_message(self, agent_id: str, new_thought: str) -> Message:
|
|
1791
1645
|
|
|
1792
1646
|
# Get the current message
|
|
1793
|
-
letta_agent = self.
|
|
1794
|
-
|
|
1647
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1648
|
+
response = letta_agent.rethink_message(new_thought=new_thought)
|
|
1649
|
+
save_agent(letta_agent, self.ms)
|
|
1650
|
+
return response
|
|
1795
1651
|
|
|
1796
1652
|
def retry_agent_message(self, agent_id: str) -> List[Message]:
|
|
1797
1653
|
|
|
1798
1654
|
# Get the current message
|
|
1799
|
-
letta_agent = self.
|
|
1800
|
-
|
|
1655
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1656
|
+
response = letta_agent.retry_message()
|
|
1657
|
+
save_agent(letta_agent, self.ms)
|
|
1658
|
+
return response
|
|
1801
1659
|
|
|
1802
1660
|
def get_user_or_default(self, user_id: Optional[str]) -> User:
|
|
1803
1661
|
"""Get the user object for user_id if it exists, otherwise return the default user object"""
|
|
@@ -1846,121 +1704,49 @@ class SyncServer(Server):
|
|
|
1846
1704
|
agent_id: str,
|
|
1847
1705
|
) -> ContextWindowOverview:
|
|
1848
1706
|
# Get the current message
|
|
1849
|
-
letta_agent = self.
|
|
1707
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1850
1708
|
return letta_agent.get_context_window()
|
|
1851
1709
|
|
|
1852
|
-
def update_agent_memory_label(self, user_id: str, agent_id: str, current_block_label: str, new_block_label: str) -> Memory:
|
|
1853
|
-
"""Update the label of a block in an agent's memory"""
|
|
1854
|
-
|
|
1855
|
-
# Get the user
|
|
1856
|
-
user = self.user_manager.get_user_by_id(user_id=user_id)
|
|
1857
|
-
|
|
1858
|
-
# Link a block to an agent's memory
|
|
1859
|
-
letta_agent = self._get_or_load_agent(agent_id=agent_id)
|
|
1860
|
-
letta_agent.memory.update_block_label(current_label=current_block_label, new_label=new_block_label)
|
|
1861
|
-
assert new_block_label in letta_agent.memory.list_block_labels()
|
|
1862
|
-
self.block_manager.create_or_update_block(block=letta_agent.memory.get_block(new_block_label), actor=user)
|
|
1863
|
-
|
|
1864
|
-
# check that the block was updated
|
|
1865
|
-
updated_block = self.block_manager.get_block_by_id(block_id=letta_agent.memory.get_block(new_block_label).id, actor=user)
|
|
1866
|
-
|
|
1867
|
-
# Recompile the agent memory
|
|
1868
|
-
letta_agent.rebuild_memory(force=True, ms=self.ms)
|
|
1869
|
-
|
|
1870
|
-
# save agent
|
|
1871
|
-
save_agent(letta_agent, self.ms)
|
|
1872
|
-
|
|
1873
|
-
updated_agent = self.ms.get_agent(agent_id=agent_id)
|
|
1874
|
-
if updated_agent is None:
|
|
1875
|
-
raise ValueError(f"Agent with id {agent_id} not found after linking block")
|
|
1876
|
-
assert new_block_label in updated_agent.memory.list_block_labels()
|
|
1877
|
-
assert current_block_label not in updated_agent.memory.list_block_labels()
|
|
1878
|
-
return updated_agent.memory
|
|
1879
|
-
|
|
1880
1710
|
def link_block_to_agent_memory(self, user_id: str, agent_id: str, block_id: str) -> Memory:
|
|
1881
1711
|
"""Link a block to an agent's memory"""
|
|
1882
|
-
|
|
1883
|
-
# Get the user
|
|
1884
|
-
user = self.user_manager.get_user_by_id(user_id=user_id)
|
|
1885
|
-
|
|
1886
|
-
# Get the block first
|
|
1887
|
-
block = self.block_manager.get_block_by_id(block_id=block_id, actor=user)
|
|
1712
|
+
block = self.block_manager.get_block_by_id(block_id=block_id, actor=self.user_manager.get_user_by_id(user_id=user_id))
|
|
1888
1713
|
if block is None:
|
|
1889
1714
|
raise ValueError(f"Block with id {block_id} not found")
|
|
1715
|
+
self.blocks_agents_manager.add_block_to_agent(agent_id, block_id, block_label=block.label)
|
|
1890
1716
|
|
|
1891
|
-
#
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
assert block.label in letta_agent.memory.list_block_labels()
|
|
1895
|
-
|
|
1896
|
-
# Recompile the agent memory
|
|
1897
|
-
letta_agent.rebuild_memory(force=True, ms=self.ms)
|
|
1898
|
-
|
|
1899
|
-
# save agent
|
|
1900
|
-
save_agent(letta_agent, self.ms)
|
|
1901
|
-
|
|
1902
|
-
updated_agent = self.ms.get_agent(agent_id=agent_id)
|
|
1903
|
-
if updated_agent is None:
|
|
1904
|
-
raise ValueError(f"Agent with id {agent_id} not found after linking block")
|
|
1905
|
-
assert block.label in updated_agent.memory.list_block_labels()
|
|
1906
|
-
|
|
1907
|
-
return updated_agent.memory
|
|
1717
|
+
# get agent memory
|
|
1718
|
+
memory = self.load_agent(agent_id=agent_id).agent_state.memory
|
|
1719
|
+
return memory
|
|
1908
1720
|
|
|
1909
1721
|
def unlink_block_from_agent_memory(self, user_id: str, agent_id: str, block_label: str, delete_if_no_ref: bool = True) -> Memory:
|
|
1910
1722
|
"""Unlink a block from an agent's memory. If the block is not linked to any agent, delete it."""
|
|
1723
|
+
self.blocks_agents_manager.remove_block_with_label_from_agent(agent_id=agent_id, block_label=block_label)
|
|
1911
1724
|
|
|
1912
|
-
#
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
# Link a block to an agent's memory
|
|
1916
|
-
letta_agent = self._get_or_load_agent(agent_id=agent_id)
|
|
1917
|
-
unlinked_block = letta_agent.memory.unlink_block(block_label=block_label)
|
|
1918
|
-
assert unlinked_block.label not in letta_agent.memory.list_block_labels()
|
|
1919
|
-
|
|
1920
|
-
# Check if the block is linked to any other agent
|
|
1921
|
-
# TODO needs reference counting GC to handle loose blocks
|
|
1922
|
-
# block = self.block_manager.get_block_by_id(block_id=unlinked_block.id, actor=user)
|
|
1923
|
-
# if block is None:
|
|
1924
|
-
# raise ValueError(f"Block with id {block_id} not found")
|
|
1925
|
-
|
|
1926
|
-
# Recompile the agent memory
|
|
1927
|
-
letta_agent.rebuild_memory(force=True, ms=self.ms)
|
|
1928
|
-
|
|
1929
|
-
# save agent
|
|
1930
|
-
save_agent(letta_agent, self.ms)
|
|
1931
|
-
|
|
1932
|
-
updated_agent = self.ms.get_agent(agent_id=agent_id)
|
|
1933
|
-
if updated_agent is None:
|
|
1934
|
-
raise ValueError(f"Agent with id {agent_id} not found after linking block")
|
|
1935
|
-
assert unlinked_block.label not in updated_agent.memory.list_block_labels()
|
|
1936
|
-
return updated_agent.memory
|
|
1725
|
+
# get agent memory
|
|
1726
|
+
memory = self.load_agent(agent_id=agent_id).agent_state.memory
|
|
1727
|
+
return memory
|
|
1937
1728
|
|
|
1938
1729
|
def update_agent_memory_limit(self, user_id: str, agent_id: str, block_label: str, limit: int) -> Memory:
|
|
1939
1730
|
"""Update the limit of a block in an agent's memory"""
|
|
1731
|
+
block = self.get_agent_block_by_label(user_id=user_id, agent_id=agent_id, label=block_label)
|
|
1732
|
+
self.block_manager.update_block(
|
|
1733
|
+
block_id=block.id, block_update=BlockUpdate(limit=limit), actor=self.user_manager.get_user_by_id(user_id=user_id)
|
|
1734
|
+
)
|
|
1735
|
+
# get agent memory
|
|
1736
|
+
memory = self.load_agent(agent_id=agent_id).agent_state.memory
|
|
1737
|
+
return memory
|
|
1738
|
+
|
|
1739
|
+
def upate_block(self, user_id: str, block_id: str, block_update: BlockUpdate) -> Block:
|
|
1740
|
+
"""Update a block"""
|
|
1741
|
+
return self.block_manager.update_block(
|
|
1742
|
+
block_id=block_id, block_update=block_update, actor=self.user_manager.get_user_by_id(user_id=user_id)
|
|
1743
|
+
)
|
|
1940
1744
|
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
# write out the update the database
|
|
1950
|
-
self.block_manager.create_or_update_block(block=letta_agent.memory.get_block(block_label), actor=user)
|
|
1951
|
-
|
|
1952
|
-
# check that the block was updated
|
|
1953
|
-
updated_block = self.block_manager.get_block_by_id(block_id=letta_agent.memory.get_block(block_label).id, actor=user)
|
|
1954
|
-
assert updated_block and updated_block.limit == limit
|
|
1955
|
-
|
|
1956
|
-
# Recompile the agent memory
|
|
1957
|
-
letta_agent.rebuild_memory(force=True, ms=self.ms)
|
|
1958
|
-
|
|
1959
|
-
# save agent
|
|
1960
|
-
save_agent(letta_agent, self.ms)
|
|
1961
|
-
|
|
1962
|
-
updated_agent = self.ms.get_agent(agent_id=agent_id)
|
|
1963
|
-
if updated_agent is None:
|
|
1964
|
-
raise ValueError(f"Agent with id {agent_id} not found after linking block")
|
|
1965
|
-
assert updated_agent.memory.get_block(label=block_label).limit == limit
|
|
1966
|
-
return updated_agent.memory
|
|
1745
|
+
def get_agent_block_by_label(self, user_id: str, agent_id: str, label: str) -> Block:
|
|
1746
|
+
"""Get a block by label"""
|
|
1747
|
+
# TODO: implement at ORM?
|
|
1748
|
+
for block_id in self.blocks_agents_manager.list_block_ids_for_agent(agent_id=agent_id):
|
|
1749
|
+
block = self.block_manager.get_block_by_id(block_id=block_id, actor=self.user_manager.get_user_by_id(user_id=user_id))
|
|
1750
|
+
if block.label == label:
|
|
1751
|
+
return block
|
|
1752
|
+
return None
|