letta-nightly 0.6.14.dev20250123104106__py3-none-any.whl → 0.6.15.dev20250124054224__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.

Files changed (59) hide show
  1. letta/__init__.py +1 -1
  2. letta/client/client.py +144 -68
  3. letta/client/streaming.py +1 -1
  4. letta/functions/function_sets/extras.py +8 -3
  5. letta/functions/function_sets/multi_agent.py +1 -1
  6. letta/functions/helpers.py +2 -2
  7. letta/llm_api/llm_api_tools.py +2 -2
  8. letta/llm_api/openai.py +30 -138
  9. letta/memory.py +4 -4
  10. letta/offline_memory_agent.py +10 -10
  11. letta/orm/agent.py +10 -2
  12. letta/orm/block.py +14 -3
  13. letta/orm/job.py +2 -1
  14. letta/orm/message.py +12 -1
  15. letta/orm/passage.py +6 -2
  16. letta/orm/source.py +6 -1
  17. letta/orm/sqlalchemy_base.py +80 -32
  18. letta/orm/tool.py +5 -2
  19. letta/schemas/embedding_config_overrides.py +3 -0
  20. letta/schemas/enums.py +4 -0
  21. letta/schemas/job.py +1 -1
  22. letta/schemas/letta_message.py +22 -5
  23. letta/schemas/llm_config.py +5 -0
  24. letta/schemas/llm_config_overrides.py +38 -0
  25. letta/schemas/message.py +61 -15
  26. letta/schemas/openai/chat_completions.py +1 -1
  27. letta/schemas/passage.py +1 -1
  28. letta/schemas/providers.py +24 -8
  29. letta/schemas/source.py +1 -1
  30. letta/server/rest_api/app.py +12 -3
  31. letta/server/rest_api/interface.py +5 -7
  32. letta/server/rest_api/routers/v1/agents.py +7 -12
  33. letta/server/rest_api/routers/v1/blocks.py +19 -0
  34. letta/server/rest_api/routers/v1/organizations.py +2 -2
  35. letta/server/rest_api/routers/v1/providers.py +2 -2
  36. letta/server/rest_api/routers/v1/runs.py +15 -7
  37. letta/server/rest_api/routers/v1/sandbox_configs.py +4 -4
  38. letta/server/rest_api/routers/v1/sources.py +2 -2
  39. letta/server/rest_api/routers/v1/tags.py +2 -2
  40. letta/server/rest_api/routers/v1/tools.py +2 -2
  41. letta/server/rest_api/routers/v1/users.py +2 -2
  42. letta/server/server.py +62 -34
  43. letta/services/agent_manager.py +80 -33
  44. letta/services/block_manager.py +15 -2
  45. letta/services/helpers/agent_manager_helper.py +11 -4
  46. letta/services/job_manager.py +19 -9
  47. letta/services/message_manager.py +14 -8
  48. letta/services/organization_manager.py +8 -4
  49. letta/services/provider_manager.py +8 -4
  50. letta/services/sandbox_config_manager.py +16 -8
  51. letta/services/source_manager.py +4 -4
  52. letta/services/tool_manager.py +3 -3
  53. letta/services/user_manager.py +9 -5
  54. {letta_nightly-0.6.14.dev20250123104106.dist-info → letta_nightly-0.6.15.dev20250124054224.dist-info}/METADATA +2 -1
  55. {letta_nightly-0.6.14.dev20250123104106.dist-info → letta_nightly-0.6.15.dev20250124054224.dist-info}/RECORD +58 -57
  56. letta/orm/job_usage_statistics.py +0 -30
  57. {letta_nightly-0.6.14.dev20250123104106.dist-info → letta_nightly-0.6.15.dev20250124054224.dist-info}/LICENSE +0 -0
  58. {letta_nightly-0.6.14.dev20250123104106.dist-info → letta_nightly-0.6.15.dev20250124054224.dist-info}/WHEEL +0 -0
  59. {letta_nightly-0.6.14.dev20250123104106.dist-info → letta_nightly-0.6.15.dev20250124054224.dist-info}/entry_points.txt +0 -0
letta/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- __version__ = "0.6.14"
2
+ __version__ = "0.6.15"
3
3
 
4
4
 
5
5
  # import clients
letta/client/client.py CHANGED
@@ -206,7 +206,7 @@ class AbstractClient(object):
206
206
  ) -> Tool:
207
207
  raise NotImplementedError
208
208
 
209
- def list_tools(self, cursor: Optional[str] = None, limit: Optional[int] = 50) -> List[Tool]:
209
+ def list_tools(self, after: Optional[str] = None, limit: Optional[int] = 50) -> List[Tool]:
210
210
  raise NotImplementedError
211
211
 
212
212
  def get_tool(self, id: str) -> Tool:
@@ -266,7 +266,7 @@ class AbstractClient(object):
266
266
  def list_attached_sources(self, agent_id: str) -> List[Source]:
267
267
  raise NotImplementedError
268
268
 
269
- def list_files_from_source(self, source_id: str, limit: int = 1000, cursor: Optional[str] = None) -> List[FileMetadata]:
269
+ def list_files_from_source(self, source_id: str, limit: int = 1000, after: Optional[str] = None) -> List[FileMetadata]:
270
270
  raise NotImplementedError
