letta-nightly 0.7.21.dev20250522104246__py3-none-any.whl → 0.7.22.dev20250523081403__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 (50) hide show
  1. letta/__init__.py +2 -2
  2. letta/agents/base_agent.py +4 -2
  3. letta/agents/letta_agent.py +3 -10
  4. letta/agents/letta_agent_batch.py +6 -6
  5. letta/cli/cli.py +0 -316
  6. letta/cli/cli_load.py +0 -52
  7. letta/client/client.py +2 -1554
  8. letta/data_sources/connectors.py +4 -2
  9. letta/functions/ast_parsers.py +33 -43
  10. letta/groups/sleeptime_multi_agent_v2.py +49 -13
  11. letta/jobs/llm_batch_job_polling.py +3 -3
  12. letta/jobs/scheduler.py +20 -19
  13. letta/llm_api/anthropic_client.py +3 -0
  14. letta/llm_api/google_vertex_client.py +5 -0
  15. letta/llm_api/openai_client.py +5 -0
  16. letta/main.py +2 -362
  17. letta/server/db.py +5 -0
  18. letta/server/rest_api/routers/v1/agents.py +72 -43
  19. letta/server/rest_api/routers/v1/llms.py +2 -2
  20. letta/server/rest_api/routers/v1/messages.py +5 -3
  21. letta/server/rest_api/routers/v1/sandbox_configs.py +18 -18
  22. letta/server/rest_api/routers/v1/sources.py +49 -36
  23. letta/server/server.py +53 -22
  24. letta/services/agent_manager.py +797 -124
  25. letta/services/block_manager.py +14 -62
  26. letta/services/group_manager.py +37 -0
  27. letta/services/identity_manager.py +9 -0
  28. letta/services/job_manager.py +17 -0
  29. letta/services/llm_batch_manager.py +88 -64
  30. letta/services/message_manager.py +19 -0
  31. letta/services/organization_manager.py +10 -0
  32. letta/services/passage_manager.py +13 -0
  33. letta/services/per_agent_lock_manager.py +4 -0
  34. letta/services/provider_manager.py +34 -0
  35. letta/services/sandbox_config_manager.py +130 -0
  36. letta/services/source_manager.py +59 -44
  37. letta/services/step_manager.py +8 -1
  38. letta/services/tool_manager.py +21 -0
  39. letta/services/tool_sandbox/e2b_sandbox.py +4 -2
  40. letta/services/tool_sandbox/local_sandbox.py +7 -3
  41. letta/services/user_manager.py +16 -0
  42. {letta_nightly-0.7.21.dev20250522104246.dist-info → letta_nightly-0.7.22.dev20250523081403.dist-info}/METADATA +1 -1
  43. {letta_nightly-0.7.21.dev20250522104246.dist-info → letta_nightly-0.7.22.dev20250523081403.dist-info}/RECORD +46 -50
  44. letta/__main__.py +0 -3
  45. letta/benchmark/benchmark.py +0 -98
  46. letta/benchmark/constants.py +0 -14
  47. letta/cli/cli_config.py +0 -227
  48. {letta_nightly-0.7.21.dev20250522104246.dist-info → letta_nightly-0.7.22.dev20250523081403.dist-info}/LICENSE +0 -0
  49. {letta_nightly-0.7.21.dev20250522104246.dist-info → letta_nightly-0.7.22.dev20250523081403.dist-info}/WHEEL +0 -0
  50. {letta_nightly-0.7.21.dev20250522104246.dist-info → letta_nightly-0.7.22.dev20250523081403.dist-info}/entry_points.txt +0 -0
letta/client/client.py CHANGED
@@ -1,27 +1,19 @@
1
- import asyncio
2
- import logging
3
1
  import sys
4
2
  import time
5
3
  from typing import Callable, Dict, Generator, List, Optional, Union
6
4
 
7
5
  import requests
8
6
 
9
- import letta.utils
10
7
  from letta.constants import ADMIN_PREFIX, BASE_MEMORY_TOOLS, BASE_TOOLS, DEFAULT_HUMAN, DEFAULT_PERSONA, FUNCTION_RETURN_CHAR_LIMIT
11
8
  from letta.data_sources.connectors import DataConnector
12
9
  from letta.functions.functions import parse_source_code
13
- from letta.orm.errors import NoResultFound
14
10
  from letta.schemas.agent import AgentState, AgentType, CreateAgent, UpdateAgent
15
11
  from letta.schemas.block import Block, BlockUpdate, CreateBlock, Human, Persona
16
12
  from letta.schemas.embedding_config import EmbeddingConfig
17
13
 
18
14
  # new schemas
19
15
  from letta.schemas.enums import JobStatus, MessageRole
20
- from letta.schemas.environment_variables import (
21
- SandboxEnvironmentVariable,
22
- SandboxEnvironmentVariableCreate,
23
- SandboxEnvironmentVariableUpdate,
24
- )
16
+ from letta.schemas.environment_variables import SandboxEnvironmentVariable
25
17
  from letta.schemas.file import FileMetadata
26
18
  from letta.schemas.job import Job
27
19
  from letta.schemas.letta_message import LettaMessage, LettaMessageUnion
@@ -35,11 +27,10 @@ from letta.schemas.organization import Organization
35
27
  from letta.schemas.passage import Passage
36
28
  from letta.schemas.response_format import ResponseFormatUnion
37
29
  from letta.schemas.run import Run
38
- from letta.schemas.sandbox_config import E2BSandboxConfig, LocalSandboxConfig, SandboxConfig, SandboxConfigCreate, SandboxConfigUpdate
30
+ from letta.schemas.sandbox_config import E2BSandboxConfig, LocalSandboxConfig, SandboxConfig
39
31
  from letta.schemas.source import Source, SourceCreate, SourceUpdate
40
32
  from letta.schemas.tool import Tool, ToolCreate, ToolUpdate
41
33
  from letta.schemas.tool_rule import BaseToolRule
42
- from letta.server.rest_api.interface import QueuingInterface
43
34
  from letta.utils import get_human_text, get_persona_text
44
35
 
45
36
  # Print deprecation notice in yellow when module is imported
@@ -53,13 +44,6 @@ print(
53
44
  )
54
45
 
55
46
 
56
- def create_client(base_url: Optional[str] = None, token: Optional[str] = None):
57
- if base_url is None:
58
- return LocalClient()
59
- else:
60
- return RESTClient(base_url, token)
61
-
62
-
63
47
  class AbstractClient(object):
