letta-client 0.1.53__py3-none-any.whl → 0.1.55__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-client might be problematic. Click here for more details.

@@ -26,6 +26,7 @@ from ..types.embedding_config import EmbeddingConfig
26
26
  from ..types.message_create import MessageCreate
27
27
  from ..core.serialization import convert_and_respect_annotation_metadata
28
28
  from ..core.jsonable_encoder import jsonable_encoder
29
+ from .. import core
29
30
  from .types.update_agent_tool_rules_item import UpdateAgentToolRulesItem
30
31
  import datetime as dt
31
32
  from ..types.passage import Passage
@@ -414,6 +415,136 @@ class AgentsClient:
414
415
  raise ApiError(status_code=_response.status_code, body=_response.text)
415
416
  raise ApiError(status_code=_response.status_code, body=_response_json)
416
417
 
418
+ def download_agent_serialized(
419
+ self, agent_id: str, *, request_options: typing.Optional[RequestOptions] = None
420
+ ) -> typing.Optional[typing.Any]:
421
+ """
422
+ Download the serialized JSON representation of an agent.
423
+
424
+ Parameters
425
+ ----------
426
+ agent_id : str
427
+
428
+ request_options : typing.Optional[RequestOptions]
429
+ Request-specific configuration.
430
+
431
+ Returns
432
+ -------
433
+ typing.Optional[typing.Any]
434
+ Successful Response
435
+
436
+ Examples
437
+ --------
438
+ from letta_client import Letta
439
+
440
+ client = Letta(
441
+ token="YOUR_TOKEN",
442
+ )
443
+ client.agents.download_agent_serialized(
444
+ agent_id="agent_id",
445
+ )
446
+ """
447
+ _response = self._client_wrapper.httpx_client.request(
448
+ f"v1/agents/{jsonable_encoder(agent_id)}/download",
449
+ method="GET",
450
+ request_options=request_options,
451
+ )
452
+ try:
453
+ if 200 <= _response.status_code < 300:
454
+ return typing.cast(
455
+ typing.Optional[typing.Any],
456
+ construct_type(
457
+ type_=typing.Optional[typing.Any], # type: ignore
458
+ object_=_response.json(),
459
+ ),
460
+ )
461
+ if _response.status_code == 422:
462
+ raise UnprocessableEntityError(
463
+ typing.cast(
464
+ HttpValidationError,
465
+ construct_type(
466
+ type_=HttpValidationError, # type: ignore
467
+ object_=_response.json(),
468
+ ),
469
+ )
470
+ )
471
+ _response_json = _response.json()
472
+ except JSONDecodeError:
473
+ raise ApiError(status_code=_response.status_code, body=_response.text)
474
+ raise ApiError(status_code=_response.status_code, body=_response_json)
475
+
476
+ def upload_agent_serialized(
477
+ self,
478
+ *,
479
+ file: core.File,
480
+ mark_as_copy: typing.Optional[bool] = None,
481
+ request_options: typing.Optional[RequestOptions] = None,
482
+ ) -> AgentState:
483
+ """
484
+ Upload a serialized agent JSON file and recreate the agent in the system.
485
+
486
+ Parameters
487
+ ----------
488
+ file : core.File
489
+ See core.File for more documentation
490
+
491
+ mark_as_copy : typing.Optional[bool]
492
+ Whether to mark the uploaded agent as a copy
493
+
494
+ request_options : typing.Optional[RequestOptions]
495
+ Request-specific configuration.
496
+
497
+ Returns
498
+ -------
499
+ AgentState
500
+ Successful Response
501
+
502
+ Examples
503
+ --------
504
+ from letta_client import Letta
505
+
506
+ client = Letta(
507
+ token="YOUR_TOKEN",
508
+ )
509
+ client.agents.upload_agent_serialized()
510
+ """
511
+ _response = self._client_wrapper.httpx_client.request(
512
+ "v1/agents/upload",
513
+ method="POST",
514
+ params={
515
+ "mark_as_copy": mark_as_copy,
516
+ },
517
+ data={},
518
+ files={
519
+ "file": file,
520
+ },
521
+ request_options=request_options,
522
+ omit=OMIT,
523
+ )
524
+ try:
525
+ if 200 <= _response.status_code < 300:
526
+ return typing.cast(
527
+ AgentState,
528
+ construct_type(
529
+ type_=AgentState, # type: ignore
530
+ object_=_response.json(),
531
+ ),
532
+ )
533
+ if _response.status_code == 422:
534
+ raise UnprocessableEntityError(
535
+ typing.cast(
536
+ HttpValidationError,
537
+ construct_type(
538
+ type_=HttpValidationError, # type: ignore
539
+ object_=_response.json(),
540
+ ),
541
+ )
542
+ )
543
+ _response_json = _response.json()
544
+ except JSONDecodeError:
545
+ raise ApiError(status_code=_response.status_code, body=_response.text)
546
+ raise ApiError(status_code=_response.status_code, body=_response_json)
547
+
417
548
  def retrieve(self, agent_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> AgentState:
418
549
  """
419
550
  Get the state of the agent.
@@ -1359,6 +1490,152 @@ class AsyncAgentsClient:
1359
1490
  raise ApiError(status_code=_response.status_code, body=_response.text)
1360
1491
  raise ApiError(status_code=_response.status_code, body=_response_json)
1361
1492
 
1493
+ async def download_agent_serialized(
1494
+ self, agent_id: str, *, request_options: typing.Optional[RequestOptions] = None
1495
+ ) -> typing.Optional[typing.Any]:
1496
+ """
1497
+ Download the serialized JSON representation of an agent.
1498
+
1499
+ Parameters
1500
+ ----------
1501
+ agent_id : str
1502
+
1503
+ request_options : typing.Optional[RequestOptions]
1504
+ Request-specific configuration.
1505
+
1506
+ Returns
1507
+ -------
1508
+ typing.Optional[typing.Any]
1509
+ Successful Response
1510
+
1511
+ Examples
1512
+ --------
1513
+ import asyncio
1514
+
1515
+ from letta_client import AsyncLetta
1516
+
1517
+ client = AsyncLetta(
1518
+ token="YOUR_TOKEN",
1519
+ )
1520
+
1521
+
1522
+ async def main() -> None:
1523
+ await client.agents.download_agent_serialized(
1524
+ agent_id="agent_id",
1525
+ )
1526
+
1527
+
1528
+ asyncio.run(main())
1529
+ """
1530
+ _response = await self._client_wrapper.httpx_client.request(
1531
+ f"v1/agents/{jsonable_encoder(agent_id)}/download",
1532
+ method="GET",
1533
+ request_options=request_options,
1534
+ )
1535
+ try:
1536
+ if 200 <= _response.status_code < 300:
1537
+ return typing.cast(
1538
+ typing.Optional[typing.Any],
1539
+ construct_type(
1540
+ type_=typing.Optional[typing.Any], # type: ignore
1541
+ object_=_response.json(),
1542
+ ),
1543
+ )
1544
+ if _response.status_code == 422:
1545
+ raise UnprocessableEntityError(
1546
+ typing.cast(
1547
+ HttpValidationError,
1548
+ construct_type(
1549
+ type_=HttpValidationError, # type: ignore
1550
+ object_=_response.json(),
1551
+ ),
1552
+ )
1553
+ )
1554
+ _response_json = _response.json()
1555
+ except JSONDecodeError:
1556
+ raise ApiError(status_code=_response.status_code, body=_response.text)
1557
+ raise ApiError(status_code=_response.status_code, body=_response_json)
1558
+
1559
+ async def upload_agent_serialized(
1560
+ self,
1561
+ *,
1562
+ file: core.File,
1563
+ mark_as_copy: typing.Optional[bool] = None,
1564
+ request_options: typing.Optional[RequestOptions] = None,
1565
+ ) -> AgentState:
1566
+ """
1567
+ Upload a serialized agent JSON file and recreate the agent in the system.
1568
+
1569
+ Parameters
1570
+ ----------
1571
+ file : core.File
1572
+ See core.File for more documentation
1573
+
1574
+ mark_as_copy : typing.Optional[bool]
1575
+ Whether to mark the uploaded agent as a copy
1576
+
1577
+ request_options : typing.Optional[RequestOptions]
1578
+ Request-specific configuration.
1579
+
1580
+ Returns
1581
+ -------
1582
+ AgentState
1583
+ Successful Response
1584
+
1585
+ Examples
1586
+ --------
1587
+ import asyncio
1588
+
1589
+ from letta_client import AsyncLetta
1590
+
1591
+ client = AsyncLetta(
1592
+ token="YOUR_TOKEN",
1593
+ )
1594
+
1595
+
1596
+ async def main() -> None:
1597
+ await client.agents.upload_agent_serialized()
1598
+
1599
+
1600
+ asyncio.run(main())
1601
+ """
1602
+ _response = await self._client_wrapper.httpx_client.request(
1603
+ "v1/agents/upload",
1604
+ method="POST",
1605
+ params={
1606
+ "mark_as_copy": mark_as_copy,
1607
+ },
1608
+ data={},
1609
+ files={
1610
+ "file": file,
1611
+ },
1612
+ request_options=request_options,
1613
+ omit=OMIT,
1614
+ )
1615
+ try:
1616
+ if 200 <= _response.status_code < 300:
1617
+ return typing.cast(
1618
+ AgentState,
1619
+ construct_type(
1620
+ type_=AgentState, # type: ignore
1621
+ object_=_response.json(),
1622
+ ),
1623
+ )
1624
+ if _response.status_code == 422:
1625
+ raise UnprocessableEntityError(
1626
+ typing.cast(
1627
+ HttpValidationError,
1628
+ construct_type(
1629
+ type_=HttpValidationError, # type: ignore
1630
+ object_=_response.json(),
1631
+ ),
1632
+ )
1633
+ )
1634
+ _response_json = _response.json()
1635
+ except JSONDecodeError:
1636
+ raise ApiError(status_code=_response.status_code, body=_response.text)
1637
+ raise ApiError(status_code=_response.status_code, body=_response_json)
1638
+
1362
1639
  async def retrieve(self, agent_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> AgentState:
1363
1640
  """
1364
1641
  Get the state of the agent.
letta_client/client.py CHANGED
@@ -1,6 +1,8 @@
1
1
  import inspect
2
2
  import typing
3
+ from pydantic import BaseModel, Field, model_validator
3
4
  from textwrap import dedent
5
+ from abc import abstractmethod
4
6
 
5
7
  from .base_client import AsyncLettaBase, LettaBase
6
8
  from .core.request_options import RequestOptions
@@ -25,12 +27,113 @@ class AsyncLetta(AsyncLettaBase):
25
27
  self.tools = ToolsClient(client_wrapper=self._client_wrapper)
26
28
 
27
29
 
30
+ class BaseTool(Tool):
31
+ name: str = Field(..., description="The name of the function.")
32
+ args_schema: typing.Optional[typing.Type[BaseModel]] = Field(default=None, description="The schema for validating the tool's arguments.")
33
+
34
+ @abstractmethod
35
+ def run(self, *args, **kwargs) -> typing.Any:
36
+ """
37
+ Execute the tool with the provided arguments.
38
+
39
+ Parameters
40
+ ----------
41
+ self
42
+ The instance of the tool
43
+ *args
44
+ Positional arguments to pass to the tool.
45
+ **kwargs
46
+ Keyword arguments to pass to the tool.
47
+
48
+ Returns
49
+ -------
50
+ typing.Any
51
+ The result of executing the tool.
52
+ """
53
+ pass
54
+
55
+
56
+ @model_validator(mode="after")
57
+ def no_self_in_run_source(self) -> "BaseTool":
58
+ """
59
+ Validate that the provided implementation does not reference `self` in the
60
+ `run` method implementation.
61
+
62
+ This check is performed after the model is created, so `self` is guaranteed
63
+ to be set.
64
+
65
+ If `self` is found in the source code of the `run` method, a `ValueError` is
66
+ raised with a message pointing to the line and value of the offending code.
67
+ """
68
+ source_code = self.get_source_code()
69
+ if "self." in source_code:
70
+ raise_on_line, line_value = None, None
71
+ for i, line in enumerate(source_code.splitlines()):
72
+ if "self." in line:
73
+ raise_on_line, line_value = i+1, line
74
+ break;
75
+ raise ValueError(
76
+ f"Detected reference to 'self' in line {raise_on_line} of implementation, " +
77
+ f"which is not allowed:\n\n{line_value}\n\n" +
78
+ f"Please pass in the arguments directly to run() instead.")
79
+ return self
80
+
81
+
82
+ def get_source_code(self) -> str:
83
+ """
84
+ Get the source code of the `run` method, which will be executed in an agent step.
85
+
86
+ Returns
87
+ -------
88
+ str
89
+ The source code of the tool.
90
+ """
91
+ source_code = dedent(inspect.getsource(self.run))
92
+
93
+ # replace tool name
94
+ source_code = source_code.replace("def run", f"def {self.name}")
95
+
96
+ # remove self, handling several cases
97
+ source_code_lines = source_code.splitlines()
98
+ if "self" in source_code_lines[0]:
99
+ # def run(self, ...): or def run (self,): or def run(self):
100
+ source_code_lines[0] = source_code_lines[0].replace("self, ", "").replace("self,", "").replace("self", "")
101
+ else:
102
+ maybe_line_to_delete = None
103
+ for i, line in enumerate(source_code_lines):
104
+ if line.strip() == "self" or line.strip() == "self,":
105
+ # def run(
106
+ # self,
107
+ # ...
108
+ # ):
109
+ maybe_line_to_delete = i
110
+ break
111
+ elif line.strip().startswith("self"):
112
+ # def run(
113
+ # self, ...
114
+ # ):
115
+ source_code_lines[i] = line.replace("self, ", "").replace("self,", "").replace("self", "")
116
+ break
117
+ if maybe_line_to_delete is not None:
118
+ del source_code_lines[maybe_line_to_delete]
119
+ if maybe_line_to_delete == 1 and source_code_lines[0].strip()[-1] == "(" and source_code_lines[1].strip()[0] == ")":
120
+ # def run(
121
+ # self
122
+ # ):
123
+ source_code_lines[0] = source_code_lines[0].strip() + source_code_lines[1].strip()
124
+ del source_code_lines[1]
125
+
126
+ source_code = "\n".join(source_code_lines)
127
+ return source_code
128
+
129
+
28
130
  class ToolsClient(ToolsClientBase):
29
131
 
30
132
  def create_from_function(
31
133
  self,
32
134
  *,
33
135
  func: typing.Callable,
136
+ args_schema: typing.Optional[typing.Type[BaseModel]] = OMIT,
34
137
  description: typing.Optional[str] = OMIT,
35
138
  tags: typing.Optional[typing.Sequence[str]] = OMIT,
36
139
  source_type: typing.Optional[str] = OMIT,
@@ -40,9 +143,72 @@ class ToolsClient(ToolsClientBase):
40
143
  return_char_limit: typing.Optional[int] = OMIT,
41
144
  request_options: typing.Optional[RequestOptions] = None,
42
145
  ) -> Tool:
146
+ """
147
+ Create a new tool from a callable
148
+
149
+ Parameters
150
+ ----------
151
+ func : typing.Callable
152
+ The callable to create the tool from.
153
+
154
+ args_schema : typing.Optional[typing.Type[BaseModel]]
155
+ The arguments schema of the function, as a Pydantic model.
156
+
157
+ description : typing.Optional[str]
158
+ The description of the tool.
159
+
160
+ tags : typing.Optional[typing.Sequence[str]]
161
+ Metadata tags.
162
+
163
+ source_type : typing.Optional[str]
164
+ The source type of the function.
165
+
166
+ json_schema : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]]
167
+ The JSON schema of the function (auto-generated from source_code if not provided)
168
+
169
+ return_char_limit : typing.Optional[int]
170
+ The maximum number of characters in the response.
171
+
172
+ request_options : typing.Optional[RequestOptions]
173
+ Request-specific configuration.
174
+
175
+ Returns
176
+ -------
177
+ Tool
178
+ Successful Response
179
+
180
+ Examples
181
+ --------
182
+ from letta_client import Letta
183
+
184
+ client = Letta(
185
+ token="YOUR_TOKEN",
186
+ )
187
+
188
+ def add_two_numbers(a: int, b: int) -> int:
189
+ return a + b
190
+
191
+ client.tools.create_from_function(
192
+ func=add_two_numbers,
193
+ )
194
+
195
+ class InventoryEntryData(BaseModel):
196
+ data: InventoryEntry
197
+ quantity_change: int
198
+
199
+ def manage_inventory(data: InventoryEntry, quantity_change: int) -> bool:
200
+ pass
201
+
202
+ client.tools.create_from_function(
203
+ func=manage_inventory,
204
+ args_schema=InventoryEntryData,
205
+ )
206
+ """
43
207
  source_code = dedent(inspect.getsource(func))
208
+ args_json_schema = args_schema.model_json_schema() if args_schema and args_schema != OMIT else None
44
209
  return self.create(
45
210
  source_code=source_code,
211
+ args_json_schema=args_json_schema,
46
212
  description=description,
47
213
  tags=tags,
48
214
  source_type=source_type,
@@ -51,10 +217,12 @@ class ToolsClient(ToolsClientBase):
51
217
  request_options=request_options,
52
218
  )
53
219
 
220
+
54
221
  def upsert_from_function(
55
222
  self,
56
223
  *,
57
224
  func: typing.Callable,
225
+ args_schema: typing.Optional[typing.Type[BaseModel]] = OMIT,
58
226
  description: typing.Optional[str] = OMIT,
59
227
  tags: typing.Optional[typing.Sequence[str]] = OMIT,
60
228
  source_type: typing.Optional[str] = OMIT,
@@ -64,9 +232,72 @@ class ToolsClient(ToolsClientBase):
64
232
  return_char_limit: typing.Optional[int] = OMIT,
65
233
  request_options: typing.Optional[RequestOptions] = None,
66
234
  ) -> Tool:
235
+ """
236
+ Create or update a tool from a callable
237
+
238
+ Parameters
239
+ ----------
240
+ func : typing.Callable
241
+ The callable to create or update the tool from.
242
+
243
+ args_schema : typing.Optional[typing.Type[BaseModel]]
244
+ The arguments schema of the function, as a Pydantic model.
245
+
246
+ description : typing.Optional[str]
247
+ The description of the tool.
248
+
249
+ tags : typing.Optional[typing.Sequence[str]]
250
+ Metadata tags.
251
+
252
+ source_type : typing.Optional[str]
253
+ The source type of the function.
254
+
255
+ json_schema : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]]
256
+ The JSON schema of the function (auto-generated from source_code if not provided)
257
+
258
+ return_char_limit : typing.Optional[int]
259
+ The maximum number of characters in the response.
260
+
261
+ request_options : typing.Optional[RequestOptions]
262
+ Request-specific configuration.
263
+
264
+ Returns
265
+ -------
266
+ Tool
267
+ Successful Response
268
+
269
+ Examples
270
+ --------
271
+ from letta_client import Letta
272
+
273
+ client = Letta(
274
+ token="YOUR_TOKEN",
275
+ )
276
+
277
+ def add_two_numbers(a: int, b: int) -> int:
278
+ return a + b
279
+
280
+ client.tools.upsert_from_function(
281
+ func=add_two_numbers,
282
+ )
283
+
284
+ class InventoryEntryData(BaseModel):
285
+ data: InventoryEntry
286
+ quantity_change: int
287
+
288
+ def manage_inventory(data: InventoryEntry, quantity_change: int) -> bool:
289
+ pass
290
+
291
+ client.tools.upsert_from_function(
292
+ func=manage_inventory,
293
+ args_schema=InventoryEntryData,
294
+ )
295
+ """
67
296
  source_code = dedent(inspect.getsource(func))
297
+ args_json_schema = args_schema.model_json_schema() if args_schema and args_schema != OMIT else None
68
298
  return self.upsert(
69
299
  source_code=source_code,
300
+ args_json_schema=args_json_schema,
70
301
  description=description,
71
302
  tags=tags,
72
303
  source_type=source_type,
@@ -74,3 +305,78 @@ class ToolsClient(ToolsClientBase):
74
305
  return_char_limit=return_char_limit,
75
306
  request_options=request_options,
76
307
  )
308
+
309
+ def add(
310
+ self,
311
+ *,
312
+ tool: BaseTool,
313
+ request_options: typing.Optional[RequestOptions] = None,
314
+ ) -> Tool:
315
+ """
316
+ Add a tool to Letta from a custom Tool class
317
+
318
+ Parameters
319
+ ----------
320
+ tool : BaseTool
321
+ The tool object to be added.
322
+
323
+ request_options : typing.Optional[RequestOptions]
324
+ Request-specific configuration.
325
+
326
+ Returns
327
+ -------
328
+ Tool
329
+ Successful Response
330
+
331
+ Examples
332
+ --------
333
+ from letta_client import Letta
334
+
335
+ client = Letta(
336
+ token="YOUR_TOKEN",
337
+ )
338
+
339
+ class InventoryItem(BaseModel):
340
+ sku: str # Unique product identifier
341
+ name: str # Product name
342
+ price: float # Current price
343
+ category: str # Product category (e.g., "Electronics", "Clothing")
344
+
345
+ class InventoryEntry(BaseModel):
346
+ timestamp: int # Unix timestamp of the transaction
347
+ item: InventoryItem # The product being updated
348
+ transaction_id: str # Unique identifier for this inventory update
349
+
350
+ class InventoryEntryData(BaseModel):
351
+ data: InventoryEntry
352
+ quantity_change: int # Change in quantity (positive for additions, negative for removals)
353
+
354
+ class ManageInventoryTool(BaseTool):
355
+ name: str = "manage_inventory"
356
+ args_schema: Type[BaseModel] = InventoryEntryData
357
+ description: str = "Update inventory catalogue with a new data entry"
358
+ tags: List[str] = ["inventory", "shop"]
359
+
360
+ def run(self, data: InventoryEntry, quantity_change: int) -> bool:
361
+ '''
362
+ Implementation of the manage_inventory tool
363
+ '''
364
+ print(f"Updated inventory for {data.item.name} with a quantity change of {quantity_change}")
365
+ return True
366
+
367
+ client.tools.add(
368
+ tool=ManageInventoryTool()
369
+ )
370
+ """
371
+ source_code = tool.get_source_code()
372
+ args_json_schema = tool.args_schema.model_json_schema() if tool.args_schema else None
373
+ return self.upsert(
374
+ source_code=source_code,
375
+ args_json_schema=args_json_schema or OMIT,
376
+ description=tool.description or OMIT,
377
+ tags=tool.tags or OMIT,
378
+ source_type=tool.source_type or OMIT,
379
+ json_schema=tool.json_schema or OMIT,
380
+ return_char_limit=tool.return_char_limit or OMIT,
381
+ request_options=request_options,
382
+ )
@@ -16,7 +16,7 @@ class BaseClientWrapper:
16
16
  headers: typing.Dict[str, str] = {
17
17
  "X-Fern-Language": "Python",
18
18
  "X-Fern-SDK-Name": "letta-client",
19
- "X-Fern-SDK-Version": "0.1.53",
19
+ "X-Fern-SDK-Version": "0.1.55",
20
20
  }
21
21
  if self.token is not None:
22
22
  headers["Authorization"] = f"Bearer {self.token}"
@@ -13,6 +13,7 @@ from ..core.jsonable_encoder import jsonable_encoder
13
13
  from ..types.message_role import MessageRole
14
14
  from ..types.letta_message_union import LettaMessageUnion
15
15
  from ..types.usage_statistics import UsageStatistics
16
+ from ..types.step import Step
16
17
  from ..core.client_wrapper import AsyncClientWrapper
17
18
 
18
19
 
@@ -395,6 +396,99 @@ class RunsClient:
395
396
  raise ApiError(status_code=_response.status_code, body=_response.text)
396
397
  raise ApiError(status_code=_response.status_code, body=_response_json)
397
398
 
399
+ def list_run_steps(
400
+ self,
401
+ run_id: str,
402
+ *,
403
+ before: typing.Optional[str] = None,
404
+ after: typing.Optional[str] = None,
405
+ limit: typing.Optional[int] = None,
406
+ order: typing.Optional[str] = None,
407
+ request_options: typing.Optional[RequestOptions] = None,
408
+ ) -> typing.List[Step]:
409
+ """
410
+ Get messages associated with a run with filtering options.
411
+
412
+ Args:
413
+ run_id: ID of the run
414
+ before: A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.
415
+ after: A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.
416
+ limit: Maximum number of steps to return
417
+ order: Sort order by the created_at timestamp of the objects. asc for ascending order and desc for descending order.
418
+
419
+ Returns:
420
+ A list of steps associated with the run.
421
+
422
+ Parameters
423
+ ----------
424
+ run_id : str
425
+
426
+ before : typing.Optional[str]
427
+ Cursor for pagination
428
+
429
+ after : typing.Optional[str]
430
+ Cursor for pagination
431
+
432
+ limit : typing.Optional[int]
433
+ Maximum number of messages to return
434
+
435
+ order : typing.Optional[str]
436
+ Sort order by the created_at timestamp of the objects. asc for ascending order and desc for descending order.
437
+
438
+ request_options : typing.Optional[RequestOptions]
439
+ Request-specific configuration.
440
+
441
+ Returns
442
+ -------
443
+ typing.List[Step]
444
+ Successful Response
445
+
446
+ Examples
447
+ --------
448
+ from letta_client import Letta
449
+
450
+ client = Letta(
451
+ token="YOUR_TOKEN",
452
+ )
453
+ client.runs.list_run_steps(
454
+ run_id="run_id",
455
+ )
456
+ """
457
+ _response = self._client_wrapper.httpx_client.request(
458
+ f"v1/runs/{jsonable_encoder(run_id)}/steps",
459
+ method="GET",
460
+ params={
461
+ "before": before,
462
+ "after": after,
463
+ "limit": limit,
464
+ "order": order,
465
+ },
466
+ request_options=request_options,
467
+ )
468
+ try:
469
+ if 200 <= _response.status_code < 300:
470
+ return typing.cast(
471
+ typing.List[Step],
472
+ construct_type(
473
+ type_=typing.List[Step], # type: ignore
474
+ object_=_response.json(),
475
+ ),
476
+ )
477
+ if _response.status_code == 422:
478
+ raise UnprocessableEntityError(
479
+ typing.cast(
480
+ HttpValidationError,
481
+ construct_type(
482
+ type_=HttpValidationError, # type: ignore
483
+ object_=_response.json(),
484
+ ),
485
+ )
486
+ )
487
+ _response_json = _response.json()
488
+ except JSONDecodeError:
489
+ raise ApiError(status_code=_response.status_code, body=_response.text)
490
+ raise ApiError(status_code=_response.status_code, body=_response_json)
491
+
398
492
 
399
493
  class AsyncRunsClient:
400
494
  def __init__(self, *, client_wrapper: AsyncClientWrapper):
@@ -822,3 +916,104 @@ class AsyncRunsClient:
822
916
  except JSONDecodeError:
823
917
  raise ApiError(status_code=_response.status_code, body=_response.text)
824
918
  raise ApiError(status_code=_response.status_code, body=_response_json)
919
+
920
+ async def list_run_steps(
921
+ self,
922
+ run_id: str,
923
+ *,
924
+ before: typing.Optional[str] = None,
925
+ after: typing.Optional[str] = None,
926
+ limit: typing.Optional[int] = None,
927
+ order: typing.Optional[str] = None,
928
+ request_options: typing.Optional[RequestOptions] = None,
929
+ ) -> typing.List[Step]:
930
+ """
931
+ Get messages associated with a run with filtering options.
932
+
933
+ Args:
934
+ run_id: ID of the run
935
+ before: A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.
936
+ after: A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.
937
+ limit: Maximum number of steps to return
938
+ order: Sort order by the created_at timestamp of the objects. asc for ascending order and desc for descending order.
939
+
940
+ Returns:
941
+ A list of steps associated with the run.
942
+
943
+ Parameters
944
+ ----------
945
+ run_id : str
946
+
947
+ before : typing.Optional[str]
948
+ Cursor for pagination
949
+
950
+ after : typing.Optional[str]
951
+ Cursor for pagination
952
+
953
+ limit : typing.Optional[int]
954
+ Maximum number of messages to return
955
+
956
+ order : typing.Optional[str]
957
+ Sort order by the created_at timestamp of the objects. asc for ascending order and desc for descending order.
958
+
959
+ request_options : typing.Optional[RequestOptions]
960
+ Request-specific configuration.
961
+
962
+ Returns
963
+ -------
964
+ typing.List[Step]
965
+ Successful Response
966
+
967
+ Examples
968
+ --------
969
+ import asyncio
970
+
971
+ from letta_client import AsyncLetta
972
+
973
+ client = AsyncLetta(
974
+ token="YOUR_TOKEN",
975
+ )
976
+
977
+
978
+ async def main() -> None:
979
+ await client.runs.list_run_steps(
980
+ run_id="run_id",
981
+ )
982
+
983
+
984
+ asyncio.run(main())
985
+ """
986
+ _response = await self._client_wrapper.httpx_client.request(
987
+ f"v1/runs/{jsonable_encoder(run_id)}/steps",
988
+ method="GET",
989
+ params={
990
+ "before": before,
991
+ "after": after,
992
+ "limit": limit,
993
+ "order": order,
994
+ },
995
+ request_options=request_options,
996
+ )
997
+ try:
998
+ if 200 <= _response.status_code < 300:
999
+ return typing.cast(
1000
+ typing.List[Step],
1001
+ construct_type(
1002
+ type_=typing.List[Step], # type: ignore
1003
+ object_=_response.json(),
1004
+ ),
1005
+ )
1006
+ if _response.status_code == 422:
1007
+ raise UnprocessableEntityError(
1008
+ typing.cast(
1009
+ HttpValidationError,
1010
+ construct_type(
1011
+ type_=HttpValidationError, # type: ignore
1012
+ object_=_response.json(),
1013
+ ),
1014
+ )
1015
+ )
1016
+ _response_json = _response.json()
1017
+ except JSONDecodeError:
1018
+ raise ApiError(status_code=_response.status_code, body=_response.text)
1019
+ raise ApiError(status_code=_response.status_code, body=_response_json)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: letta-client
3
- Version: 0.1.53
3
+ Version: 0.1.55
4
4
  Summary:
5
5
  Requires-Python: >=3.8,<4.0
6
6
  Classifier: Intended Audience :: Developers
@@ -2,7 +2,7 @@ letta_client/__init__.py,sha256=gE-ZEiBn_82WiGeafvXwxQh49hjm0E16DPA5jB6s7L4,5652
2
2
  letta_client/agents/__init__.py,sha256=dsO71a7KQVSsJkkd31kf6RwxZOeJzjhvJZRQBS7Mlws,22384
3
3
  letta_client/agents/blocks/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
4
4
  letta_client/agents/blocks/client.py,sha256=u5zvutxoH_DqfSLWhRtNSRBC9_ezQDx682cxkxDz3JA,23822
5
- letta_client/agents/client.py,sha256=YUFtT3ZBHyvUcK-IbAchSbYRJprGB9aR2eedNdI6r3g,73961
5
+ letta_client/agents/client.py,sha256=rMgrx7nZH0KR-T1ye5yQIDRVpWWAbFX4ZQVN90zv0S0,82633
6
6
  letta_client/agents/context/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
7
7
  letta_client/agents/context/client.py,sha256=GKKvoG4N_K8Biz9yDjeIHpFG0C8Cwc7tHmEX3pTL_9U,4815
8
8
  letta_client/agents/core_memory/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
@@ -206,10 +206,10 @@ letta_client/agents/types/update_agent_tool_rules_item.py,sha256=gCA9oFxIWEcbVV5
206
206
  letta_client/base_client.py,sha256=ZD62gkVcq-3piS6Az6L9yM9vD1kssEdtK3c0pHoRWMI,8395
207
207
  letta_client/blocks/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
208
208
  letta_client/blocks/client.py,sha256=AeQQ-IdYhV-zqLTt3PTrJOtJ6XtBZcXNC108Y5EogVU,29178
209
- letta_client/client.py,sha256=y2cXN0ApFul2Lz-fVh5TbeYbQ8oUjnXcwJ6wUczEf2c,2457
209
+ letta_client/client.py,sha256=xdSrD4IkWokZHujowd1r7zESBoVgKGNvo6RqgZ3f0Fg,12808
210
210
  letta_client/core/__init__.py,sha256=OKbX2aCZXgHCDUsCouqv-OiX32xA6eFFCKIUH9M5Vzk,1591
211
211
  letta_client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
212
- letta_client/core/client_wrapper.py,sha256=TvgT0TcIkbpFdwAFthzGU08_LZr1j9f-6cSYUd_EcyY,1997
212
+ letta_client/core/client_wrapper.py,sha256=bCQa8ib0wvPkNfoMS5vVB9DVMabROs5bjmtio3h5swI,1997
213
213
  letta_client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
214
214
  letta_client/core/file.py,sha256=d4NNbX8XvXP32z8KpK2Xovv33nFfruIrpz0QWxlgpZk,2663
215
215
  letta_client/core/http_client.py,sha256=siUQ6UV0ARZALlxubqWSSAAPC9B4VW8y6MGlHStfaeo,19552
@@ -238,7 +238,7 @@ letta_client/providers/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAK
238
238
  letta_client/providers/client.py,sha256=RLpTHd9iQ5wlZqYEG4cF8YsDCdaQZ0odCFprukauCuc,18228
239
239
  letta_client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
240
240
  letta_client/runs/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
241
- letta_client/runs/client.py,sha256=F4yTdhwAA20Ftjr9UZ9tU7wc209n1RNVdJkw42eiBqY,27330
241
+ letta_client/runs/client.py,sha256=fu6RNu6qkG5kOkT7rfIjeuHp_REW5ZGxNwzFqTmhb-o,34546
242
242
  letta_client/sources/__init__.py,sha256=kswgCv4UdkSVk1Y4tsMM1HadOwvhh_Fr96VTSMV4Umc,128
243
243
  letta_client/sources/client.py,sha256=GbMg3ZR0JufGPOfYiptr9yDWKJ0FgT6zLD_k6fET0zs,28223
244
244
  letta_client/sources/files/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
@@ -590,6 +590,6 @@ letta_client/voice/__init__.py,sha256=ZrZEuXIukVGhsfM-i0dIFfqjeSOBMPeEgDva7Vvnip
590
590
  letta_client/voice/client.py,sha256=j3feSlNzeTVFXE7RUKEHGeMl_w0TJFBRUI3pXpLpUEI,6148
591
591
  letta_client/voice/types/__init__.py,sha256=hBLJcrom99DkDxxsVRU2ni8kPx6SsCy8gtAJvNOz26w,199
592
592
  letta_client/voice/types/create_voice_chat_completions_request.py,sha256=K4__83rXRCshfdobyAmH-5fUDJQ_PeSQetTUeC4Abk0,381
593
- letta_client-0.1.53.dist-info/METADATA,sha256=oVZYFUJh8SNpPLEL6CeccP46YqIkh8qCLwh-qDtf-Fs,4942
594
- letta_client-0.1.53.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
595
- letta_client-0.1.53.dist-info/RECORD,,
593
+ letta_client-0.1.55.dist-info/METADATA,sha256=zOTxA6hciGwFiAy5G9uIaqsMm5QZaPA4rVsm12p_Go8,4942
594
+ letta_client-0.1.55.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
595
+ letta_client-0.1.55.dist-info/RECORD,,