271
271
 
272
272
  def update_source(self, source_id: str, name: Optional[str] = None) -> Source:
@@ -279,12 +279,12 @@ class AbstractClient(object):
279
279
  raise NotImplementedError
280
280
 
281
281
  def get_archival_memory(
282
- self, agent_id: str, before: Optional[str] = None, after: Optional[str] = None, limit: Optional[int] = 1000
282
+ self, agent_id: str, after: Optional[str] = None, before: Optional[str] = None, limit: Optional[int] = 1000
283
283
  ) -> List[Passage]:
284
284
  raise NotImplementedError
285
285
 
286
286
  def get_messages(
287
- self, agent_id: str, before: Optional[str] = None, after: Optional[str] = None, limit: Optional[int] = 1000
287
+ self, agent_id: str, after: Optional[str] = None, before: Optional[str] = None, limit: Optional[int] = 1000
288
288
  ) -> List[Message]:
289
289
  raise NotImplementedError
290
290
 
@@ -297,7 +297,7 @@ class AbstractClient(object):
297
297
  def create_org(self, name: Optional[str] = None) -> Organization:
298
298
  raise NotImplementedError
299
299
 
300
- def list_orgs(self, cursor: Optional[str] = None, limit: Optional[int] = 50) -> List[Organization]:
300
+ def list_orgs(self, after: Optional[str] = None, limit: Optional[int] = 50) -> List[Organization]:
301
301
  raise NotImplementedError
302
302
 
303
303
  def delete_org(self, org_id: str) -> Organization:
@@ -337,13 +337,13 @@ class AbstractClient(object):
337
337
  """
338
338
  raise NotImplementedError
339
339
 
340
- def list_sandbox_configs(self, limit: int = 50, cursor: Optional[str] = None) -> List[SandboxConfig]:
340
+ def list_sandbox_configs(self, limit: int = 50, after: Optional[str] = None) -> List[SandboxConfig]:
341
341
  """
342
342
  List all sandbox configurations.
343
343
 
344
344
  Args:
345
345
  limit (int, optional): The maximum number of sandbox configurations to return. Defaults to 50.
346
- cursor (Optional[str], optional): The pagination cursor for retrieving the next set of results.
346
+ after (Optional[str], optional): The pagination cursor for retrieving the next set of results.
347
347
 
348
348
  Returns:
349
349
  List[SandboxConfig]: A list of sandbox configurations.
@@ -394,7 +394,7 @@ class AbstractClient(object):
394
394
  raise NotImplementedError
395
395
 
396
396
  def list_sandbox_env_vars(
397
- self, sandbox_config_id: str, limit: int = 50, cursor: Optional[str] = None
397
+ self, sandbox_config_id: str, limit: int = 50, after: Optional[str] = None
398
398
  ) -> List[SandboxEnvironmentVariable]:
399
399
  """
400
400
  List all environment variables associated with a sandbox configuration.
@@ -402,7 +402,7 @@ class AbstractClient(object):
402
402
  Args:
403
403
  sandbox_config_id (str): The ID of the sandbox configuration to retrieve environment variables for.
404
404
  limit (int, optional): The maximum number of environment variables to return. Defaults to 50.
405
- cursor (Optional[str], optional): The pagination cursor for retrieving the next set of results.
405
+ after (Optional[str], optional): The pagination cursor for retrieving the next set of results.
406
406
 
407
407
  Returns:
408
408
  List[SandboxEnvironmentVariable]: A list of environment variables.
@@ -477,7 +477,12 @@ class RESTClient(AbstractClient):
477
477
  self._default_embedding_config = default_embedding_config
478
478
 
479
479
  def list_agents(
480
- self, tags: Optional[List[str]] = None, query_text: Optional[str] = None, limit: int = 50, cursor: Optional[str] = None
480
+ self,
481
+ tags: Optional[List[str]] = None,
482
+ query_text: Optional[str] = None,
483
+ limit: int = 50,
484
+ before: Optional[str] = None,
485
+ after: Optional[str] = None,
481
486
  ) -> List[AgentState]:
482
487
  params = {"limit": limit}
483
488
  if tags:
@@ -487,11 +492,13 @@ class RESTClient(AbstractClient):
487
492
  if query_text:
488
493
  params["query_text"] = query_text
489
494
 
490
- if cursor:
491
- params["cursor"] = cursor
495
+ if before:
496
+ params["before"] = before
497
+
498
+ if after:
499
+ params["after"] = after
492
500
 
493
501
  response = requests.get(f"{self.base_url}/{self.api_prefix}/agents", headers=self.headers, params=params)
494
- print(f"\nLIST RESPONSE\n{response.json()}\n")
495
502
  return [AgentState(**agent) for agent in response.json()]
496
503
 
497
504
  def agent_exists(self, agent_id: str) -> bool:
@@ -636,7 +643,7 @@ class RESTClient(AbstractClient):
636
643
  ) -> Message:
637
644
  request = MessageUpdate(
638
645
  role=role,
639
- text=text,
646
+ content=text,
640
647
  name=name,
641
648
  tool_calls=tool_calls,
642
649
  tool_call_id=tool_call_id,
@@ -1009,7 +1016,7 @@ class RESTClient(AbstractClient):
1009
1016
  response (LettaResponse): Response from the agent
1010
1017
  """
