letta-nightly 0.6.13.dev20250122185528__py3-none-any.whl → 0.6.14.dev20250123104106__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.
Files changed (61) hide show
  1. letta/__init__.py +2 -2
  2. letta/agent.py +69 -100
  3. letta/chat_only_agent.py +1 -1
  4. letta/client/client.py +153 -137
  5. letta/constants.py +1 -8
  6. letta/data_sources/connectors.py +1 -1
  7. letta/functions/helpers.py +29 -4
  8. letta/functions/schema_generator.py +55 -0
  9. letta/llm_api/helpers.py +51 -1
  10. letta/memory.py +9 -7
  11. letta/orm/agent.py +2 -2
  12. letta/orm/block.py +3 -1
  13. letta/orm/custom_columns.py +5 -4
  14. letta/orm/enums.py +1 -0
  15. letta/orm/message.py +2 -2
  16. letta/orm/sqlalchemy_base.py +5 -0
  17. letta/schemas/agent.py +3 -3
  18. letta/schemas/block.py +2 -2
  19. letta/schemas/environment_variables.py +1 -1
  20. letta/schemas/job.py +1 -1
  21. letta/schemas/letta_base.py +6 -0
  22. letta/schemas/letta_message.py +6 -6
  23. letta/schemas/memory.py +3 -2
  24. letta/schemas/message.py +21 -13
  25. letta/schemas/passage.py +1 -1
  26. letta/schemas/source.py +4 -4
  27. letta/schemas/tool.py +38 -43
  28. letta/server/rest_api/app.py +1 -16
  29. letta/server/rest_api/routers/v1/agents.py +101 -84
  30. letta/server/rest_api/routers/v1/blocks.py +8 -46
  31. letta/server/rest_api/routers/v1/jobs.py +4 -4
  32. letta/server/rest_api/routers/v1/providers.py +2 -2
  33. letta/server/rest_api/routers/v1/runs.py +6 -6
  34. letta/server/rest_api/routers/v1/sources.py +8 -38
  35. letta/server/rest_api/routers/v1/tags.py +1 -1
  36. letta/server/rest_api/routers/v1/tools.py +6 -7
  37. letta/server/server.py +3 -3
  38. letta/services/agent_manager.py +43 -9
  39. letta/services/block_manager.py +3 -3
  40. letta/services/job_manager.py +5 -3
  41. letta/services/organization_manager.py +1 -1
  42. letta/services/passage_manager.py +3 -3
  43. letta/services/provider_manager.py +2 -2
  44. letta/services/sandbox_config_manager.py +2 -2
  45. letta/services/source_manager.py +3 -3
  46. letta/services/tool_execution_sandbox.py +3 -1
  47. letta/services/tool_manager.py +8 -3
  48. letta/services/user_manager.py +2 -2
  49. letta/settings.py +29 -0
  50. letta/system.py +2 -2
  51. {letta_nightly-0.6.13.dev20250122185528.dist-info → letta_nightly-0.6.14.dev20250123104106.dist-info}/METADATA +1 -1
  52. {letta_nightly-0.6.13.dev20250122185528.dist-info → letta_nightly-0.6.14.dev20250123104106.dist-info}/RECORD +55 -61
  53. letta/server/rest_api/routers/openai/__init__.py +0 -0
  54. letta/server/rest_api/routers/openai/assistants/__init__.py +0 -0
  55. letta/server/rest_api/routers/openai/assistants/assistants.py +0 -115
  56. letta/server/rest_api/routers/openai/assistants/schemas.py +0 -115
  57. letta/server/rest_api/routers/openai/chat_completions/__init__.py +0 -0
  58. letta/server/rest_api/routers/openai/chat_completions/chat_completions.py +0 -120
  59. {letta_nightly-0.6.13.dev20250122185528.dist-info → letta_nightly-0.6.14.dev20250123104106.dist-info}/LICENSE +0 -0
  60. {letta_nightly-0.6.13.dev20250122185528.dist-info → letta_nightly-0.6.14.dev20250123104106.dist-info}/WHEEL +0 -0
  61. {letta_nightly-0.6.13.dev20250122185528.dist-info → letta_nightly-0.6.14.dev20250123104106.dist-info}/entry_points.txt +0 -0
letta/client/client.py CHANGED
@@ -3,6 +3,7 @@ import time
3
3
  from typing import Callable, Dict, Generator, List, Optional, Union
4
4
 
5
5
  import requests
6
+ from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall as OpenAIToolCall
6
7
 
7
8
  import letta.utils
8
9
  from letta.constants import ADMIN_PREFIX, BASE_MEMORY_TOOLS, BASE_TOOLS, DEFAULT_HUMAN, DEFAULT_PERSONA, FUNCTION_RETURN_CHAR_LIMIT