64
48
  def __init__(
65
49
  self,
@@ -2229,1539 +2213,3 @@ class RESTClient(AbstractClient):
2229
2213
  if response.status_code != 200:
2230
2214
  raise ValueError(f"Failed to get tags: {response.text}")
2231
2215
  return response.json()
2232
-
2233
-
2234
- class LocalClient(AbstractClient):
2235
- """
2236
- A local client for Letta, which corresponds to a single user.
2237
-
2238
- Attributes:
2239
- user_id (str): The user ID.
2240
- debug (bool): Whether to print debug information.
2241
- interface (QueuingInterface): The interface for the client.
2242
- server (SyncServer): The server for the client.
2243
- """
2244
-
2245
- def __init__(
2246
- self,
2247
- user_id: Optional[str] = None,
2248
- org_id: Optional[str] = None,
2249
- debug: bool = False,
2250
- default_llm_config: Optional[LLMConfig] = None,
2251
- default_embedding_config: Optional[EmbeddingConfig] = None,
2252
- ):
2253
- """
2254
- Initializes a new instance of Client class.
2255
-
2256
- Args:
2257
- user_id (str): The user ID.
2258
- debug (bool): Whether to print debug information.
2259
- """
2260
-
2261
- from letta.server.server import SyncServer
2262
-
2263
- # set logging levels
2264
- letta.utils.DEBUG = debug
2265
- logging.getLogger().setLevel(logging.CRITICAL)
2266
-
2267
- # save default model config
2268
- self._default_llm_config = default_llm_config
2269
- self._default_embedding_config = default_embedding_config
2270
-
2271
- # create server
2272
- self.interface = QueuingInterface(debug=debug)
2273
- self.server = SyncServer(default_interface_factory=lambda: self.interface)
2274
-
2275
- # save org_id that `LocalClient` is associated with
2276
- if org_id:
2277
- self.org_id = org_id
2278
- else:
2279
- self.org_id = self.server.organization_manager.DEFAULT_ORG_ID
2280
- # save user_id that `LocalClient` is associated with
2281
- if user_id:
2282
- self.user_id = user_id
2283
- else:
2284
- # get default user
2285
- self.user_id = self.server.user_manager.DEFAULT_USER_ID
2286
-
2287
- self.user = self.server.user_manager.get_user_or_default(self.user_id)
2288
- self.organization = self.server.get_organization_or_default(self.org_id)
2289
-
2290
- # agents
2291
- def list_agents(
2292
- self,
2293
- query_text: Optional[str] = None,
2294
- tags: Optional[List[str]] = None,
2295
- limit: int = 100,
2296
- before: Optional[str] = None,
2297
- after: Optional[str] = None,
2298
- ) -> List[AgentState]:
2299
- self.interface.clear()
2300
-
2301
- return self.server.agent_manager.list_agents(
2302
- actor=self.user, tags=tags, query_text=query_text, limit=limit, before=before, after=after
2303
- )
2304
-
2305
- def agent_exists(self, agent_id: Optional[str] = None, agent_name: Optional[str] = None) -> bool:
2306
- """
2307
- Check if an agent exists
2308
-
2309
- Args:
2310
- agent_id (str): ID of the agent
2311
- agent_name (str): Name of the agent
2312
-
2313
- Returns:
2314
- exists (bool): `True` if the agent exists, `False` otherwise
2315
- """
2316
-
2317
- if not (agent_id or agent_name):
2318
- raise ValueError(f"Either agent_id or agent_name must be provided")
2319
- if agent_id and agent_name:
2320
- raise ValueError(f"Only one of agent_id or agent_name can be provided")
2321
- existing = self.list_agents()
2322
- if agent_id:
2323
- return str(agent_id) in [str(agent.id) for agent in existing]
2324
- else:
2325
- return agent_name in [str(agent.name) for agent in existing]
2326
-
2327
- def create_agent(
2328
- self,
2329
- name: Optional[str] = None,
2330
- # agent config
2331
- agent_type: Optional[AgentType] = AgentType.memgpt_agent,
2332
- # model configs
2333
- embedding_config: EmbeddingConfig = None,
2334
- llm_config: LLMConfig = None,
2335
- # memory
2336
- memory: Memory = ChatMemory(human=get_human_text(DEFAULT_HUMAN), persona=get_persona_text(DEFAULT_PERSONA)),
2337
- block_ids: Optional[List[str]] = None,
2338
- # TODO: change to this when we are ready to migrate all the tests/examples (matches the REST API)
2339
- # memory_blocks=[
2340
- # {"label": "human", "value": get_human_text(DEFAULT_HUMAN), "limit": 5000},
2341
- # {"label": "persona", "value": get_persona_text(DEFAULT_PERSONA), "limit": 5000},
2342
- # ],
2343
- # system
2344
- system: Optional[str] = None,
2345
- # tools
2346
- tool_ids: Optional[List[str]] = None,
2347
- tool_rules: Optional[List[BaseToolRule]] = None,
2348
- include_base_tools: Optional[bool] = True,
2349
- include_multi_agent_tools: bool = False,
2350
- include_base_tool_rules: bool = True,
2351
- # metadata
2352
- metadata: Optional[Dict] = {"human:": DEFAULT_HUMAN, "persona": DEFAULT_PERSONA},
2353
- description: Optional[str] = None,
2354
- initial_message_sequence: Optional[List[Message]] = None,
2355
- tags: Optional[List[str]] = None,
2356
- message_buffer_autoclear: bool = False,
2357
- response_format: Optional[ResponseFormatUnion] = None,
2358
- ) -> AgentState:
2359
- """Create an agent
2360
-
2361
- Args:
2362
- name (str): Name of the agent
2363
- embedding_config (EmbeddingConfig): Embedding configuration
2364
- llm_config (LLMConfig): LLM configuration
2365
- memory_blocks (List[Dict]): List of configurations for the memory blocks (placed in core-memory)
2366
- system (str): System configuration
2367
- tools (List[str]): List of tools
2368
- tool_rules (Optional[List[BaseToolRule]]): List of tool rules
2369
- include_base_tools (bool): Include base tools
2370
- include_multi_agent_tools (bool): Include multi agent tools
2371
- metadata (Dict): Metadata
2372
- description (str): Description
2373
- tags (List[str]): Tags for filtering agents
2374
-
2375
- Returns:
2376
- agent_state (AgentState): State of the created agent
2377
- """
2378
- # construct list of tools
2379
- tool_ids = tool_ids or []
2380
-
2381
- # check if default configs are provided
2382
- assert embedding_config or self._default_embedding_config, f"Embedding config must be provided"
2383
- assert llm_config or self._default_llm_config, f"LLM config must be provided"
2384
-
2385
- # TODO: This should not happen here, we need to have clear separation between create/add blocks
2386
- for block in memory.get_blocks():
2387
- self.server.block_manager.create_or_update_block(block, actor=self.user)
2388
-
2389
- # Also get any existing block_ids passed in
2390
- block_ids = block_ids or []
2391
-
2392
- # create agent
2393
- # Create the base parameters
2394
- create_params = {
2395
- "description": description,
2396
- "metadata": metadata,
2397
- "memory_blocks": [],
2398
- "block_ids": [b.id for b in memory.get_blocks()] + block_ids,
2399
- "tool_ids": tool_ids,
2400
- "tool_rules": tool_rules,
2401
- "include_base_tools": include_base_tools,
2402
- "include_multi_agent_tools": include_multi_agent_tools,
2403
- "include_base_tool_rules": include_base_tool_rules,
2404
- "system": system,
2405
- "agent_type": agent_type,
2406
- "llm_config": llm_config if llm_config else self._default_llm_config,
2407
- "embedding_config": embedding_config if embedding_config else self._default_embedding_config,
2408
- "initial_message_sequence": initial_message_sequence,
2409
- "tags": tags,
2410
- "message_buffer_autoclear": message_buffer_autoclear,
2411
- "response_format": response_format,
2412
- }
2413
-
2414
- # Only add name if it's not None
2415
- if name is not None:
2416
- create_params["name"] = name
2417
-
2418
- agent_state = self.server.create_agent(
2419
- CreateAgent(**create_params),
2420
- actor=self.user,
2421
- )
2422
-
2423
- # TODO: get full agent state
2424
- return self.server.agent_manager.get_agent_by_id(agent_state.id, actor=self.user)
2425
-
2426
- def update_agent(
2427
- self,
2428
- agent_id: str,
2429
- name: Optional[str] = None,
2430
- description: Optional[str] = None,
2431
- system: Optional[str] = None,
2432
- tool_ids: Optional[List[str]] = None,
2433
- tags: Optional[List[str]] = None,
2434
- metadata: Optional[Dict] = None,
2435
- llm_config: Optional[LLMConfig] = None,
2436
- embedding_config: Optional[EmbeddingConfig] = None,
2437
- message_ids: Optional[List[str]] = None,
2438
- response_format: Optional[ResponseFormatUnion] = None,
2439
- ):
2440
- """
2441
- Update an existing agent
2442
-
2443
- Args:
2444
- agent_id (str): ID of the agent
2445
- name (str): Name of the agent
2446
- description (str): Description of the agent
2447
- system (str): System configuration
2448
- tools (List[str]): List of tools
2449
- metadata (Dict): Metadata
2450
- llm_config (LLMConfig): LLM configuration
2451
- embedding_config (EmbeddingConfig): Embedding configuration
2452
- message_ids (List[str]): List of message IDs
2453
- tags (List[str]): Tags for filtering agents
2454
-
2455
- Returns:
2456
- agent_state (AgentState): State of the updated agent
2457
- """
2458
- # TODO: add the ability to reset linked block_ids
2459
- self.interface.clear()
2460
- agent_state = self.server.agent_manager.update_agent(
2461
- agent_id,
2462
- UpdateAgent(
2463
- name=name,
2464
- system=system,
2465
- tool_ids=tool_ids,
2466
- tags=tags,
2467
- description=description,
2468
- metadata=metadata,
2469
- llm_config=llm_config,
2470
- embedding_config=embedding_config,
2471
- message_ids=message_ids,
2472
- response_format=response_format,
2473
- ),
2474
- actor=self.user,
2475
- )
2476
- return agent_state
2477
-
2478
- def get_tools_from_agent(self, agent_id: str) -> List[Tool]:
2479
- """
2480
- Get tools from an existing agent.
2481
-
2482
- Args:
2483
- agent_id (str): ID of the agent
2484
-
2485
- Returns:
2486
- List[Tool]: A list of Tool objs
2487
- """
2488
- self.interface.clear()
2489
- return self.server.agent_manager.get_agent_by_id(agent_id=agent_id, actor=self.user).tools
2490
-
2491
- def attach_tool(self, agent_id: str, tool_id: str) -> AgentState:
2492
- """
2493
- Add tool to an existing agent
2494
-
2495
- Args:
2496
- agent_id (str): ID of the agent
2497
- tool_id (str): A tool id
2498
-
2499
- Returns:
2500
- agent_state (AgentState): State of the updated agent
2501
- """
2502
- self.interface.clear()
2503
- agent_state = self.server.agent_manager.attach_tool(agent_id=agent_id, tool_id=tool_id, actor=self.user)
2504
- return agent_state
2505
-
2506
- def detach_tool(self, agent_id: str, tool_id: str) -> AgentState:
2507
- """
2508
- Removes tools from an existing agent
2509
-
2510
- Args:
2511
- agent_id (str): ID of the agent
2512
- tool_id (str): The tool id
2513
-
2514
- Returns:
2515
- agent_state (AgentState): State of the updated agent
2516
- """
2517
- self.interface.clear()
2518
- agent_state = self.server.agent_manager.detach_tool(agent_id=agent_id, tool_id=tool_id, actor=self.user)
2519
- return agent_state
2520
-
2521
- def rename_agent(self, agent_id: str, new_name: str) -> AgentState:
2522
- """
2523
- Rename an agent
2524
-
2525
- Args:
2526
- agent_id (str): ID of the agent
2527
- new_name (str): New name for the agent
2528
-
2529
- Returns:
2530
- agent_state (AgentState): State of the updated agent
2531
- """
2532
- return self.update_agent(agent_id, name=new_name)
2533
-
2534
- def delete_agent(self, agent_id: str) -> None:
2535
- """
2536
- Delete an agent
2537
-
2538
- Args:
2539
- agent_id (str): ID of the agent to delete
2540
- """
2541
- self.server.agent_manager.delete_agent(agent_id=agent_id, actor=self.user)
2542
-
2543
- def get_agent_by_name(self, agent_name: str) -> AgentState:
2544
- """
2545
- Get an agent by its name
2546
-
2547
- Args:
2548
- agent_name (str): Name of the agent
2549
-
2550
- Returns:
2551
- agent_state (AgentState): State of the agent
2552
- """
2553
- self.interface.clear()
2554
- return self.server.agent_manager.get_agent_by_name(agent_name=agent_name, actor=self.user)
2555
-
2556
- def get_agent(self, agent_id: str) -> AgentState:
2557
- """
2558
- Get an agent's state by its ID.
2559
-
2560
- Args:
2561
- agent_id (str): ID of the agent
2562
-
2563
- Returns:
2564
- agent_state (AgentState): State representation of the agent
2565
- """
2566
- self.interface.clear()
2567
- return self.server.agent_manager.get_agent_by_id(agent_id=agent_id, actor=self.user)
2568
-
2569
- def get_agent_id(self, agent_name: str) -> Optional[str]:
2570
- """
2571
- Get the ID of an agent by name (names are unique per user)
2572
-
2573
- Args:
2574
- agent_name (str): Name of the agent
2575
-
2576
- Returns:
2577
- agent_id (str): ID of the agent
2578
- """
2579
-
2580
- self.interface.clear()
2581
- assert agent_name, f"Agent name must be provided"
2582
-
2583
- # TODO: Refactor this futher to not have downstream users expect Optionals - this should just error
2584
- try:
2585
- return self.server.agent_manager.get_agent_by_name(agent_name=agent_name, actor=self.user).id
2586
- except NoResultFound:
2587
- return None
2588
-
2589
- # memory
2590
- def get_in_context_memory(self, agent_id: str) -> Memory:
2591
- """
2592
- Get the in-context (i.e. core) memory of an agent
2593
-
2594
- Args:
2595
- agent_id (str): ID of the agent
2596
-
2597
- Returns:
2598
- memory (Memory): In-context memory of the agent
2599
- """
2600
- memory = self.server.get_agent_memory(agent_id=agent_id, actor=self.user)
2601
- return memory
2602
-
2603
- def get_core_memory(self, agent_id: str) -> Memory:
2604
- return self.get_in_context_memory(agent_id)
2605
-
2606
- def update_in_context_memory(self, agent_id: str, section: str, value: Union[List[str], str]) -> Memory:
2607
- """
2608
- Update the in-context memory of an agent
2609
-
2610
- Args:
2611
- agent_id (str): ID of the agent
2612
-
2613
- Returns:
2614
- memory (Memory): The updated in-context memory of the agent
2615
-
2616
- """
2617
- # TODO: implement this (not sure what it should look like)
2618
- memory = self.server.update_agent_core_memory(agent_id=agent_id, label=section, value=value, actor=self.user)
2619
- return memory
2620
-
2621
- def get_archival_memory_summary(self, agent_id: str) -> ArchivalMemorySummary:
2622
- """
2623
- Get a summary of the archival memory of an agent
2624
-
2625
- Args:
2626
- agent_id (str): ID of the agent
2627
-
2628
- Returns:
2629
- summary (ArchivalMemorySummary): Summary of the archival memory
2630
-
2631
- """
2632
- return self.server.get_archival_memory_summary(agent_id=agent_id, actor=self.user)
2633
-
2634
- def get_recall_memory_summary(self, agent_id: str) -> RecallMemorySummary:
2635
- """
2636
- Get a summary of the recall memory of an agent
2637
-
2638
- Args:
2639
- agent_id (str): ID of the agent
2640
-
2641
- Returns:
2642
- summary (RecallMemorySummary): Summary of the recall memory
2643
- """
2644
- return self.server.get_recall_memory_summary(agent_id=agent_id, actor=self.user)
2645
-
2646
- def get_in_context_messages(self, agent_id: str) -> List[Message]:
2647
- """
2648
- Get in-context messages of an agent
2649
-
2650
- Args:
2651
- agent_id (str): ID of the agent
2652
-
2653
- Returns:
2654
- messages (List[Message]): List of in-context messages
2655
- """
2656
- return self.server.agent_manager.get_in_context_messages(agent_id=agent_id, actor=self.user)
2657
-
2658
- # agent interactions
2659
-
2660
- def send_messages(
2661
- self,
2662
- agent_id: str,
2663
- messages: List[Union[Message | MessageCreate]],
2664
- ):
2665
- """
2666
- Send pre-packed messages to an agent.
2667
-
2668
- Args:
2669
- agent_id (str): ID of the agent
2670
- messages (List[Union[Message | MessageCreate]]): List of messages to send
2671
-
2672
- Returns:
2673
- response (LettaResponse): Response from the agent
2674
- """
2675
- self.interface.clear()
2676
- usage = self.server.send_messages(actor=self.user, agent_id=agent_id, input_messages=messages)
2677
-
2678
- # format messages
2679
- return LettaResponse(messages=messages, usage=usage)
2680
-
2681
- def send_message(
2682
- self,
2683
- message: str,
2684
- role: str,
2685
- name: Optional[str] = None,
2686
- agent_id: Optional[str] = None,
2687
- agent_name: Optional[str] = None,
2688
- stream_steps: bool = False,
2689
- stream_tokens: bool = False,
2690
- ) -> LettaResponse:
2691
- """
2692
- Send a message to an agent
2693
-
2694
- Args:
2695
- message (str): Message to send
2696
- role (str): Role of the message
2697
- agent_id (str): ID of the agent
2698
- name(str): Name of the sender
2699
- stream (bool): Stream the response (default: `False`)
2700
-
2701
- Returns:
2702
- response (LettaResponse): Response from the agent
2703
- """
2704
- if not agent_id:
2705
- # lookup agent by name
2706
- assert agent_name, f"Either agent_id or agent_name must be provided"
2707
- agent_id = self.get_agent_id(agent_name=agent_name)
2708
- assert agent_id, f"Agent with name {agent_name} not found"
2709
-
2710
- if stream_steps or stream_tokens:
2711
- # TODO: implement streaming with stream=True/False
2712
- raise NotImplementedError
2713
- self.interface.clear()
2714
-
2715
- usage = self.server.send_messages(
2716
- actor=self.user,
2717
- agent_id=agent_id,
2718
- input_messages=[MessageCreate(role=MessageRole(role), content=message, name=name)],
2719
- )
2720
-
2721
- ## TODO: need to make sure date/timestamp is propely passed
2722
- ## TODO: update self.interface.to_list() to return actual Message objects
2723
- ## here, the message objects will have faulty created_by timestamps
2724
- # messages = self.interface.to_list()
2725
- # for m in messages:
2726
- # assert isinstance(m, Message), f"Expected Message object, got {type(m)}"
2727
- # letta_messages = []
2728
- # for m in messages:
2729
- # letta_messages += m.to_letta_messages()
2730
- # return LettaResponse(messages=letta_messages, usage=usage)
2731
-
2732
- # format messages
2733
- messages = self.interface.to_list()
2734
- letta_messages = []
2735
- for m in messages:
2736
- letta_messages += m.to_letta_messages()
2737
-
2738
- return LettaResponse(messages=letta_messages, usage=usage)
2739
-
2740
- def user_message(self, agent_id: str, message: str) -> LettaResponse:
2741
- """
2742
- Send a message to an agent as a user
2743
-
2744
- Args:
2745
- agent_id (str): ID of the agent
2746
- message (str): Message to send
2747
-
2748
- Returns:
2749
- response (LettaResponse): Response from the agent
2750
- """
2751
- self.interface.clear()
2752
- return self.send_message(role="user", agent_id=agent_id, message=message)
2753
-
2754
- def run_command(self, agent_id: str, command: str) -> LettaResponse:
2755
- """
2756
- Run a command on the agent
2757
-
2758
- Args:
2759
- agent_id (str): The agent ID
2760
- command (str): The command to run
2761
-
2762
- Returns:
2763
- LettaResponse: The response from the agent
2764
-
2765
- """
2766
- self.interface.clear()
2767
- usage = self.server.run_command(user_id=self.user_id, agent_id=agent_id, command=command)
2768
-
2769
- # NOTE: messages/usage may be empty, depending on the command
2770
- return LettaResponse(messages=self.interface.to_list(), usage=usage)
2771
-
2772
- # archival memory
2773
-
2774
- # humans / personas
2775
-
2776
- def get_block_id(self, name: str, label: str) -> str | None:
2777
- return None
2778
-
2779
- def create_human(self, name: str, text: str):
2780
- """
2781
- Create a human block template (saved human string to pre-fill `ChatMemory`)
2782
-
2783
- Args:
2784
- name (str): Name of the human block
2785
- text (str): Text of the human block
2786
-
2787
- Returns:
2788
- human (Human): Human block
2789
- """
2790
- return self.server.block_manager.create_or_update_block(Human(template_name=name, value=text), actor=self.user)
2791
-
2792
- def create_persona(self, name: str, text: str):
2793
- """
2794
- Create a persona block template (saved persona string to pre-fill `ChatMemory`)
2795
-
2796
- Args:
2797
- name (str): Name of the persona block
2798
- text (str): Text of the persona block
2799
-
2800
- Returns:
2801
- persona (Persona): Persona block
2802
- """
2803
- return self.server.block_manager.create_or_update_block(Persona(template_name=name, value=text), actor=self.user)
2804
-
2805
- def list_humans(self):
2806
- """
2807
- List available human block templates
2808
-
2809
- Returns:
2810
- humans (List[Human]): List of human blocks
2811
- """
2812
- return []
2813
-
2814
- def list_personas(self) -> List[Persona]:
2815
- """
2816
- List available persona block templates
2817
-
2818
- Returns:
2819
- personas (List[Persona]): List of persona blocks
2820
- """
2821
- return []
2822
-
2823
- def update_human(self, human_id: str, text: str):
2824
- """
2825
- Update a human block template
2826
-
2827
- Args:
2828
- human_id (str): ID of the human block
2829
- text (str): Text of the human block
2830
-
2831
- Returns:
2832
- human (Human): Updated human block
2833
- """
2834
- return self.server.block_manager.update_block(
2835
- block_id=human_id, block_update=UpdateHuman(value=text, is_template=True), actor=self.user
2836
- )
2837
-
2838
- def update_persona(self, persona_id: str, text: str):
2839
- """
2840
- Update a persona block template
2841
-
2842
- Args:
2843
- persona_id (str): ID of the persona block
2844
- text (str): Text of the persona block
2845
-
2846
- Returns:
2847
- persona (Persona): Updated persona block
2848
- """
2849
- return self.server.block_manager.update_block(
2850
- block_id=persona_id, block_update=UpdatePersona(value=text, is_template=True), actor=self.user
2851
- )
2852
-
2853
- def get_persona(self, id: str) -> Persona:
2854
- """
2855
- Get a persona block template
2856
-
2857
- Args:
2858
- id (str): ID of the persona block
2859
-
2860
- Returns:
2861
- persona (Persona): Persona block
2862
- """
2863
- assert id, f"Persona ID must be provided"
2864
- return Persona(**self.server.block_manager.get_block_by_id(id, actor=self.user).model_dump())
2865
-
2866
- def get_human(self, id: str) -> Human:
2867
- """
2868
- Get a human block template
2869
-
2870
- Args:
2871
- id (str): ID of the human block
2872
-
2873
- Returns:
2874
- human (Human): Human block
2875
- """
2876
- assert id, f"Human ID must be provided"
2877
- return Human(**self.server.block_manager.get_block_by_id(id, actor=self.user).model_dump())
2878
-
2879
- def get_persona_id(self, name: str) -> str | None:
2880
- """
2881
- Get the ID of a persona block template
2882
-
2883
- Args:
2884
- name (str): Name of the persona block
2885
-
2886
- Returns:
2887
- id (str): ID of the persona block
2888
- """
2889
- return None
2890
-
2891
- def get_human_id(self, name: str) -> str | None:
2892
- """
2893
- Get the ID of a human block template
2894
-
2895
- Args:
2896
- name (str): Name of the human block
2897
-
2898
- Returns:
2899
- id (str): ID of the human block
2900
- """
2901
- return None
2902
-
2903
- def delete_persona(self, id: str):
2904
- """
2905
- Delete a persona block template
2906
-
2907
- Args:
2908
- id (str): ID of the persona block
2909
- """
2910
- self.delete_block(id)
2911
-
2912
- def delete_human(self, id: str):
2913
- """
2914
- Delete a human block template
2915
-
2916
- Args:
2917
- id (str): ID of the human block
2918
- """
2919
- self.delete_block(id)
2920
-
2921
- # tools
2922
- def load_langchain_tool(self, langchain_tool: "LangChainBaseTool", additional_imports_module_attr_map: dict[str, str] = None) -> Tool:
2923
- tool_create = ToolCreate.from_langchain(
2924
- langchain_tool=langchain_tool,
2925
- additional_imports_module_attr_map=additional_imports_module_attr_map,
2926
- )
2927
- return self.server.tool_manager.create_or_update_langchain_tool(tool_create=tool_create, actor=self.user)
2928
-
2929
- def load_composio_tool(self, action: "ActionType") -> Tool:
2930
- tool_create = ToolCreate.from_composio(action_name=action.name)
2931
- return self.server.tool_manager.create_or_update_composio_tool(tool_create=tool_create, actor=self.user)
2932
-
2933
- def create_tool(
2934
- self,
2935
- func,
2936
- tags: Optional[List[str]] = None,
2937
- description: Optional[str] = None,
2938
- return_char_limit: int = FUNCTION_RETURN_CHAR_LIMIT,
2939
- ) -> Tool:
2940
- """
2941
- Create a tool. This stores the source code of function on the server, so that the server can execute the function and generate an OpenAI JSON schemas for it when using with an agent.
2942
-
2943
- Args:
2944
- func (callable): The function to create a tool for.
2945
- tags (Optional[List[str]], optional): Tags for the tool. Defaults to None.
2946
- description (str, optional): The description.
2947
- return_char_limit (int): The character limit for the tool's return value. Defaults to FUNCTION_RETURN_CHAR_LIMIT.
2948
-
2949
- Returns:
2950
- tool (Tool): The created tool.
2951
- """
2952
- # TODO: check if tool already exists
2953
- # TODO: how to load modules?
2954
- # parse source code/schema
2955
- source_code = parse_source_code(func)
2956
- source_type = "python"
2957
- name = func.__name__ # Initialize name using function's __name__
2958
- if not tags:
2959
- tags = []
2960
-
2961
- # call server function
2962
- return self.server.tool_manager.create_tool(
2963
- Tool(
2964
- source_type=source_type,
2965
- source_code=source_code,
2966
- name=name,
2967
- tags=tags,
2968
- description=description,
2969
- return_char_limit=return_char_limit,
2970
- ),
2971
- actor=self.user,
2972
- )
2973
-
2974
- def create_or_update_tool(
2975
- self,
2976
- func,
2977
- tags: Optional[List[str]] = None,
2978
- description: Optional[str] = None,
2979
- return_char_limit: int = FUNCTION_RETURN_CHAR_LIMIT,
2980
- ) -> Tool:
2981
- """
2982
- Creates or updates a tool. This stores the source code of function on the server, so that the server can execute the function and generate an OpenAI JSON schemas for it when using with an agent.
2983
-
2984
- Args:
2985
- func (callable): The function to create a tool for.
2986
- tags (Optional[List[str]], optional): Tags for the tool. Defaults to None.
2987
- description (str, optional): The description.
2988
- return_char_limit (int): The character limit for the tool's return value. Defaults to FUNCTION_RETURN_CHAR_LIMIT.
2989
-
2990
- Returns:
2991
- tool (Tool): The created tool.
2992
- """
2993
- source_code = parse_source_code(func)
2994
- source_type = "python"
2995
- if not tags:
2996
- tags = []
2997
-
2998
- # call server function
2999
- return self.server.tool_manager.create_or_update_tool(
3000
- Tool(
3001
- source_type=source_type,
3002
- source_code=source_code,
3003
- tags=tags,
3004
- description=description,
3005
- return_char_limit=return_char_limit,
3006
- ),
3007
- actor=self.user,
3008
- )
3009
-
3010
- def update_tool(
3011
- self,
3012
- id: str,
3013
- description: Optional[str] = None,
3014
- func: Optional[Callable] = None,
3015
- tags: Optional[List[str]] = None,
3016
- return_char_limit: int = FUNCTION_RETURN_CHAR_LIMIT,
3017
- ) -> Tool:
3018
- """
3019
- Update a tool with provided parameters (name, func, tags)
3020
-
3021
- Args:
3022
- id (str): ID of the tool
3023
- func (callable): Function to wrap in a tool
3024
- tags (List[str]): Tags for the tool
3025
- return_char_limit (int): The character limit for the tool's return value. Defaults to FUNCTION_RETURN_CHAR_LIMIT.
3026
-
3027
- Returns:
3028
- tool (Tool): Updated tool
3029
- """
3030
- update_data = {
3031
- "source_type": "python", # Always include source_type
3032
- "source_code": parse_source_code(func) if func else None,
3033
- "tags": tags,
3034
- "description": description,
3035
- "return_char_limit": return_char_limit,
3036
- }
3037
-
3038
- # Filter out any None values from the dictionary
3039
- update_data = {key: value for key, value in update_data.items() if value is not None}
3040
-
3041
- return self.server.tool_manager.update_tool_by_id(tool_id=id, tool_update=ToolUpdate(**update_data), actor=self.user)
3042
-
3043
- def list_tools(self, after: Optional[str] = None, limit: Optional[int] = 50) -> List[Tool]:
3044
- """
3045
- List available tools for the user.
3046
-
3047
- Returns:
3048
- tools (List[Tool]): List of tools
3049
- """
3050
- # Get the current event loop or create a new one if there isn't one
3051
- try:
3052
- loop = asyncio.get_event_loop()
3053
- if loop.is_running():
3054
- # We're in an async context but can't await - use a new loop via run_coroutine_threadsafe
3055
- concurrent_future = asyncio.run_coroutine_threadsafe(
3056
- self.server.tool_manager.list_tools_async(actor=self.user, after=after, limit=limit), loop
3057
- )
3058
- return concurrent_future.result()
3059
- else:
3060
- # We have a loop but it's not running - we can just run the coroutine
3061
- return loop.run_until_complete(self.server.tool_manager.list_tools_async(actor=self.user, after=after, limit=limit))
3062
- except RuntimeError:
3063
- # No running event loop - create a new one with asyncio.run
3064
- return asyncio.run(self.server.tool_manager.list_tools_async(actor=self.user, after=after, limit=limit))
3065
-
3066
- def get_tool(self, id: str) -> Optional[Tool]:
3067
- """
3068
- Get a tool given its ID.
3069
-
3070
- Args:
3071
- id (str): ID of the tool
3072
-
3073
- Returns:
3074
- tool (Tool): Tool
3075
- """
3076
- return self.server.tool_manager.get_tool_by_id(id, actor=self.user)
3077
-
3078
- def delete_tool(self, id: str):
3079
- """
3080
- Delete a tool given the ID.
3081
-
3082
- Args:
3083
- id (str): ID of the tool
3084
- """
3085
- return self.server.tool_manager.delete_tool_by_id(id, actor=self.user)
3086
-
3087
- def get_tool_id(self, name: str) -> Optional[str]:
3088
- """
3089
- Get the ID of a tool from its name. The client will use the org_id it is configured with.
3090
-
3091
- Args:
3092
- name (str): Name of the tool
3093
-
3094
- Returns:
3095
- id (str): ID of the tool (`None` if not found)
3096
- """
3097
- tool = self.server.tool_manager.get_tool_by_name(tool_name=name, actor=self.user)
3098
- return tool.id if tool else None
3099
-
3100
- def list_attached_tools(self, agent_id: str) -> List[Tool]:
3101
- """
3102
- List all tools attached to an agent.
3103
-
3104
- Args:
3105
- agent_id (str): ID of the agent
3106
-
3107
- Returns:
3108
- List[Tool]: List of tools attached to the agent
3109
- """
3110
- return self.server.agent_manager.list_attached_tools(agent_id=agent_id, actor=self.user)
3111
-
3112
- def load_data(self, connector: DataConnector, source_name: str):
3113
- """
3114
- Load data into a source
3115
-
3116
- Args:
3117
- connector (DataConnector): Data connector
3118
- source_name (str): Name of the source
3119
- """
3120
- self.server.load_data(user_id=self.user_id, connector=connector, source_name=source_name)
3121
-
3122
- def load_file_to_source(self, filename: str, source_id: str, blocking=True):
3123
- """
3124
- Load a file into a source
3125
-
3126
- Args:
3127
- filename (str): Name of the file
3128
- source_id (str): ID of the source
3129
- blocking (bool): Block until the job is complete
3130
-
3131
- Returns:
3132
- job (Job): Data loading job including job status and metadata
3133
- """
3134
- job = Job(
3135
- user_id=self.user_id,
3136
- status=JobStatus.created,
3137
- metadata={"type": "embedding", "filename": filename, "source_id": source_id},
3138
- )
3139
- job = self.server.job_manager.create_job(pydantic_job=job, actor=self.user)
3140
-
3141
- # TODO: implement blocking vs. non-blocking
3142
- self.server.load_file_to_source(source_id=source_id, file_path=filename, job_id=job.id, actor=self.user)
3143
- return job
3144
-
3145
- def delete_file_from_source(self, source_id: str, file_id: str) -> None:
3146
- self.server.source_manager.delete_file(file_id, actor=self.user)
3147
-
3148
- def get_job(self, job_id: str):
3149
- return self.server.job_manager.get_job_by_id(job_id=job_id, actor=self.user)
3150
-
3151
- def delete_job(self, job_id: str):
3152
- return self.server.job_manager.delete_job_by_id(job_id=job_id, actor=self.user)
3153
-
3154
- def list_jobs(self):
3155
- return self.server.job_manager.list_jobs(actor=self.user)
3156
-
3157
- def list_active_jobs(self):
3158
- return self.server.job_manager.list_jobs(actor=self.user, statuses=[JobStatus.created, JobStatus.running])
3159
-
3160
- def create_source(self, name: str, embedding_config: Optional[EmbeddingConfig] = None) -> Source:
3161
- """
3162
- Create a source
3163
-
3164
- Args:
3165
- name (str): Name of the source
3166
-
3167
- Returns:
3168
- source (Source): Created source
3169
- """
3170
- assert embedding_config or self._default_embedding_config, f"Must specify embedding_config for source"
3171
- source = Source(
3172
- name=name, embedding_config=embedding_config or self._default_embedding_config, organization_id=self.user.organization_id
3173
- )
3174
- return self.server.source_manager.create_source(source=source, actor=self.user)
3175
-
3176
- def delete_source(self, source_id: str):
3177
- """
3178
- Delete a source
3179
-
3180
- Args:
3181
- source_id (str): ID of the source
3182
- """
3183
-
3184
- # TODO: delete source data
3185
- self.server.delete_source(source_id=source_id, actor=self.user)
3186
-
3187
- def get_source(self, source_id: str) -> Source:
3188
- """
3189
- Get a source given the ID.
3190
-
3191
- Args:
3192
- source_id (str): ID of the source
3193
-
3194
- Returns:
3195
- source (Source): Source
3196
- """
3197
- return self.server.source_manager.get_source_by_id(source_id=source_id, actor=self.user)
3198
-
3199
- def get_source_id(self, source_name: str) -> str:
3200
- """
3201
- Get the ID of a source
3202
-
3203
- Args:
3204
- source_name (str): Name of the source
3205
-
3206
- Returns:
3207
- source_id (str): ID of the source
3208
- """
3209
- return self.server.source_manager.get_source_by_name(source_name=source_name, actor=self.user).id
3210
-
3211
- def attach_source(self, agent_id: str, source_id: Optional[str] = None, source_name: Optional[str] = None) -> AgentState:
3212
- """
3213
- Attach a source to an agent
3214
-
3215
- Args:
3216
- agent_id (str): ID of the agent
3217
- source_id (str): ID of the source
3218
- source_name (str): Name of the source
3219
- """
3220
- if source_name:
3221
- source = self.server.source_manager.get_source_by_id(source_id=source_id, actor=self.user)
3222
- source_id = source.id
3223
-
3224
- return self.server.agent_manager.attach_source(source_id=source_id, agent_id=agent_id, actor=self.user)
3225
-
3226
- def detach_source(self, agent_id: str, source_id: Optional[str] = None, source_name: Optional[str] = None) -> AgentState:
3227
- """
3228
- Detach a source from an agent by removing all `Passage` objects that were loaded from the source from archival memory.
3229
- Args:
3230
- agent_id (str): ID of the agent
3231
- source_id (str): ID of the source
3232
- source_name (str): Name of the source
3233
- Returns:
3234
- source (Source): Detached source
3235
- """
3236
- if source_name:
3237
- source = self.server.source_manager.get_source_by_id(source_id=source_id, actor=self.user)
3238
- source_id = source.id
3239
- return self.server.agent_manager.detach_source(agent_id=agent_id, source_id=source_id, actor=self.user)
3240
-
3241
- def list_sources(self) -> List[Source]:
3242
- """
3243
- List available sources
3244
-
3245
- Returns:
3246
- sources (List[Source]): List of sources
3247
- """
3248
-
3249
- return self.server.list_all_sources(actor=self.user)
3250
-
3251
- def list_attached_sources(self, agent_id: str) -> List[Source]:
3252
- """
3253
- List sources attached to an agent
3254
-
3255
- Args:
3256
- agent_id (str): ID of the agent
3257
-
3258
- Returns:
3259
- sources (List[Source]): List of sources
3260
- """
3261
- return self.server.agent_manager.list_attached_sources(agent_id=agent_id, actor=self.user)
3262
-
3263
- def list_files_from_source(self, source_id: str, limit: int = 1000, after: Optional[str] = None) -> List[FileMetadata]:
3264
- """
3265
- List files from source.
3266
-
3267
- Args:
3268
- source_id (str): ID of the source
3269
- limit (int): The # of items to return
3270
- after (str): The cursor for fetching the next page
3271
-
3272
- Returns:
3273
- files (List[FileMetadata]): List of files
3274
- """
3275
- return self.server.source_manager.list_files(source_id=source_id, limit=limit, after=after, actor=self.user)
3276
-
3277
- def update_source(self, source_id: str, name: Optional[str] = None) -> Source:
3278
- """
3279
- Update a source
3280
-
3281
- Args:
3282
- source_id (str): ID of the source
3283
- name (str): Name of the source
3284
-
3285
- Returns:
3286
- source (Source): Updated source
3287
- """
3288
- # TODO should the arg here just be "source_update: Source"?
3289
- request = SourceUpdate(name=name)
3290
- return self.server.source_manager.update_source(source_id=source_id, source_update=request, actor=self.user)
3291
-
3292
- # archival memory
3293
-
3294
- def insert_archival_memory(self, agent_id: str, memory: str) -> List[Passage]:
3295
- """
3296
- Insert archival memory into an agent
3297
-
3298
- Args:
3299
- agent_id (str): ID of the agent
3300
- memory (str): Memory string to insert
3301
-
3302
- Returns:
3303
- passages (List[Passage]): List of inserted passages
3304
- """
3305
- return self.server.insert_archival_memory(agent_id=agent_id, memory_contents=memory, actor=self.user)
3306
-
3307
- def delete_archival_memory(self, agent_id: str, memory_id: str):
3308
- """
3309
- Delete archival memory from an agent
3310
-
3311
- Args:
3312
- agent_id (str): ID of the agent
3313
- memory_id (str): ID of the memory
3314
- """
3315
- self.server.delete_archival_memory(memory_id=memory_id, actor=self.user)
3316
-
3317
- def get_archival_memory(
3318
- self, agent_id: str, before: Optional[str] = None, after: Optional[str] = None, limit: Optional[int] = 1000
3319
- ) -> List[Passage]:
3320
- """
3321
- Get archival memory from an agent with pagination.
3322
-
3323
- Args:
3324
- agent_id (str): ID of the agent
3325
- before (str): Get memories before a certain time
3326
- after (str): Get memories after a certain time
3327
- limit (int): Limit number of memories
3328
-
3329
- Returns:
3330
- passages (List[Passage]): List of passages
3331
- """
3332
-
3333
- return self.server.get_agent_archival(user_id=self.user_id, agent_id=agent_id, limit=limit)
3334
-
3335
- # recall memory
3336
-
3337
- def get_messages(
3338
- self, agent_id: str, before: Optional[str] = None, after: Optional[str] = None, limit: Optional[int] = 1000
3339
- ) -> List[LettaMessage]:
3340
- """
3341
- Get messages from an agent with pagination.
3342
-
3343
- Args:
3344
- agent_id (str): ID of the agent
3345
- before (str): Get messages before a certain time
3346
- after (str): Get messages after a certain time
3347
- limit (int): Limit number of messages
3348
-
3349
- Returns:
3350
- messages (List[Message]): List of messages
3351
- """
3352
-
3353
- self.interface.clear()
3354
- return self.server.get_agent_recall(
3355
- user_id=self.user_id,
3356
- agent_id=agent_id,
3357
- before=before,
3358
- after=after,
3359
- limit=limit,
3360
- reverse=True,
3361
- return_message_object=False,
3362
- )
3363
-
3364
- def list_blocks(self, label: Optional[str] = None, templates_only: Optional[bool] = True) -> List[Block]:
3365
- """
3366
- List available blocks
3367
-
3368
- Args:
3369
- label (str): Label of the block
3370
- templates_only (bool): List only templates
3371
-
3372
- Returns:
3373
- blocks (List[Block]): List of blocks
3374
- """
3375
- return []
3376
-
3377
- def create_block(
3378
- self, label: str, value: str, limit: Optional[int] = None, template_name: Optional[str] = None, is_template: bool = False
3379
- ) -> Block: #
3380
- """
3381
- Create a block
3382
-
3383
- Args:
3384
- label (str): Label of the block
3385
- name (str): Name of the block
3386
- text (str): Text of the block
3387
- limit (int): Character of the block
3388
-
3389
- Returns:
3390
- block (Block): Created block
3391
- """
3392
- block = Block(label=label, template_name=template_name, value=value, is_template=is_template)
3393
- if limit:
3394
- block.limit = limit
3395
- return self.server.block_manager.create_or_update_block(block, actor=self.user)
3396
-
3397
- def update_block(self, block_id: str, name: Optional[str] = None, text: Optional[str] = None, limit: Optional[int] = None) -> Block:
3398
- """
3399
- Update a block
3400
-
3401
- Args:
3402
- block_id (str): ID of the block
3403
- name (str): Name of the block
3404
- text (str): Text of the block
3405
-
3406
- Returns:
3407
- block (Block): Updated block
3408
- """
3409
- return self.server.block_manager.update_block(
3410
- block_id=block_id,
3411
- block_update=BlockUpdate(template_name=name, value=text, limit=limit if limit else self.get_block(block_id).limit),
3412
- actor=self.user,
3413
- )
3414
-
3415
- def get_block(self, block_id: str) -> Block:
3416
- """
3417
- Get a block
3418
-
3419
- Args:
3420
- block_id (str): ID of the block
3421
-
3422
- Returns:
3423
- block (Block): Block
3424
- """
3425
- return self.server.block_manager.get_block_by_id(block_id, actor=self.user)
3426
-
3427
- def delete_block(self, id: str) -> Block:
3428
- """
3429
- Delete a block
3430
-
3431
- Args:
3432
- id (str): ID of the block
3433
-
3434
- Returns:
3435
- block (Block): Deleted block
3436
- """
3437
- return self.server.block_manager.delete_block(id, actor=self.user)
3438
-
3439
- def set_default_llm_config(self, llm_config: LLMConfig):
3440
- """
3441
- Set the default LLM configuration for agents.
3442
-
3443
- Args:
3444
- llm_config (LLMConfig): LLM configuration
3445
- """
3446
- self._default_llm_config = llm_config
3447
-
3448
- def set_default_embedding_config(self, embedding_config: EmbeddingConfig):
3449
- """
3450
- Set the default embedding configuration for agents.
3451
-
3452
- Args:
3453
- embedding_config (EmbeddingConfig): Embedding configuration
3454
- """
3455
- self._default_embedding_config = embedding_config
3456
-
3457
- def list_llm_configs(self) -> List[LLMConfig]:
3458
- """
3459
- List available LLM configurations
3460
-
3461
- Returns:
3462
- configs (List[LLMConfig]): List of LLM configurations
3463
- """
3464
- return self.server.list_llm_models(actor=self.user)
3465
-
3466
- def list_embedding_configs(self) -> List[EmbeddingConfig]:
3467
- """
3468
- List available embedding configurations
3469
-
3470
- Returns:
3471
- configs (List[EmbeddingConfig]): List of embedding configurations
3472
- """
3473
- return self.server.list_embedding_models(actor=self.user)
3474
-
3475
- def create_org(self, name: Optional[str] = None) -> Organization:
3476
- return self.server.organization_manager.create_organization(pydantic_org=Organization(name=name))
3477
-
3478
- def list_orgs(self, after: Optional[str] = None, limit: Optional[int] = 50) -> List[Organization]:
3479
- return self.server.organization_manager.list_organizations(limit=limit, after=after)
3480
-
3481
- def delete_org(self, org_id: str) -> Organization:
3482
- return self.server.organization_manager.delete_organization_by_id(org_id=org_id)
3483
-
3484
- def create_sandbox_config(self, config: Union[LocalSandboxConfig, E2BSandboxConfig]) -> SandboxConfig:
3485
- """
3486
- Create a new sandbox configuration.
3487
- """
3488
- config_create = SandboxConfigCreate(config=config)
3489
- return self.server.sandbox_config_manager.create_or_update_sandbox_config(sandbox_config_create=config_create, actor=self.user)
3490
-
3491
- def update_sandbox_config(self, sandbox_config_id: str, config: Union[LocalSandboxConfig, E2BSandboxConfig]) -> SandboxConfig:
3492
- """
3493
- Update an existing sandbox configuration.
3494
- """
3495
- sandbox_update = SandboxConfigUpdate(config=config)
3496
- return self.server.sandbox_config_manager.update_sandbox_config(
3497
- sandbox_config_id=sandbox_config_id, sandbox_update=sandbox_update, actor=self.user
3498
- )
3499
-
3500
- def delete_sandbox_config(self, sandbox_config_id: str) -> None:
3501
- """
3502
- Delete a sandbox configuration.
3503
- """
3504
- return self.server.sandbox_config_manager.delete_sandbox_config(sandbox_config_id=sandbox_config_id, actor=self.user)
3505
-
3506
- def list_sandbox_configs(self, limit: int = 50, after: Optional[str] = None) -> List[SandboxConfig]:
3507
- """
3508
- List all sandbox configurations.
3509
- """
3510
- return self.server.sandbox_config_manager.list_sandbox_configs(actor=self.user, limit=limit, after=after)
3511
-
3512
- def create_sandbox_env_var(
3513
- self, sandbox_config_id: str, key: str, value: str, description: Optional[str] = None
3514
- ) -> SandboxEnvironmentVariable:
3515
- """
3516
- Create a new environment variable for a sandbox configuration.
3517
- """
3518
- env_var_create = SandboxEnvironmentVariableCreate(key=key, value=value, description=description)
3519
- return self.server.sandbox_config_manager.create_sandbox_env_var(
3520
- env_var_create=env_var_create, sandbox_config_id=sandbox_config_id, actor=self.user
3521
- )
3522
-
3523
- def update_sandbox_env_var(
3524
- self, env_var_id: str, key: Optional[str] = None, value: Optional[str] = None, description: Optional[str] = None
3525
- ) -> SandboxEnvironmentVariable:
3526
- """
3527
- Update an existing environment variable.
3528
- """
3529
- env_var_update = SandboxEnvironmentVariableUpdate(key=key, value=value, description=description)
3530
- return self.server.sandbox_config_manager.update_sandbox_env_var(
3531
- env_var_id=env_var_id, env_var_update=env_var_update, actor=self.user
3532
- )
3533
-
3534
- def delete_sandbox_env_var(self, env_var_id: str) -> None:
3535
- """
3536
- Delete an environment variable by its ID.
3537
- """
3538
- return self.server.sandbox_config_manager.delete_sandbox_env_var(env_var_id=env_var_id, actor=self.user)
3539
-
3540
- def list_sandbox_env_vars(
3541
- self, sandbox_config_id: str, limit: int = 50, after: Optional[str] = None
3542
- ) -> List[SandboxEnvironmentVariable]:
3543
- """
3544
- List all environment variables associated with a sandbox configuration.
3545
- """
3546
- return self.server.sandbox_config_manager.list_sandbox_env_vars(
3547
- sandbox_config_id=sandbox_config_id, actor=self.user, limit=limit, after=after
3548
- )
3549
-
3550
- def update_agent_memory_block_label(self, agent_id: str, current_label: str, new_label: str) -> Memory:
3551
- """Rename a block in the agent's core memory
3552
-
3553
- Args:
3554
- agent_id (str): The agent ID
3555
- current_label (str): The current label of the block
3556
- new_label (str): The new label of the block
3557
-
3558
- Returns:
3559
- memory (Memory): The updated memory
3560
- """
3561
- block = self.get_agent_memory_block(agent_id, current_label)
3562
- return self.update_block(block.id, label=new_label)
3563
-
3564
- def get_agent_memory_blocks(self, agent_id: str) -> List[Block]:
3565
- """
3566
- Get all the blocks in the agent's core memory
3567
-
3568
- Args:
3569
- agent_id (str): The agent ID
3570
-
3571
- Returns:
3572
- blocks (List[Block]): The blocks in the agent's core memory
3573
- """
3574
- agent = self.server.agent_manager.get_agent_by_id(agent_id=agent_id, actor=self.user)
3575
- return agent.memory.blocks
3576
-
3577
- def get_agent_memory_block(self, agent_id: str, label: str) -> Block:
3578
- """
3579
- Get a block in the agent's core memory by its label
3580
-
3581
- Args:
3582
- agent_id (str): The agent ID
3583
- label (str): The label in the agent's core memory
3584
-
3585
- Returns:
3586
- block (Block): The block corresponding to the label
3587
- """
3588
- return self.server.agent_manager.get_block_with_label(agent_id=agent_id, block_label=label, actor=self.user)
3589
-
3590
- def update_agent_memory_block(
3591
- self,
3592
- agent_id: str,
3593
- label: str,
3594
- value: Optional[str] = None,
3595
- limit: Optional[int] = None,
3596
- ):
3597
- """
3598
- Update a block in the agent's core memory by specifying its label
3599
-
3600
- Args:
3601
- agent_id (str): The agent ID
3602
- label (str): The label of the block
3603
- value (str): The new value of the block
3604
- limit (int): The new limit of the block
3605
-
3606
- Returns:
3607
- block (Block): The updated block
3608
- """
3609
- block = self.get_agent_memory_block(agent_id, label)
3610
- data = {}
3611
- if value:
3612
- data["value"] = value
3613
- if limit:
3614
- data["limit"] = limit
3615
- return self.server.block_manager.update_block(block.id, actor=self.user, block_update=BlockUpdate(**data))
3616
-
3617
- def update_block(
3618
- self,
3619
- block_id: str,
3620
- label: Optional[str] = None,
3621
- value: Optional[str] = None,
3622
- limit: Optional[int] = None,
3623
- ):
3624
- """
3625
- Update a block given the ID with the provided fields
3626
-
3627
- Args:
3628
- block_id (str): ID of the block
3629
- label (str): Label to assign to the block
3630
- value (str): Value to assign to the block
3631
- limit (int): Token limit to assign to the block
3632
-
3633
- Returns:
3634
- block (Block): Updated block
3635
- """
3636
- data = {}
3637
- if value:
3638
- data["value"] = value
3639
- if limit:
3640
- data["limit"] = limit
3641
- if label:
3642
- data["label"] = label
3643
- return self.server.block_manager.update_block(block_id, actor=self.user, block_update=BlockUpdate(**data))
3644
-
3645
- def attach_block(self, agent_id: str, block_id: str) -> AgentState:
3646
- """
3647
- Attach a block to an agent.
3648
-
3649
- Args:
3650
- agent_id (str): ID of the agent
3651
- block_id (str): ID of the block to attach
3652
- """
3653
- return self.server.agent_manager.attach_block(agent_id=agent_id, block_id=block_id, actor=self.user)
3654
-
3655
- def detach_block(self, agent_id: str, block_id: str) -> AgentState:
3656
- """
3657
- Detach a block from an agent.
3658
-
3659
- Args:
3660
- agent_id (str): ID of the agent
3661
- block_id (str): ID of the block to detach
3662
- """
3663
- return self.server.agent_manager.detach_block(agent_id=agent_id, block_id=block_id, actor=self.user)
3664
-
3665
- def get_run_messages(
3666
- self,
3667
- run_id: str,
3668
- before: Optional[str] = None,
3669
- after: Optional[str] = None,
3670
- limit: Optional[int] = 100,
3671
- ascending: bool = True,
3672
- role: Optional[MessageRole] = None,
3673
- ) -> List[LettaMessageUnion]:
3674
- """
3675
- Get messages associated with a job with filtering options.
3676
-
3677
- Args:
3678
- run_id: ID of the run
3679
- before: Cursor for pagination
3680
- after: Cursor for pagination
3681
- limit: Maximum number of messages to return
3682
- ascending: Sort order by creation time
3683
- role: Filter by message role (user/assistant/system/tool)
3684
- Returns:
3685
- List of messages matching the filter criteria
3686
- """
3687
- params = {
3688
- "before": before,
3689
- "after": after,
3690
- "limit": limit,
3691
- "ascending": ascending,
3692
- "role": role,
3693
- }
3694
-
3695
- return self.server.job_manager.get_run_messages(run_id=run_id, actor=self.user, **params)
3696
-
3697
- def get_run_usage(
3698
- self,
3699
- run_id: str,
3700
- ) -> List[UsageStatistics]:
3701
- """
3702
- Get usage statistics associated with a job.
3703
-
3704
- Args:
3705
- run_id (str): ID of the run
3706
-
3707
- Returns:
3708
- List[UsageStatistics]: List of usage statistics associated with the run
3709
- """
3710
- usage = self.server.job_manager.get_job_usage(job_id=run_id, actor=self.user)
3711
- return [
3712
- UsageStatistics(completion_tokens=stat.completion_tokens, prompt_tokens=stat.prompt_tokens, total_tokens=stat.total_tokens)
3713
- for stat in usage
3714
- ]
3715
-
3716
- def get_run(self, run_id: str) -> Run:
3717
- """
3718
- Get a run by ID.
3719
-
3720
- Args:
3721
- run_id (str): ID of the run
3722
-
3723
- Returns:
3724
- run (Run): Run
3725
- """
3726
- return self.server.job_manager.get_job_by_id(job_id=run_id, actor=self.user)
3727
-
3728
- def delete_run(self, run_id: str) -> None:
3729
- """
3730
- Delete a run by ID.
3731
-
3732
- Args:
3733
- run_id (str): ID of the run
3734
- """
3735
- return self.server.job_manager.delete_job_by_id(job_id=run_id, actor=self.user)
3736
-
3737
- def list_runs(self) -> List[Run]:
3738
- """
3739
- List all runs.
3740
-
3741
- Returns:
3742
- runs (List[Run]): List of runs
3743
- """
3744
- return self.server.job_manager.list_jobs(actor=self.user, job_type=JobType.RUN)
3745
-
3746
- def list_active_runs(self) -> List[Run]:
3747
- """
3748
- List all active runs.
3749
-
3750
- Returns:
3751
- runs (List[Run]): List of active runs
3752
- """
3753
- return self.server.job_manager.list_jobs(actor=self.user, job_type=JobType.RUN, statuses=[JobStatus.created, JobStatus.running])
3754
-
3755
- def get_tags(
3756
- self,
3757
- after: Optional[str] = None,
3758
- limit: Optional[int] = None,
3759
- query_text: Optional[str] = None,
3760
- ) -> List[str]:
3761
- """
3762
- Get all tags.
3763
-
3764
- Returns:
3765
- tags (List[str]): List of tags
3766
- """
3767
- return self.server.agent_manager.list_tags(actor=self.user, after=after, limit=limit, query_text=query_text)