1011
1018
  # TODO: implement include_full_message
1012
- messages = [MessageCreate(role=MessageRole(role), text=message, name=name)]
1019
+ messages = [MessageCreate(role=MessageRole(role), content=message, name=name)]
1013
1020
  # TODO: figure out how to handle stream_steps and stream_tokens
1014
1021
 
1015
1022
  # When streaming steps is True, stream_tokens must be False
@@ -1056,7 +1063,7 @@ class RESTClient(AbstractClient):
1056
1063
  Returns:
1057
1064
  job (Job): Information about the async job
1058
1065
  """
1059
- messages = [MessageCreate(role=MessageRole(role), text=message, name=name)]
1066
+ messages = [MessageCreate(role=MessageRole(role), content=message, name=name)]
1060
1067
 
1061
1068
  request = LettaRequest(messages=messages)
1062
1069
  response = requests.post(
@@ -1359,7 +1366,7 @@ class RESTClient(AbstractClient):
1359
1366
  def load_data(self, connector: DataConnector, source_name: str):
1360
1367
  raise NotImplementedError
1361
1368
 
1362
- def load_file_to_source(self, filename: str, source_id: str, blocking=True):
1369
+ def load_file_to_source(self, filename: str, source_id: str, blocking=True) -> Job:
1363
1370
  """
1364
1371
  Load a file into a source
1365
1372
 
@@ -1427,20 +1434,20 @@ class RESTClient(AbstractClient):
1427
1434
  raise ValueError(f"Failed to list attached sources: {response.text}")
1428
1435
  return [Source(**source) for source in response.json()]
1429
1436
 
1430
- def list_files_from_source(self, source_id: str, limit: int = 1000, cursor: Optional[str] = None) -> List[FileMetadata]:
1437
+ def list_files_from_source(self, source_id: str, limit: int = 1000, after: Optional[str] = None) -> List[FileMetadata]:
1431
1438
  """
1432
1439
  List files from source with pagination support.
1433
1440
 
1434
1441
  Args:
1435
1442
  source_id (str): ID of the source
1436
1443
  limit (int): Number of files to return
1437
- cursor (Optional[str]): Pagination cursor for fetching the next page
1444
+ after (str): Get files after a certain time
1438
1445
 
1439
1446
  Returns:
1440
1447
  List[FileMetadata]: List of files
1441
1448
  """
1442
1449
  # Prepare query parameters for pagination
1443
- params = {"limit": limit, "cursor": cursor}
1450
+ params = {"limit": limit, "after": after}
1444
1451
 
1445
1452
  # Make the request to the FastAPI endpoint
1446
1453
  response = requests.get(f"{self.base_url}/{self.api_prefix}/sources/{source_id}/files", headers=self.headers, params=params)
@@ -1640,7 +1647,7 @@ class RESTClient(AbstractClient):
1640
1647
  raise ValueError(f"Failed to update tool: {response.text}")
1641
1648
  return Tool(**response.json())
1642
1649
 
1643
- def list_tools(self, cursor: Optional[str] = None, limit: Optional[int] = 50) -> List[Tool]:
1650
+ def list_tools(self, after: Optional[str] = None, limit: Optional[int] = 50) -> List[Tool]:
1644
1651
  """
1645
1652
  List available tools for the user.
1646
1653
 
@@ -1648,8 +1655,8 @@ class RESTClient(AbstractClient):
1648
1655
  tools (List[Tool]): List of tools
1649
1656
  """
1650
1657
  params = {}
1651
- if cursor:
1652
- params["cursor"] = str(cursor)
1658
+ if after:
1659
+ params["after"] = after
1653
1660
  if limit:
1654
1661
  params["limit"] = limit
1655
1662
 
@@ -1728,15 +1735,15 @@ class RESTClient(AbstractClient):
1728
1735
  raise ValueError(f"Failed to list embedding configs: {response.text}")
1729
1736
  return [EmbeddingConfig(**config) for config in response.json()]
1730
1737
 
1731
- def list_orgs(self, cursor: Optional[str] = None, limit: Optional[int] = 50) -> List[Organization]:
1738
+ def list_orgs(self, after: Optional[str] = None, limit: Optional[int] = 50) -> List[Organization]:
1732
1739
  """
1733
1740
  Retrieves a list of all organizations in the database, with optional pagination.
1734
1741
 
1735
- @param cursor: the pagination cursor, if any
1742
+ @param after: the pagination cursor, if any
1736
1743
  @param limit: the maximum number of organizations to retrieve
1737
1744
  @return: a list of Organization objects
1738
1745
  """
1739
- params = {"cursor": cursor, "limit": limit}
1746
+ params = {"after": after, "limit": limit}
1740
1747
  response = requests.get(f"{self.base_url}/{ADMIN_PREFIX}/orgs", headers=self.headers, params=params)