@@ -29,7 +30,6 @@ from letta.schemas.llm_config import LLMConfig
29
30
  from letta.schemas.memory import ArchivalMemorySummary, ChatMemory, CreateArchivalMemory, Memory, RecallMemorySummary
30
31
  from letta.schemas.message import Message, MessageCreate, MessageUpdate
31
32
  from letta.schemas.openai.chat_completion_response import UsageStatistics
32
- from letta.schemas.openai.chat_completions import ToolCall
33
33
  from letta.schemas.organization import Organization
34
34
  from letta.schemas.passage import Passage
35
35
  from letta.schemas.run import Run
@@ -92,19 +92,19 @@ class AbstractClient(object):
92
92
  ):
93
93
  raise NotImplementedError
94
94
 
95
- def get_tools_from_agent(self, agent_id: str):
95
+ def get_tools_from_agent(self, agent_id: str) -> List[Tool]:
96
96
  raise NotImplementedError
97
97
 
98
- def add_tool_to_agent(self, agent_id: str, tool_id: str):
98
+ def attach_tool(self, agent_id: str, tool_id: str) -> AgentState:
99
99
  raise NotImplementedError
100
100
 
101
- def remove_tool_from_agent(self, agent_id: str, tool_id: str):
101
+ def detach_tool(self, agent_id: str, tool_id: str) -> AgentState:
102
102
  raise NotImplementedError
103
103
 
104
- def rename_agent(self, agent_id: str, new_name: str):
104
+ def rename_agent(self, agent_id: str, new_name: str) -> AgentState:
105
105
  raise NotImplementedError
106
106
 
107
- def delete_agent(self, agent_id: str):
107
+ def delete_agent(self, agent_id: str) -> None:
108
108
  raise NotImplementedError
109
109
 
110
110
  def get_agent(self, agent_id: str) -> AgentState:
@@ -218,6 +218,18 @@ class AbstractClient(object):
218
218
  def get_tool_id(self, name: str) -> Optional[str]:
219
219
  raise NotImplementedError
220
220
 
221
+ def list_attached_tools(self, agent_id: str) -> List[Tool]:
222
+ """
223
+ List all tools attached to an agent.
224
+
225
+ Args:
226
+ agent_id (str): ID of the agent
227
+
228
+ Returns:
229
+ List[Tool]: A list of attached tools
230
+ """
231
+ raise NotImplementedError
232
+
221
233
  def upsert_base_tools(self) -> List[Tool]:
222
234
  raise NotImplementedError
223
235
 
@@ -242,10 +254,10 @@ class AbstractClient(object):
242
254
  def get_source_id(self, source_name: str) -> str:
243
255
  raise NotImplementedError
244
256
 
245
- def attach_source_to_agent(self, agent_id: str, source_id: Optional[str] = None, source_name: Optional[str] = None):
257
+ def attach_source(self, agent_id: str, source_id: Optional[str] = None, source_name: Optional[str] = None) -> AgentState:
246
258
  raise NotImplementedError
247
259
 
248
- def detach_source_from_agent(self, agent_id: str, source_id: Optional[str] = None, source_name: Optional[str] = None):
260
+ def detach_source(self, agent_id: str, source_id: Optional[str] = None, source_name: Optional[str] = None) -> AgentState:
249
261
  raise NotImplementedError
250
262
 
251
263
  def list_sources(self) -> List[Source]:
@@ -397,6 +409,26 @@ class AbstractClient(object):
397
409
  """
398
410
  raise NotImplementedError
399
411
 
412
+ def attach_block(self, agent_id: str, block_id: str) -> AgentState:
413
+ """
414
+ Attach a block to an agent.
415
+
416
+ Args:
417
+ agent_id (str): ID of the agent
418
+ block_id (str): ID of the block to attach
419
+ """
420
+ raise NotImplementedError
421
+
422
+ def detach_block(self, agent_id: str, block_id: str) -> AgentState:
423
+ """
424
+ Detach a block from an agent.
425
+
426
+ Args:
427
+ agent_id (str): ID of the agent
428
+ block_id (str): ID of the block to detach
429
+ """
430
+ raise NotImplementedError
431
+
400
432
 
401
433
  class RESTClient(AbstractClient):
402
434
  """
@@ -554,7 +586,7 @@ class RESTClient(AbstractClient):
554
586
  # create agent
555
587
  create_params = {
556
588
  "description": description,
557
- "metadata_": metadata,
589
+ "metadata": metadata,
558
590
  "memory_blocks": [],
559
591
  "block_ids": [b.id for b in memory.get_blocks()] + block_ids,
560
592
  "tool_ids": tool_ids,
@@ -599,7 +631,7 @@ class RESTClient(AbstractClient):
599
631
  role: Optional[MessageRole] = None,
600
632
  text: Optional[str] = None,
601
633
  name: Optional[str] = None,
602
- tool_calls: Optional[List[ToolCall]] = None,
634
+ tool_calls: Optional[List[OpenAIToolCall]] = None,
603
635
  tool_call_id: Optional[str] = None,
604
636
  ) -> Message:
605
637
  request = MessageUpdate(
@@ -628,7 +660,7 @@ class RESTClient(AbstractClient):
628
660
  embedding_config: Optional[EmbeddingConfig] = None,
629
661
  message_ids: Optional[List[str]] = None,
630
662
  tags: Optional[List[str]] = None,
631
- ):
663
+ ) -> AgentState:
632
664
  """
633
665
  Update an existing agent
634
666
 
@@ -653,7 +685,7 @@ class RESTClient(AbstractClient):
653
685
  tool_ids=tool_ids,
654
686
  tags=tags,
655
687
  description=description,
656
- metadata_=metadata,
688
+ metadata=metadata,
657
689
  llm_config=llm_config,
658
690
  embedding_config=embedding_config,
659
691
  message_ids=message_ids,
@@ -678,7 +710,7 @@ class RESTClient(AbstractClient):
678
710
  raise ValueError(f"Failed to get tools from agents: {response.text}")
679
711
  return [Tool(**tool) for tool in response.json()]
680
712
 
681
- def add_tool_to_agent(self, agent_id: str, tool_id: str):
713
+ def attach_tool(self, agent_id: str, tool_id: str) -> AgentState:
682
714
  """
683
715
  Add tool to an existing agent
684
716
 
@@ -689,12 +721,12 @@ class RESTClient(AbstractClient):
689
721
  Returns:
690
722
  agent_state (AgentState): State of the updated agent
691
723
  """
692
- response = requests.patch(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/add-tool/{tool_id}", headers=self.headers)
724
+ response = requests.patch(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/tools/attach/{tool_id}", headers=self.headers)
693
725
  if response.status_code != 200:
694
726
  raise ValueError(f"Failed to update agent: {response.text}")
695
727
  return AgentState(**response.json())
696
728
 
697
- def remove_tool_from_agent(self, agent_id: str, tool_id: str):
729
+ def detach_tool(self, agent_id: str, tool_id: str) -> AgentState:
698
730
  """
699
731
  Removes tools from an existing agent
700
732
 
@@ -706,12 +738,12 @@ class RESTClient(AbstractClient):
706
738
  agent_state (AgentState): State of the updated agent
707
739
  """
708
740
 
709
- response = requests.patch(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/remove-tool/{tool_id}", headers=self.headers)
741
+ response = requests.patch(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/tools/detach/{tool_id}", headers=self.headers)
710
742
  if response.status_code != 200:
711
743
  raise ValueError(f"Failed to update agent: {response.text}")
712
744
  return AgentState(**response.json())
713
745
 
714
- def rename_agent(self, agent_id: str, new_name: str):
746
+ def rename_agent(self, agent_id: str, new_name: str) -> AgentState:
715
747
  """
716
748
  Rename an agent
717
749
 
@@ -719,10 +751,12 @@ class RESTClient(AbstractClient):
719
751
  agent_id (str): ID of the agent
720
752
  new_name (str): New name for the agent
721
753
 
754
+ Returns:
755
+ agent_state (AgentState): State of the updated agent
722
756
  """
723
757
  return self.update_agent(agent_id, name=new_name)
724
758
 
725
- def delete_agent(self, agent_id: str):
759
+ def delete_agent(self, agent_id: str) -> None:
726
760
  """
727
761
  Delete an agent
728
762
 
@@ -776,7 +810,8 @@ class RESTClient(AbstractClient):
776
810
  Returns:
777
811
  memory (Memory): In-context memory of the agent
778
812
  """
779
- response = requests.get(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/core_memory", headers=self.headers)
813
+
814
+ response = requests.get(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/core-memory", headers=self.headers)
780
815
  if response.status_code != 200:
781
816
  raise ValueError(f"Failed to get in-context memory: {response.text}")
782
817
  return Memory(**response.json())
@@ -797,7 +832,7 @@ class RESTClient(AbstractClient):
797
832
  """
798
833
  memory_update_dict = {section: value}
799
834
  response = requests.patch(
800
- f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/core_memory", json=memory_update_dict, headers=self.headers
835
+ f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/core-memory", json=memory_update_dict, headers=self.headers
801
836
  )
802
837
  if response.status_code != 200:
803
838
  raise ValueError(f"Failed to update in-context memory: {response.text}")
@@ -890,7 +925,7 @@ class RESTClient(AbstractClient):
890
925
  if after:
891
926
  params["after"] = str(after)
892
927
  response = requests.get(
893
- f"{self.base_url}/{self.api_prefix}/agents/{str(agent_id)}/archival_memory", params=params, headers=self.headers
928
+ f"{self.base_url}/{self.api_prefix}/agents/{str(agent_id)}/archival-memory", params=params, headers=self.headers
894
929
  )
895
930
  assert response.status_code == 200, f"Failed to get archival memory: {response.text}"
896
931
  return [Passage(**passage) for passage in response.json()]
@@ -908,7 +943,7 @@ class RESTClient(AbstractClient):
908
943
  """
909
944
  request = CreateArchivalMemory(text=memory)
910
945
  response = requests.post(
911
- f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/archival_memory", headers=self.headers, json=request.model_dump()
946
+ f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/archival-memory", headers=self.headers, json=request.model_dump()
912
947
  )
913
948
  if response.status_code != 200:
914
949
  raise ValueError(f"Failed to insert archival memory: {response.text}")
@@ -922,7 +957,7 @@ class RESTClient(AbstractClient):
922
957
  agent_id (str): ID of the agent
923
958
  memory_id (str): ID of the memory
924
959
  """
925
- response = requests.delete(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/archival_memory/{memory_id}", headers=self.headers)
960
+ response = requests.delete(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/archival-memory/{memory_id}", headers=self.headers)
926
961
  assert response.status_code == 200, f"Failed to delete archival memory: {response.text}"
927
962
 
928
963
  # messages (recall memory)
@@ -1433,7 +1468,7 @@ class RESTClient(AbstractClient):
1433
1468
  raise ValueError(f"Failed to update source: {response.text}")
1434
1469
  return Source(**response.json())
1435
1470
 
1436
- def attach_source_to_agent(self, source_id: str, agent_id: str):
1471
+ def attach_source(self, source_id: str, agent_id: str) -> AgentState:
1437
1472
  """
1438
1473
  Attach a source to an agent
1439
1474
 
@@ -1443,15 +1478,20 @@ class RESTClient(AbstractClient):
1443
1478
  source_name (str): Name of the source
1444
1479
  """
1445
1480
  params = {"agent_id": agent_id}
1446
- response = requests.post(f"{self.base_url}/{self.api_prefix}/sources/{source_id}/attach", params=params, headers=self.headers)
1481
+ response = requests.patch(
1482
+ f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/sources/attach/{source_id}", params=params, headers=self.headers
1483
+ )
1447
1484
  assert response.status_code == 200, f"Failed to attach source to agent: {response.text}"
1485
+ return AgentState(**response.json())
1448
1486
 
1449
- def detach_source(self, source_id: str, agent_id: str):
1487
+ def detach_source(self, source_id: str, agent_id: str) -> AgentState:
1450
1488
  """Detach a source from an agent"""
1451
1489
  params = {"agent_id": str(agent_id)}
1452
- response = requests.post(f"{self.base_url}/{self.api_prefix}/sources/{source_id}/detach", params=params, headers=self.headers)
1490
+ response = requests.patch(
1491
+ f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/sources/detach/{source_id}", params=params, headers=self.headers
1492
+ )
1453
1493
  assert response.status_code == 200, f"Failed to detach source from agent: {response.text}"
1454
- return Source(**response.json())
1494
+ return AgentState(**response.json())
1455
1495
 
1456
1496
  # tools
1457
1497
 
@@ -1474,6 +1514,21 @@ class RESTClient(AbstractClient):
1474
1514
  return None
1475
1515
  return tools[0].id
1476
1516
 
1517
+ def list_attached_tools(self, agent_id: str) -> List[Tool]:
1518
+ """
1519
+ List all tools attached to an agent.
1520
+
1521
+ Args:
1522
+ agent_id (str): ID of the agent
1523
+
1524
+ Returns:
1525
+ List[Tool]: A list of attached tools
1526
+ """
1527
+ response = requests.get(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/tools", headers=self.headers)
1528
+ if response.status_code != 200:
1529
+ raise ValueError(f"Failed to list attached tools: {response.text}")
1530
+ return [Tool(**tool) for tool in response.json()]
1531
+
1477
1532
  def upsert_base_tools(self) -> List[Tool]:
1478
1533
  response = requests.post(f"{self.base_url}/{self.api_prefix}/tools/add-base-tools/", headers=self.headers)
1479
1534
  if response.status_code != 200:
@@ -1843,66 +1898,36 @@ class RESTClient(AbstractClient):
1843
1898
  block = self.get_agent_memory_block(agent_id, current_label)
1844
1899
  return self.update_block(block.id, label=new_label)
1845
1900
 
1846
- # TODO: remove this
1847
- def add_agent_memory_block(self, agent_id: str, create_block: CreateBlock) -> Memory:
1848
- """
1849
- Create and link a memory block to an agent's core memory
1850
-
1851
- Args:
1852
- agent_id (str): The agent ID
1853
- create_block (CreateBlock): The block to create
1854
-
1855
- Returns:
1856
- memory (Memory): The updated memory
1901
+ def attach_block(self, agent_id: str, block_id: str) -> AgentState:
1857
1902
  """
1858
- response = requests.post(
1859
- f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/core_memory/blocks",
1860
- headers=self.headers,
1861
- json=create_block.model_dump(),
1862
- )
1863
- if response.status_code != 200:
1864
- raise ValueError(f"Failed to add agent memory block: {response.text}")
1865
- return Memory(**response.json())
1866
-
1867
- def link_agent_memory_block(self, agent_id: str, block_id: str) -> Memory:
1868
- """
1869
- Link a block to an agent's core memory
1903
+ Attach a block to an agent.
1870
1904
 
1871
1905
  Args:
1872
- agent_id (str): The agent ID
1873
- block_id (str): The block ID
1874
-
1875
- Returns:
1876
- memory (Memory): The updated memory
1906
+ agent_id (str): ID of the agent
1907
+ block_id (str): ID of the block to attach
1877
1908
  """
1878
- params = {"agent_id": agent_id}
1879
1909
  response = requests.patch(
1880
- f"{self.base_url}/{self.api_prefix}/blocks/{block_id}/attach",
1881
- params=params,
1910
+ f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/core-memory/blocks/attach/{block_id}",
1882
1911
  headers=self.headers,
1883
1912
  )
1884
1913
  if response.status_code != 200:
1885
- raise ValueError(f"Failed to link agent memory block: {response.text}")
1886
- return Block(**response.json())
1914
+ raise ValueError(f"Failed to attach block to agent: {response.text}")
1915
+ return AgentState(**response.json())
1887
1916
 
1888
- def remove_agent_memory_block(self, agent_id: str, block_label: str) -> Memory:
1917
+ def detach_block(self, agent_id: str, block_id: str) -> AgentState:
1889
1918
  """
1890
- Unlike a block from the agent's core memory
1919
+ Detach a block from an agent.
1891
1920
 
1892
1921
  Args:
1893
- agent_id (str): The agent ID
1894
- block_label (str): The block label
1895
-
1896
- Returns:
1897
- memory (Memory): The updated memory
1922
+ agent_id (str): ID of the agent
1923
+ block_id (str): ID of the block to detach
1898
1924
  """
1899
- response = requests.delete(
1900
- f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/core_memory/blocks/{block_label}",
1901
- headers=self.headers,
1925
+ response = requests.patch(
1926
+ f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/core-memory/blocks/detach/{block_id}", headers=self.headers
1902
1927
  )
1903
1928
  if response.status_code != 200:
1904
- raise ValueError(f"Failed to remove agent memory block: {response.text}")
1905
- return Memory(**response.json())
1929
+ raise ValueError(f"Failed to detach block from agent: {response.text}")
1930
+ return AgentState(**response.json())
1906
1931
 
1907
1932
  def list_agent_memory_blocks(self, agent_id: str) -> List[Block]:
1908
1933
  """
@@ -1914,7 +1939,7 @@ class RESTClient(AbstractClient):
1914
1939
  Returns:
1915
1940
  blocks (List[Block]): The blocks in the agent's core memory
1916
1941
  """
1917
- response = requests.get(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/core_memory/blocks", headers=self.headers)
1942
+ response = requests.get(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/core-memory/blocks", headers=self.headers)
1918
1943
  if response.status_code != 200:
1919
1944
  raise ValueError(f"Failed to get agent memory blocks: {response.text}")
1920
1945
  return [Block(**block) for block in response.json()]
@@ -1931,7 +1956,7 @@ class RESTClient(AbstractClient):
1931
1956
  block (Block): The block corresponding to the label
1932
1957
  """
1933
1958
  response = requests.get(
1934
- f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/core_memory/blocks/{label}",
1959
+ f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/core-memory/blocks/{label}",
1935
1960
  headers=self.headers,
1936
1961
  )
1937
1962
  if response.status_code != 200:
@@ -1964,7 +1989,7 @@ class RESTClient(AbstractClient):
1964
1989
  if limit:
1965
1990
  data["limit"] = limit
1966
1991
  response = requests.patch(
1967
- f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/core_memory/blocks/{label}",
1992
+ f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/core-memory/blocks/{label}",
1968
1993
  headers=self.headers,
1969
1994
  json=data,
1970
1995
  )
@@ -2307,7 +2332,7 @@ class LocalClient(AbstractClient):
2307
2332
  # Create the base parameters
2308
2333
  create_params = {
2309
2334
  "description": description,
2310
- "metadata_": metadata,
2335
+ "metadata": metadata,
2311
2336
  "memory_blocks": [],
2312
2337
  "block_ids": [b.id for b in memory.get_blocks()] + block_ids,
2313
2338
  "tool_ids": tool_ids,
@@ -2341,7 +2366,7 @@ class LocalClient(AbstractClient):
2341
2366
  role: Optional[MessageRole] = None,
2342
2367
  text: Optional[str] = None,
2343
2368
  name: Optional[str] = None,
2344
- tool_calls: Optional[List[ToolCall]] = None,
2369
+ tool_calls: Optional[List[OpenAIToolCall]] = None,
2345
2370
  tool_call_id: Optional[str] = None,
2346
2371
  ) -> Message:
2347
2372
  message = self.server.update_agent_message(
@@ -2389,7 +2414,7 @@ class LocalClient(AbstractClient):
2389
2414
  Returns:
2390
2415
  agent_state (AgentState): State of the updated agent
2391
2416
  """
2392
- # TODO: add the abilitty to reset linked block_ids
2417
+ # TODO: add the ability to reset linked block_ids
2393
2418
  self.interface.clear()
2394
2419
  agent_state = self.server.agent_manager.update_agent(
2395
2420
  agent_id,
@@ -2399,7 +2424,7 @@ class LocalClient(AbstractClient):
2399
2424
  tool_ids=tool_ids,
2400
2425
  tags=tags,
2401
2426
  description=description,
2402
- metadata_=metadata,
2427
+ metadata=metadata,
2403
2428
  llm_config=llm_config,
2404
2429
  embedding_config=embedding_config,
2405
2430
  message_ids=message_ids,
@@ -2421,7 +2446,7 @@ class LocalClient(AbstractClient):
2421
2446
  self.interface.clear()
2422
2447
  return self.server.agent_manager.get_agent_by_id(agent_id=agent_id, actor=self.user).tools
2423
2448
 
2424
- def add_tool_to_agent(self, agent_id: str, tool_id: str):
2449
+ def attach_tool(self, agent_id: str, tool_id: str) -> AgentState:
2425
2450
  """
2426
2451
  Add tool to an existing agent
2427
2452
 
@@ -2436,7 +2461,7 @@ class LocalClient(AbstractClient):
2436
2461
  agent_state = self.server.agent_manager.attach_tool(agent_id=agent_id, tool_id=tool_id, actor=self.user)
2437
2462
  return agent_state
2438
2463
 
2439
- def remove_tool_from_agent(self, agent_id: str, tool_id: str):
2464
+ def detach_tool(self, agent_id: str, tool_id: str) -> AgentState:
2440
2465
  """
2441
2466
  Removes tools from an existing agent
2442
2467
 
@@ -2451,17 +2476,20 @@ class LocalClient(AbstractClient):
2451
2476
  agent_state = self.server.agent_manager.detach_tool(agent_id=agent_id, tool_id=tool_id, actor=self.user)
2452
2477
  return agent_state
2453
2478
 
2454
- def rename_agent(self, agent_id: str, new_name: str):
2479
+ def rename_agent(self, agent_id: str, new_name: str) -> AgentState:
2455
2480
  """
2456
2481
  Rename an agent
2457
2482
 
2458
2483
  Args:
2459
2484
  agent_id (str): ID of the agent
2460
2485
  new_name (str): New name for the agent
2486
+
2487
+ Returns:
2488
+ agent_state (AgentState): State of the updated agent
2461
2489
  """
2462
- self.update_agent(agent_id, name=new_name)
2490
+ return self.update_agent(agent_id, name=new_name)
2463
2491
 
2464
- def delete_agent(self, agent_id: str):
2492
+ def delete_agent(self, agent_id: str) -> None:
2465
2493
  """
2466
2494
  Delete an agent
2467
2495
 
@@ -2874,7 +2902,7 @@ class LocalClient(AbstractClient):
2874
2902
 
2875
2903
  def load_composio_tool(self, action: "ActionType") -> Tool:
2876
2904
  tool_create = ToolCreate.from_composio(action_name=action.name)
2877
- return self.server.tool_manager.create_or_update_tool(pydantic_tool=Tool(**tool_create.model_dump()), actor=self.user)
2905
+ return self.server.tool_manager.create_or_update_composio_tool(pydantic_tool=Tool(**tool_create.model_dump()), actor=self.user)
2878
2906
 
2879
2907
  def create_tool(
2880
2908
  self,
@@ -3036,6 +3064,18 @@ class LocalClient(AbstractClient):
3036
3064
  tool = self.server.tool_manager.get_tool_by_name(tool_name=name, actor=self.user)
3037
3065
  return tool.id if tool else None
3038
3066
 
3067
+ def list_attached_tools(self, agent_id: str) -> List[Tool]:
3068
+ """
3069
+ List all tools attached to an agent.
3070
+
3071
+ Args:
3072
+ agent_id (str): ID of the agent
3073
+
3074
+ Returns:
3075
+ List[Tool]: List of tools attached to the agent
3076
+ """
3077
+ return self.server.agent_manager.list_attached_tools(agent_id=agent_id, actor=self.user)
3078
+
3039
3079
  def load_data(self, connector: DataConnector, source_name: str):
3040
3080
  """
3041
3081
  Load data into a source
@@ -3061,7 +3101,7 @@ class LocalClient(AbstractClient):
3061
3101
  job = Job(
3062
3102
  user_id=self.user_id,
3063
3103
  status=JobStatus.created,
3064
- metadata_={"type": "embedding", "filename": filename, "source_id": source_id},
3104
+ metadata={"type": "embedding", "filename": filename, "source_id": source_id},
3065
3105
  )
3066
3106
  job = self.server.job_manager.create_job(pydantic_job=job, actor=self.user)
3067
3107
 
@@ -3069,14 +3109,14 @@ class LocalClient(AbstractClient):
3069
3109
  self.server.load_file_to_source(source_id=source_id, file_path=filename, job_id=job.id, actor=self.user)
3070
3110
  return job
3071
3111
 
3072
- def delete_file_from_source(self, source_id: str, file_id: str):
3112
+ def delete_file_from_source(self, source_id: str, file_id: str) -> None:
3073
3113
  self.server.source_manager.delete_file(file_id, actor=self.user)
3074
3114
 
3075
3115
  def get_job(self, job_id: str):
3076
3116
  return self.server.job_manager.get_job_by_id(job_id=job_id, actor=self.user)
3077
3117
 
3078
3118
  def delete_job(self, job_id: str):
3079
- return self.server.job_manager.delete_job(job_id=job_id, actor=self.user)
3119
+ return self.server.job_manager.delete_job_by_id(job_id=job_id, actor=self.user)
3080
3120
 
3081
3121
  def list_jobs(self):
3082
3122
  return self.server.job_manager.list_jobs(actor=self.user)
@@ -3135,7 +3175,7 @@ class LocalClient(AbstractClient):
3135
3175
  """
3136
3176
  return self.server.source_manager.get_source_by_name(source_name=source_name, actor=self.user).id
3137
3177
 
3138
- def attach_source_to_agent(self, agent_id: str, source_id: Optional[str] = None, source_name: Optional[str] = None):
3178
+ def attach_source(self, agent_id: str, source_id: Optional[str] = None, source_name: Optional[str] = None) -> AgentState:
3139
3179
  """
3140
3180
  Attach a source to an agent
3141
3181
 
@@ -3148,9 +3188,9 @@ class LocalClient(AbstractClient):
3148
3188
  source = self.server.source_manager.get_source_by_id(source_id=source_id, actor=self.user)
3149
3189
  source_id = source.id
3150
3190
 
3151
- self.server.agent_manager.attach_source(source_id=source_id, agent_id=agent_id, actor=self.user)
3191
+ return self.server.agent_manager.attach_source(source_id=source_id, agent_id=agent_id, actor=self.user)
3152
3192
 
3153
- def detach_source_from_agent(self, agent_id: str, source_id: Optional[str] = None, source_name: Optional[str] = None):
3193
+ def detach_source(self, agent_id: str, source_id: Optional[str] = None, source_name: Optional[str] = None) -> AgentState:
3154
3194
  """
3155
3195
  Detach a source from an agent by removing all `Passage` objects that were loaded from the source from archival memory.
3156
3196
  Args:
@@ -3483,51 +3523,7 @@ class LocalClient(AbstractClient):
3483
3523
  block = self.get_agent_memory_block(agent_id, current_label)
3484
3524
  return self.update_block(block.id, label=new_label)
3485
3525
 
3486
- # TODO: remove this
3487
- def add_agent_memory_block(self, agent_id: str, create_block: CreateBlock) -> Memory:
3488
- """
3489
- Create and link a memory block to an agent's core memory
3490
-
3491
- Args:
3492
- agent_id (str): The agent ID
3493
- create_block (CreateBlock): The block to create
3494
-
3495
- Returns:
3496
- memory (Memory): The updated memory
3497
- """
3498
- block_req = Block(**create_block.model_dump())
3499
- block = self.server.block_manager.create_or_update_block(actor=self.user, block=block_req)
3500
- # Link the block to the agent
3501
- agent = self.server.agent_manager.attach_block(agent_id=agent_id, block_id=block.id, actor=self.user)
3502
- return agent.memory
3503
-
3504
- def link_agent_memory_block(self, agent_id: str, block_id: str) -> Memory:
3505
- """
3506
- Link a block to an agent's core memory
3507
-
3508
- Args:
3509
- agent_id (str): The agent ID
3510
- block_id (str): The block ID
3511
-
3512
- Returns:
3513
- memory (Memory): The updated memory
3514
- """
3515
- return self.server.agent_manager.attach_block(agent_id=agent_id, block_id=block_id, actor=self.user)
3516
-
3517
- def remove_agent_memory_block(self, agent_id: str, block_label: str) -> Memory:
3518
- """
3519
- Unlike a block from the agent's core memory
3520
-
3521
- Args:
3522
- agent_id (str): The agent ID
3523
- block_label (str): The block label
3524
-
3525
- Returns:
3526
- memory (Memory): The updated memory
3527
- """
3528
- return self.server.agent_manager.detach_block_with_label(agent_id=agent_id, block_label=block_label, actor=self.user)
3529
-
3530
- def list_agent_memory_blocks(self, agent_id: str) -> List[Block]:
3526
+ def get_agent_memory_blocks(self, agent_id: str) -> List[Block]:
3531
3527
  """
3532
3528
  Get all the blocks in the agent's core memory
3533
3529
 
@@ -3608,6 +3604,26 @@ class LocalClient(AbstractClient):
3608
3604
  data["label"] = label
3609
3605
  return self.server.block_manager.update_block(block_id, actor=self.user, block_update=BlockUpdate(**data))
3610
3606
 
3607
+ def attach_block(self, agent_id: str, block_id: str) -> AgentState:
3608
+ """
3609
+ Attach a block to an agent.
3610
+
3611
+ Args:
3612
+ agent_id (str): ID of the agent
3613
+ block_id (str): ID of the block to attach
3614
+ """
3615
+ return self.server.agent_manager.attach_block(agent_id=agent_id, block_id=block_id, actor=self.user)
3616
+
3617
+ def detach_block(self, agent_id: str, block_id: str) -> AgentState:
3618
+ """
3619
+ Detach a block from an agent.
3620
+
3621
+ Args:
3622
+ agent_id (str): ID of the agent
3623
+ block_id (str): ID of the block to detach
3624
+ """
3625
+ return self.server.agent_manager.detach_block(agent_id=agent_id, block_id=block_id, actor=self.user)
3626
+
3611
3627
  def get_run_messages(
3612
3628
  self,
3613
3629
  run_id: str,
letta/constants.py CHANGED
@@ -125,8 +125,6 @@ LLM_MAX_TOKENS = {
125
125
  "gpt-3.5-turbo-16k-0613": 16385, # legacy
126
126
  "gpt-3.5-turbo-0301": 4096, # legacy
127
127
  }
128
- # The amount of tokens before a sytem warning about upcoming truncation is sent to Letta
129
- MESSAGE_SUMMARY_WARNING_FRAC = 0.75
130
128
  # The error message that Letta will receive
131
129
  # MESSAGE_SUMMARY_WARNING_STR = f"Warning: the conversation history will soon reach its maximum length and be trimmed. Make sure to save any important information from the conversation to your memory before it is removed."
132
130
  # Much longer and more specific variant of the prompt
@@ -138,15 +136,10 @@ MESSAGE_SUMMARY_WARNING_STR = " ".join(
138
136
  # "Remember to pass request_heartbeat = true if you would like to send a message immediately after.",
139
137
  ]
140
138
  )
141
- # The fraction of tokens we truncate down to
142
- MESSAGE_SUMMARY_TRUNC_TOKEN_FRAC = 0.75
139
+
143
140
  # The ackknowledgement message used in the summarize sequence
144
141
  MESSAGE_SUMMARY_REQUEST_ACK = "Understood, I will respond with a summary of the message (and only the summary, nothing else) once I receive the conversation history. I'm ready."
145
142
 
146
- # Even when summarizing, we want to keep a handful of recent messages
147
- # These serve as in-context examples of how to use functions / what user messages look like
148
- MESSAGE_SUMMARY_TRUNC_KEEP_N_LAST = 3
149
-
150
143
  # Maximum length of an error message
151
144
  MAX_ERROR_MESSAGE_CHAR_LIMIT = 500
152
145
 
@@ -77,7 +77,7 @@ def load_data(connector: DataConnector, source: Source, passage_manager: Passage
77
77
  text=passage_text,
78
78
  file_id=file_metadata.id,
79
79
  source_id=source.id,
80
- metadata_=passage_metadata,
80
+ metadata=passage_metadata,
81
81
  organization_id=source.organization_id,
82
82
  embedding_config=source.embedding_config,
83
83
  embedding=embedding,