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/client/client.py
CHANGED
|
@@ -5,20 +5,17 @@ from typing import Callable, Dict, Generator, List, Optional, Union
|
|
|
5
5
|
import requests
|
|
6
6
|
|
|
7
7
|
import letta.utils
|
|
8
|
-
from letta.constants import
|
|
8
|
+
from letta.constants import (
|
|
9
|
+
ADMIN_PREFIX,
|
|
10
|
+
BASE_MEMORY_TOOLS,
|
|
11
|
+
BASE_TOOLS,
|
|
12
|
+
DEFAULT_HUMAN,
|
|
13
|
+
DEFAULT_PERSONA,
|
|
14
|
+
)
|
|
9
15
|
from letta.data_sources.connectors import DataConnector
|
|
10
16
|
from letta.functions.functions import parse_source_code
|
|
11
|
-
from letta.memory import get_memory_functions
|
|
12
17
|
from letta.schemas.agent import AgentState, AgentType, CreateAgent, UpdateAgentState
|
|
13
|
-
from letta.schemas.block import
|
|
14
|
-
Block,
|
|
15
|
-
BlockCreate,
|
|
16
|
-
BlockUpdate,
|
|
17
|
-
Human,
|
|
18
|
-
Persona,
|
|
19
|
-
UpdateHuman,
|
|
20
|
-
UpdatePersona,
|
|
21
|
-
)
|
|
18
|
+
from letta.schemas.block import Block, BlockUpdate, CreateBlock, Human, Persona
|
|
22
19
|
from letta.schemas.embedding_config import EmbeddingConfig
|
|
23
20
|
|
|
24
21
|
# new schemas
|
|
@@ -82,7 +79,7 @@ class AbstractClient(object):
|
|
|
82
79
|
agent_type: Optional[AgentType] = AgentType.memgpt_agent,
|
|
83
80
|
embedding_config: Optional[EmbeddingConfig] = None,
|
|
84
81
|
llm_config: Optional[LLMConfig] = None,
|
|
85
|
-
memory
|
|
82
|
+
memory=None,
|
|
86
83
|
system: Optional[str] = None,
|
|
87
84
|
tools: Optional[List[str]] = None,
|
|
88
85
|
tool_rules: Optional[List[BaseToolRule]] = None,
|
|
@@ -154,11 +151,10 @@ class AbstractClient(object):
|
|
|
154
151
|
stream: Optional[bool] = False,
|
|
155
152
|
stream_steps: bool = False,
|
|
156
153
|
stream_tokens: bool = False,
|
|
157
|
-
include_full_message: Optional[bool] = False,
|
|
158
154
|
) -> LettaResponse:
|
|
159
155
|
raise NotImplementedError
|
|
160
156
|
|
|
161
|
-
def user_message(self, agent_id: str, message: str
|
|
157
|
+
def user_message(self, agent_id: str, message: str) -> LettaResponse:
|
|
162
158
|
raise NotImplementedError
|
|
163
159
|
|
|
164
160
|
def create_human(self, name: str, text: str) -> Human:
|
|
@@ -523,27 +519,13 @@ class RESTClient(AbstractClient):
|
|
|
523
519
|
Returns:
|
|
524
520
|
agent_state (AgentState): State of the created agent
|
|
525
521
|
"""
|
|
526
|
-
|
|
527
|
-
# TODO: implement this check once name lookup works
|
|
528
|
-
# if name:
|
|
529
|
-
# exist_agent_id = self.get_agent_id(agent_name=name)
|
|
530
|
-
|
|
531
|
-
# raise ValueError(f"Agent with name {name} already exists")
|
|
532
|
-
|
|
533
|
-
# construct list of tools
|
|
534
522
|
tool_names = []
|
|
535
523
|
if tools:
|
|
536
524
|
tool_names += tools
|
|
537
525
|
if include_base_tools:
|
|
538
526
|
tool_names += BASE_TOOLS
|
|
527
|
+
tool_names += BASE_MEMORY_TOOLS
|
|
539
528
|
|
|
540
|
-
# add memory tools
|
|
541
|
-
memory_functions = get_memory_functions(memory)
|
|
542
|
-
for func_name, func in memory_functions.items():
|
|
543
|
-
tool = self.create_or_update_tool(func, name=func_name, tags=["memory", "letta-base"])
|
|
544
|
-
tool_names.append(tool.name)
|
|
545
|
-
|
|
546
|
-
# check if default configs are provided
|
|
547
529
|
assert embedding_config or self._default_embedding_config, f"Embedding config must be provided"
|
|
548
530
|
assert llm_config or self._default_llm_config, f"LLM config must be provided"
|
|
549
531
|
|
|
@@ -552,7 +534,7 @@ class RESTClient(AbstractClient):
|
|
|
552
534
|
name=name,
|
|
553
535
|
description=description,
|
|
554
536
|
metadata_=metadata,
|
|
555
|
-
|
|
537
|
+
memory_blocks=[],
|
|
556
538
|
tools=tool_names,
|
|
557
539
|
tool_rules=tool_rules,
|
|
558
540
|
system=system,
|
|
@@ -574,7 +556,20 @@ class RESTClient(AbstractClient):
|
|
|
574
556
|
|
|
575
557
|
if response.status_code != 200:
|
|
576
558
|
raise ValueError(f"Status {response.status_code} - Failed to create agent: {response.text}")
|
|
577
|
-
|
|
559
|
+
|
|
560
|
+
# gather agent state
|
|
561
|
+
agent_state = AgentState(**response.json())
|
|
562
|
+
|
|
563
|
+
# create and link blocks
|
|
564
|
+
for block in memory.get_blocks():
|
|
565
|
+
if not self.get_block(block.id):
|
|
566
|
+
# note: this does not update existing blocks
|
|
567
|
+
# WARNING: this resets the block ID - this method is a hack for backwards compat, should eventually use CreateBlock not Memory
|
|
568
|
+
block = self.create_block(label=block.label, value=block.value, limit=block.limit)
|
|
569
|
+
self.link_agent_memory_block(agent_id=agent_state.id, block_id=block.id)
|
|
570
|
+
|
|
571
|
+
# refresh and return agent
|
|
572
|
+
return self.get_agent(agent_state.id)
|
|
578
573
|
|
|
579
574
|
def update_message(
|
|
580
575
|
self,
|
|
@@ -607,12 +602,11 @@ class RESTClient(AbstractClient):
|
|
|
607
602
|
name: Optional[str] = None,
|
|
608
603
|
description: Optional[str] = None,
|
|
609
604
|
system: Optional[str] = None,
|
|
610
|
-
|
|
605
|
+
tool_names: Optional[List[str]] = None,
|
|
611
606
|
metadata: Optional[Dict] = None,
|
|
612
607
|
llm_config: Optional[LLMConfig] = None,
|
|
613
608
|
embedding_config: Optional[EmbeddingConfig] = None,
|
|
614
609
|
message_ids: Optional[List[str]] = None,
|
|
615
|
-
memory: Optional[Memory] = None,
|
|
616
610
|
tags: Optional[List[str]] = None,
|
|
617
611
|
):
|
|
618
612
|
"""
|
|
@@ -623,12 +617,11 @@ class RESTClient(AbstractClient):
|
|
|
623
617
|
name (str): Name of the agent
|
|
624
618
|
description (str): Description of the agent
|
|
625
619
|
system (str): System configuration
|
|
626
|
-
|
|
620
|
+
tool_names (List[str]): List of tools
|
|
627
621
|
metadata (Dict): Metadata
|
|
628
622
|
llm_config (LLMConfig): LLM configuration
|
|
629
623
|
embedding_config (EmbeddingConfig): Embedding configuration
|
|
630
624
|
message_ids (List[str]): List of message IDs
|
|
631
|
-
memory (Memory): Memory configuration
|
|
632
625
|
tags (List[str]): Tags for filtering agents
|
|
633
626
|
|
|
634
627
|
Returns:
|
|
@@ -638,14 +631,13 @@ class RESTClient(AbstractClient):
|
|
|
638
631
|
id=agent_id,
|
|
639
632
|
name=name,
|
|
640
633
|
system=system,
|
|
641
|
-
|
|
634
|
+
tool_names=tool_names,
|
|
642
635
|
tags=tags,
|
|
643
636
|
description=description,
|
|
644
637
|
metadata_=metadata,
|
|
645
638
|
llm_config=llm_config,
|
|
646
639
|
embedding_config=embedding_config,
|
|
647
640
|
message_ids=message_ids,
|
|
648
|
-
memory=memory,
|
|
649
641
|
)
|
|
650
642
|
response = requests.patch(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}", json=request.model_dump(), headers=self.headers)
|
|
651
643
|
if response.status_code != 200:
|
|
@@ -839,7 +831,7 @@ class RESTClient(AbstractClient):
|
|
|
839
831
|
|
|
840
832
|
# agent interactions
|
|
841
833
|
|
|
842
|
-
def user_message(self, agent_id: str, message: str
|
|
834
|
+
def user_message(self, agent_id: str, message: str) -> LettaResponse:
|
|
843
835
|
"""
|
|
844
836
|
Send a message to an agent as a user
|
|
845
837
|
|
|
@@ -850,7 +842,7 @@ class RESTClient(AbstractClient):
|
|
|
850
842
|
Returns:
|
|
851
843
|
response (LettaResponse): Response from the agent
|
|
852
844
|
"""
|
|
853
|
-
return self.send_message(agent_id, message, role="user"
|
|
845
|
+
return self.send_message(agent_id=agent_id, message=message, role="user")
|
|
854
846
|
|
|
855
847
|
def save(self):
|
|
856
848
|
raise NotImplementedError
|
|
@@ -937,13 +929,13 @@ class RESTClient(AbstractClient):
|
|
|
937
929
|
|
|
938
930
|
def send_message(
|
|
939
931
|
self,
|
|
940
|
-
agent_id: str,
|
|
941
932
|
message: str,
|
|
942
933
|
role: str,
|
|
934
|
+
agent_id: Optional[str] = None,
|
|
943
935
|
name: Optional[str] = None,
|
|
936
|
+
stream: Optional[bool] = False,
|
|
944
937
|
stream_steps: bool = False,
|
|
945
938
|
stream_tokens: bool = False,
|
|
946
|
-
include_full_message: bool = False,
|
|
947
939
|
) -> Union[LettaResponse, Generator[LettaStreamingResponse, None, None]]:
|
|
948
940
|
"""
|
|
949
941
|
Send a message to an agent
|
|
@@ -964,17 +956,11 @@ class RESTClient(AbstractClient):
|
|
|
964
956
|
# TODO: figure out how to handle stream_steps and stream_tokens
|
|
965
957
|
|
|
966
958
|
# When streaming steps is True, stream_tokens must be False
|
|
967
|
-
request = LettaRequest(
|
|
968
|
-
messages=messages,
|
|
969
|
-
stream_steps=stream_steps,
|
|
970
|
-
stream_tokens=stream_tokens,
|
|
971
|
-
return_message_object=include_full_message,
|
|
972
|
-
)
|
|
959
|
+
request = LettaRequest(messages=messages)
|
|
973
960
|
if stream_tokens or stream_steps:
|
|
974
961
|
from letta.client.streaming import _sse_post
|
|
975
962
|
|
|
976
|
-
request.
|
|
977
|
-
return _sse_post(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/messages", request.model_dump(), self.headers)
|
|
963
|
+
return _sse_post(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/messages/stream", request.model_dump(), self.headers)
|
|
978
964
|
else:
|
|
979
965
|
response = requests.post(
|
|
980
966
|
f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/messages", json=request.model_dump(), headers=self.headers
|
|
@@ -1008,8 +994,12 @@ class RESTClient(AbstractClient):
|
|
|
1008
994
|
else:
|
|
1009
995
|
return [Block(**block) for block in response.json()]
|
|
1010
996
|
|
|
1011
|
-
def create_block(
|
|
1012
|
-
|
|
997
|
+
def create_block(
|
|
998
|
+
self, label: str, value: str, limit: Optional[int] = None, template_name: Optional[str] = None, is_template: bool = False
|
|
999
|
+
) -> Block: #
|
|
1000
|
+
request = CreateBlock(label=label, value=value, template=is_template, template_name=template_name)
|
|
1001
|
+
if limit:
|
|
1002
|
+
request.limit = limit
|
|
1013
1003
|
response = requests.post(f"{self.base_url}/{self.api_prefix}/blocks", json=request.model_dump(), headers=self.headers)
|
|
1014
1004
|
if response.status_code != 200:
|
|
1015
1005
|
raise ValueError(f"Failed to create block: {response.text}")
|
|
@@ -1779,21 +1769,32 @@ class RESTClient(AbstractClient):
|
|
|
1779
1769
|
raise ValueError(f"Failed to list environment variables for sandbox config ID '{sandbox_config_id}': {response.text}")
|
|
1780
1770
|
return [SandboxEnvironmentVariable(**var_data) for var_data in response.json()]
|
|
1781
1771
|
|
|
1782
|
-
def
|
|
1772
|
+
def update_agent_memory_block_label(self, agent_id: str, current_label: str, new_label: str) -> Memory:
|
|
1773
|
+
"""Rename a block in the agent's core memory
|
|
1783
1774
|
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1775
|
+
Args:
|
|
1776
|
+
agent_id (str): The agent ID
|
|
1777
|
+
current_label (str): The current label of the block
|
|
1778
|
+
new_label (str): The new label of the block
|
|
1779
|
+
|
|
1780
|
+
Returns:
|
|
1781
|
+
memory (Memory): The updated memory
|
|
1782
|
+
"""
|
|
1783
|
+
block = self.get_agent_memory_block(agent_id, current_label)
|
|
1784
|
+
return self.update_block(block.id, label=new_label)
|
|
1785
|
+
|
|
1786
|
+
# TODO: remove this
|
|
1787
|
+
def add_agent_memory_block(self, agent_id: str, create_block: CreateBlock) -> Memory:
|
|
1788
|
+
"""
|
|
1789
|
+
Create and link a memory block to an agent's core memory
|
|
1793
1790
|
|
|
1794
|
-
|
|
1791
|
+
Args:
|
|
1792
|
+
agent_id (str): The agent ID
|
|
1793
|
+
create_block (CreateBlock): The block to create
|
|
1795
1794
|
|
|
1796
|
-
|
|
1795
|
+
Returns:
|
|
1796
|
+
memory (Memory): The updated memory
|
|
1797
|
+
"""
|
|
1797
1798
|
response = requests.post(
|
|
1798
1799
|
f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/memory/block",
|
|
1799
1800
|
headers=self.headers,
|
|
@@ -1803,9 +1804,38 @@ class RESTClient(AbstractClient):
|
|
|
1803
1804
|
raise ValueError(f"Failed to add agent memory block: {response.text}")
|
|
1804
1805
|
return Memory(**response.json())
|
|
1805
1806
|
|
|
1807
|
+
def link_agent_memory_block(self, agent_id: str, block_id: str) -> Memory:
|
|
1808
|
+
"""
|
|
1809
|
+
Link a block to an agent's core memory
|
|
1810
|
+
|
|
1811
|
+
Args:
|
|
1812
|
+
agent_id (str): The agent ID
|
|
1813
|
+
block_id (str): The block ID
|
|
1814
|
+
|
|
1815
|
+
Returns:
|
|
1816
|
+
memory (Memory): The updated memory
|
|
1817
|
+
"""
|
|
1818
|
+
params = {"agent_id": agent_id}
|
|
1819
|
+
response = requests.patch(
|
|
1820
|
+
f"{self.base_url}/{self.api_prefix}/blocks/{block_id}/attach",
|
|
1821
|
+
params=params,
|
|
1822
|
+
headers=self.headers,
|
|
1823
|
+
)
|
|
1824
|
+
if response.status_code != 200:
|
|
1825
|
+
raise ValueError(f"Failed to link agent memory block: {response.text}")
|
|
1826
|
+
return Block(**response.json())
|
|
1827
|
+
|
|
1806
1828
|
def remove_agent_memory_block(self, agent_id: str, block_label: str) -> Memory:
|
|
1829
|
+
"""
|
|
1830
|
+
Unlike a block from the agent's core memory
|
|
1807
1831
|
|
|
1808
|
-
|
|
1832
|
+
Args:
|
|
1833
|
+
agent_id (str): The agent ID
|
|
1834
|
+
block_label (str): The block label
|
|
1835
|
+
|
|
1836
|
+
Returns:
|
|
1837
|
+
memory (Memory): The updated memory
|
|
1838
|
+
"""
|
|
1809
1839
|
response = requests.delete(
|
|
1810
1840
|
f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/memory/block/{block_label}",
|
|
1811
1841
|
headers=self.headers,
|
|
@@ -1814,17 +1844,108 @@ class RESTClient(AbstractClient):
|
|
|
1814
1844
|
raise ValueError(f"Failed to remove agent memory block: {response.text}")
|
|
1815
1845
|
return Memory(**response.json())
|
|
1816
1846
|
|
|
1817
|
-
def
|
|
1847
|
+
def get_agent_memory_blocks(self, agent_id: str) -> List[Block]:
|
|
1848
|
+
"""
|
|
1849
|
+
Get all the blocks in the agent's core memory
|
|
1818
1850
|
|
|
1819
|
-
|
|
1851
|
+
Args:
|
|
1852
|
+
agent_id (str): The agent ID
|
|
1853
|
+
|
|
1854
|
+
Returns:
|
|
1855
|
+
blocks (List[Block]): The blocks in the agent's core memory
|
|
1856
|
+
"""
|
|
1857
|
+
response = requests.get(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/memory/block", headers=self.headers)
|
|
1858
|
+
if response.status_code != 200:
|
|
1859
|
+
raise ValueError(f"Failed to get agent memory blocks: {response.text}")
|
|
1860
|
+
return [Block(**block) for block in response.json()]
|
|
1861
|
+
|
|
1862
|
+
def get_agent_memory_block(self, agent_id: str, label: str) -> Block:
|
|
1863
|
+
"""
|
|
1864
|
+
Get a block in the agent's core memory by its label
|
|
1865
|
+
|
|
1866
|
+
Args:
|
|
1867
|
+
agent_id (str): The agent ID
|
|
1868
|
+
label (str): The label in the agent's core memory
|
|
1869
|
+
|
|
1870
|
+
Returns:
|
|
1871
|
+
block (Block): The block corresponding to the label
|
|
1872
|
+
"""
|
|
1873
|
+
response = requests.get(
|
|
1874
|
+
f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/memory/block/{label}",
|
|
1875
|
+
headers=self.headers,
|
|
1876
|
+
)
|
|
1877
|
+
if response.status_code != 200:
|
|
1878
|
+
raise ValueError(f"Failed to get agent memory block: {response.text}")
|
|
1879
|
+
return Block(**response.json())
|
|
1880
|
+
|
|
1881
|
+
def update_agent_memory_block(
|
|
1882
|
+
self,
|
|
1883
|
+
agent_id: str,
|
|
1884
|
+
label: str,
|
|
1885
|
+
value: Optional[str] = None,
|
|
1886
|
+
limit: Optional[int] = None,
|
|
1887
|
+
):
|
|
1888
|
+
"""
|
|
1889
|
+
Update a block in the agent's core memory by specifying its label
|
|
1890
|
+
|
|
1891
|
+
Args:
|
|
1892
|
+
agent_id (str): The agent ID
|
|
1893
|
+
label (str): The label of the block
|
|
1894
|
+
value (str): The new value of the block
|
|
1895
|
+
limit (int): The new limit of the block
|
|
1896
|
+
|
|
1897
|
+
Returns:
|
|
1898
|
+
block (Block): The updated block
|
|
1899
|
+
"""
|
|
1900
|
+
# setup data
|
|
1901
|
+
data = {}
|
|
1902
|
+
if value:
|
|
1903
|
+
data["value"] = value
|
|
1904
|
+
if limit:
|
|
1905
|
+
data["limit"] = limit
|
|
1820
1906
|
response = requests.patch(
|
|
1821
|
-
f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/memory/
|
|
1907
|
+
f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/memory/block/{label}",
|
|
1822
1908
|
headers=self.headers,
|
|
1823
|
-
json=
|
|
1909
|
+
json=data,
|
|
1824
1910
|
)
|
|
1825
1911
|
if response.status_code != 200:
|
|
1826
|
-
raise ValueError(f"Failed to update agent memory
|
|
1827
|
-
return
|
|
1912
|
+
raise ValueError(f"Failed to update agent memory block: {response.text}")
|
|
1913
|
+
return Block(**response.json())
|
|
1914
|
+
|
|
1915
|
+
def update_block(
|
|
1916
|
+
self,
|
|
1917
|
+
block_id: str,
|
|
1918
|
+
label: Optional[str] = None,
|
|
1919
|
+
value: Optional[str] = None,
|
|
1920
|
+
limit: Optional[int] = None,
|
|
1921
|
+
):
|
|
1922
|
+
"""
|
|
1923
|
+
Update a block given the ID with the provided fields
|
|
1924
|
+
|
|
1925
|
+
Args:
|
|
1926
|
+
block_id (str): ID of the block
|
|
1927
|
+
label (str): Label to assign to the block
|
|
1928
|
+
value (str): Value to assign to the block
|
|
1929
|
+
limit (int): Token limit to assign to the block
|
|
1930
|
+
|
|
1931
|
+
Returns:
|
|
1932
|
+
block (Block): Updated block
|
|
1933
|
+
"""
|
|
1934
|
+
data = {}
|
|
1935
|
+
if value:
|
|
1936
|
+
data["value"] = value
|
|
1937
|
+
if limit:
|
|
1938
|
+
data["limit"] = limit
|
|
1939
|
+
if label:
|
|
1940
|
+
data["label"] = label
|
|
1941
|
+
response = requests.patch(
|
|
1942
|
+
f"{self.base_url}/{self.api_prefix}/blocks/{block_id}",
|
|
1943
|
+
headers=self.headers,
|
|
1944
|
+
json=data,
|
|
1945
|
+
)
|
|
1946
|
+
if response.status_code != 200:
|
|
1947
|
+
raise ValueError(f"Failed to update block: {response.text}")
|
|
1948
|
+
return Block(**response.json())
|
|
1828
1949
|
|
|
1829
1950
|
|
|
1830
1951
|
class LocalClient(AbstractClient):
|
|
@@ -1923,6 +2044,11 @@ class LocalClient(AbstractClient):
|
|
|
1923
2044
|
llm_config: LLMConfig = None,
|
|
1924
2045
|
# memory
|
|
1925
2046
|
memory: Memory = ChatMemory(human=get_human_text(DEFAULT_HUMAN), persona=get_persona_text(DEFAULT_PERSONA)),
|
|
2047
|
+
# TODO: change to this when we are ready to migrate all the tests/examples (matches the REST API)
|
|
2048
|
+
# memory_blocks=[
|
|
2049
|
+
# {"label": "human", "value": get_human_text(DEFAULT_HUMAN), "limit": 5000},
|
|
2050
|
+
# {"label": "persona", "value": get_persona_text(DEFAULT_PERSONA), "limit": 5000},
|
|
2051
|
+
# ],
|
|
1926
2052
|
# system
|
|
1927
2053
|
system: Optional[str] = None,
|
|
1928
2054
|
# tools
|
|
@@ -1941,7 +2067,7 @@ class LocalClient(AbstractClient):
|
|
|
1941
2067
|
name (str): Name of the agent
|
|
1942
2068
|
embedding_config (EmbeddingConfig): Embedding configuration
|
|
1943
2069
|
llm_config (LLMConfig): LLM configuration
|
|
1944
|
-
|
|
2070
|
+
memory_blocks (List[Dict]): List of configurations for the memory blocks (placed in core-memory)
|
|
1945
2071
|
system (str): System configuration
|
|
1946
2072
|
tools (List[str]): List of tools
|
|
1947
2073
|
tool_rules (Optional[List[BaseToolRule]]): List of tool rules
|
|
@@ -1963,14 +2089,7 @@ class LocalClient(AbstractClient):
|
|
|
1963
2089
|
tool_names += tools
|
|
1964
2090
|
if include_base_tools:
|
|
1965
2091
|
tool_names += BASE_TOOLS
|
|
1966
|
-
|
|
1967
|
-
# add memory tools
|
|
1968
|
-
memory_functions = get_memory_functions(memory)
|
|
1969
|
-
for func_name, func in memory_functions.items():
|
|
1970
|
-
tool = self.create_or_update_tool(func, name=func_name, tags=["memory", "letta-base"])
|
|
1971
|
-
tool_names.append(tool.name)
|
|
1972
|
-
|
|
1973
|
-
self.interface.clear()
|
|
2092
|
+
tool_names += BASE_MEMORY_TOOLS
|
|
1974
2093
|
|
|
1975
2094
|
# check if default configs are provided
|
|
1976
2095
|
assert embedding_config or self._default_embedding_config, f"Embedding config must be provided"
|
|
@@ -1982,7 +2101,10 @@ class LocalClient(AbstractClient):
|
|
|
1982
2101
|
name=name,
|
|
1983
2102
|
description=description,
|
|
1984
2103
|
metadata_=metadata,
|
|
1985
|
-
memory=memory,
|
|
2104
|
+
# memory=memory,
|
|
2105
|
+
memory_blocks=[],
|
|
2106
|
+
# memory_blocks = memory.get_blocks(),
|
|
2107
|
+
# memory_tools=memory_tools,
|
|
1986
2108
|
tools=tool_names,
|
|
1987
2109
|
tool_rules=tool_rules,
|
|
1988
2110
|
system=system,
|
|
@@ -1994,7 +2116,18 @@ class LocalClient(AbstractClient):
|
|
|
1994
2116
|
),
|
|
1995
2117
|
actor=self.user,
|
|
1996
2118
|
)
|
|
1997
|
-
|
|
2119
|
+
|
|
2120
|
+
# TODO: remove when we fully migrate to block creation CreateAgent model
|
|
2121
|
+
# Link additional blocks to the agent (block ids created on the client)
|
|
2122
|
+
# This needs to happen since the create agent does not allow passing in blocks which have already been persisted and have an ID
|
|
2123
|
+
# So we create the agent and then link the blocks afterwards
|
|
2124
|
+
user = self.server.get_user_or_default(self.user_id)
|
|
2125
|
+
for block in memory.get_blocks():
|
|
2126
|
+
self.server.block_manager.create_or_update_block(block, actor=user)
|
|
2127
|
+
self.server.link_block_to_agent_memory(user_id=self.user_id, agent_id=agent_state.id, block_id=block.id)
|
|
2128
|
+
|
|
2129
|
+
# TODO: get full agent state
|
|
2130
|
+
return self.server.get_agent(agent_state.id)
|
|
1998
2131
|
|
|
1999
2132
|
def update_message(
|
|
2000
2133
|
self,
|
|
@@ -2031,7 +2164,6 @@ class LocalClient(AbstractClient):
|
|
|
2031
2164
|
llm_config: Optional[LLMConfig] = None,
|
|
2032
2165
|
embedding_config: Optional[EmbeddingConfig] = None,
|
|
2033
2166
|
message_ids: Optional[List[str]] = None,
|
|
2034
|
-
memory: Optional[Memory] = None,
|
|
2035
2167
|
):
|
|
2036
2168
|
"""
|
|
2037
2169
|
Update an existing agent
|
|
@@ -2046,26 +2178,25 @@ class LocalClient(AbstractClient):
|
|
|
2046
2178
|
llm_config (LLMConfig): LLM configuration
|
|
2047
2179
|
embedding_config (EmbeddingConfig): Embedding configuration
|
|
2048
2180
|
message_ids (List[str]): List of message IDs
|
|
2049
|
-
memory (Memory): Memory configuration
|
|
2050
2181
|
tags (List[str]): Tags for filtering agents
|
|
2051
2182
|
|
|
2052
2183
|
Returns:
|
|
2053
2184
|
agent_state (AgentState): State of the updated agent
|
|
2054
2185
|
"""
|
|
2186
|
+
# TODO: add the abilitty to reset linked block_ids
|
|
2055
2187
|
self.interface.clear()
|
|
2056
2188
|
agent_state = self.server.update_agent(
|
|
2057
2189
|
UpdateAgentState(
|
|
2058
2190
|
id=agent_id,
|
|
2059
2191
|
name=name,
|
|
2060
2192
|
system=system,
|
|
2061
|
-
|
|
2193
|
+
tool_names=tools,
|
|
2062
2194
|
tags=tags,
|
|
2063
2195
|
description=description,
|
|
2064
2196
|
metadata_=metadata,
|
|
2065
2197
|
llm_config=llm_config,
|
|
2066
2198
|
embedding_config=embedding_config,
|
|
2067
2199
|
message_ids=message_ids,
|
|
2068
|
-
memory=memory,
|
|
2069
2200
|
),
|
|
2070
2201
|
actor=self.user,
|
|
2071
2202
|
)
|
|
@@ -2204,7 +2335,7 @@ class LocalClient(AbstractClient):
|
|
|
2204
2335
|
|
|
2205
2336
|
"""
|
|
2206
2337
|
# TODO: implement this (not sure what it should look like)
|
|
2207
|
-
memory = self.server.update_agent_core_memory(user_id=self.user_id, agent_id=agent_id,
|
|
2338
|
+
memory = self.server.update_agent_core_memory(user_id=self.user_id, agent_id=agent_id, label=section, value=value)
|
|
2208
2339
|
return memory
|
|
2209
2340
|
|
|
2210
2341
|
def get_archival_memory_summary(self, agent_id: str) -> ArchivalMemorySummary:
|
|
@@ -2250,7 +2381,6 @@ class LocalClient(AbstractClient):
|
|
|
2250
2381
|
self,
|
|
2251
2382
|
agent_id: str,
|
|
2252
2383
|
messages: List[Union[Message | MessageCreate]],
|
|
2253
|
-
include_full_message: Optional[bool] = False,
|
|
2254
2384
|
):
|
|
2255
2385
|
"""
|
|
2256
2386
|
Send pre-packed messages to an agent.
|
|
@@ -2270,15 +2400,7 @@ class LocalClient(AbstractClient):
|
|
|
2270
2400
|
self.save()
|
|
2271
2401
|
|
|
2272
2402
|
# format messages
|
|
2273
|
-
messages =
|
|
2274
|
-
if include_full_message:
|
|
2275
|
-
letta_messages = messages
|
|
2276
|
-
else:
|
|
2277
|
-
letta_messages = []
|
|
2278
|
-
for m in messages:
|
|
2279
|
-
letta_messages += m.to_letta_message()
|
|
2280
|
-
|
|
2281
|
-
return LettaResponse(messages=letta_messages, usage=usage)
|
|
2403
|
+
return LettaResponse(messages=messages, usage=usage)
|
|
2282
2404
|
|
|
2283
2405
|
def send_message(
|
|
2284
2406
|
self,
|
|
@@ -2289,7 +2411,6 @@ class LocalClient(AbstractClient):
|
|
|
2289
2411
|
agent_name: Optional[str] = None,
|
|
2290
2412
|
stream_steps: bool = False,
|
|
2291
2413
|
stream_tokens: bool = False,
|
|
2292
|
-
include_full_message: Optional[bool] = False,
|
|
2293
2414
|
) -> LettaResponse:
|
|
2294
2415
|
"""
|
|
2295
2416
|
Send a message to an agent
|
|
@@ -2338,16 +2459,13 @@ class LocalClient(AbstractClient):
|
|
|
2338
2459
|
|
|
2339
2460
|
# format messages
|
|
2340
2461
|
messages = self.interface.to_list()
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
letta_messages = []
|
|
2345
|
-
for m in messages:
|
|
2346
|
-
letta_messages += m.to_letta_message()
|
|
2462
|
+
letta_messages = []
|
|
2463
|
+
for m in messages:
|
|
2464
|
+
letta_messages += m.to_letta_message()
|
|
2347
2465
|
|
|
2348
2466
|
return LettaResponse(messages=letta_messages, usage=usage)
|
|
2349
2467
|
|
|
2350
|
-
def user_message(self, agent_id: str, message: str
|
|
2468
|
+
def user_message(self, agent_id: str, message: str) -> LettaResponse:
|
|
2351
2469
|
"""
|
|
2352
2470
|
Send a message to an agent as a user
|
|
2353
2471
|
|
|
@@ -2359,7 +2477,7 @@ class LocalClient(AbstractClient):
|
|
|
2359
2477
|
response (LettaResponse): Response from the agent
|
|
2360
2478
|
"""
|
|
2361
2479
|
self.interface.clear()
|
|
2362
|
-
return self.send_message(role="user", agent_id=agent_id, message=message
|
|
2480
|
+
return self.send_message(role="user", agent_id=agent_id, message=message)
|
|
2363
2481
|
|
|
2364
2482
|
def run_command(self, agent_id: str, command: str) -> LettaResponse:
|
|
2365
2483
|
"""
|
|
@@ -2951,7 +3069,6 @@ class LocalClient(AbstractClient):
|
|
|
2951
3069
|
after=after,
|
|
2952
3070
|
limit=limit,
|
|
2953
3071
|
reverse=True,
|
|
2954
|
-
return_message_object=True,
|
|
2955
3072
|
)
|
|
2956
3073
|
|
|
2957
3074
|
def list_blocks(self, label: Optional[str] = None, templates_only: Optional[bool] = True) -> List[Block]:
|
|
@@ -2967,7 +3084,9 @@ class LocalClient(AbstractClient):
|
|
|
2967
3084
|
"""
|
|
2968
3085
|
return self.server.block_manager.get_blocks(actor=self.user, label=label, is_template=templates_only)
|
|
2969
3086
|
|
|
2970
|
-
def create_block(
|
|
3087
|
+
def create_block(
|
|
3088
|
+
self, label: str, value: str, limit: Optional[int] = None, template_name: Optional[str] = None, is_template: bool = False
|
|
3089
|
+
) -> Block: #
|
|
2971
3090
|
"""
|
|
2972
3091
|
Create a block
|
|
2973
3092
|
|
|
@@ -2975,13 +3094,15 @@ class LocalClient(AbstractClient):
|
|
|
2975
3094
|
label (str): Label of the block
|
|
2976
3095
|
name (str): Name of the block
|
|
2977
3096
|
text (str): Text of the block
|
|
3097
|
+
limit (int): Character of the block
|
|
2978
3098
|
|
|
2979
3099
|
Returns:
|
|
2980
3100
|
block (Block): Created block
|
|
2981
3101
|
"""
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
3102
|
+
block = Block(label=label, template_name=template_name, value=value, is_template=is_template)
|
|
3103
|
+
if limit:
|
|
3104
|
+
block.limit = limit
|
|
3105
|
+
return self.server.block_manager.create_or_update_block(block, actor=self.user)
|
|
2985
3106
|
|
|
2986
3107
|
def update_block(self, block_id: str, name: Optional[str] = None, text: Optional[str] = None, limit: Optional[int] = None) -> Block:
|
|
2987
3108
|
"""
|
|
@@ -3136,20 +3257,142 @@ class LocalClient(AbstractClient):
|
|
|
3136
3257
|
sandbox_config_id=sandbox_config_id, actor=self.user, limit=limit, cursor=cursor
|
|
3137
3258
|
)
|
|
3138
3259
|
|
|
3139
|
-
def
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3260
|
+
def update_agent_memory_block_label(self, agent_id: str, current_label: str, new_label: str) -> Memory:
|
|
3261
|
+
"""Rename a block in the agent's core memory
|
|
3262
|
+
|
|
3263
|
+
Args:
|
|
3264
|
+
agent_id (str): The agent ID
|
|
3265
|
+
current_label (str): The current label of the block
|
|
3266
|
+
new_label (str): The new label of the block
|
|
3267
|
+
|
|
3268
|
+
Returns:
|
|
3269
|
+
memory (Memory): The updated memory
|
|
3270
|
+
"""
|
|
3271
|
+
block = self.get_agent_memory_block(agent_id, current_label)
|
|
3272
|
+
return self.update_block(block.id, label=new_label)
|
|
3273
|
+
|
|
3274
|
+
# TODO: remove this
|
|
3275
|
+
def add_agent_memory_block(self, agent_id: str, create_block: CreateBlock) -> Memory:
|
|
3276
|
+
"""
|
|
3277
|
+
Create and link a memory block to an agent's core memory
|
|
3278
|
+
|
|
3279
|
+
Args:
|
|
3280
|
+
agent_id (str): The agent ID
|
|
3281
|
+
create_block (CreateBlock): The block to create
|
|
3143
3282
|
|
|
3144
|
-
|
|
3283
|
+
Returns:
|
|
3284
|
+
memory (Memory): The updated memory
|
|
3285
|
+
"""
|
|
3145
3286
|
block_req = Block(**create_block.model_dump())
|
|
3146
3287
|
block = self.server.block_manager.create_or_update_block(actor=self.user, block=block_req)
|
|
3147
3288
|
# Link the block to the agent
|
|
3148
3289
|
updated_memory = self.server.link_block_to_agent_memory(user_id=self.user_id, agent_id=agent_id, block_id=block.id)
|
|
3149
3290
|
return updated_memory
|
|
3150
3291
|
|
|
3292
|
+
def link_agent_memory_block(self, agent_id: str, block_id: str) -> Memory:
|
|
3293
|
+
"""
|
|
3294
|
+
Link a block to an agent's core memory
|
|
3295
|
+
|
|
3296
|
+
Args:
|
|
3297
|
+
agent_id (str): The agent ID
|
|
3298
|
+
block_id (str): The block ID
|
|
3299
|
+
|
|
3300
|
+
Returns:
|
|
3301
|
+
memory (Memory): The updated memory
|
|
3302
|
+
"""
|
|
3303
|
+
return self.server.link_block_to_agent_memory(user_id=self.user_id, agent_id=agent_id, block_id=block_id)
|
|
3304
|
+
|
|
3151
3305
|
def remove_agent_memory_block(self, agent_id: str, block_label: str) -> Memory:
|
|
3306
|
+
"""
|
|
3307
|
+
Unlike a block from the agent's core memory
|
|
3308
|
+
|
|
3309
|
+
Args:
|
|
3310
|
+
agent_id (str): The agent ID
|
|
3311
|
+
block_label (str): The block label
|
|
3312
|
+
|
|
3313
|
+
Returns:
|
|
3314
|
+
memory (Memory): The updated memory
|
|
3315
|
+
"""
|
|
3152
3316
|
return self.server.unlink_block_from_agent_memory(user_id=self.user_id, agent_id=agent_id, block_label=block_label)
|
|
3153
3317
|
|
|
3154
|
-
def
|
|
3155
|
-
|
|
3318
|
+
def get_agent_memory_blocks(self, agent_id: str) -> List[Block]:
|
|
3319
|
+
"""
|
|
3320
|
+
Get all the blocks in the agent's core memory
|
|
3321
|
+
|
|
3322
|
+
Args:
|
|
3323
|
+
agent_id (str): The agent ID
|
|
3324
|
+
|
|
3325
|
+
Returns:
|
|
3326
|
+
blocks (List[Block]): The blocks in the agent's core memory
|
|
3327
|
+
"""
|
|
3328
|
+
block_ids = self.server.blocks_agents_manager.list_block_ids_for_agent(agent_id=agent_id)
|
|
3329
|
+
return [self.server.block_manager.get_block_by_id(block_id, actor=self.user) for block_id in block_ids]
|
|
3330
|
+
|
|
3331
|
+
def get_agent_memory_block(self, agent_id: str, label: str) -> Block:
|
|
3332
|
+
"""
|
|
3333
|
+
Get a block in the agent's core memory by its label
|
|
3334
|
+
|
|
3335
|
+
Args:
|
|
3336
|
+
agent_id (str): The agent ID
|
|
3337
|
+
label (str): The label in the agent's core memory
|
|
3338
|
+
|
|
3339
|
+
Returns:
|
|
3340
|
+
block (Block): The block corresponding to the label
|
|
3341
|
+
"""
|
|
3342
|
+
block_id = self.server.blocks_agents_manager.get_block_id_for_label(agent_id=agent_id, block_label=label)
|
|
3343
|
+
return self.server.block_manager.get_block_by_id(block_id, actor=self.user)
|
|
3344
|
+
|
|
3345
|
+
def update_agent_memory_block(
|
|
3346
|
+
self,
|
|
3347
|
+
agent_id: str,
|
|
3348
|
+
label: str,
|
|
3349
|
+
value: Optional[str] = None,
|
|
3350
|
+
limit: Optional[int] = None,
|
|
3351
|
+
):
|
|
3352
|
+
"""
|
|
3353
|
+
Update a block in the agent's core memory by specifying its label
|
|
3354
|
+
|
|
3355
|
+
Args:
|
|
3356
|
+
agent_id (str): The agent ID
|
|
3357
|
+
label (str): The label of the block
|
|
3358
|
+
value (str): The new value of the block
|
|
3359
|
+
limit (int): The new limit of the block
|
|
3360
|
+
|
|
3361
|
+
Returns:
|
|
3362
|
+
block (Block): The updated block
|
|
3363
|
+
"""
|
|
3364
|
+
block = self.get_agent_memory_block(agent_id, label)
|
|
3365
|
+
data = {}
|
|
3366
|
+
if value:
|
|
3367
|
+
data["value"] = value
|
|
3368
|
+
if limit:
|
|
3369
|
+
data["limit"] = limit
|
|
3370
|
+
return self.server.block_manager.update_block(block.id, actor=self.user, block_update=BlockUpdate(**data))
|
|
3371
|
+
|
|
3372
|
+
def update_block(
|
|
3373
|
+
self,
|
|
3374
|
+
block_id: str,
|
|
3375
|
+
label: Optional[str] = None,
|
|
3376
|
+
value: Optional[str] = None,
|
|
3377
|
+
limit: Optional[int] = None,
|
|
3378
|
+
):
|
|
3379
|
+
"""
|
|
3380
|
+
Update a block given the ID with the provided fields
|
|
3381
|
+
|
|
3382
|
+
Args:
|
|
3383
|
+
block_id (str): ID of the block
|
|
3384
|
+
label (str): Label to assign to the block
|
|
3385
|
+
value (str): Value to assign to the block
|
|
3386
|
+
limit (int): Token limit to assign to the block
|
|
3387
|
+
|
|
3388
|
+
Returns:
|
|
3389
|
+
block (Block): Updated block
|
|
3390
|
+
"""
|
|
3391
|
+
data = {}
|
|
3392
|
+
if value:
|
|
3393
|
+
data["value"] = value
|
|
3394
|
+
if limit:
|
|
3395
|
+
data["limit"] = limit
|
|
3396
|
+
if label:
|
|
3397
|
+
data["label"] = label
|
|
3398
|
+
return self.server.block_manager.update_block(block_id, actor=self.user, block_update=BlockUpdate(**data))
|