1741
1748
  if response.status_code != 200:
1742
1749
  raise ValueError(f"Failed to retrieve organizations: {response.text}")
@@ -1779,6 +1786,12 @@ class RESTClient(AbstractClient):
1779
1786
  def create_sandbox_config(self, config: Union[LocalSandboxConfig, E2BSandboxConfig]) -> SandboxConfig:
1780
1787
  """
1781
1788
  Create a new sandbox configuration.
1789
+
1790
+ Args:
1791
+ config (Union[LocalSandboxConfig, E2BSandboxConfig]): The sandbox settings.
1792
+
1793
+ Returns:
1794
+ SandboxConfig: The created sandbox configuration.
1782
1795
  """
1783
1796
  payload = {
1784
1797
  "config": config.model_dump(),
@@ -1791,6 +1804,13 @@ class RESTClient(AbstractClient):
1791
1804
  def update_sandbox_config(self, sandbox_config_id: str, config: Union[LocalSandboxConfig, E2BSandboxConfig]) -> SandboxConfig:
1792
1805
  """
1793
1806
  Update an existing sandbox configuration.
1807
+
1808
+ Args:
1809
+ sandbox_config_id (str): The ID of the sandbox configuration to update.
1810
+ config (Union[LocalSandboxConfig, E2BSandboxConfig]): The updated sandbox settings.
1811
+
1812
+ Returns:
1813
+ SandboxConfig: The updated sandbox configuration.
1794
1814
  """
1795
1815
  payload = {
1796
1816
  "config": config.model_dump(),
@@ -1807,6 +1827,9 @@ class RESTClient(AbstractClient):
1807
1827
  def delete_sandbox_config(self, sandbox_config_id: str) -> None:
1808
1828
  """
1809
1829
  Delete a sandbox configuration.
1830
+
1831
+ Args:
1832
+ sandbox_config_id (str): The ID of the sandbox configuration to delete.
1810
1833
  """
1811
1834
  response = requests.delete(f"{self.base_url}/{self.api_prefix}/sandbox-config/{sandbox_config_id}", headers=self.headers)
1812
1835
  if response.status_code == 404:
@@ -1814,11 +1837,18 @@ class RESTClient(AbstractClient):
1814
1837
  elif response.status_code != 204:
1815
1838
  raise ValueError(f"Failed to delete sandbox config with ID '{sandbox_config_id}': {response.text}")
1816
1839
 
1817
- def list_sandbox_configs(self, limit: int = 50, cursor: Optional[str] = None) -> List[SandboxConfig]:
1840
+ def list_sandbox_configs(self, limit: int = 50, after: Optional[str] = None) -> List[SandboxConfig]:
1818
1841
  """
1819
1842
  List all sandbox configurations.
1843
+
1844
+ Args:
1845
+ limit (int, optional): The maximum number of sandbox configurations to return. Defaults to 50.
1846
+ after (Optional[str], optional): The pagination cursor for retrieving the next set of results.
1847
+
1848
+ Returns:
1849
+ List[SandboxConfig]: A list of sandbox configurations.
1820
1850
  """
1821
- params = {"limit": limit, "cursor": cursor}
1851
+ params = {"limit": limit, "after": after}
1822
1852
  response = requests.get(f"{self.base_url}/{self.api_prefix}/sandbox-config", headers=self.headers, params=params)
1823
1853
  if response.status_code != 200:
1824
1854
  raise ValueError(f"Failed to list sandbox configs: {response.text}")
@@ -1829,6 +1859,15 @@ class RESTClient(AbstractClient):
1829
1859
  ) -> SandboxEnvironmentVariable:
1830
1860
  """
1831
1861
  Create a new environment variable for a sandbox configuration.
1862
+
1863
+ Args:
1864
+ sandbox_config_id (str): The ID of the sandbox configuration to associate the environment variable with.
1865
+ key (str): The name of the environment variable.
1866
+ value (str): The value of the environment variable.
1867
+ description (Optional[str], optional): A description of the environment variable. Defaults to None.
1868
+
1869
+ Returns:
1870
+ SandboxEnvironmentVariable: The created environment variable.
1832
1871
  """
1833
1872
  payload = {"key": key, "value": value, "description": description}
1834
1873
  response = requests.post(
@@ -1845,6 +1884,15 @@ class RESTClient(AbstractClient):
1845
1884
  ) -> SandboxEnvironmentVariable:
1846
1885
  """
1847
1886
  Update an existing environment variable.
1887
+
1888
+ Args:
1889
+ env_var_id (str): The ID of the environment variable to update.
1890
+ key (Optional[str], optional): The updated name of the environment variable. Defaults to None.
1891
+ value (Optional[str], optional): The updated value of the environment variable. Defaults to None.
1892
+ description (Optional[str], optional): The updated description of the environment variable. Defaults to None.
1893
+
1894
+ Returns:
1895
+ SandboxEnvironmentVariable: The updated environment variable.
1848
1896
  """
1849
1897
  payload = {k: v for k, v in {"key": key, "value": value, "description": description}.items() if v is not None}
1850
1898
  response = requests.patch(
@@ -1859,6 +1907,9 @@ class RESTClient(AbstractClient):
1859
1907
  def delete_sandbox_env_var(self, env_var_id: str) -> None:
1860
1908
  """
1861
1909
  Delete an environment variable by its ID.
1910
+
1911
+ Args:
1912
+ env_var_id (str): The ID of the environment variable to delete.
1862
1913
  """
1863
1914
  response = requests.delete(
1864
1915
  f"{self.base_url}/{self.api_prefix}/sandbox-config/environment-variable/{env_var_id}", headers=self.headers
@@ -1869,12 +1920,20 @@ class RESTClient(AbstractClient):
1869
1920
  raise ValueError(f"Failed to delete environment variable with ID '{env_var_id}': {response.text}")
1870
1921
 
1871
1922
  def list_sandbox_env_vars(
1872
- self, sandbox_config_id: str, limit: int = 50, cursor: Optional[str] = None
1923
+ self, sandbox_config_id: str, limit: int = 50, after: Optional[str] = None
1873
1924
  ) -> List[SandboxEnvironmentVariable]:
1874
1925
  """
1875
1926
  List all environment variables associated with a sandbox configuration.
1927
+
1928
+ Args:
1929
+ sandbox_config_id (str): The ID of the sandbox configuration to retrieve environment variables for.
1930
+ limit (int, optional): The maximum number of environment variables to return. Defaults to 50.
1931
+ after (Optional[str], optional): The pagination cursor for retrieving the next set of results.
1932
+
1933
+ Returns:
1934
+ List[SandboxEnvironmentVariable]: A list of environment variables.
1876
1935
  """
1877
- params = {"limit": limit, "cursor": cursor}
1936
+ params = {"limit": limit, "after": after}
1878
1937
  response = requests.get(
1879
1938
  f"{self.base_url}/{self.api_prefix}/sandbox-config/{sandbox_config_id}/environment-variable",
1880
1939
  headers=self.headers,
@@ -2035,7 +2094,8 @@ class RESTClient(AbstractClient):
2035
2094
  def get_run_messages(
2036
2095
  self,
2037
2096
  run_id: str,
2038
- cursor: Optional[str] = None,
2097
+ before: Optional[str] = None,
2098
+ after: Optional[str] = None,
2039
2099
  limit: Optional[int] = 100,
2040
2100
  ascending: bool = True,
2041
2101
  role: Optional[MessageRole] = None,
@@ -2045,7 +2105,8 @@ class RESTClient(AbstractClient):
2045
2105
 
2046
2106
  Args:
2047
2107
  job_id: ID of the job
2048
- cursor: Cursor for pagination
2108
+ before: Cursor for pagination
2109
+ after: Cursor for pagination
2049
2110
  limit: Maximum number of messages to return
2050
2111
  ascending: Sort order by creation time
2051
2112
  role: Filter by message role (user/assistant/system/tool)
@@ -2053,7 +2114,8 @@ class RESTClient(AbstractClient):
2053
2114
  List of messages matching the filter criteria
2054
2115
  """
2055
2116
  params = {
2056
- "cursor": cursor,
2117
+ "before": before,
2118
+ "after": after,
2057
2119
  "limit": limit,
2058
2120
  "ascending": ascending,
2059
2121
  "role": role,
@@ -2151,15 +2213,15 @@ class RESTClient(AbstractClient):
2151
2213
 
2152
2214
  def get_tags(
2153
2215
  self,
2154
- cursor: Optional[str] = None,
2155
- limit: Optional[int] = None,
2216
+ after: Optional[str] = None,
2217
+ limit: int = 100,
2156
2218
  query_text: Optional[str] = None,
2157
2219
  ) -> List[str]:
2158
2220
  """
2159
2221
  Get a list of all unique tags.
2160
2222
 
2161
2223
  Args:
2162
- cursor: Optional cursor for pagination (last tag seen)
2224
+ after: Optional cursor for pagination (first tag seen)
2163
2225
  limit: Optional maximum number of tags to return
2164
2226
  query_text: Optional text to filter tags
2165
2227
 
@@ -2167,8 +2229,8 @@ class RESTClient(AbstractClient):
2167
2229
  List[str]: List of unique tags
2168
2230
  """
2169
2231
  params = {}
2170
- if cursor:
2171
- params["cursor"] = cursor
2232
+ if after:
2233
+ params["after"] = after
2172
2234
  if limit:
2173
2235
  params["limit"] = limit
2174
2236
  if query_text:
@@ -2238,11 +2300,18 @@ class LocalClient(AbstractClient):
2238
2300
 
2239
2301
  # agents
2240
2302
  def list_agents(
2241
- self, query_text: Optional[str] = None, tags: Optional[List[str]] = None, limit: int = 100, cursor: Optional[str] = None
2303
+ self,
2304
+ query_text: Optional[str] = None,
2305
+ tags: Optional[List[str]] = None,
2306
+ limit: int = 100,
2307
+ before: Optional[str] = None,
2308
+ after: Optional[str] = None,
2242
2309
  ) -> List[AgentState]:
2243
2310
  self.interface.clear()
2244
2311
 
2245
- return self.server.agent_manager.list_agents(actor=self.user, tags=tags, query_text=query_text, limit=limit, cursor=cursor)
2312
+ return self.server.agent_manager.list_agents(
2313
+ actor=self.user, tags=tags, query_text=query_text, limit=limit, before=before, after=after
2314
+ )
2246
2315
 
2247
2316
  def agent_exists(self, agent_id: Optional[str] = None, agent_name: Optional[str] = None) -> bool:
2248
2317
  """
@@ -2374,7 +2443,7 @@ class LocalClient(AbstractClient):
2374
2443
  message_id=message_id,
2375
2444
  request=MessageUpdate(
2376
2445
  role=role,
2377
- text=text,
2446
+ content=text,
2378
2447
  name=name,
2379
2448
  tool_calls=tool_calls,
2380
2449
  tool_call_id=tool_call_id,
@@ -2673,7 +2742,7 @@ class LocalClient(AbstractClient):
2673
2742
  usage = self.server.send_messages(
2674
2743
  actor=self.user,
2675
2744
  agent_id=agent_id,
2676
- messages=[MessageCreate(role=MessageRole(role), text=message, name=name)],
2745
+ messages=[MessageCreate(role=MessageRole(role), content=message, name=name)],
2677
2746
  )
2678
2747
 
2679
2748
  ## TODO: need to make sure date/timestamp is propely passed
@@ -2990,7 +3059,7 @@ class LocalClient(AbstractClient):
2990
3059
  id: str,
2991
3060
  name: Optional[str] = None,
2992
3061
  description: Optional[str] = None,
2993
- func: Optional[callable] = None,
3062
+ func: Optional[Callable] = None,
2994
3063
  tags: Optional[List[str]] = None,
2995
3064
  return_char_limit: int = FUNCTION_RETURN_CHAR_LIMIT,
2996
3065
  ) -> Tool:
@@ -3021,14 +3090,14 @@ class LocalClient(AbstractClient):
3021
3090
 
3022
3091
  return self.server.tool_manager.update_tool_by_id(tool_id=id, tool_update=ToolUpdate(**update_data), actor=self.user)
3023
3092
 
3024
- def list_tools(self, cursor: Optional[str] = None, limit: Optional[int] = 50) -> List[Tool]:
3093
+ def list_tools(self, after: Optional[str] = None, limit: Optional[int] = 50) -> List[Tool]:
3025
3094
  """
3026
3095
  List available tools for the user.
3027
3096
 
3028
3097
  Returns:
3029
3098
  tools (List[Tool]): List of tools
3030
3099
  """
3031
- return self.server.tool_manager.list_tools(cursor=cursor, limit=limit, actor=self.user)
3100
+ return self.server.tool_manager.list_tools(after=after, limit=limit, actor=self.user)
3032
3101
 
3033
3102
  def get_tool(self, id: str) -> Optional[Tool]:
3034
3103
  """
@@ -3227,19 +3296,19 @@ class LocalClient(AbstractClient):
3227
3296
  """
3228
3297
  return self.server.agent_manager.list_attached_sources(agent_id=agent_id, actor=self.user)
3229
3298
 
3230
- def list_files_from_source(self, source_id: str, limit: int = 1000, cursor: Optional[str] = None) -> List[FileMetadata]:
3299
+ def list_files_from_source(self, source_id: str, limit: int = 1000, after: Optional[str] = None) -> List[FileMetadata]:
3231
3300
  """
3232
3301
  List files from source.
3233
3302
 
3234
3303
  Args:
3235
3304
  source_id (str): ID of the source
3236
3305
  limit (int): The # of items to return
3237
- cursor (str): The cursor for fetching the next page
3306
+ after (str): The cursor for fetching the next page
3238
3307
 
3239
3308
  Returns:
3240
3309
  files (List[FileMetadata]): List of files
3241
3310
  """
3242
- return self.server.source_manager.list_files(source_id=source_id, limit=limit, cursor=cursor, actor=self.user)
3311
+ return self.server.source_manager.list_files(source_id=source_id, limit=limit, after=after, actor=self.user)
3243
3312
 
3244
3313
  def update_source(self, source_id: str, name: Optional[str] = None) -> Source:
3245
3314
  """
@@ -3297,17 +3366,20 @@ class LocalClient(AbstractClient):
3297
3366
  passages (List[Passage]): List of passages
3298
3367
  """
3299
3368
 
3300
- return self.server.get_agent_archival_cursor(user_id=self.user_id, agent_id=agent_id, limit=limit)
3369
+ return self.server.get_agent_archival(user_id=self.user_id, agent_id=agent_id, limit=limit)
3301
3370
 
3302
3371
  # recall memory
3303
3372
 
3304
- def get_messages(self, agent_id: str, cursor: Optional[str] = None, limit: Optional[int] = 1000) -> List[Message]:
3373
+ def get_messages(
3374
+ self, agent_id: str, before: Optional[str] = None, after: Optional[str] = None, limit: Optional[int] = 1000
3375
+ ) -> List[Message]:
3305
3376
  """
3306
3377
  Get messages from an agent with pagination.
3307
3378
 
3308
3379
  Args:
3309
3380
  agent_id (str): ID of the agent
3310
- cursor (str): Get messages after a certain time
3381
+ before (str): Get messages before a certain time
3382
+ after (str): Get messages after a certain time
3311
3383
  limit (int): Limit number of messages
3312
3384
 
3313
3385
  Returns:
@@ -3315,10 +3387,11 @@ class LocalClient(AbstractClient):
3315
3387
  """
3316
3388
 
3317
3389
  self.interface.clear()
3318
- return self.server.get_agent_recall_cursor(
3390
+ return self.server.get_agent_recall(
3319
3391
  user_id=self.user_id,
3320
3392
  agent_id=agent_id,
3321
- before=cursor,
3393
+ before=before,
3394
+ after=after,
3322
3395
  limit=limit,
3323
3396
  reverse=True,
3324
3397
  )
@@ -3437,8 +3510,8 @@ class LocalClient(AbstractClient):
3437
3510
  def create_org(self, name: Optional[str] = None) -> Organization:
3438
3511
  return self.server.organization_manager.create_organization(pydantic_org=Organization(name=name))
3439
3512
 
3440
- def list_orgs(self, cursor: Optional[str] = None, limit: Optional[int] = 50) -> List[Organization]:
3441
- return self.server.organization_manager.list_organizations(cursor=cursor, limit=limit)
3513
+ def list_orgs(self, after: Optional[str] = None, limit: Optional[int] = 50) -> List[Organization]:
3514
+ return self.server.organization_manager.list_organizations(limit=limit, after=after)
3442
3515
 
3443
3516
  def delete_org(self, org_id: str) -> Organization:
3444
3517
  return self.server.organization_manager.delete_organization_by_id(org_id=org_id)
@@ -3465,11 +3538,11 @@ class LocalClient(AbstractClient):
3465
3538
  """
3466
3539
  return self.server.sandbox_config_manager.delete_sandbox_config(sandbox_config_id=sandbox_config_id, actor=self.user)
3467
3540
 
3468
- def list_sandbox_configs(self, limit: int = 50, cursor: Optional[str] = None) -> List[SandboxConfig]:
3541
+ def list_sandbox_configs(self, limit: int = 50, after: Optional[str] = None) -> List[SandboxConfig]:
3469
3542
  """
3470
3543
  List all sandbox configurations.
3471
3544
  """
3472
- return self.server.sandbox_config_manager.list_sandbox_configs(actor=self.user, limit=limit, cursor=cursor)
3545
+ return self.server.sandbox_config_manager.list_sandbox_configs(actor=self.user, limit=limit, after=after)
3473
3546
 
3474
3547
  def create_sandbox_env_var(
3475
3548
  self, sandbox_config_id: str, key: str, value: str, description: Optional[str] = None
@@ -3500,13 +3573,13 @@ class LocalClient(AbstractClient):
3500
3573
  return self.server.sandbox_config_manager.delete_sandbox_env_var(env_var_id=env_var_id, actor=self.user)
3501
3574
 
3502
3575
  def list_sandbox_env_vars(
3503
- self, sandbox_config_id: str, limit: int = 50, cursor: Optional[str] = None
3576
+ self, sandbox_config_id: str, limit: int = 50, after: Optional[str] = None
3504
3577
  ) -> List[SandboxEnvironmentVariable]:
3505
3578
  """
3506
3579
  List all environment variables associated with a sandbox configuration.
3507
3580
  """
3508
3581
  return self.server.sandbox_config_manager.list_sandbox_env_vars(
3509
- sandbox_config_id=sandbox_config_id, actor=self.user, limit=limit, cursor=cursor
3582
+ sandbox_config_id=sandbox_config_id, actor=self.user, limit=limit, after=after
3510
3583
  )
3511
3584
 
3512
3585
  def update_agent_memory_block_label(self, agent_id: str, current_label: str, new_label: str) -> Memory:
@@ -3627,7 +3700,8 @@ class LocalClient(AbstractClient):
3627
3700
  def get_run_messages(
3628
3701
  self,
3629
3702
  run_id: str,
3630
- cursor: Optional[str] = None,
3703
+ before: Optional[str] = None,
3704
+ after: Optional[str] = None,
3631
3705
  limit: Optional[int] = 100,
3632
3706
  ascending: bool = True,
3633
3707
  role: Optional[MessageRole] = None,
@@ -3637,21 +3711,23 @@ class LocalClient(AbstractClient):
3637
3711
 
3638
3712
  Args:
3639
3713
  run_id: ID of the run
3640
- cursor: Cursor for pagination
3714
+ before: Cursor for pagination
3715
+ after: Cursor for pagination
3641
3716
  limit: Maximum number of messages to return
3642
3717
  ascending: Sort order by creation time
3643
3718
  role: Filter by message role (user/assistant/system/tool)
3644
-
3645
3719
  Returns:
3646
3720
  List of messages matching the filter criteria
3647
3721
  """
3648
3722
  params = {
3649
- "cursor": cursor,
3723
+ "before": before,
3724
+ "after": after,
3650
3725
  "limit": limit,
3651
3726
  "ascending": ascending,
3652
3727
  "role": role,
3653
3728
  }
3654
- return self.server.job_manager.get_run_messages_cursor(run_id=run_id, actor=self.user, **params)
3729
+
3730
+ return self.server.job_manager.get_run_messages(run_id=run_id, actor=self.user, **params)
3655
3731
 
3656
3732
  def get_run_usage(
3657
3733
  self,
@@ -3713,9 +3789,9 @@ class LocalClient(AbstractClient):
3713
3789
 
3714
3790
  def get_tags(
3715
3791
  self,
3716
- cursor: str = None,
3717
- limit: int = 100,
3718
- query_text: str = None,
3792
+ after: Optional[str] = None,
3793
+ limit: Optional[int] = None,
3794
+ query_text: Optional[str] = None,
3719
3795
  ) -> List[str]:
3720
3796
  """
3721
3797
  Get all tags.
@@ -3723,4 +3799,4 @@ class LocalClient(AbstractClient):
3723
3799
  Returns:
3724
3800
  tags (List[str]): List of tags
3725
3801
  """
3726
- return self.server.agent_manager.list_tags(actor=self.user, cursor=cursor, limit=limit, query_text=query_text)
3802
+ return self.server.agent_manager.list_tags(actor=self.user, after=after, limit=limit, query_text=query_text)
letta/client/streaming.py CHANGED
@@ -50,7 +50,7 @@ def _sse_post(url: str, data: dict, headers: dict) -> Generator[LettaStreamingRe
50
50
  chunk_data = json.loads(sse.data)
51
51
  if "reasoning" in chunk_data:
52
52
  yield ReasoningMessage(**chunk_data)
53
- elif "assistant_message" in chunk_data:
53
+ elif "message_type" in chunk_data and chunk_data["message_type"] == "assistant_message":
54
54
  yield AssistantMessage(**chunk_data)
55
55
  elif "tool_call" in chunk_data:
56
56
  yield ToolCallMessage(**chunk_data)
@@ -6,7 +6,7 @@ import requests
6
6
 
7
7
  from letta.constants import MESSAGE_CHATGPT_FUNCTION_MODEL, MESSAGE_CHATGPT_FUNCTION_SYSTEM_MESSAGE
8
8
  from letta.llm_api.llm_api_tools import create
9
- from letta.schemas.message import Message
9
+ from letta.schemas.message import Message, TextContent
10
10
  from letta.utils import json_dumps, json_loads
11
11
 
12
12
 
@@ -23,8 +23,13 @@ def message_chatgpt(self, message: str):
23
23
  dummy_user_id = uuid.uuid4()
24
24
  dummy_agent_id = uuid.uuid4()
25
25
  message_sequence = [
26
- Message(user_id=dummy_user_id, agent_id=dummy_agent_id, role="system", text=MESSAGE_CHATGPT_FUNCTION_SYSTEM_MESSAGE),
27
- Message(user_id=dummy_user_id, agent_id=dummy_agent_id, role="user", text=str(message)),
26
+ Message(
27
+ user_id=dummy_user_id,
28
+ agent_id=dummy_agent_id,
29
+ role="system",
30
+ content=[TextContent(text=MESSAGE_CHATGPT_FUNCTION_SYSTEM_MESSAGE)],
31
+ ),
32
+ Message(user_id=dummy_user_id, agent_id=dummy_agent_id, role="user", content=[TextContent(text=str(message))]),
28
33
  ]
29
34
  # TODO: this will error without an LLMConfig
30
35
  response = create(
@@ -74,7 +74,7 @@ def send_message_to_agents_matching_all_tags(self: "Agent", message: str, tags:
74
74
  server = get_letta_server()
75
75
 
76
76
  # Retrieve agents that match ALL specified tags
77
- matching_agents = server.agent_manager.list_agents(actor=self.user, tags=tags, match_all_tags=True, cursor=None, limit=100)
77
+ matching_agents = server.agent_manager.list_agents(actor=self.user, tags=tags, match_all_tags=True, limit=100)
78
78
 
79
79
  async def send_messages_to_all_agents():
80
80
  tasks = [
@@ -246,7 +246,7 @@ def parse_letta_response_for_assistant_message(
246
246
  reasoning_message = ""
247
247
  for m in letta_response.messages:
248
248
  if isinstance(m, AssistantMessage):
249
- return m.assistant_message
249
+ return m.content
250
250
  elif isinstance(m, ToolCallMessage) and m.tool_call.name == assistant_message_tool_name:
251
251
  try:
252
252
  return json.loads(m.tool_call.arguments)[assistant_message_tool_kwarg]
@@ -290,7 +290,7 @@ async def async_send_message_with_retries(
290
290
  logging_prefix = logging_prefix or "[async_send_message_with_retries]"
291
291
  for attempt in range(1, max_retries + 1):
292
292
  try:
293
- messages = [MessageCreate(role=MessageRole.user, text=message_text, name=sender_agent.agent_state.name)]
293
+ messages = [MessageCreate(role=MessageRole.user, content=message_text, name=sender_agent.agent_state.name)]
294
294
  # Wrap in a timeout
295
295
  response = await asyncio.wait_for(
296
296
  server.send_message_to_agent(