letta-nightly 0.5.4.dev20241127104220__py3-none-any.whl → 0.5.4.dev20241128104217__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/functions/function_sets/base.py +38 -1
- letta/helpers/tool_rule_solver.py +6 -5
- letta/main.py +1 -1
- letta/metadata.py +39 -41
- letta/o1_agent.py +1 -4
- letta/persistence_manager.py +1 -0
- letta/schemas/agent.py +57 -52
- letta/schemas/block.py +69 -25
- 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 +90 -86
- letta/server/rest_api/routers/v1/blocks.py +50 -5
- letta/server/server.py +237 -459
- letta/server/static_files/assets/index-9fa459a2.js +1 -1
- letta/services/block_manager.py +6 -3
- letta/services/blocks_agents_manager.py +15 -0
- letta/services/tool_execution_sandbox.py +1 -1
- letta/services/tool_manager.py +2 -1
- {letta_nightly-0.5.4.dev20241127104220.dist-info → letta_nightly-0.5.4.dev20241128104217.dist-info}/METADATA +1 -1
- {letta_nightly-0.5.4.dev20241127104220.dist-info → letta_nightly-0.5.4.dev20241128104217.dist-info}/RECORD +38 -38
- {letta_nightly-0.5.4.dev20241127104220.dist-info → letta_nightly-0.5.4.dev20241128104217.dist-info}/LICENSE +0 -0
- {letta_nightly-0.5.4.dev20241127104220.dist-info → letta_nightly-0.5.4.dev20241128104217.dist-info}/WHEEL +0 -0
- {letta_nightly-0.5.4.dev20241127104220.dist-info → letta_nightly-0.5.4.dev20241128104217.dist-info}/entry_points.txt +0 -0
letta/server/server.py
CHANGED
|
@@ -18,24 +18,10 @@ from letta.agent_store.storage import StorageConnector, TableType
|
|
|
18
18
|
from letta.credentials import LettaCredentials
|
|
19
19
|
from letta.data_sources.connectors import DataConnector, load_data
|
|
20
20
|
|
|
21
|
-
# from letta.data_types import (
|
|
22
|
-
# AgentState,
|
|
23
|
-
# EmbeddingConfig,
|
|
24
|
-
# LLMConfig,
|
|
25
|
-
# Message,
|
|
26
|
-
# Preset,
|
|
27
|
-
# Source,
|
|
28
|
-
# Token,
|
|
29
|
-
# User,
|
|
30
|
-
# )
|
|
31
|
-
from letta.functions.functions import generate_schema, parse_source_code
|
|
32
|
-
from letta.functions.schema_generator import generate_schema
|
|
33
|
-
|
|
34
21
|
# TODO use custom interface
|
|
35
22
|
from letta.interface import AgentInterface # abstract
|
|
36
23
|
from letta.interface import CLIInterface # for printing to terminal
|
|
37
24
|
from letta.log import get_logger
|
|
38
|
-
from letta.memory import get_memory_functions
|
|
39
25
|
from letta.metadata import MetadataStore
|
|
40
26
|
from letta.o1_agent import O1Agent
|
|
41
27
|
from letta.orm import Base
|
|
@@ -54,8 +40,15 @@ from letta.providers import (
|
|
|
54
40
|
VLLMChatCompletionsProvider,
|
|
55
41
|
VLLMCompletionsProvider,
|
|
56
42
|
)
|
|
57
|
-
from letta.schemas.agent import
|
|
43
|
+
from letta.schemas.agent import (
|
|
44
|
+
AgentState,
|
|
45
|
+
AgentType,
|
|
46
|
+
CreateAgent,
|
|
47
|
+
PersistedAgentState,
|
|
48
|
+
UpdateAgentState,
|
|
49
|
+
)
|
|
58
50
|
from letta.schemas.api_key import APIKey, APIKeyCreate
|
|
51
|
+
from letta.schemas.block import Block, BlockUpdate
|
|
59
52
|
from letta.schemas.embedding_config import EmbeddingConfig
|
|
60
53
|
|
|
61
54
|
# openai schemas
|
|
@@ -87,9 +80,6 @@ from letta.services.tool_manager import ToolManager
|
|
|
87
80
|
from letta.services.user_manager import UserManager
|
|
88
81
|
from letta.utils import create_random_username, json_dumps, json_loads
|
|
89
82
|
|
|
90
|
-
# from letta.llm_api_tools import openai_get_model_list, azure_openai_get_model_list, smart_urljoin
|
|
91
|
-
|
|
92
|
-
|
|
93
83
|
logger = get_logger(__name__)
|
|
94
84
|
|
|
95
85
|
|
|
@@ -129,10 +119,11 @@ class Server(object):
|
|
|
129
119
|
@abstractmethod
|
|
130
120
|
def create_agent(
|
|
131
121
|
self,
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
interface
|
|
135
|
-
|
|
122
|
+
request: CreateAgent,
|
|
123
|
+
actor: User,
|
|
124
|
+
# interface
|
|
125
|
+
interface: Union[AgentInterface, None] = None,
|
|
126
|
+
) -> AgentState:
|
|
136
127
|
"""Create a new agent using a config"""
|
|
137
128
|
raise NotImplementedError
|
|
138
129
|
|
|
@@ -254,8 +245,8 @@ class SyncServer(Server):
|
|
|
254
245
|
self.block_manager = BlockManager()
|
|
255
246
|
self.source_manager = SourceManager()
|
|
256
247
|
self.agents_tags_manager = AgentsTagsManager()
|
|
257
|
-
self.blocks_agents_manager = BlocksAgentsManager()
|
|
258
248
|
self.sandbox_config_manager = SandboxConfigManager(tool_settings)
|
|
249
|
+
self.blocks_agents_manager = BlocksAgentsManager()
|
|
259
250
|
|
|
260
251
|
# Managers that interface with parallelism
|
|
261
252
|
self.per_agent_lock_manager = PerAgentLockManager()
|
|
@@ -372,92 +363,23 @@ class SyncServer(Server):
|
|
|
372
363
|
}
|
|
373
364
|
)
|
|
374
365
|
|
|
375
|
-
def
|
|
376
|
-
"""
|
|
377
|
-
|
|
378
|
-
|
|
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)
|
|
379
370
|
|
|
380
|
-
|
|
381
|
-
if
|
|
382
|
-
interface =
|
|
383
|
-
|
|
384
|
-
try:
|
|
385
|
-
logger.debug(f"Grabbing agent user_id={user_id} agent_id={agent_id} from database")
|
|
386
|
-
agent_state = self.ms.get_agent(agent_id=agent_id, user_id=user_id)
|
|
387
|
-
if not agent_state:
|
|
388
|
-
logger.exception(f"agent_id {agent_id} does not exist")
|
|
389
|
-
raise ValueError(f"agent_id {agent_id} does not exist")
|
|
390
|
-
|
|
391
|
-
# Instantiate an agent object using the state retrieved
|
|
392
|
-
logger.debug(f"Creating an agent object")
|
|
393
|
-
tool_objs = []
|
|
394
|
-
for name in agent_state.tools:
|
|
395
|
-
# TODO: This should be a hard failure, but for migration reasons, we patch it for now
|
|
396
|
-
tool_obj = self.tool_manager.get_tool_by_name(tool_name=name, actor=actor)
|
|
397
|
-
if tool_obj:
|
|
398
|
-
tool_obj = self.tool_manager.get_tool_by_name(tool_name=name, actor=actor)
|
|
399
|
-
tool_objs.append(tool_obj)
|
|
400
|
-
else:
|
|
401
|
-
warnings.warn(f"Tried to retrieve a tool with name {name} from the agent_state, but does not exist in tool db.")
|
|
402
|
-
|
|
403
|
-
# set agent_state tools to only the names of the available tools
|
|
404
|
-
agent_state.tools = [t.name for t in tool_objs]
|
|
405
|
-
|
|
406
|
-
# Make sure the memory is a memory object
|
|
407
|
-
assert isinstance(agent_state.memory, Memory)
|
|
408
|
-
|
|
409
|
-
if agent_state.agent_type == AgentType.memgpt_agent:
|
|
410
|
-
letta_agent = Agent(agent_state=agent_state, interface=interface, tools=tool_objs, user=actor)
|
|
411
|
-
elif agent_state.agent_type == AgentType.o1_agent:
|
|
412
|
-
letta_agent = O1Agent(agent_state=agent_state, interface=interface, tools=tool_objs, user=actor)
|
|
413
|
-
else:
|
|
414
|
-
raise NotImplementedError("Not a supported agent type")
|
|
415
|
-
|
|
416
|
-
# Add the agent to the in-memory store and return its reference
|
|
417
|
-
logger.debug(f"Adding agent to the agent cache: user_id={user_id}, agent_id={agent_id}")
|
|
418
|
-
self._add_agent(user_id=user_id, agent_id=agent_id, agent_obj=letta_agent)
|
|
419
|
-
return letta_agent
|
|
420
|
-
|
|
421
|
-
except Exception as e:
|
|
422
|
-
logger.exception(f"Error occurred while trying to get agent {agent_id}:\n{e}")
|
|
423
|
-
raise
|
|
424
|
-
|
|
425
|
-
def _get_or_load_agent(self, agent_id: str, caching: bool = True) -> Agent:
|
|
426
|
-
"""Check if the agent is in-memory, then load"""
|
|
427
|
-
|
|
428
|
-
# Gets the agent state
|
|
429
|
-
agent_state = self.ms.get_agent(agent_id=agent_id)
|
|
430
|
-
if not agent_state:
|
|
431
|
-
raise ValueError(f"Agent does not exist")
|
|
432
|
-
user_id = agent_state.user_id
|
|
433
|
-
actor = self.user_manager.get_user_by_id(user_id)
|
|
434
|
-
|
|
435
|
-
logger.debug(f"Checking for agent user_id={user_id} agent_id={agent_id}")
|
|
436
|
-
if caching:
|
|
437
|
-
# TODO: consider disabling loading cached agents due to potential concurrency issues
|
|
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
|
-
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)
|
|
442
374
|
else:
|
|
443
|
-
|
|
444
|
-
letta_agent = self._load_agent(agent_id=agent_id, actor=actor)
|
|
445
|
-
|
|
446
|
-
# letta_agent = self._get_agent(user_id=user_id, agent_id=agent_id)
|
|
447
|
-
# if not letta_agent:
|
|
448
|
-
# logger.debug(f"Agent not loaded, loading agent user_id={user_id} agent_id={agent_id}")
|
|
449
|
-
|
|
450
|
-
# NOTE: no longer caching, always forcing a lot from the database
|
|
451
|
-
# Loads the agent objects
|
|
452
|
-
# letta_agent = self._load_agent(agent_id=agent_id, actor=actor)
|
|
453
|
-
|
|
454
|
-
return letta_agent
|
|
375
|
+
return O1Agent(agent_state=agent_state, interface=interface, user=actor)
|
|
455
376
|
|
|
456
377
|
def _step(
|
|
457
378
|
self,
|
|
458
379
|
user_id: str,
|
|
459
380
|
agent_id: str,
|
|
460
381
|
input_messages: Union[Message, List[Message]],
|
|
382
|
+
interface: Union[AgentInterface, None] = None, # needed to getting responses
|
|
461
383
|
# timestamp: Optional[datetime],
|
|
462
384
|
) -> LettaUsageStatistics:
|
|
463
385
|
"""Send the input message through the agent"""
|
|
@@ -473,7 +395,8 @@ class SyncServer(Server):
|
|
|
473
395
|
try:
|
|
474
396
|
|
|
475
397
|
# Get the agent object (loaded in memory)
|
|
476
|
-
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)
|
|
477
400
|
if letta_agent is None:
|
|
478
401
|
raise KeyError(f"Agent (user={user_id}, agent={agent_id}) is not loaded")
|
|
479
402
|
|
|
@@ -490,6 +413,9 @@ class SyncServer(Server):
|
|
|
490
413
|
skip_verify=True,
|
|
491
414
|
)
|
|
492
415
|
|
|
416
|
+
# save agent after step
|
|
417
|
+
save_agent(letta_agent, self.ms)
|
|
418
|
+
|
|
493
419
|
except Exception as e:
|
|
494
420
|
logger.error(f"Error in server._step: {e}")
|
|
495
421
|
print(traceback.print_exc())
|
|
@@ -507,7 +433,7 @@ class SyncServer(Server):
|
|
|
507
433
|
logger.debug(f"Got command: {command}")
|
|
508
434
|
|
|
509
435
|
# Get the agent object (loaded in memory)
|
|
510
|
-
letta_agent = self.
|
|
436
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
511
437
|
usage = None
|
|
512
438
|
|
|
513
439
|
if command.lower() == "exit":
|
|
@@ -544,7 +470,7 @@ class SyncServer(Server):
|
|
|
544
470
|
elif command.lower() == "memory":
|
|
545
471
|
ret_str = (
|
|
546
472
|
f"\nDumping memory contents:\n"
|
|
547
|
-
+ f"\n{str(letta_agent.memory)}"
|
|
473
|
+
+ f"\n{str(letta_agent.agent_state.memory)}"
|
|
548
474
|
+ f"\n{str(letta_agent.persistence_manager.archival_memory)}"
|
|
549
475
|
+ f"\n{str(letta_agent.persistence_manager.recall_memory)}"
|
|
550
476
|
)
|
|
@@ -736,6 +662,7 @@ class SyncServer(Server):
|
|
|
736
662
|
# whether or not to wrap user and system message as MemGPT-style stringified JSON
|
|
737
663
|
wrap_user_message: bool = True,
|
|
738
664
|
wrap_system_message: bool = True,
|
|
665
|
+
interface: Union[AgentInterface, None] = None, # needed to getting responses
|
|
739
666
|
) -> LettaUsageStatistics:
|
|
740
667
|
"""Send a list of messages to the agent
|
|
741
668
|
|
|
@@ -788,7 +715,7 @@ class SyncServer(Server):
|
|
|
788
715
|
raise ValueError(f"All messages must be of type Message or MessageCreate, got {[type(message) for message in messages]}")
|
|
789
716
|
|
|
790
717
|
# Run the agent state forward
|
|
791
|
-
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)
|
|
792
719
|
|
|
793
720
|
# @LockingServer.agent_lock_decorator
|
|
794
721
|
def run_command(self, user_id: str, agent_id: str, command: str) -> LettaUsageStatistics:
|
|
@@ -836,129 +763,109 @@ class SyncServer(Server):
|
|
|
836
763
|
else:
|
|
837
764
|
raise ValueError(f"Invalid agent type: {request.agent_type}")
|
|
838
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
|
|
839
785
|
logger.debug(f"Attempting to find user: {user_id}")
|
|
840
786
|
user = self.user_manager.get_user_by_id(user_id=user_id)
|
|
841
787
|
if not user:
|
|
842
788
|
raise ValueError(f"cannot find user with associated client id: {user_id}")
|
|
843
789
|
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
for func_name, func in memory_functions.items():
|
|
864
|
-
|
|
865
|
-
if request.tools and func_name in request.tools:
|
|
866
|
-
# tool already added
|
|
867
|
-
continue
|
|
868
|
-
source_code = parse_source_code(func)
|
|
869
|
-
# memory functions are not terminal
|
|
870
|
-
json_schema = generate_schema(func, name=func_name)
|
|
871
|
-
source_type = "python"
|
|
872
|
-
tags = ["memory", "memgpt-base"]
|
|
873
|
-
tool = self.tool_manager.create_or_update_tool(
|
|
874
|
-
Tool(
|
|
875
|
-
source_code=source_code,
|
|
876
|
-
source_type=source_type,
|
|
877
|
-
tags=tags,
|
|
878
|
-
json_schema=json_schema,
|
|
879
|
-
),
|
|
880
|
-
actor=actor,
|
|
881
|
-
)
|
|
882
|
-
tool_objs.append(tool)
|
|
883
|
-
if not request.tools:
|
|
884
|
-
request.tools = []
|
|
885
|
-
request.tools.append(tool.name)
|
|
886
|
-
|
|
887
|
-
# TODO: save the agent state
|
|
888
|
-
agent_state = AgentState(
|
|
889
|
-
name=request.name,
|
|
890
|
-
user_id=user_id,
|
|
891
|
-
tools=request.tools if request.tools else [],
|
|
892
|
-
tool_rules=request.tool_rules if request.tool_rules else [],
|
|
893
|
-
agent_type=request.agent_type or AgentType.memgpt_agent,
|
|
894
|
-
llm_config=llm_config,
|
|
895
|
-
embedding_config=embedding_config,
|
|
896
|
-
system=request.system,
|
|
897
|
-
memory=request.memory,
|
|
898
|
-
description=request.description,
|
|
899
|
-
metadata_=request.metadata_,
|
|
900
|
-
tags=request.tags,
|
|
901
|
-
)
|
|
902
|
-
if request.agent_type == AgentType.memgpt_agent:
|
|
903
|
-
agent = Agent(
|
|
904
|
-
interface=interface,
|
|
905
|
-
agent_state=agent_state,
|
|
906
|
-
tools=tool_objs,
|
|
907
|
-
# gpt-3.5-turbo tends to omit inner monologue, relax this requirement for now
|
|
908
|
-
first_message_verify_mono=(
|
|
909
|
-
True if (llm_config and llm_config.model is not None and "gpt-4" in llm_config.model) else False
|
|
910
|
-
),
|
|
911
|
-
user=actor,
|
|
912
|
-
initial_message_sequence=request.initial_message_sequence,
|
|
913
|
-
)
|
|
914
|
-
elif request.agent_type == AgentType.o1_agent:
|
|
915
|
-
agent = O1Agent(
|
|
916
|
-
interface=interface,
|
|
917
|
-
agent_state=agent_state,
|
|
918
|
-
tools=tool_objs,
|
|
919
|
-
# gpt-3.5-turbo tends to omit inner monologue, relax this requirement for now
|
|
920
|
-
first_message_verify_mono=(
|
|
921
|
-
True if (llm_config and llm_config.model is not None and "gpt-4" in llm_config.model) else False
|
|
922
|
-
),
|
|
923
|
-
user=actor,
|
|
924
|
-
)
|
|
925
|
-
# rebuilding agent memory on agent create in case shared memory blocks
|
|
926
|
-
# were specified in the new agent's memory config. we're doing this for two reasons:
|
|
927
|
-
# 1. if only the ID of the shared memory block was specified, we can fetch its most recent value
|
|
928
|
-
# 2. if the shared block state changed since this agent initialization started, we can be sure to have the latest value
|
|
929
|
-
agent.rebuild_memory(force=True, ms=self.ms)
|
|
930
|
-
# FIXME: this is a hacky way to get the system prompts injected into agent into the DB
|
|
931
|
-
# self.ms.update_agent(agent.agent_state)
|
|
932
|
-
except Exception as e:
|
|
933
|
-
logger.exception(e)
|
|
934
|
-
try:
|
|
935
|
-
if agent:
|
|
936
|
-
self.ms.delete_agent(agent_id=agent.agent_state.id, per_agent_lock_manager=self.per_agent_lock_manager)
|
|
937
|
-
except Exception as delete_e:
|
|
938
|
-
logger.exception(f"Failed to delete_agent:\n{delete_e}")
|
|
939
|
-
raise e
|
|
790
|
+
# TODO: create the message objects (NOTE: do this after we migrate to `CreateMessage`)
|
|
791
|
+
|
|
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)
|
|
940
809
|
|
|
941
|
-
#
|
|
942
|
-
|
|
943
|
-
logger.debug(f"Created new agent from config: {agent}")
|
|
810
|
+
# Note: mappings (e.g. tags, blocks) are created after the agent is persisted
|
|
811
|
+
# TODO: add source mappings here as well
|
|
944
812
|
|
|
945
|
-
#
|
|
813
|
+
# create the tags
|
|
946
814
|
if request.tags:
|
|
947
815
|
for tag in request.tags:
|
|
948
|
-
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)
|
|
949
817
|
|
|
950
|
-
|
|
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)
|
|
951
822
|
|
|
952
|
-
|
|
953
|
-
|
|
823
|
+
in_memory_agent_state = self.get_agent(agent_state.id)
|
|
824
|
+
return in_memory_agent_state
|
|
954
825
|
|
|
955
|
-
|
|
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)
|
|
838
|
+
|
|
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
|
|
841
|
+
|
|
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)
|
|
851
|
+
|
|
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)
|
|
956
863
|
|
|
957
864
|
def update_agent(
|
|
958
865
|
self,
|
|
959
866
|
request: UpdateAgentState,
|
|
960
867
|
actor: User,
|
|
961
|
-
):
|
|
868
|
+
) -> AgentState:
|
|
962
869
|
"""Update the agents core memory block, return the new state"""
|
|
963
870
|
try:
|
|
964
871
|
self.user_manager.get_user_by_id(user_id=actor.id)
|
|
@@ -969,13 +876,7 @@ class SyncServer(Server):
|
|
|
969
876
|
raise ValueError(f"Agent agent_id={request.id} does not exist")
|
|
970
877
|
|
|
971
878
|
# Get the agent object (loaded in memory)
|
|
972
|
-
letta_agent = self.
|
|
973
|
-
|
|
974
|
-
# update the core memory of the agent
|
|
975
|
-
if request.memory:
|
|
976
|
-
assert isinstance(request.memory, Memory), type(request.memory)
|
|
977
|
-
new_memory_contents = request.memory.to_flat_dict()
|
|
978
|
-
_ = 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)
|
|
979
880
|
|
|
980
881
|
# update the system prompt
|
|
981
882
|
if request.system:
|
|
@@ -989,13 +890,13 @@ class SyncServer(Server):
|
|
|
989
890
|
letta_agent.set_message_buffer(message_ids=request.message_ids)
|
|
990
891
|
|
|
991
892
|
# tools
|
|
992
|
-
if request.
|
|
893
|
+
if request.tool_names:
|
|
993
894
|
# Replace tools and also re-link
|
|
994
895
|
|
|
995
896
|
# (1) get tools + make sure they exist
|
|
996
897
|
# Current and target tools as sets of tool names
|
|
997
|
-
current_tools = set(letta_agent.agent_state.
|
|
998
|
-
target_tools = set(request.
|
|
898
|
+
current_tools = set(letta_agent.agent_state.tool_names)
|
|
899
|
+
target_tools = set(request.tool_names)
|
|
999
900
|
|
|
1000
901
|
# Calculate tools to add and remove
|
|
1001
902
|
tools_to_add = target_tools - current_tools
|
|
@@ -1012,7 +913,7 @@ class SyncServer(Server):
|
|
|
1012
913
|
self.add_tool_to_agent(agent_id=request.id, tool_id=tool.id, user_id=actor.id)
|
|
1013
914
|
|
|
1014
915
|
# reload agent
|
|
1015
|
-
letta_agent = self.
|
|
916
|
+
letta_agent = self.load_agent(agent_id=request.id)
|
|
1016
917
|
|
|
1017
918
|
# configs
|
|
1018
919
|
if request.llm_config:
|
|
@@ -1040,7 +941,6 @@ class SyncServer(Server):
|
|
|
1040
941
|
self.agents_tags_manager.delete_tag_from_agent(agent_id=letta_agent.agent_state.id, tag=tag, actor=actor)
|
|
1041
942
|
|
|
1042
943
|
# save the agent
|
|
1043
|
-
assert isinstance(letta_agent.memory, Memory)
|
|
1044
944
|
save_agent(letta_agent, self.ms)
|
|
1045
945
|
# TODO: probably reload the agent somehow?
|
|
1046
946
|
return letta_agent.agent_state
|
|
@@ -1053,8 +953,8 @@ class SyncServer(Server):
|
|
|
1053
953
|
raise ValueError(f"Agent agent_id={agent_id} does not exist")
|
|
1054
954
|
|
|
1055
955
|
# Get the agent object (loaded in memory)
|
|
1056
|
-
letta_agent = self.
|
|
1057
|
-
return letta_agent.tools
|
|
956
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
957
|
+
return letta_agent.agent_state.tools
|
|
1058
958
|
|
|
1059
959
|
def add_tool_to_agent(
|
|
1060
960
|
self,
|
|
@@ -1072,7 +972,7 @@ class SyncServer(Server):
|
|
|
1072
972
|
raise ValueError(f"Agent agent_id={agent_id} does not exist")
|
|
1073
973
|
|
|
1074
974
|
# Get the agent object (loaded in memory)
|
|
1075
|
-
letta_agent = self.
|
|
975
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1076
976
|
|
|
1077
977
|
# Get all the tool objects from the request
|
|
1078
978
|
tool_objs = []
|
|
@@ -1080,7 +980,7 @@ class SyncServer(Server):
|
|
|
1080
980
|
assert tool_obj, f"Tool with id={tool_id} does not exist"
|
|
1081
981
|
tool_objs.append(tool_obj)
|
|
1082
982
|
|
|
1083
|
-
for tool in letta_agent.tools:
|
|
983
|
+
for tool in letta_agent.agent_state.tools:
|
|
1084
984
|
tool_obj = self.tool_manager.get_tool_by_id(tool_id=tool.id, actor=user)
|
|
1085
985
|
assert tool_obj, f"Tool with id={tool.id} does not exist"
|
|
1086
986
|
|
|
@@ -1089,7 +989,7 @@ class SyncServer(Server):
|
|
|
1089
989
|
tool_objs.append(tool_obj)
|
|
1090
990
|
|
|
1091
991
|
# replace the list of tool names ("ids") inside the agent state
|
|
1092
|
-
letta_agent.agent_state.
|
|
992
|
+
letta_agent.agent_state.tool_names = [tool.name for tool in tool_objs]
|
|
1093
993
|
|
|
1094
994
|
# then attempt to link the tools modules
|
|
1095
995
|
letta_agent.link_tools(tool_objs)
|
|
@@ -1114,11 +1014,11 @@ class SyncServer(Server):
|
|
|
1114
1014
|
raise ValueError(f"Agent agent_id={agent_id} does not exist")
|
|
1115
1015
|
|
|
1116
1016
|
# Get the agent object (loaded in memory)
|
|
1117
|
-
letta_agent = self.
|
|
1017
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1118
1018
|
|
|
1119
1019
|
# Get all the tool_objs
|
|
1120
1020
|
tool_objs = []
|
|
1121
|
-
for tool in letta_agent.tools:
|
|
1021
|
+
for tool in letta_agent.agent_state.tools:
|
|
1122
1022
|
tool_obj = self.tool_manager.get_tool_by_id(tool_id=tool.id, actor=user)
|
|
1123
1023
|
assert tool_obj, f"Tool with id={tool.id} does not exist"
|
|
1124
1024
|
|
|
@@ -1127,7 +1027,7 @@ class SyncServer(Server):
|
|
|
1127
1027
|
tool_objs.append(tool_obj)
|
|
1128
1028
|
|
|
1129
1029
|
# replace the list of tool names ("ids") inside the agent state
|
|
1130
|
-
letta_agent.agent_state.
|
|
1030
|
+
letta_agent.agent_state.tool_names = [tool.name for tool in tool_objs]
|
|
1131
1031
|
|
|
1132
1032
|
# then attempt to link the tools modules
|
|
1133
1033
|
letta_agent.link_tools(tool_objs)
|
|
@@ -1136,18 +1036,9 @@ class SyncServer(Server):
|
|
|
1136
1036
|
save_agent(letta_agent, self.ms)
|
|
1137
1037
|
return letta_agent.agent_state
|
|
1138
1038
|
|
|
1139
|
-
def
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
agent_config = {
|
|
1144
|
-
"id": agent_state.id,
|
|
1145
|
-
"name": agent_state.name,
|
|
1146
|
-
"human": agent_state._metadata.get("human", None),
|
|
1147
|
-
"persona": agent_state._metadata.get("persona", None),
|
|
1148
|
-
"created_at": agent_state.created_at.isoformat(),
|
|
1149
|
-
}
|
|
1150
|
-
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)
|
|
1151
1042
|
|
|
1152
1043
|
def list_agents(self, user_id: str, tags: Optional[List[str]] = None) -> List[AgentState]:
|
|
1153
1044
|
"""List all available agents to a user"""
|
|
@@ -1155,13 +1046,13 @@ class SyncServer(Server):
|
|
|
1155
1046
|
|
|
1156
1047
|
if tags is None:
|
|
1157
1048
|
agents_states = self.ms.list_agents(user_id=user_id)
|
|
1158
|
-
|
|
1049
|
+
agent_ids = [agent.id for agent in agents_states]
|
|
1159
1050
|
else:
|
|
1160
1051
|
agent_ids = []
|
|
1161
1052
|
for tag in tags:
|
|
1162
1053
|
agent_ids += self.agents_tags_manager.get_agents_by_tag(tag=tag, actor=user)
|
|
1163
1054
|
|
|
1164
|
-
|
|
1055
|
+
return [self.get_agent(agent_id=agent_id) for agent_id in agent_ids]
|
|
1165
1056
|
|
|
1166
1057
|
# convert name->id
|
|
1167
1058
|
|
|
@@ -1185,34 +1076,34 @@ class SyncServer(Server):
|
|
|
1185
1076
|
|
|
1186
1077
|
def get_agent_memory(self, agent_id: str) -> Memory:
|
|
1187
1078
|
"""Return the memory of an agent (core memory)"""
|
|
1188
|
-
agent = self.
|
|
1189
|
-
return agent.memory
|
|
1079
|
+
agent = self.load_agent(agent_id=agent_id)
|
|
1080
|
+
return agent.agent_state.memory
|
|
1190
1081
|
|
|
1191
1082
|
def get_archival_memory_summary(self, agent_id: str) -> ArchivalMemorySummary:
|
|
1192
|
-
agent = self.
|
|
1083
|
+
agent = self.load_agent(agent_id=agent_id)
|
|
1193
1084
|
return ArchivalMemorySummary(size=len(agent.persistence_manager.archival_memory))
|
|
1194
1085
|
|
|
1195
1086
|
def get_recall_memory_summary(self, agent_id: str) -> RecallMemorySummary:
|
|
1196
|
-
agent = self.
|
|
1087
|
+
agent = self.load_agent(agent_id=agent_id)
|
|
1197
1088
|
return RecallMemorySummary(size=len(agent.persistence_manager.recall_memory))
|
|
1198
1089
|
|
|
1199
1090
|
def get_in_context_message_ids(self, agent_id: str) -> List[str]:
|
|
1200
1091
|
"""Get the message ids of the in-context messages in the agent's memory"""
|
|
1201
1092
|
# Get the agent object (loaded in memory)
|
|
1202
|
-
|
|
1203
|
-
return [m.id for m in
|
|
1093
|
+
agent = self.load_agent(agent_id=agent_id)
|
|
1094
|
+
return [m.id for m in agent._messages]
|
|
1204
1095
|
|
|
1205
1096
|
def get_in_context_messages(self, agent_id: str) -> List[Message]:
|
|
1206
1097
|
"""Get the in-context messages in the agent's memory"""
|
|
1207
1098
|
# Get the agent object (loaded in memory)
|
|
1208
|
-
|
|
1209
|
-
return
|
|
1099
|
+
agent = self.load_agent(agent_id=agent_id)
|
|
1100
|
+
return agent._messages
|
|
1210
1101
|
|
|
1211
1102
|
def get_agent_message(self, agent_id: str, message_id: str) -> Message:
|
|
1212
1103
|
"""Get a single message from the agent's memory"""
|
|
1213
1104
|
# Get the agent object (loaded in memory)
|
|
1214
|
-
|
|
1215
|
-
message =
|
|
1105
|
+
agent = self.load_agent(agent_id=agent_id)
|
|
1106
|
+
message = agent.persistence_manager.recall_memory.storage.get(id=message_id)
|
|
1216
1107
|
return message
|
|
1217
1108
|
|
|
1218
1109
|
def get_agent_messages(
|
|
@@ -1220,11 +1111,10 @@ class SyncServer(Server):
|
|
|
1220
1111
|
agent_id: str,
|
|
1221
1112
|
start: int,
|
|
1222
1113
|
count: int,
|
|
1223
|
-
return_message_object: bool = True,
|
|
1224
1114
|
) -> Union[List[Message], List[LettaMessage]]:
|
|
1225
1115
|
"""Paginated query of all messages in agent message queue"""
|
|
1226
1116
|
# Get the agent object (loaded in memory)
|
|
1227
|
-
letta_agent = self.
|
|
1117
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1228
1118
|
|
|
1229
1119
|
if start < 0 or count < 0:
|
|
1230
1120
|
raise ValueError("Start and count values should be non-negative")
|
|
@@ -1242,10 +1132,6 @@ class SyncServer(Server):
|
|
|
1242
1132
|
# Slice the list for pagination
|
|
1243
1133
|
messages = reversed_messages[start:end_index]
|
|
1244
1134
|
|
|
1245
|
-
## Convert to json
|
|
1246
|
-
## Add a tag indicating in-context or not
|
|
1247
|
-
# json_messages = [{**record.to_json(), "in_context": True} for record in messages]
|
|
1248
|
-
|
|
1249
1135
|
else:
|
|
1250
1136
|
# need to access persistence manager for additional messages
|
|
1251
1137
|
db_iterator = letta_agent.persistence_manager.recall_memory.storage.get_all_paginated(page_size=count, offset=start)
|
|
@@ -1265,9 +1151,6 @@ class SyncServer(Server):
|
|
|
1265
1151
|
# for d in json_messages:
|
|
1266
1152
|
# d["in_context"] = True if str(d["id"]) in in_context_message_ids else False
|
|
1267
1153
|
|
|
1268
|
-
if not return_message_object:
|
|
1269
|
-
messages = [msg for m in messages for msg in m.to_letta_message()]
|
|
1270
|
-
|
|
1271
1154
|
return messages
|
|
1272
1155
|
|
|
1273
1156
|
def get_agent_archival(self, user_id: str, agent_id: str, start: int, count: int) -> List[Passage]:
|
|
@@ -1278,7 +1161,7 @@ class SyncServer(Server):
|
|
|
1278
1161
|
raise ValueError(f"Agent agent_id={agent_id} does not exist")
|
|
1279
1162
|
|
|
1280
1163
|
# Get the agent object (loaded in memory)
|
|
1281
|
-
letta_agent = self.
|
|
1164
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1282
1165
|
|
|
1283
1166
|
# iterate over records
|
|
1284
1167
|
db_iterator = letta_agent.persistence_manager.archival_memory.storage.get_all_paginated(page_size=count, offset=start)
|
|
@@ -1303,7 +1186,7 @@ class SyncServer(Server):
|
|
|
1303
1186
|
raise ValueError(f"Agent agent_id={agent_id} does not exist")
|
|
1304
1187
|
|
|
1305
1188
|
# Get the agent object (loaded in memory)
|
|
1306
|
-
letta_agent = self.
|
|
1189
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1307
1190
|
|
|
1308
1191
|
# iterate over recorde
|
|
1309
1192
|
cursor, records = letta_agent.persistence_manager.archival_memory.storage.get_all_cursor(
|
|
@@ -1318,11 +1201,15 @@ class SyncServer(Server):
|
|
|
1318
1201
|
raise ValueError(f"Agent agent_id={agent_id} does not exist")
|
|
1319
1202
|
|
|
1320
1203
|
# Get the agent object (loaded in memory)
|
|
1321
|
-
letta_agent = self.
|
|
1204
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1322
1205
|
|
|
1323
1206
|
# Insert into archival memory
|
|
1324
1207
|
passage_ids = letta_agent.persistence_manager.archival_memory.insert(memory_string=memory_contents, return_ids=True)
|
|
1325
1208
|
|
|
1209
|
+
# Update the agent
|
|
1210
|
+
# TODO: should this update the system prompt?
|
|
1211
|
+
save_agent(letta_agent, self.ms)
|
|
1212
|
+
|
|
1326
1213
|
# TODO: this is gross, fix
|
|
1327
1214
|
return [letta_agent.persistence_manager.archival_memory.storage.get(id=passage_id) for passage_id in passage_ids]
|
|
1328
1215
|
|
|
@@ -1335,7 +1222,7 @@ class SyncServer(Server):
|
|
|
1335
1222
|
# TODO: should return a passage
|
|
1336
1223
|
|
|
1337
1224
|
# Get the agent object (loaded in memory)
|
|
1338
|
-
letta_agent = self.
|
|
1225
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1339
1226
|
|
|
1340
1227
|
# Delete by ID
|
|
1341
1228
|
# TODO check if it exists first, and throw error if not
|
|
@@ -1354,9 +1241,8 @@ class SyncServer(Server):
|
|
|
1354
1241
|
order: Optional[str] = "asc",
|
|
1355
1242
|
reverse: Optional[bool] = False,
|
|
1356
1243
|
return_message_object: bool = True,
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
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,
|
|
1360
1246
|
) -> Union[List[Message], List[LettaMessage]]:
|
|
1361
1247
|
if self.user_manager.get_user_by_id(user_id=user_id) is None:
|
|
1362
1248
|
raise ValueError(f"User user_id={user_id} does not exist")
|
|
@@ -1364,7 +1250,7 @@ class SyncServer(Server):
|
|
|
1364
1250
|
raise ValueError(f"Agent agent_id={agent_id} does not exist")
|
|
1365
1251
|
|
|
1366
1252
|
# Get the agent object (loaded in memory)
|
|
1367
|
-
letta_agent = self.
|
|
1253
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1368
1254
|
|
|
1369
1255
|
# iterate over records
|
|
1370
1256
|
cursor, records = letta_agent.persistence_manager.recall_memory.storage.get_all_cursor(
|
|
@@ -1375,50 +1261,19 @@ class SyncServer(Server):
|
|
|
1375
1261
|
|
|
1376
1262
|
if not return_message_object:
|
|
1377
1263
|
# If we're GETing messages in reverse, we need to reverse the inner list (generated by to_letta_message)
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
)[::-1]
|
|
1387
|
-
]
|
|
1388
|
-
else:
|
|
1389
|
-
records = [
|
|
1390
|
-
msg
|
|
1391
|
-
for m in records
|
|
1392
|
-
for msg in m.to_letta_message(
|
|
1393
|
-
assistant_message=use_assistant_message,
|
|
1394
|
-
assistant_message_function_name=assistant_message_function_name,
|
|
1395
|
-
assistant_message_function_kwarg=assistant_message_function_kwarg,
|
|
1396
|
-
)
|
|
1397
|
-
]
|
|
1398
|
-
|
|
1399
|
-
return records
|
|
1400
|
-
|
|
1401
|
-
def get_agent_state(self, user_id: str, agent_id: Optional[str], agent_name: Optional[str] = None) -> Optional[AgentState]:
|
|
1402
|
-
"""Return the config of an agent"""
|
|
1403
|
-
user = self.user_manager.get_user_by_id(user_id=user_id)
|
|
1404
|
-
if agent_id:
|
|
1405
|
-
if self.ms.get_agent(agent_id=agent_id, user_id=user_id) is None:
|
|
1406
|
-
return None
|
|
1407
|
-
else:
|
|
1408
|
-
agent_state = self.ms.get_agent(agent_name=agent_name, user_id=user_id)
|
|
1409
|
-
if agent_state is None:
|
|
1410
|
-
raise ValueError(f"Agent agent_name={agent_name} does not exist")
|
|
1411
|
-
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
|
+
]
|
|
1412
1272
|
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
assert isinstance(letta_agent.memory, Memory)
|
|
1273
|
+
if reverse:
|
|
1274
|
+
records = records[::-1]
|
|
1416
1275
|
|
|
1417
|
-
|
|
1418
|
-
agent_state = letta_agent.agent_state.model_copy(deep=True)
|
|
1419
|
-
# Load the tags in for the agent_state
|
|
1420
|
-
agent_state.tags = self.agents_tags_manager.get_tags_for_agent(agent_id=agent_id, actor=user)
|
|
1421
|
-
return agent_state
|
|
1276
|
+
return records
|
|
1422
1277
|
|
|
1423
1278
|
def get_server_config(self, include_defaults: bool = False) -> dict:
|
|
1424
1279
|
"""Return the base config"""
|
|
@@ -1443,39 +1298,23 @@ class SyncServer(Server):
|
|
|
1443
1298
|
|
|
1444
1299
|
return response
|
|
1445
1300
|
|
|
1446
|
-
def update_agent_core_memory(self, user_id: str, agent_id: str,
|
|
1447
|
-
"""Update the
|
|
1448
|
-
if self.user_manager.get_user_by_id(user_id=user_id) is None:
|
|
1449
|
-
raise ValueError(f"User user_id={user_id} does not exist")
|
|
1450
|
-
if self.ms.get_agent(agent_id=agent_id, user_id=user_id) is None:
|
|
1451
|
-
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"""
|
|
1452
1303
|
|
|
1453
|
-
#
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
# raise ValueError(f"Key {key} not found in agent memory {list(letta_agent.memory.list_block_names())}")
|
|
1462
|
-
raise ValueError(f"Key {key} not found in agent memory {str(letta_agent.memory.memory)}")
|
|
1463
|
-
if value is None:
|
|
1464
|
-
continue
|
|
1465
|
-
if letta_agent.memory.get_block(key) != value:
|
|
1466
|
-
letta_agent.memory.update_block_value(label=key, value=value) # update agent memory
|
|
1467
|
-
modified = True
|
|
1468
|
-
|
|
1469
|
-
# If we modified the memory contents, we need to rebuild the memory block inside the system message
|
|
1470
|
-
if modified:
|
|
1471
|
-
letta_agent.rebuild_memory()
|
|
1472
|
-
# letta_agent.rebuild_memory(force=True, ms=self.ms) # This breaks unit tests in test_local_client.py
|
|
1473
|
-
# save agent
|
|
1474
|
-
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
|
+
)
|
|
1475
1312
|
|
|
1476
|
-
|
|
1313
|
+
# load agent
|
|
1314
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1315
|
+
return letta_agent.agent_state.memory
|
|
1477
1316
|
|
|
1478
|
-
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:
|
|
1479
1318
|
"""Update the name of the agent in the database"""
|
|
1480
1319
|
if self.user_manager.get_user_by_id(user_id=user_id) is None:
|
|
1481
1320
|
raise ValueError(f"User user_id={user_id} does not exist")
|
|
@@ -1483,7 +1322,7 @@ class SyncServer(Server):
|
|
|
1483
1322
|
raise ValueError(f"Agent agent_id={agent_id} does not exist")
|
|
1484
1323
|
|
|
1485
1324
|
# Get the agent object (loaded in memory)
|
|
1486
|
-
letta_agent = self.
|
|
1325
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1487
1326
|
|
|
1488
1327
|
current_name = letta_agent.agent_state.name
|
|
1489
1328
|
if current_name == new_agent_name:
|
|
@@ -1505,6 +1344,7 @@ class SyncServer(Server):
|
|
|
1505
1344
|
# TODO: REMOVE THIS ONCE WE MIGRATE AGENTMODEL TO ORM MODEL
|
|
1506
1345
|
# TODO: EVENTUALLY WE GET AUTO-DELETES WHEN WE SPECIFY RELATIONSHIPS IN THE ORM
|
|
1507
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)
|
|
1508
1348
|
|
|
1509
1349
|
if self.ms.get_agent(agent_id=agent_id, user_id=user_id) is None:
|
|
1510
1350
|
raise ValueError(f"Agent agent_id={agent_id} does not exist")
|
|
@@ -1672,9 +1512,10 @@ class SyncServer(Server):
|
|
|
1672
1512
|
raise ValueError(f"Need to provide at least source_id or source_name to find the source.")
|
|
1673
1513
|
# get connection to data source storage
|
|
1674
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"
|
|
1675
1516
|
|
|
1676
1517
|
# load agent
|
|
1677
|
-
agent = self.
|
|
1518
|
+
agent = self.load_agent(agent_id=agent_id)
|
|
1678
1519
|
|
|
1679
1520
|
# attach source to agent
|
|
1680
1521
|
agent.attach_source(data_source.id, source_connector, self.ms)
|
|
@@ -1699,7 +1540,7 @@ class SyncServer(Server):
|
|
|
1699
1540
|
source_id = source.id
|
|
1700
1541
|
|
|
1701
1542
|
# delete all Passage objects with source_id==source_id from agent's archival memory
|
|
1702
|
-
agent = self.
|
|
1543
|
+
agent = self.load_agent(agent_id=agent_id)
|
|
1703
1544
|
archival_memory = agent.persistence_manager.archival_memory
|
|
1704
1545
|
archival_memory.storage.delete({"source_id": source_id})
|
|
1705
1546
|
|
|
@@ -1778,34 +1619,43 @@ class SyncServer(Server):
|
|
|
1778
1619
|
def get_agent_message(self, agent_id: str, message_id: str) -> Optional[Message]:
|
|
1779
1620
|
"""Get a single message from the agent's memory"""
|
|
1780
1621
|
# Get the agent object (loaded in memory)
|
|
1781
|
-
letta_agent = self.
|
|
1622
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1782
1623
|
message = letta_agent.persistence_manager.recall_memory.storage.get(id=message_id)
|
|
1624
|
+
save_agent(letta_agent, self.ms)
|
|
1783
1625
|
return message
|
|
1784
1626
|
|
|
1785
1627
|
def update_agent_message(self, agent_id: str, request: UpdateMessage) -> Message:
|
|
1786
1628
|
"""Update the details of a message associated with an agent"""
|
|
1787
1629
|
|
|
1788
1630
|
# Get the current message
|
|
1789
|
-
letta_agent = self.
|
|
1790
|
-
|
|
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
|
|
1791
1635
|
|
|
1792
1636
|
def rewrite_agent_message(self, agent_id: str, new_text: str) -> Message:
|
|
1793
1637
|
|
|
1794
1638
|
# Get the current message
|
|
1795
|
-
letta_agent = self.
|
|
1796
|
-
|
|
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
|
|
1797
1643
|
|
|
1798
1644
|
def rethink_agent_message(self, agent_id: str, new_thought: str) -> Message:
|
|
1799
1645
|
|
|
1800
1646
|
# Get the current message
|
|
1801
|
-
letta_agent = self.
|
|
1802
|
-
|
|
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
|
|
1803
1651
|
|
|
1804
1652
|
def retry_agent_message(self, agent_id: str) -> List[Message]:
|
|
1805
1653
|
|
|
1806
1654
|
# Get the current message
|
|
1807
|
-
letta_agent = self.
|
|
1808
|
-
|
|
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
|
|
1809
1659
|
|
|
1810
1660
|
def get_user_or_default(self, user_id: Optional[str]) -> User:
|
|
1811
1661
|
"""Get the user object for user_id if it exists, otherwise return the default user object"""
|
|
@@ -1854,121 +1704,49 @@ class SyncServer(Server):
|
|
|
1854
1704
|
agent_id: str,
|
|
1855
1705
|
) -> ContextWindowOverview:
|
|
1856
1706
|
# Get the current message
|
|
1857
|
-
letta_agent = self.
|
|
1707
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1858
1708
|
return letta_agent.get_context_window()
|
|
1859
1709
|
|
|
1860
|
-
def update_agent_memory_label(self, user_id: str, agent_id: str, current_block_label: str, new_block_label: str) -> Memory:
|
|
1861
|
-
"""Update the label of a block in an agent's memory"""
|
|
1862
|
-
|
|
1863
|
-
# Get the user
|
|
1864
|
-
user = self.user_manager.get_user_by_id(user_id=user_id)
|
|
1865
|
-
|
|
1866
|
-
# Link a block to an agent's memory
|
|
1867
|
-
letta_agent = self._get_or_load_agent(agent_id=agent_id)
|
|
1868
|
-
letta_agent.memory.update_block_label(current_label=current_block_label, new_label=new_block_label)
|
|
1869
|
-
assert new_block_label in letta_agent.memory.list_block_labels()
|
|
1870
|
-
self.block_manager.create_or_update_block(block=letta_agent.memory.get_block(new_block_label), actor=user)
|
|
1871
|
-
|
|
1872
|
-
# check that the block was updated
|
|
1873
|
-
updated_block = self.block_manager.get_block_by_id(block_id=letta_agent.memory.get_block(new_block_label).id, actor=user)
|
|
1874
|
-
|
|
1875
|
-
# Recompile the agent memory
|
|
1876
|
-
letta_agent.rebuild_memory(force=True, ms=self.ms)
|
|
1877
|
-
|
|
1878
|
-
# save agent
|
|
1879
|
-
save_agent(letta_agent, self.ms)
|
|
1880
|
-
|
|
1881
|
-
updated_agent = self.ms.get_agent(agent_id=agent_id)
|
|
1882
|
-
if updated_agent is None:
|
|
1883
|
-
raise ValueError(f"Agent with id {agent_id} not found after linking block")
|
|
1884
|
-
assert new_block_label in updated_agent.memory.list_block_labels()
|
|
1885
|
-
assert current_block_label not in updated_agent.memory.list_block_labels()
|
|
1886
|
-
return updated_agent.memory
|
|
1887
|
-
|
|
1888
1710
|
def link_block_to_agent_memory(self, user_id: str, agent_id: str, block_id: str) -> Memory:
|
|
1889
1711
|
"""Link a block to an agent's memory"""
|
|
1890
|
-
|
|
1891
|
-
# Get the user
|
|
1892
|
-
user = self.user_manager.get_user_by_id(user_id=user_id)
|
|
1893
|
-
|
|
1894
|
-
# Get the block first
|
|
1895
|
-
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))
|
|
1896
1713
|
if block is None:
|
|
1897
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)
|
|
1898
1716
|
|
|
1899
|
-
#
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
assert block.label in letta_agent.memory.list_block_labels()
|
|
1903
|
-
|
|
1904
|
-
# Recompile the agent memory
|
|
1905
|
-
letta_agent.rebuild_memory(force=True, ms=self.ms)
|
|
1906
|
-
|
|
1907
|
-
# save agent
|
|
1908
|
-
save_agent(letta_agent, self.ms)
|
|
1909
|
-
|
|
1910
|
-
updated_agent = self.ms.get_agent(agent_id=agent_id)
|
|
1911
|
-
if updated_agent is None:
|
|
1912
|
-
raise ValueError(f"Agent with id {agent_id} not found after linking block")
|
|
1913
|
-
assert block.label in updated_agent.memory.list_block_labels()
|
|
1914
|
-
|
|
1915
|
-
return updated_agent.memory
|
|
1717
|
+
# get agent memory
|
|
1718
|
+
memory = self.load_agent(agent_id=agent_id).agent_state.memory
|
|
1719
|
+
return memory
|
|
1916
1720
|
|
|
1917
1721
|
def unlink_block_from_agent_memory(self, user_id: str, agent_id: str, block_label: str, delete_if_no_ref: bool = True) -> Memory:
|
|
1918
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)
|
|
1919
1724
|
|
|
1920
|
-
#
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
# Link a block to an agent's memory
|
|
1924
|
-
letta_agent = self._get_or_load_agent(agent_id=agent_id)
|
|
1925
|
-
unlinked_block = letta_agent.memory.unlink_block(block_label=block_label)
|
|
1926
|
-
assert unlinked_block.label not in letta_agent.memory.list_block_labels()
|
|
1927
|
-
|
|
1928
|
-
# Check if the block is linked to any other agent
|
|
1929
|
-
# TODO needs reference counting GC to handle loose blocks
|
|
1930
|
-
# block = self.block_manager.get_block_by_id(block_id=unlinked_block.id, actor=user)
|
|
1931
|
-
# if block is None:
|
|
1932
|
-
# raise ValueError(f"Block with id {block_id} not found")
|
|
1933
|
-
|
|
1934
|
-
# Recompile the agent memory
|
|
1935
|
-
letta_agent.rebuild_memory(force=True, ms=self.ms)
|
|
1936
|
-
|
|
1937
|
-
# save agent
|
|
1938
|
-
save_agent(letta_agent, self.ms)
|
|
1939
|
-
|
|
1940
|
-
updated_agent = self.ms.get_agent(agent_id=agent_id)
|
|
1941
|
-
if updated_agent is None:
|
|
1942
|
-
raise ValueError(f"Agent with id {agent_id} not found after linking block")
|
|
1943
|
-
assert unlinked_block.label not in updated_agent.memory.list_block_labels()
|
|
1944
|
-
return updated_agent.memory
|
|
1725
|
+
# get agent memory
|
|
1726
|
+
memory = self.load_agent(agent_id=agent_id).agent_state.memory
|
|
1727
|
+
return memory
|
|
1945
1728
|
|
|
1946
1729
|
def update_agent_memory_limit(self, user_id: str, agent_id: str, block_label: str, limit: int) -> Memory:
|
|
1947
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
|
+
)
|
|
1948
1744
|
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
# write out the update the database
|
|
1958
|
-
self.block_manager.create_or_update_block(block=letta_agent.memory.get_block(block_label), actor=user)
|
|
1959
|
-
|
|
1960
|
-
# check that the block was updated
|
|
1961
|
-
updated_block = self.block_manager.get_block_by_id(block_id=letta_agent.memory.get_block(block_label).id, actor=user)
|
|
1962
|
-
assert updated_block and updated_block.limit == limit
|
|
1963
|
-
|
|
1964
|
-
# Recompile the agent memory
|
|
1965
|
-
letta_agent.rebuild_memory(force=True, ms=self.ms)
|
|
1966
|
-
|
|
1967
|
-
# save agent
|
|
1968
|
-
save_agent(letta_agent, self.ms)
|
|
1969
|
-
|
|
1970
|
-
updated_agent = self.ms.get_agent(agent_id=agent_id)
|
|
1971
|
-
if updated_agent is None:
|
|
1972
|
-
raise ValueError(f"Agent with id {agent_id} not found after linking block")
|
|
1973
|
-
assert updated_agent.memory.get_block(label=block_label).limit == limit
|
|
1974
|
-
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
|